この記事を書いた経緯
aws-sdk-goを用いた案件ソース中にkmsを用いた暗号・復号化の実装があり
kmsという概念すらわからなかった私が、ローカル環境での検証に四苦八苦した時の覚書を認めたものです
コマンドだけ知りたい人へ
local-kmsコンテナを立ち上げる
そのままdev環境・本番環境のKMSに作られている鍵を使おうとしても、IAMの認証をかけられてしまうので
権限のないローカル環境では暗号化・復号操作を行うことができません。
ローカル環境においてkmsを用いた処理をデバッグしたい場合、local-kmsというdocker imageを用いて実現することが可能です。
他コンテナとポートが重複する場合は、環境変数PORT
を用いることで競合を回避できます
$ docker run -p 8080:8081 -e PORT=8081 nsmithuk/local-kms
aws configureの作成
local-kmsを使用する際も通常のインスタンスを用いる場合と同様にクレデンシャル情報が参照されますが
適当なダミーデータで問題ありません
すでに何かしらのconfigureがローカルに作成されていればスルーで問題ないと思います
$ aws configure
AWS Access Key ID [None]: dummy
AWS Secret Access Key [None]: dummy
Default region name [None]: ap-northeast-1
Default output format [None]:
create-key コマンドによりCMKを作成する
暗号・復号化に必要な、いわゆるCMK(Customer Master Key)と呼ばれるものを作成します。
この時出力されるAccountID・KeyIDなどは適当なダミー値になります
$ aws kms create-key --endpoint-url http://localhost:8080
{
"KeyMetadata": {
"AWSAccountId": "111122223333",
"KeyId": "23aeebbd-cb83-43a3-8870-a6f9ee5a4ace",
"Arn": "arn:aws:kms:eu-west-2:111122223333:key/23aeebbd-cb83-43a3-8870-a6f9ee5a4ace",
"CreationDate": 1590853469,
"Enabled": true,
"KeyUsage": "ENCRYPT_DECRYPT",
"KeyState": "Enabled",
"Origin": "AWS_KMS",
"KeyManager": "CUSTOMER",
"CustomerMasterKeySpec": "SYMMETRIC_DEFAULT",
"EncryptionAlgorithms": [
"SYMMETRIC_DEFAULT"
]
}
}
ここで出力されたKeyID
を用いて、kms encryptを実行します
aws-sdk-goからlocal-kmsを介してkms encryptとdecryptを使う
aws-sdk-goのこちらのドキュメントに記載されているサンプルソースを拝借し、若干加筆しました。
WithEndPoint()
へlocal-kmsのコンテナのアドレスを持たせ、WithDisableSSL(true)
を使ってhttpへのアクセスを前提とさせたものを
aws.NewConfig()へメソッドチェーンさせることで、local-kmsを用いた暗号・復号化を可能とします
package main
import (
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/kms"
"fmt"
"os"
)
func main() {
sess, err := session.NewSession()
if err != nil {
fmt.Println("aws newsession error: ", err)
os.Exit(1)
}
svc := kms.New(sess, aws.NewConfig().WithRegion("ap-northeast-1").WithEndpoint("localhost:8080").WithDisableSSL(true))
keyId := "arn:aws:kms:eu-west-2:111122223333:key/23aeebbd-cb83-43a3-8870-a6f9ee5a4ace"
text := "1234567890"
//kms encrypt
encResult, err := svc.Encrypt(&kms.EncryptInput{
KeyId: aws.String(keyId),
Plaintext: []byte(text),
})
if err != nil {
fmt.Println("got error encrypting data: ", err)
os.Exit(1)
}
//kms decrypt
decResult, err := svc.Decrypt(&kms.DecryptInput{
CiphertextBlob: encResult.CiphertextBlob,
})
if err != nil {
fmt.Println("got error decrypting data: ", err)
os.Exit(1)
}
fmt.Println("decoded Plaintext:")
fmt.Println(string(decResult.Plaintext))
}
ソースのビルド
$ go build localkms.go
ソースの実行
$ go run localkms.go
decoded Plaintext: 1234567890
Dockerfileでgoのソースをビルドしたい場合
このような階層の簡素なプロジェクトがあることを想定します
tree -L 3 localkms/
localkms/
├── Dockerfile
└── src
├── go.mod
├── go.sum
├── localkms
├── localkms.go
└── vendor
├── github.com
└── modules.txt
DockerFile
マルチステージビルドの書き方が望ましいですが、とりあえず簡素に作ってみます
FROM golang:1.13.0
ADD ./src /go/src/local-kms
WORKDIR /go/src/local-kms
RUN cd /go/src/local-kms
ENV AWS_ACCESS_KEY_ID=dummy
ENV AWS_SECRET_ACCESS_KEY=dummy
RUN go build -mod=vendor localkms.go
ENTRYPOINT ["./localkms"]
go.mod
ファイルを作成後、go mod vendorを実行し、srcディレクトリの下にvendorディレクトリを作成します
module src/localkms
go 1.13
require github.com/aws/aws-sdk-go v1.25.14-0.20200528180948-645efefb5bce
ソースの修正
コンテナの中からはlocalhost
でlocal-kmsを参照できなくなるので
WithEndPointの記述を下記のように変更します(コンテナ名を記載することでそのコンテナのIPアドレスにリクエストが飛ぶようになります)
- svc := kms.New(sess, aws.NewConfig().WithRegion("ap-northeast-1").WithEndpoint("localhost:8080").WithDisableSSL(true))
+ svc := kms.New(sess, aws.NewConfig().WithRegion("ap-northeast-1").WithEndpoint("local-kms:8081").WithDisableSSL(true))
ソースのビルド
$ docker build ./ -t go-container
ソースの実行
local-kmsコンテナと連携するため--link
オプションを利用します
$ docker run --link local-kms:local-kms go-container
decoded Plaintext: 1234567890
ここから参考文献など
aws cliのインストール
aws kms コマンドの実行にはaws cliのインストールが必要となります
AWS CLI バージョン 1 のインストール
kmsとは?
データの暗号化に利用されるCMK(Customer Master Key)の作成と管理を容易にするマネージド型サービスです。
kms encryot
CMKのKeyIDを用いて平文を暗号化します
encryptコマンドリファレンス
暗号化されたテキストは、EncryptionAlgorithm
でのエンクリプションを行った結果ががさらに
base64エンコードされて出力されます。
$ aws kms encrypt --key-id arn:aws:kms:eu-west-2:111122223333:key/23aeebbd-cb83-43a3-8870-a6f9ee5a4ace --plaintext "hoge" --endpoint-url http://localhost:8080
{
"CiphertextBlob": "S2Fybjphd3M6a21zOmV1LXdlc3QtMjoxMTExMjIyMjMzMzM6a2V5LzIzYWVlYmJkLWNiODMtNDNhMy04ODcwLWE2ZjllZTVhNGFjZQAAAADmlRx1OQCCTk5LQoIkNseoCofCRhLXo3iPpQ2lwx8E5A==",
"KeyId": "arn:aws:kms:eu-west-2:111122223333:key/23aeebbd-cb83-43a3-8870-a6f9ee5a4ace",
"EncryptionAlgorithm": "SYMMETRIC_DEFAULT"
}
kms decrypt
CMKにより暗号化されたCiphertextを復号します。
encryptコマンドと異なり、この時keyIDを指定する必要はありません
(ciphertextblobに含まれているメタデータからこの情報を取り込むため、明示的に指定する必要がありません)
また、この時の復号結果の出力であるPlaintext
はbase64エンコードされて出てくるため
本来の平文を得るためにはbase64デコードする必要があります
$ aws kms decrypt --ciphertext-blob fileb://<(echo 'S2Fybjphd3M6a21zOmV1LXdlc3QtMjoxMTExMjIyMjMzMzM6a2V5LzIzYWVlYmJkLWNiODMtNDNhMy04ODcwLWE2ZjllZTVhNGFjZQAAAADmlRx1OQCCTk5LQoIkNseoCofCRhLXo3iPpQ2lwx8E5A==' | base64 --decode) --endpoint-url http://localhost:8080 | jq .Plaintext --raw-output | base64 --decode
hoge