AWS Lambdaのイベントソースマッピングのフィルタリング条件とZipデプロイパッケージがカスタマーマネージドキーによる暗号化に対応していたのでAWS SAMで実装してみた

AWS Lambdaのイベントソースマッピングのフィルタリング条件とZipデプロイパッケージがカスタマーマネージドキーによる暗号化に対応していたのでAWS SAMで実装してみた

Clock Icon2025.03.26

初めに

しばらく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側にも取り込んだもののようです。

https://aws.amazon.com/jp/about-aws/whats-new/2024/08/aws-lambda-encryption-filter-criteria-event-source-mappings/

https://aws.amazon.com/about-aws/whats-new/2024/11/aws-lambda-cmk-encryption-zip-function-code-artifacts/

この辺り当ブログでも触れられてなかったのとESM使ったことなかったのでSAMで実装しつつ触ってみます

ESM側についてはCDK経由ですが触れられていました。

https://dev.classmethod.jp/articles/enabling-encryption-for-aws-lambda-event-filtering-conditions-with-aws-cdk/

ESMのフィルタリング条件の暗号化?

Zipパッケージアーティファクトの方はすぐにイメージできたもののESMの方にあまり馴染みがなくなんだこれ?となったのですが、Lambda関数をイベントでトリガーして呼び出す場合かつDynamoDBやSQS等一部のサービスの場合はイベントの内容にマッチした場合のみLambda関数をトリガーできるように制御できるようです。

記法もEventBridgeのルールがベースであり概念としても近いので、ご存知の方はそれのLambdaのトリガー版とイメージしてもらうのがわかりやすいと思います。

https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/invocation-eventfiltering.html

今回はこの条件文(式?)がCMKで暗号化できるようになったようです。

具体的にはこの赤枠の部分が暗号化のターゲットです。

event-filter-cmk-target

すごいところ指定できるなと思ったんですが、実際のアプリデータの値フィルタリングしようとする場合は機密性高めの情報が入る場合もあるので需要は結構あるのかもしれません。

AWS SAMで実装してみる

SAMテンプレートを構築しSAM CLIでデプロイします。

SAM CLIとしてはESMのフィルタリング条件のCMK暗号化はv1.122.0、ZipコードアーティファクトのCMK暗号化はv1.128.0のタイミングで取り込んでいます。

今回は現時点で最新のバージョンであるv1.135.0を利用し、プロジェクトは以下をベースに生成しています。

サンプルプロジェクトはSQSをトリガーにLambda関数を起動しその内容を出力するだけのシンプルな物です。

https://github.com/aws/aws-sam-cli-app-templates/tree/master/nodejs22.x/sqs/{{cookiecutter.project_name}}

% sam --version
SAM CLI, version 1.135.0
% sam init --runtime nodejs22.x --app-template quick-start-sqs --name sqs-app
...

鍵の指定

コード側の暗号化はAWS::Serverless::FunctionProperties直下の属性であるSourceKMSKeyArnに指定を行います。

https://docs.aws.amazon.com/ja_jp/serverless-application-model/latest/developerguide/sam-resource-function.html#sam-function-sourcekmskeyarn

ESM側の暗号化はEvents配下に実装される各イベント毎の定義に実装します。種別によって異なりますがSQSの場合はその配下のKmsKeyArnに指定すればOKです。

https://docs.aws.amazon.com/ja_jp/serverless-application-model/latest/developerguide/sam-property-function-sqs.html

KmsKeyArn
Lambda が関数の環境変数の暗号化と復号に使用する AWS Key Management Service (AWS KMS) キーの ARN。
SourceKmsKeyArn
お客様の ZIP 関数コードを暗号化するために使用される KMS キー ARN を表します。

Properties直下にもKmsKeyArnという属性がありますがこれはLambda関数の環境変数暗号化用の鍵の指定です。
属性名は同じですがEvents配下にあるESM向けのKmsKeyArnとは別の指定であり、またコードアーティファクトの鍵とも別の指定ですので気をつけましょう。

なお現時点ではSourceKMSKeyArnKMSで全て大文字、KmsKeyArnKmsで先頭だけ大文字なようです。

テンプレート

上記を踏まえ実装すると以下のようになります。

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が埋め込まれるのではなく専用のマスク文字列で潰してくれるみたいです。

lambda-event-filter-cmk-deny

lambda-code-cmk-deny

また先ほど少し触れましたが環境変数の暗号キーの設定は今回の箇所とは別の設定になるためこの状態でも確認可能です(デフォルトのAWS管理キー利用)。

lambda-env-encrypt-not-deny

ESMの動作確認

本筋ではないですがESM使ったことないのでついでに試してみます。

本当はメッセージボディで判定しようとしたのですが雑に送るとパースされたJSON文字列(プレーン文字列)をイベント情報として持つ形になります。

{
...
            "body": "{\"message\": \"Im from SAM CLI\"}"
...
}

イベントフィルタ条件の場合これはJSON文字列ではなくプレーン文字列として扱われてしまう関係で、JSONの属性値一致ではなく文字列の部分一致で取る必要がありますが、なんとなく部分一致で取るのが嫌なので今回はメッセージ属性に値を埋め込んで判定する形としました。

https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/with-sqs-filtering.html

条件は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"
}
20253月26日 水曜日 22時03分35秒 JST

この場合は条件を満たさないので実行されません。

lambda-recent-invoke

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メトリクスは値なしのため裏でシステム的に削除されていそうです。

esm-drop-sqs-message

終わりに

CMKの暗号化の確認がてらESMの動作も簡単に確認してみました。
個人的にはCMKの動作確認できたというよりはこの機会にESMを試せて良かったなという感じではあります。

今回追加されたCMK暗号化は元々指定できた環境変数用の鍵とは別の設定のため、鍵ベースで環境変数だけ触れる人、コードを触れる人、ESMの条件を触れる人というふうに細々コントロールできるので痒い所に手が届くシーンもあるのではないでしょうか。

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.