ENECHANGE Developer Blog

ENECHANGE開発者ブログ

「Lambdaレス」な実装例:AWS WAFのIPセット自動更新

VPoTの岩本 (iwamot) です。

この記事では、AWS Lambdaを使わずにサーバーレスな処理を実装する、いわゆる「Lambdaレス」な実装の例をご紹介します。具体的には「AWSサービスのIPレンジ変更を検知し、AWS WAFのIPセットを自動更新してみた」例です。

Lambdaレスな考え方は、AWS Summit Japan 2024のセッション「サーバーレス開発のベストプラクティス ~より効果的に、より賢く使いこなすために~」(動画資料)で取り上げられ、注目されています。*1

いざ実装してみると、Lambda関数の保守がいらなくなるメリットが大きく感じられました。

Lambdaレス以前の実装例

AWSは、AWSサービスのIPレンジ変更を検知し、AWS WAFのIPセットを自動更新するサンプルとして、下記のGitHubリポジトリを公開しています。

github.com

github.com

いずれも、IPレンジ変更が通知されるSNSトピック (arn:aws:sns:us-east-1:806199016981:AmazonIpSpaceChanged) にサブスクライブしたLambda関数で、IPセットを更新する実装です。

                          +-----------------+         +----------------+
                          | Lambda          |         |                |
                          | Execution Role  |    +--->+AWS WAF IPv4 Set|
                          +--------+--------+    |    |                |
                                   |             |    +----------------+
                                   |             |
+--------------------+    +--------+--------+    |
|SNS Topic           +--->+ Lambda function +----+
|AmazonIpSpaceChanged|    +--------+--------+    |
+--------------------+             |             |    +----------------+
                                   |             |    |                |
                                   v             +--->+AWS WAF IPv6 Set|
                          +--------+--------+         |                |
                          | CloudWatch Logs |         +----------------+
                          +-----------------+

https://github.com/aws-samples/aws-waf-ipset-auto-update-aws-ip-ranges


                          +-----------------+         +---------------------+
                          | Lambda          |         |                     |
                          | Execution Role  |    +--->+AWS WAF IPv4/IPv6 Set|
                          +--------+--------+    |    |                     |
                                   |             |    +---------------------+
                                   |             |
+--------------------+    +--------+--------+    |
|SNS Topic           +--->+ Lambda function +----+
|AmazonIpSpaceChanged|    +--------+--------+    |
+--------------------+             |             |    +-------------------+
                                   |             |    |                   |
                                   v             +--->+AWS VPC Prefix List|
                          +--------+--------+         |                   |
                          | CloudWatch Logs |         +-------------------+
                          +-----------------+

https://github.com/aws-samples/update-aws-ip-ranges

これらのサンプルを活用してもよいのですが、できればLambdaレスにして、保守の手間をなくしたいですよね。

Lambdaレスな実装例

どうしたらよいのか、ぼくなりに考えて出た答えが下記でした。

  1. IPセットの更新にStep Functionsを利用
  2. トリガーにSQSとEventBridge Pipesを利用

以下、説明します。

IPセットの更新にStep Functionsを利用

IPセットの更新は、AWS WAFのUpdateIPSet APIを呼び出して実行します。

Lambdaレスで呼び出す手段として、ぼくはStep FunctionsのAWS SDK統合を選びました。具体的なワークフローは下記の通りです。

{
  "Comment": "Updates WAFv2 IP set with latest Route 53 health check IP ranges when AWS IP ranges change.",
  "StartAt": "Parallel",
  "States": {
    "Parallel": {
      "Branches": [
        {
          "StartAt": "GetManagedPrefixListEntries",
          "States": {
            "GetManagedPrefixListEntries": {
              "End": true,
              "Parameters": {
                "PrefixListId": "pl-xxxxxxxxxxxxxxxxx"
              },
              "Resource": "arn:aws:states:::aws-sdk:ec2:getManagedPrefixListEntries",
              "Type": "Task"
            }
          }
        },
        {
          "StartAt": "GetIPSet",
          "States": {
            "GetIPSet": {
              "End": true,
              "Parameters": {
                "Id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
                "Name": "my-ip-set",
                "Scope": "CLOUDFRONT"
              },
              "Resource": "arn:aws:states:::aws-sdk:wafv2:getIPSet",
              "Type": "Task"
            }
          }
        }
      ],
      "Next": "UpdateIPSet",
      "Type": "Parallel"
    },
    "UpdateIPSet": {
      "End": true,
      "Parameters": {
        "Addresses.$": "$[0].Entries[*].Cidr",
        "Id.$": "$[1].IpSet.Id",
        "LockToken.$": "$[1].LockToken",
        "Name.$": "$[1].IpSet.Name",
        "Scope": "CLOUDFRONT"
      },
      "Resource": "arn:aws:states:::aws-sdk:wafv2:updateIPSet",
      "Type": "Task"
    }
  }
}

マネージドプレフィックスリストを参照しているのは、今回の要件が「Route 53ヘルスチェックで使われるIPv4レンジをIPセットとして提供すること」だったためです。pl-xxxxxxxxxxxxxxxxxcom.amazonaws.<region>.route53-healthchecks を参照し、エントリをAWS WAFのIPセットにコピーしています。

Step FunctionsではHTTPリクエストも可能です。今回は試していませんが、Lambdaレス以前の実装のように、https://ip-ranges.amazonaws.com/ip-ranges.json を参照するワークフローも可能でしょう。

ちなみに、UpdateIPSetの前にGetIPSetしているのは、その順で呼び出す仕様だからです。詳しくはUpdateIPSet APIのドキュメントをご覧ください。

トリガーにSQSとEventBridge Pipesを利用

ここまでできれば、あとはSNSとStep Functionsをつなぐだけです。SNSトピックに直接Step Functionsをサブスクライブできるとよいのですが、現時点ではできません。

今回は「SNS → SQS → EventBridge Pipes → Step Functions」とつないでみました。EventBridge Pipesは、まさにこのような統合に使えるサービスです。

https://aws.amazon.com/jp/eventbridge/pipes/

おわりに

以上、Lambdaレスな実装例をご紹介しました。Lambdaを「SQS → EventBridge Pipes → Step Functions」で置き換えているため複雑に見えるかもしれませんが、実際にはワークフローを組み立ててサービスをつないでいくだけです。

実装してみて、Lambda関数の保守がいらなくなるメリットが大きく感じられました。今後も、Lambdaレスな実装をまず考えるようにします。

*1:2024年8月23日から24日にかけて開催されるイベント「JAWS PANKRATION 2024」でも、AWSコミュニティビルダーの鈴木さんが「What is "Lambdaless" serverless?」を発表予定です。