🙆‍♀️

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

2024/09/05に公開

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

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によるデプロイ

GitHub>aws-cdk-devops-toolkit/workspaces/waf-ip-whitelist-auto-updaternpm 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