Serverless勢朗報!GCPの新しいSecret Manager

Secret Managerを使ったServerlessの構築をします。

January 23, 2020
serverless cloud functions secret manager

Secret Managerを使ったアーキテクチャ図

こんにちは。つい2時間ほど前にIntroducing Google Cloud’s Secret Managerという投稿でSecret ManagerがPre-Release Stageになったことが報告されました。 居ても立ってもいられず、自分のプロダクトのDev版に実装し、検証をしました。

このプロダクトはServerless界隈では強く求められていたのではないかなと思っています。 その理由と、今回のSecret Managerを使った構築例を紹介しようと思います。しかしまだasia-northeastがないのが残念な点で、プロダクションに使うことを検討している方はしばらく辛抱が必要です。

また運用という観点にも注目したいと思っています。(アイコンがCloud Functionsにそっくり…)

Secret Managerのアイコン

Serverlessで求められていた理由

Serverlessのデメリットとして「コールドスタート」が有名なのではないかと思っています。ゼロスケールをするからこそ、逆に言うと「ゼロから作る必要がある」ということです。 そもそもServerlessは起動が遅く、同期的なアプリケーションには適していません。

しかしながら、イベント数に対してインスタンスはスケールをして、線形な課金がされるようなServerlessの理想の姿にできる可能性があります。つまりユースケースに応じてServerlessが有用であり、実際に私自身もServerlessで比較的、低コストでサービスを運用しています。

Secret ManagerがServerlessで求められている理由を知るには以下の点を留意・注意する必要があります。

1. Secretは環境変数ではない

これは当たり前ですが、Serverlessプロダクトとの相性が悪い概念なので常に注意する必要があります。 

Serverlessと特徴として「ビジネスロジックに集中できる」がよく取り上げられますが、Securtity的な保証は自分がしなければなりません。 

例えばデータベースへ接続するServerlessアプリケーションがあったとしましょう。この場合、以下のものが大抵の場合は必要ですがSecretと環境変数に分けることができます。

  • データベースのURL
  • データベースのPort
  • データベースのUser名
  • データベースのPassword <<==これだけSecret

PasswordだけがSecret = 漏洩してはいけないものなのです。または漏洩しても影響がない形にしなければなりません。

Cloud Runではデプロイ時に以下のように環境変数を渡すことができます。

$ gcloud run deploy $_DEPLOYMENT_NAME --platform=managed \
--allow-unauthenticated \
--region=asia-northeast1 \
--image=$_IMAGE \
--service-account=$_CLOUD_RUN_SERVICE_ACCOUNT
--set-env-vars DATABASE_PW=DATABASE_PW,DATABASE_USEL=$_DATABASE_USER

Cloud Functionsではデプロイ時に以下のようにファイルを指定します。

$ gcloud functions deploy $_FUNCTION_NAME --runtime nodejs10 --env-vars-file=.env.yml

もちろんCloud Runのように--set-env-varsと指定して渡すこともできます。

Securityを犠牲にしてEnvと同じ用にSecretも関数へ渡しているケースをよく見かけます。これらの情報はCloud RunやCloud FunctionsのUI上でViewer権限があれば誰でもRaw Stringで見ることができてしまいます。

Cloud RunでSecretが露出する様子
Cloud FunctionsでSecretが露出する様子

これらの対策としてSealed Secretを実装したとします。次の問題が起きます。

2. Cloud KMSのAvailability SLOとレイテンシ

ここでのSealed Secretはアプリケーション内で復号化してSecretをさします。

Cloud KMSとServerlessは相性が悪い

Sealed Secretを使っていてCloud RunなどのSLOが99.9%だったとします。Cloud KMSのSLOが99.5%と非常に低いため、Cloud KMSでやりたいことはSealed Secretの復号化なのに使っているServerlessのSLOをかなり下回るプロダクトができてしまうこともあります。

またCloud KMSはレイテンシが大きく、Serverlessのようなイベント駆動型のアプリケーションで使うことを想定していません。そのため復号化を何回も頻繁に行うのは望ましくなく、GCEインスタンスのようにSealed Secretをインスタンス起動時に復号化するというアプリケーションの方が適しています。

1.と2.のことを踏まえServerlessではCloud KMSを使ったSealed Secretがうまく行かず、アプリケーションで復号化をするようなアプローチを取れなかった背景があります。

