アクトインディ開発者ブログ

子供とお出かけ情報「いこーよ」を運営する、アクトインディ株式会社の開発者ブログです

Severlessの変数にSSMパラメータストアを使って秘密情報を分離する

morishitaです。

アクトインディでは AWS Lambda を利用しています。

Alexaスキル「いこーよのおでかけナビ」はプロダクトそのものが Lambda 関数ですし、 いこレポでは CI に Lambda を活用しています。

それらは Serverless Framework を使ってビルドしたりデプロイしています。 SSM のパラメータストアと組み合わせて使ってみたのでそれについて書きます。

秘密情報とコードを分離したい

Lambda 関数内で利用する各種 API のキーや認証情報はコードに書き込んでリポジトリに入れたくはありません。 そういう値は Lambda には環境変数で渡すようにしています。

Serverless Framework では、Lambda の環境変数を設定できます。 その機能を使ってデプロイとともに環境変数も設定しています。 しかし、Serverless Framework の設定ファイル serverless.ymlにその値を書き込んでしまうと、 今度は、serverless.ymlをリポジトリに入れにくくなってしまいます。

serverless.yml内で使える変数の機能は外部ファイルを読み込むことができるのでリポジトリに入れたくない値は設定ファイルにして、 その設定ファイルは .gitignoreに加えたりしていました。

ただ、プロダクトには通常複数の人が関わるので、リポジトリに入れないファイルを別途やり取りする必要があるなど共有しづらく管理が面倒です。

serverless.yml の変数の機能とは?

前述したように Serverless Framework の設定ファイルであるserverless.ymlでは変数が利用できます。

変数は次の値を参照できます。

  • serverlessコマンドの実行環境の環境変数
  • serverlessコマンドのオプション
  • 他のファイル(YML や JSON,JavaScript ファイル)
  • S3
  • SSM パラメータストア

変数によりデプロイ先を変更したり、Lambda に渡す環境変数の値をプロダクションと開発環境で別々にするのに使えとても便利です。

パラメータストアって?

SSM とは AWS Systems Managerのことです。 SSM は「インフラストラクチャを可視化し、制御するためのサービスです」と謳っており、AWS 上のリソースのみならずオンプレのサーバもエージェントをインストールすれば管理できてしまうというサービスです。 その中に秘密データと設定データをコードから分離するためのサービスとして位置づけられているのが、パラメータストアです。 パラメータストアを利用すれば、例えばデータベースの接続先のようなプレーンテキストデータや パスワードのような秘密データなどをコードから分離して管理できます。

パラメータストアに秘密情報を預けてしまい、アプリケーションからはそれを参照するようにすると 秘密情報をコードから分離し、その共有の煩わしさからも解放されそうです。

パラメータストアの作成

では、実際にやってみましょう。

SSM パラメータストアは SSM の一機能ではありますが、独立しておりインフラ全体を SSM で管理していなくても単体で使えるサービスです。

AWS コンソールのパラメータ作成画面は次のようになっています。

f:id:HeRo:20180905054804p:plain

名前には/が使えるので例のようにプロダクト名やステージ名を含めるようなルールを設計して運用するのが良いでしょう。

タイプで安全な文字列を選択すると KMS キーを利用して暗号化されます。 後述しますが、暗号化してもあんまりセキュリティ強度は変わらないと思うのでどちらでもいいと思います。 安全な文字列を利用場合の KMS のキーは影響範囲を各プロダクト内に閉じ込めるためにプロダクト毎に分けておくのが良いでしょう1。 今回は安全な文字列を選択するとして説明します。

serverless.ymlから参照

パラメータストアから値を読むためのssm:GetParametersや、暗号化した値を復号するkms:Decryptなどの権限を serverless.ymlprovider.iamRoleStatementsに追加しなければならないと思いきや必要ありません。 というのもデプロイ時に取得、復号されるようで、デプロイに利用する IAM2が権限を持っていれば良いようです。

serverless.yml で参照するには次のSOME_API_KEYの値の様に記述します。

functions:
  handler:
    handler: index.handler
    environment:
      SOME_API_KEY: ${ssm:/productName/${self:provider.stage}/paramName~true}

SSM パラメータストアで暗号化している場合には末尾に~trueを付けておきます。 ${self:provider.stage}という変数を更に内包させていますが、こうしておくとステージごとに参照するパラメータを切り替えられます。 ステージングとプロダクションでAPIキー等が異なる場合、こうして切り替えられます。

Lambda 関数に設定する環境変数の値として利用する場合はちょっと注意が必要です。 というのも、暗号化した値がデプロイ時に復号されるからです3。つまり、Lambda のコンソールを見れば復号された値が見えてしまうのです。 SSM パラメータストアで暗号化しても強度的にあまり変わらないと書いたのはこういう理由です。 Lambda のコンソールでも見えるとまずいというセキュリティポリシーの場合には別の手を考える必要があるでしょう4

AWS のコンソールで Lambda を参照できるのは権限のある人間だけで、 その人間は Lambda が使う API 等のクレデンシャル情報を見てもよいというのなら、問題ないでしょう。

まとめ

  • Serverless Framework の変数管理にパラメータストアは便利
  • Lambda の環境変数の設定に利用する場合には注意が必要
  • リポジトリに入れない秘密情報をどこに置く問題を解決できる

最後に

アクトインディではエンジニアを募集しています。


  1. KMS キーの管理は IAM の「暗号化キー」というメニューの中にあります。グロバールサービスである IAM の中にありながら「暗号化キー」はリージョンに依存しています。うっかりしているとハマるので注意。

  2. Serverless は AdministratorAccess を持つアカウントで使うようにドキュメントには書かれています。結構強力なので権限を絞りたければ、Narrowing the Serverless IAM Deployment Policy · Issue #1439 · serverless/serverlessを参考に調整する必要があります。

  3. 変数を使ってデプロイ先まで変更できるので、デプロイ時に復号するのは当然といえば当然でしょう。

  4. KMS を利用して暗号化した値を環境変数に設定し、その値をLambdaのコード内で復号する必要があります。serverless-kms-secretsを使うと便利かな。