Zenn
🙆‍♀️

【実例】AWS WAFのIP制限を自動化!S3アップロードだけでホワイトリストを瞬時に更新!

2024/09/05に公開

Contents

はじめに

セキュリティと運用効率の両立は、多くの組織にとって重要な課題です。特に、AWS WAF の IP 制限を頻繁に更新する必要がある環境では、その管理が大きな負担となります。

今回紹介する「waf-ip-whitelist-auto-updater」を使えばこの課題を解決し、IP 制限の管理を劇的に簡素化します。

なぜ必要になったか

AWS を利用したシステム開発プロジェクトの開発フェーズでは、AWS WAF の IPSet を使いアクセス元の IP を制限するがよくあります。弊社環境においては、固定で必要な IP は数個程度でした。その他、個人の作業場所や検証内容に応じて随時追加・削除するものは、頻度が低いため手作業でも問題になっていませんでした。

しかし、社内方針変更によりアクセス元となる IP 数が 30 個以上と大幅に増えました。さらに1~2か月のサイクルで IP アドレスが増減します。このような状況で、手作業で実施するのは煩雑なため、今回の仕組みを実装しました。

導入のメリット

  1. 手動更新の煩わしさ解消:数十の IP アドレスを数分で更新可能
  2. 即時反映によるセキュリティ向上:変更が瞬時に適用され、セキュリティギャップを最小化
  3. ヒューマンエラーのリスク軽減:手動入力によるミスを完全に排除
  4. 運用コストの大幅削減:更新作業時間を 90%以上削減可能

ソースコード

GitHub>aws-cdk-devops-toolkit/workspaces/waf-ip-whitelist-auto-updater

アーキテクチャ

overview

  1. AWS WAF の IPSet に設定する IP の情報を記載した JSON ファイルを S3 にアップロード
  2. S3 に設定されたトリガーで AWS Lambda が起動
  3. AWS Lambda によって AWS WAF の IPSet を更新
  4. (オプション)更新した IP 情報を履歴として保存
  5. (オプション)処理結果を SNS で通知

主な特徴

  • S3 への JSON ファイルアップロードだけで AWS WAF の IPSet 更新完了
  • CloudFront とリージョナル WAF の両方に対応
  • SNS 通知機能で更新状況を即時把握
  • S3 バケットに IP 更新履歴 JSON ファイルを保存し、監査にも対応
  • アップロードする JSON ファイルにデバッグモードを指定でき、安全に動作確認が可能
  • AWS CDK による簡単デプロイ

アップロードする JSON ファイルの仕様

S3 にアップロードする JSON ファイルは次のとおりです。JSON ファイル名は任意の名称を指定できます。

JSON ファイルで指定した情報に従って AWS WAF の IPSet を更新します。

{
  "ipSetName": "MyIPSet",
  "ipSetId": "abcd1234-a123-456a-a12b-a123b456c789",
  "allowedIpAddressRanges": ["192.0.2.0/24", "203.0.113.0/24"],
  "scope": "REGIONAL",
  "region": "ap-northeast-1",
  "isDebug": false,
  "snsTopicArn": "arn:aws:sns:ap-northeast-1:123456789012:MyTopic"
}
  • ipSetName: 【必須】更新する WAF IP セットの名前
  • ipSetId: 【必須】更新する WAF IP セットの ID
  • allowedIpAddressRanges: 【必須】許可する IP アドレス範囲のリスト。空にする場合は[]とする。
  • scope: 【必須】"CLOUDFRONT"または"REGIONAL"
  • region: リージョナルスコープの場合、使用するリージョン(空または未指定の場合、Lambda 関数のリージョンを使用)
  • isDebug: true の場合、実際の更新を実施せずログ出力のみ。未指定または false の場合はデバッグモードが無効になる
  • snsTopicArn: 結果通知用の SNS トピック ARN(オプション:未指定の場合、通知しません)

この JSON ファイルは Lambda によって、以下のように読み込みます。

# Read the contents of the file from the S3 object
s3 = boto3.client('s3')
file_obj = s3.get_object(Bucket=bucket_name, Key=file_key)
file_content = file_obj['Body'].read().decode('utf-8')
lambda_region = context.invoked_function_arn.split(":")[3]

# Parse the contents of the JSON file
config = json.loads(file_content)

ipset_name = config['ipSetName']
ipset_id = config['ipSetId']
ip_addresses = config['allowedIpAddressRanges']
scope = config['scope']
# Use Lambda's region if 'region' is not specified or is an empty string
config_region = config.get('region', '')
region = config_region if config_region.strip() else lambda_region
is_debug = config.get('isDebug', False)
sns_topic_arn = config.get('snsTopicArn', '')

IP 更新履歴 JSON ファイルの仕様

Lambda で更新した IP アドレスを履歴で保持できるようになっています。Lambda の環境変数で S3 バケットを指定した場合のみ保存されます。次のようなレイアウトで保存されます。初回追加された日時、削除された日時、再有効化された日時が保存されます。