Secret Managerの登場

このような背景がある中、本日Secret Managerが登場しました。

用語を先に抑えるとドキュメントが読みやすくなります。

  • Secret Version: バージョンごとの実際のデータ。5など指定するときとlatestがある。
  • Secret Name: Secret Versionを一意に特定するためのパス。例えばprojects/my-project/secrets/my-secret/versions/5のようなものである

Secret VersionにどんどんSecretを格納することになります。Versionはシーケンシャルに番号が振られます。 アプリケーションからSecretを生成することよりも読み取ることの方が多いと思うので以下にサンプルを載せておきます。

const {SecretManagerServiceClient} = require('@google-cloud/secret-manager');
const client = new SecretManagerServiceClient();

async function accessSecretVersion() {
  const [version] = await client.accessSecretVersion({
    name: name,
  });

  const payload = version.payload.data.toString('utf8');
  console.info(`Payload: ${payload}`);
}

accessSecretVersion();

このようにアクセスをすることで環境変数で渡す必要のあるものはSecret Nameぐらいになります。 それは漏洩してもリソースへのアクセス権限がなければSecretを取得することはできないため安全です。

簡単でシンプルです。

アーキテクチャ

アーキテクチャはシンプルです。Cloud Functionsからアクセスをすればよいです。

Secret Managerを使ったアーキテクチャ図

運用の観点で

いくつかの観点でSecret Managerが使えるのかどうかを見たいと思います。

料金体系

以下のような料金体系になっています。参照: Pricing

対象 値段
オペレーション 10000回につき0.03ドル
アクティブなSecretのバージョン 0.06ドル/アクテイブなSecret バージョン/リージョナル レプリカ数

料金を抑えるためにはリージョン数を絞るのは現実的かもしれません。

クオータ

ここが大きな注意点です。以下のようなクオータが設定されています。

対象 クオータ
読み取りリクエスト プロジェクトにつき毎分600
アクセスリクエスト プロジェクトにつき毎分90000
書き込みリクエスト プロジェクトにつき毎分600  

書き込みについては毎分600回を超えることはないと思いますが、大規模なアプリケーションで使用する場合アクセスリクエストはアプリケーションに対して注意深く見る必要があると思います。

Cloud IAM

Secret ManagerのためのRoleとして以下のようなものがあります。

Secretを取得するだけにはroles/secretmanager.secretAccessorで十分です。

Secretの管理

以下のようにSecretをSealed Secretとしてアップロードをして、CI内のジョブとしてSecretをアップロードする必要性があると思います。

Secretを管理するためのCI

そしてアプリケーションからアクセスをする仕組みづくりが必要なのではないかなと思っています。 もちろんSecretなのでTerraform管理をするわけにもいかないし、なにか策がほしいものです…

Secretの内容

Secretは単純なStringではなく、ファイルとしてアップロードできます。つまりJSONでもYAMLでも、どんなファイルでもSecretとしてプログラムからアクセスができるためアプリケーションに合わせて柔軟に使っていくことができます。

UI上で権限があると中身を見ることもできるので、権限管理には注意が必要です。

UI上でSecretの中身を見る

Latency

LatencyはCloud KMSほどではありませんがローカル環境からAPIを叩いても約1秒程度レイテンシが毎回発生していました。

$ time node get-secret.js

Serverlessが非同期的なAPIとして有用であることと、約1秒程度なら許容できる(Cloud KMSは5秒などかかっていたこともあった)のでいいとしましょう。 Serverlessの場合も一般的なServerと同様に実行時間によって課金されるので注意してください。

まとめ

Secret Managerが登場して、ServerlessプロダクトでのSecret管理が非常に楽になりました。 UI上でSecretが露出してしまうことがなくなり、より安全に運用できていると思います。

しかしながら、どのようにして継続的に管理をしていくか考える必要があります。現状はGithubでの復号化して、それをアップロードするというSecretをアップロードステップと、プロダクトのデプロイステップの2つのステップがあります。時間があったらSecret Manager用のアップロードツールを作ろうと思っています。

Secret Managerのようなものが出てくるおかげでSecretの管理もし易くすむのでよりServerlessのメリットがより活かせそうだなと感じました。 最後までありがとうございました。