Cargo Lambda で Lambda layer を使って Parameter Store から値を取得する
内容
AWS 公式が用意している Lambda
から Parameter Store
や Secrets Manager
から値を取得する方法を Rust
、Cargo Lambda
で説明する。
Lambda で Parameter Store などから何かシークレットを取得するとき AWS SDK でパラメーターを取得することはできる。しかし、頻繁に呼び出される Lambda の場合はクォータに引っかかり、値が取得できなくなってしまう。
シンプルに解決するなら Lambda の環境変数に突っ込んでしまえばいいのだが CloudFormation や Lambda のコンソールの画面で値が見えてしまう。組織などの運用であればこれ健全ではない。
Lambda はキャッシュすることもできるのでグローバルな環境変数に1度保存しておけば毎回リクエストすることは無くなる。しかし、パラメーターを変更したときなどのリフレッシュの頻度は制御が難しい。
以上の問題を解決する方法として AWS が用意しているのが AWS Parameters and Secrets Lambda Extension
という Lambda extensions
を設定できる Lambda layers
である。
これを実現するための手順とサンプルを示す。
AWS Parameters and Secrets Lambda Extension とは
シンプルに言えば Lambda に張り付けるとわざわざ SDK を入れなくても Parameter Store などの値を取得し、さらにキャッシュを行ってくれる。
パラメーターの取得の仕方はシンプルで localhost に対して取得したい値のキーをリクエストするだけで良い。
例えば Parameter Store に "/lambda" (1単語だと Parameter Store では / が表示されない模様) というキーで保存した値を取得したい場合、
http://localhost:2773/systemsmanager/parameters/get?name=%2Flambda
というような HTTP Request をするだけで取得できる。
header に取得しなければいけない値であったり、 ポートがデフォルトは 2773 であったり、詳細は公式ドキュメントで。ちなみに Secret Manager も Parameter Store も手順は同様だがリクエスト URL が違っている。
手順
はじめに
Lambda extensions に対しては前述通り HTTP Request をしなければならなくなる。
Cargo Lambda でやるのなら reqwest などであろう。これを愚直に追加すると OpenSSL の依存関係などで途端にビルドできなくなったり、動かなかったりする。
これに関しては別の記事を参考にされたい。
環境変数の取得
基本的には Lambda の環境変数から AWS_SESSION_TOKEN
が取得さえできればいい。サンプルではポートを変更する想定もされているが別にしなくていい。
environment | details |
---|---|
AWS_SESSION_TOKEN |
からリクエストのヘッダーに必要な値を取得する。 |
PARAMETERS_SECRETS_EXTENSION_HTTP_PORT |
Lambda extensions と喋るためのポート (デフォルトが2773) |
リクエストURLの作成
先ほど取得したポートをいれている。サンプルは Parameter Store の値を取得するもの。前述通り Secrets Manager の場合は少し URL が違うので注意。
そしてクエリパラメーターで値を設定している。
environment | details |
---|---|
name |
パラメーターストアに保存したキー。 |
withDecryption |
パラメーターストアの値が SecureString (安全な文字列) の場合は必要 |
エスケープなどが面倒なので直接 format! などで文字列を作るより reqwest に任せてしまうのがいいだろう。
withDecryption
はそもそも Parameter Store を使うのならつけるだろうか。固定の値であるならば基本的には Lambda の環境変数にしてしまったほうがレイテンシーが少なくていいはず。
レスポンス
HTTP Request のレスポンスの型を次のように定義している。
実際のレスポンスは次のようなものらしい。
実際に必要な値は ".Parameter.Value" のみなので struct の定義は絞っている。
{
"Parameter": {
"ARN": "arn:aws:ssm:us-east-1:000000000000:parameter/<PARAMETER_NAME>",
"DataType": "text",
"LastModifiedDate": "2025-01-01T00:00:00.000Z",
"Name": "<PARAMETER_NAME>",
"Selector": null,
"SourceResult": null,
"Type": "SecureString",
"Value": "<PARAMETER_VALUE>",
"Version": 1
},
"ResultMetadata": {}
}
ちなみに値がないときのレスポンスは
an unexpected error occurred while executing request
となり JSON ではないらしい。パースできないので大丈夫であろう。
リクエスト
ヘッダーに X-Aws-Parameters-Secrets-Token
というキーで最初に環境変数から取得した AWS_SESSION_TOKEN
をいれてリクエストしている。
CDK
Lambda layer
layer を AWS 公式が準備した既存の Arn から作成して Lambda に付与している。
ここで使われる AWS 公式の Lambda extensions の Arn は リージョン
と アーキテクチャー
によって異なっている。
サンプルでは us-east-1
の arm
を使っている。
ドキュメントに載っているので確認して対応するものを使う。
Lambda Policy
あと気を付ける点で言うと ssm:GetParameter
のポリシーが必要。 SecureString をデクリプトするために kms:Decrypt
も必要かもしれない。権限は適宜絞ること。
まとめ
Cargo Lambda で AWS Parameters and Secrets Lambda Extension
を使い、 Parameter Store の値を取得する手順とサンプルを紹介した。
手順は少し込み入っているが実際に慣れてしまうとコードからはただ HTTP Request を飛ばすだけなので結構楽なものである。
近年は GitHub Copilot などの利用により、ソースコードが学習されるという危険をはらんでいる。なあなあでクレデンシャルがコード上に存在するのは特に危険な状態になってきた。
秘匿しなければならない情報は正しく秘匿し、保存して安全な運用を心掛けていきたい。
Discussion