{
  "ipSetName": "MyIPSet",
  "ipSetId": "abcd1234-a123-456a-a12b-a123b456c789",
  "Addresses": [
    {
      "ipAddress": "10.0.0.0/24",
      "createdAt": "2024-04-26T06:25:47.344Z",
      "status": "active"
    },
    {
      "ipAddress": "192.168.1.0/24",
      "createdAt": "2024-04-26T06:25:47.344Z",
      "status": "active"
    },
    {
      "ipAddress": "172.16.0.0/16",
      "createdAt": "2024-04-25T10:15:30.123Z",
      "status": "deleted",
      "deletedAt": "2024-04-26T06:25:47.344Z"
    },
    {
      "ipAddress": "203.0.113.0/24",
      "createdAt": "2024-04-24T08:30:00.000Z",
      "status": "active",
      "reactivatedAt": "2024-04-26T06:25:47.344Z"
    }
  ]
}
  • ipSetName: アップロード JSON と同じ値
  • ipSetId: アップロード JSON と同じ値
  • Addresses: IP アドレスの履歴を保持
    • ipAddress: IP アドレスまたは CIDR 範囲
    • createdAt: IP アドレスが最初に追加されたタイムスタンプ
    • status: IP アドレスの現在のステータス("active"または"deleted")
    • deletedAt: IP アドレスが削除されたタイムスタンプ(該当する場合)
    • reactivatedAt: 以前に削除された IP アドレスが再有効化されたタイムスタンプ(該当する場合)

デプロイ方法

手作業でデプロイ

手動でデプロイする場合は次の手順になります。

  1. JSON ファイルをアップロードする S3 バケットを作成
  2. (オプション)IP 更新履歴を保存する S3 バケットを作成
  3. Lambda 関数用の IAM ロールを作成し、以下の権限を付与
    • service-role/AWSLambdaBasicExecutionRole
    • AmazonS3ReadOnlyAccess
    • sns:Publish
    • arn:aws:wafv2:*:*:*/ipset/*
      • wafv2:GetIPSet
      • wafv2:UpdateIPSet
    • IP 更新履歴出力する場合はバケットに対する権限
      • s3:GetObject
      • s3:PutObject
  4. Lambda 関数のコードを AWS マネジメントコンソールで登録
    • src/lambda/waf-ip-whitelist-auto-updater
    • Lambda 関数の環境変数
      • IP_RECORD_BUCKET_NAME:(オプション)IP 更新履歴を保存する S3 バケットを指定する。指定しない場合は、IP 更新履歴を保存しません。
      • LOG_LEVEL:(オプション)ログレベル(INFO/DEBUG/ERROR/WARN)を指定する(デフォルト:INFO)。
  5. 対象とする S3 バケットにトリガーとして登録

AWS CDK によるデプロイ

以下では、npm workspacesを使用していますので、デプロイする場合はaws-cdk-devops-toolkitの階層で実施します。

CDK でデプロイすると以下のリソースが作成されます。

  • S3
    • JSON アップロード用バケット:project-env-waf-ip-auto-updater-upload-AWSアカウントID
    • IP 履歴保存用バケット:project-env-waf-ip-auto-updater-record-AWSアカウントID
  • Lambda
    • Lambda 関数:project-env-waf-ip-whitelist-auto-updater
    • Lambda 実行用 IAM ロール:@role-lambda-project-env-waf-ip-whitelist-auto-updater

準備

  • AWS CDK のインストール
npm install -g aws-cdk
  • パッケージをインストール
npm install
  • .aws\credentialsの定義作成

接続プロファイル名は、デプロイコマンドに使用する<project>-<env>で作成しておきます。

[sample-dev]
region = ap-northeast-1
role_arn = arn:aws:iam::123456789012:role/hogehoge
mfa_serial = arn:aws:iam::123456789012:mfa/foobar
source_profile = sample-dev-accesskey

デプロイ実行

npm run cdk:deploy:all --env=dev --project=hogehgoe -w workspaces\waf-ip-whitelist-auto-updater
  • 削除
npm run cdk:destroy:all --env=dev --project=hogehgoe -w workspaces\waf-ip-whitelist-auto-updater

カスタマイズ

Lambda のソースコードは、src/lambda/waf-ip-whitelist-auto-updaterにあります。

Lambda のランタイムバージョンを変更したい場合は、lib/waf-ip-whitelist-auto-updater-stack.tsの 99 行目lambda.Runtime.PYTHON_3_12,を変更します。

 98: handler: 'index.lambda_handler',
 99: runtime: lambda.Runtime.PYTHON_3_12,
100: timeout: cdk.Duration.seconds(defaultLambdaTimeoutSeconds),
101: architecture: lambda.Architecture.ARM_64,

まとめ

このソリューションを導入することで、AWS WAF の IP 制限管理が大幅に簡素化されます。S3 へのファイルアップロードという簡単な操作だけで、セキュリティ設定を迅速かつ正確に更新できるようになります。さらに、AWS CDK を使用することで、インフラのバージョン管理や再現性が向上し、より安全で効率的な運用が可能になります。

次のステップとして、以下のような対応を実施することでさらなる自動化と効率化が期待できます。

  • JSON ファイルを Git リポジトリで管理にして、CI/CD パイプラインで自動的に S3 にアップロード
  • IP 更新履歴 JSON ファイルを月 1 回などの頻度で自動的にチェックし、IP アドレスが追加されてからの日数によって棚卸しを促す

Discussion

ログインするとコメントできます