Cloud KMS とは
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 を有効にする
- 課金が有効になっているプロジェクトで Cloud KMS API を有効に する。
KeyRing を作成する
- GCP Console > Security > Cryptographic Keys を開く
- [Create Key Ring] をクリック
- Key ring name を入力し Key ring location を選択して [Create]
- KeyRing は、Key をグルーピングする粒度の名前
- KeyRing Location は基本 global で良いらしいが、公式ドキュメント を見るとパフォーマンスを考慮するなら特定region(サービスを提供する地域)を選択せよとのこと
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" ) // KMSClient is a client to access Cloud KMS type KMSClient struct { keypath string service *cloudkms.ProjectsLocationsKeyRingsCryptoKeysService } // KMSOptions means the options for constructor type KMSOptions struct { ProjectID, Location, KeyRing, CryptoKey string } // NewKMSClient makes KMS client 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 } // Encrypt encrypts plaintext 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 } // Decrypt decrypts ciphertext 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 }