
AWS Lambdaのイベントソースマッピングのフィルタリング条件とZipデプロイパッケージがカスタマーマネージドキーによる暗号化に対応していたのでAWS SAMで実装してみた
初めに
しばらくAWS SAM(CLIではない)のリリースを見ていなかったので遡っていたところ、特定のissueに結びついてなさそうな機能追加が2つ含まれていることに気づきました。
https://github.com/aws/serverless-application-model/releases/tag/v1.90.0
feat: Support KmsKeyArn for Events
https://github.com/aws/serverless-application-model/releases/tag/v1.92.0
feat: support SourceKMSKeyArn
なんだろうと思いAWS側の機能リリースの内容と照らし合わせていたところ、どうやら2024/08頃にリリースされたイベントソースマッピング(以降ESM)のフィルタリング条件が、2024/11にZipパッケージでデプロイされたコードアーティファクトがカスタマーマネージドキー(以降CMK)で暗号化できるようになった対応をSAM側にも取り込んだもののようです。
この辺り当ブログでも触れられてなかったのとESM使ったことなかったのでSAMで実装しつつ触ってみます
ESM側についてはCDK経由ですが触れられていました。
ESMのフィルタリング条件の暗号化?
Zipパッケージアーティファクトの方はすぐにイメージできたもののESMの方にあまり馴染みがなくなんだこれ?となったのですが、Lambda関数をイベントでトリガーして呼び出す場合かつDynamoDBやSQS等一部のサービスの場合はイベントの内容にマッチした場合のみLambda関数をトリガーできるように制御できるようです。
記法もEventBridgeのルールがベースであり概念としても近いので、ご存知の方はそれのLambdaのトリガー版とイメージしてもらうのがわかりやすいと思います。
今回はこの条件文(式?)がCMKで暗号化できるようになったようです。
具体的にはこの赤枠の部分が暗号化のターゲットです。
すごいところ指定できるなと思ったんですが、実際のアプリデータの値フィルタリングしようとする場合は機密性高めの情報が入る場合もあるので需要は結構あるのかもしれません。
AWS SAMで実装してみる
SAMテンプレートを構築しSAM CLIでデプロイします。
SAM CLIとしてはESMのフィルタリング条件のCMK暗号化はv1.122.0
、ZipコードアーティファクトのCMK暗号化はv1.128.0
のタイミングで取り込んでいます。
今回は現時点で最新のバージョンであるv1.135.0
を利用し、プロジェクトは以下をベースに生成しています。
サンプルプロジェクトはSQSをトリガーにLambda関数を起動しその内容を出力するだけのシンプルな物です。
% sam --version
SAM CLI, version 1.135.0
% sam init --runtime nodejs22.x --app-template quick-start-sqs --name sqs-app
...
鍵の指定
コード側の暗号化はAWS::Serverless::Function
のProperties
直下の属性であるSourceKMSKeyArn
に指定を行います。
ESM側の暗号化はEvents配下に実装される各イベント毎の定義に実装します。種別によって異なりますがSQSの場合はその配下のKmsKeyArn
に指定すればOKです。
KmsKeyArn
Lambda が関数の環境変数の暗号化と復号に使用する AWS Key Management Service (AWS KMS) キーの ARN。
SourceKmsKeyArn
お客様の ZIP 関数コードを暗号化するために使用される KMS キー ARN を表します。
Properties
直下にもKmsKeyArn
という属性がありますがこれはLambda関数の環境変数暗号化用の鍵の指定です。
属性名は同じですがEvents
配下にあるESM向けのKmsKeyArn
とは別の指定であり、またコードアーティファクトの鍵とも別の指定ですので気をつけましょう。
なお現時点ではSourceKMSKeyArn
はKMSで全て大文字、KmsKeyArn
はKmsで先頭だけ大文字なようです。
テンプレート
上記を踏まえ実装すると以下のようになります。
AWSTemplateFormatVersion: 2010-09-09
Transform: AWS::Serverless-2016-10-31
Resources:
SimpleQueue:
Type: AWS::SQS::Queue
SQSPayloadLogger:
Type: AWS::Serverless::Function
Properties:
Description: A Lambda function that logs the payload of messages sent to an associated SQS queue.
Runtime: nodejs22.x
Architectures:
- arm64
Handler: src/handlers/sqs-payload-logger.sqsPayloadLoggerHandler
SourceKMSKeyArn: !GetAtt MyKey.Arn
Environment:
Variables:
Env: Dev
Events:
SQSQueueEvent:
Type: SQS
Properties:
Queue: !GetAtt SimpleQueue.Arn
KmsKeyArn: !GetAtt MyKey.Arn
FilterCriteria:
Filters:
- Pattern: '{"messageAttributes": {"Enabled": {"stringValue" : ["True"] }}}'
MemorySize: 128
Timeout: 25
Policies:
- AWSLambdaBasicExecutionRole
MyKey:
Type: AWS::KMS::Key
Properties:
KeyPolicy:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
AWS: !Sub arn:aws:iam::${AWS::AccountId}:root
Action:
- kms:*
Resource: '*'
- Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action:
- kms:Decrypt
- kms:GenerateDataKey
Resource: '*'
こちらをsam deploy
でデプロイしリソースを生成します。
CMKによる暗号化の確認
今回生成したキーに対するDenyポリシーをユーザ側に定義し確認できないようにしてみます。
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "Statement1",
"Effect": "Deny",
"Action": [
"kms:*"
],
"Resource": [
"arn:aws:kms:ap-northeast-1:xxxxx:key/xxxxx"
]
}
]
}
想定通りコードおよびイベントフィルタ条件が見えなくなってます。イベントフィルタ条件はあくまでESMの細部的な属性のためか汎用のDenyUIが埋め込まれるのではなく専用のマスク文字列で潰してくれるみたいです。
また先ほど少し触れましたが環境変数の暗号キーの設定は今回の箇所とは別の設定になるためこの状態でも確認可能です(デフォルトのAWS管理キー利用)。
ESMの動作確認
本筋ではないですがESM使ったことないのでついでに試してみます。
本当はメッセージボディで判定しようとしたのですが雑に送るとパースされたJSON文字列(プレーン文字列)をイベント情報として持つ形になります。
{
...
"body": "{\"message\": \"Im from SAM CLI\"}"
...
}
イベントフィルタ条件の場合これはJSON文字列ではなくプレーン文字列として扱われてしまう関係で、JSONの属性値一致ではなく文字列の部分一致で取る必要がありますが、なんとなく部分一致で取るのが嫌なので今回はメッセージ属性に値を埋め込んで判定する形としました。
条件はEnabledにTrue文字列が入った場合のみ反応するようにしています。
{
"messageAttributes": {
"Enabled": {
"stringValue" : ["True"]
}
}
}
sam remote invoke
経由でSQSにメッセージを送信します。
$ sam remote invoke SimpleQueue -e '{"message": "Im from SAM CLI!"}' --parameter MessageAttributes='{"Enabled": {"DataType": "String", "StringValue": "True"}}' && date
Sending message to SQS queue SimpleQueue
{
"MD5OfMessageBody": "d6eaf4441aff3dcccfd156744195d574",
"MessageId": "xxxxx",
"MD5OfMessageAttributes": "ebe992a5ee8c8f4d19cf919b979b2531"
}
2025年 3月26日 水曜日 21時40分42秒 JST
無事に実行されログからメッセージイベントを取得できました。
{
"Records": [
{
"messageId": "xxxxx",
"receiptHandle": "xxxxx",
"body": "{\"message\": \"Im from SAM CLI!\"}",
"attributes": {
"ApproximateReceiveCount": "1",
"SentTimestamp": "1742992842103",
"SenderId": "xxxxx",
"ApproximateFirstReceiveTimestamp": "1742992842116"
},
"messageAttributes": {
"Enabled": {
"stringValue": "True",
"stringListValues": [],
"binaryListValues": [],
"dataType": "String"
}
},
"md5OfBody": "d6eaf4441aff3dcccfd156744195d574",
"md5OfMessageAttributes": "ebe992a5ee8c8f4d19cf919b979b2531",
"eventSource": "aws:sqs",
"eventSourceARN": "arn:aws:sqs:ap-northeast-1:xxxxx:sqs-app-SimpleQueue-xxxxx",
"awsRegion": "ap-northeast-1"
}
]
}
続いてフィルタリング条件に一致しないメッセージを送ってみます。
% sam remote invoke SimpleQueue -e '{"message": "Im from SAM CLI!"}' --parameter MessageAttributes='{"Enabled": {"DataType": "String", "StringValue": "False"}}' && date
Sending message to SQS queue SimpleQueue
{
"MD5OfMessageBody": "d6eaf4441aff3dcccfd156744195d574",
"MessageId": "xxxx",
"MD5OfMessageAttributes": "1603ca5e388017216a7a11caa86c6ed0"
}
2025年 3月26日 水曜日 22時03分35秒 JST
この場合は条件を満たさないので実行されません。
https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/invocation-eventfiltering.html#filtering-criteria-not-met
Amazon SQSでは、メッセージがフィルター条件を満たさない場合、Lambda はそのメッセージをキューから自動的に削除します。Amazon SQS でこれらのメッセージを手動で削除する必要はありません。
処理されなかったメッセージはSQS上に滞留するのではなくそのまま削除されるようです。一応Lambda関数側から削除されそうな記述になっていますがInvocationsメトリクスは値なしのため裏でシステム的に削除されていそうです。
終わりに
CMKの暗号化の確認がてらESMの動作も簡単に確認してみました。
個人的にはCMKの動作確認できたというよりはこの機会にESMを試せて良かったなという感じではあります。
今回追加されたCMK暗号化は元々指定できた環境変数用の鍵とは別の設定のため、鍵ベースで環境変数だけ触れる人、コードを触れる人、ESMの条件を触れる人というふうに細々コントロールできるので痒い所に手が届くシーンもあるのではないでしょうか。