LoginSignup
4
2

More than 3 years have passed since last update.

aws-sdk-goを通してlocal-kmsを使う

Posted at

この記事を書いた経緯

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を用いた暗号・復号化を可能とします

localkms.go
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

マルチステージビルドの書き方が望ましいですが、とりあえず簡素に作ってみます

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ディレクトリを作成します

go.mod
module src/localkms

go 1.13

require github.com/aws/aws-sdk-go v1.25.14-0.20200528180948-645efefb5bce

ソースの修正

コンテナの中からはlocalhostでlocal-kmsを参照できなくなるので
WithEndPointの記述を下記のように変更します(コンテナ名を記載することでそのコンテナのIPアドレスにリクエストが飛ぶようになります)

19行目
- 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エンコードされて出力されます。

local-kmsを使ったencryptの一例
$ 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デコードする必要があります

local-kmsを使ったdecryptの一例
$ aws kms decrypt --ciphertext-blob fileb://<(echo 'S2Fybjphd3M6a21zOmV1LXdlc3QtMjoxMTExMjIyMjMzMzM6a2V5LzIzYWVlYmJkLWNiODMtNDNhMy04ODcwLWE2ZjllZTVhNGFjZQAAAADmlRx1OQCCTk5LQoIkNseoCofCRhLXo3iPpQ2lwx8E5A==' | base64 --decode) --endpoint-url http://localhost:8080 | jq .Plaintext --raw-output | base64 --decode
hoge

decryptコマンドリファレンス

4
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
2