LoginSignup
4
7

More than 3 years have passed since last update.

GCP KMS を使って秘密情報を対称暗号化・復号化する

Last updated at Posted at 2019-10-02

概要

KMSを利用して、認証情報などの秘密を暗号化・復号化する方法を紹介します

サンプルコードと環境

サンプルコード:
「GCP で Google Calendar を CalDAV サーバに複製する」記事で紹介した バッチスクリプトのコード を例にとります。コードを動かしたい人は、この記事よりも readme.md 見てもらうと早いです。

対策前のコードでは Calendar API を叩くためにGCPサービスアカウントの鍵 credential.json ファイルを認証情報として利用しますが、暗号化せずコードに同梱しています。対策後のコードでは、KMSを利用して鍵 credential.json の内容を暗号化し、かつ暗号化した鍵をコードに含めない(Cloud Functions実行時に環境変数で与える)ことでセキュリティを向上しています。

環境:

灰色アローは手作業、青アローはプログラム処理です。
image.png

  1. 生JSON鍵を得てKMSで対称暗号化し、バイナリ暗号鍵を得る
  2. バイナリ暗号鍵をASCII化し、Cloud Functions の環境変数にセットする
  3. コードはgit、暗号鍵は環境変数をソースとして、Cloud Functions上にデプロイする
  4. Cloud Functions上のスクリプトは、暗号鍵をKMS APIで復号化する

GKE KeyRingを作る

暗号鍵メニューから、キーリングを適当な名前で作ります。今回は、今後も増えるだろうサービスアカウントのクレデンシャルをたくさんぶら下げるためのキーリング名にしました。
image.png

GKE Keyを作る

キーリングにぶら下げるキーを作ります。Calendar APIアクセス用サービスアカウントのJSON形式のクレデンシャルである事を端的に示すキー名で作りました。目的は対称暗号化/復号化です。
image.png
サービスアカウントは強い権限(プロジェクトの管理者)を持たせる事が多いと思いますが、ローテーションを自動でかけて同じ暗号を使い続けないように設定する事もできます。
image.png

秘密を暗号化する

作業に先立って、生クレデンシャルJSONファイルは、非公開のStorage private-bucket を介してアップロードします。

暗号化はCloudShellで作業をします。サービスアカウントのJSON形式生クレデンシャルcredentials.jsonを暗号化して、バイナリファイルcredentials.json.enc を得ます。

$ gsutil cp gs://private-bucket/credentials.json ./
$ gcloud kms encrypt \
    --plaintext-file=./credentials.json \
    --ciphertext-file=credentials.json.enc \
    --location=global \
    --keyring=service-account-credentials \
    --key=gcal-api-json-cred \
    && rm ./credentials.json

暗号化文字列を得る

アプリケーションで取り扱いやすい様にPythonでcredentials.json.encを文字列に変換します。

$ python
>>> import base64, io
>>> with io.open('credentials.json.enc', 'rb') as f:
>>>     base64.b64encode(f.read()).decode('ascii')
QOsAErqw....bI=   # 暗号化されたクレデンシャル文字列
[1]+  Stopped                 python
$ exit
logout
There are stopped jobs.
$

秘密を復号化する

次のようなコードでもって、復号化して使います。

import json
from base64 import b64decode
from google.oauth2 import service_account
from googleapiclient.discovery import build

def decrypt_credectial():
    kms_client = build('cloudkms', 'v1')
    key_path = 'projects/{pj}/locations/{loc}/keyRings/{key_ring}/cryptoKeys/{key}'.format(
        pj='GoogleのプロジェクトID',
        loc='KMSキーリングのロケーション。本例だとglobal',
        key_ring='KMSキーリング名',
        key='KMSキー名')
    crypto_keys = kms_client.projects().locations().keyRings().cryptoKeys()
    kms_request = crypto_keys.decrypt(name=key_path, body={'ciphertext': '暗号化クレデンシャル'})
    kms_response = kms_request.execute()
    return json.loads(b64decode(kms_response['plaintext'].encode('ascii')))

creds = service_account.Credentials.from_service_account_info(
    decrypt_credectial(),
    scopes=[
        'https://www.googleapis.com/auth/calendar.events.readonly',
        'https://www.googleapis.com/auth/calendar.readonly'
    ]
).with_subject('username@gsuite-domain')

前記事のコードと比べてこのような違いがあります。生クレデンシャルを文字列やファイルとしてソースコードに含める必要が無くなって、安心感が出ました。

4
7
1

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
7