Cloud KMS とは
cloud.google.com
Cloud KMS は GCP で提供されている鍵管理サービスです。(Cloud Key Management Service )
GAE上で秘匿情報を管理したいときに Datastoreにそのまま入れるのも...
secret.yaml を作ってリポジトリ管理外にして app.yaml で include してというのも...
と思っていたら、Cloud KMS がとっくにSLA適用されていて結構使っている人が多いので触ってみることにしました。
Cloud KMS のオブジェクト階層
アクセス制御を管理するために設計された階層構造に、CryptKey(暗号鍵)を格納します。
- Project - プロジェクト
- Location - ロケーション
- KeyRing - キーリング
- Key - 鍵
- Key Version - 鍵バージョン
この構造のリソース (KeyRing, CryptKey) へのアクセスは、Cloud IAM で制御できます。
手順
※ 注意点
- KeyRing と Key は無効化はできますが削除ができないので、自分みたいに適当な KeyRing を作成してしまうとゴミが残ってしまうので気をつけましょう...
Cloud KMS API を有効にする
KeyRing を作成する
Key を作成
- KeyRing を作成したら [Create Key] クリック
- Key Name、Purpose、Rotation 等を入力して [Create]
- Rotation は、新しく Key Version が作成されるスケジュールの設定
- Rotation を設定しても古い Version の Key が使えなくなるわけではない
- 古い Key Version を無効化するには、 破棄のスケジュール登録 が別途必要になる
作成フォーム
作成後
Key を使って暗号化/復号化
Cloud KMS のクラインアントライブラリ には C#/Go/Java/Node.js/PHP/Python/Ruby が用意されてます。
Goで書くとこんなイメージ
base64でエンコード/デコードする必要があるくらいです。
import (
"encoding/base64"
"fmt"
"golang.org/x/net/context"
"golang.org/x/oauth2/google"
"google.golang.org/api/cloudkms/v1"
)
type KMSClient struct {
keypath string
service *cloudkms.ProjectsLocationsKeyRingsCryptoKeysService
}
type KMSOptions struct {
ProjectID, Location, KeyRing, CryptoKey string
}
func NewKMSClient(ctx context.Context, opt *KMSOptions) (*KMSClient, error) {
client, err := google.DefaultClient(ctx, cloudkms.CloudPlatformScope)
if err != nil {
return nil, fmt.Errorf("failed DefaultClient. error: %v", err)
}
cloudkmsService, err := cloudkms.New(client)
if err != nil {
return nil, fmt.Errorf("failed cloudkms.New. error: %v", err)
}
keyPath := fmt.Sprintf("projects/%s/locations/%s/keyRings/%s/cryptoKeys/%s",
opt.ProjectID, opt.Location, opt.KeyRing, opt.CryptoKey)
return &KMSClient{
keypath: keyPath,
service: cloudkmsService.Projects.Locations.KeyRings.CryptoKeys,
}, nil
}
func (k *KMSClient) Encrypt(plaintext string) (string, error) {
resp, err := k.service.Encrypt(k.keypath, &cloudkms.EncryptRequest{
Plaintext: base64.StdEncoding.EncodeToString([]byte(plaintext)),
}).Do()
if err != nil {
return "", fmt.Errorf("failed to encrypt. error: %v", err)
}
return resp.Ciphertext, nil
}
func (k *KMSClient) Decrypt(ciphertext string) (string, error) {
resp, err := k.service.Decrypt(k.keypath, &cloudkms.DecryptRequest{
Ciphertext: ciphertext,
}).Do()
if err != nil {
return "", fmt.Errorf("failed to ecrypt. error: %v", err)
}
text, err := base64.StdEncoding.DecodeString(resp.Plaintext)
if err != nil {
return "", fmt.Errorf("failed base64 decode. error: %v", err)
}
return string(text), nil
}