Amazon Web Services ブログ
AWS WAF で AWS サービスの IP レンジを自動更新する方法
注:このブログでは、AWS サービスの IP レンジの情報から AWS WAF IP セットを自動的にアップデートする方法を説明しています。こちらの関連ブログでは VPC Security Group で使用されている Amazon CloudFront の IP レンジについて、同様のアップデートを行う方法を説明しています。
AWS Managed Rules for AWS WAF を使用すると、Web アプリケーションのベースライン保護をすばやく構築することができます。しかし、状況によっては AWS サービスの IP アドレスレンジで IP セットを作成し、これらサービスからのトラフィックを許可したい時があります。このブログでは AWS WAF の IP セットを、AWS サービスの Amazon CloudFront、Amazon Route 53 のヘルスチェック、Amazon EC2(さらに AWS Lambda、Amazon CloudWatch など、同じ IP アドレスレンジを共有するサービス)の IP アドレスレンジで自動的に更新するソリューションを紹介します。これらのサービスは AWS Managed Rules のAnonymous IP リストに存在していることがあるため、不用意にルールをブロックに設定すると、AWS サービスからのトラフィックを期待しているアプリケーションにおいて問題が発生する可能性があります。
アプリケーションの所有者は AWS WAF の Web アクセス制御リスト (Web ACL) に Anonymous IP リストを使用して、特定のホスティングプロバイダーや、VPN、プロキシ、Tor ノードなどの匿名化サービスからの送信元 IP をブロックすることで、セキュリティリスクを低減することができます。Anonymous IP リストを使用する際にも、特定の送信元からのトラフィックを許可するために、ブロックする IP のリストから特定の IP を除外したい場合があります。
また、Web ACL で特定の AWS サービスからの IP アドレスのみを許可することもできます。これは CloudFront の IP レンジから受信するトラフィックのみ許可することで、Application Load Balancer を保護する場合によく見られる要件です。AWS ネットワークからのトラフィックを許可する独自のカスタムリストを作成するには、AWS が提供する IP レンジを使用して定期的にリストを更新する必要があります。今回ご紹介するソリューションでは許可リストを手動で管理する必要はなく、新しい AWS の IP レンジが公開されると、ソリューションが自動的にリストを取得して更新します。
注:このソリューションは、AWS WAF Classic では動作しませんのでご注意ください
ソリューションの概要
図1にソリューションのアーキテクチャを示します。
図 1 : サービス IP の自動更新プロセス
AWS は AWS サービスのパブリック IP アドレスに更新が行われると、Amazon Simple Notification Service (Amazon SNS) 通知を AmazonIPSpaceChanged SNS トピックのサブスクライバーに送信します。このソリューションでは AWS CloudFormation テンプレートを使用して、これらの SNS 通知によってトリガーされる AWS Lambda 関数を展開します。この関数は、Web ACL の IPv4 と IPv6 のアドレス範囲に AWS WAF の IP セットを作成します。
ソリューションのワークフローは以下の通りです。
1. CloudFormation テンプレートで、AWS WAF IP セットを更新したいサービスを選択します。
2. テンプレートから AWS Lambda の関数など、必要な AWS リソースを展開します。
3. AWS WAF IP セットに AWS IP レンジから選択した IP を投入するため、Lambda 関数が初回だけ手動で起動されます。
4. AWS IP レンジが更新されると、Amazon SNS の通知が SNS トピックのサブスクライバーに送信されます。
5. SNS 通知は Lambda 関数をトリガーします。
6. Lambda 関数は選択された IP レンジを取得して、IPv4 アドレスと IPv6 アドレスの IP セットを更新します。
7. アプリケーションの所有者は IP セットを使用して、選択した AWS サービスからのトラフィックを許可するカスタムルールを追加します。このようにして Web ACL は常に更新されたAWS WAFのIPセットを参照するようになり、お客様側ではそれ以上のアクションは必要ありません。
ソリューションの前提条件
このソリューションは、GitHub ページにある CloudFormation テンプレートを展開すると自動的に作成されます。テンプレートをデプロイする前に、3つのリソースを用意する必要があります。
・Lambda 関数として使用される Python コード
GitHub のプロジェクトの AWS Lambda ディレクトリから、update_aws_waf_ipset.py という Python コードをダウンロードします。この関数は AWS の IP を常にチェックし、AWS WAF の IP セットが選択した AWS サービスで使用されている最新のIP セットに更新します。
・圧縮した Python コードを保存する Amazon S3 のバケット
ファイルを .zip ファイルに圧縮して、テンプレートをデプロイする AWS リージョンの Amazon S3 バケットにアップロードします。S3 バケットの作成方法についてはバケットの作成を参照してください。
・信頼できる送信元からのリクエストをフィルタするための AWS WAF の Web ACL
Web ACL はソリューションが作成し、必要な IP アドレスで更新する IP セットを使用します。新しい AWS WAF Web ACL を作成するか、テンプレートを展開するリージョンにある既存のものを使用します。
AWS CloudFormation テンプレートのデプロイ
CloudFormation テンプレートは、このソリューションに必要なリソースをお客様のアカウントにデプロイします。以下のリソースがデプロイされます。
・2 つの AWS WAF IP セット IPv4Set と IPv6Set は、許可したいと考えているサービスからの IPv4 と IPv6の IP アドレスを格納するために使用します。これらの IP セットは、テンプレートがデプロイされたリージョンの AWS WAF コンソールで確認することができます。
注:テンプレートに表示される 192.0.2.0/24 の IP アドレスは、ソリューションによって登録される IP アドレスのプレースホルダーであり、ドキュメント作成の目的でのみ使用されています。
・update_aws_waf_ipset.py という Python コードは、UpdateWAFIPSet という AWS Lambda 関数で使用されています。これはどのサービスから IP を収集すべきか、またどの IP セットを投入すべきかを読み取る関数です。これらのパラメータを変更しない場合、この関数はデフォルトの IP セットのサフィックスを使用します。デフォルトでは IP をダウンロードするサービスとして ROUTE53_HEALTHCHECKS と CLOUDFRONT を選択します。サービス名と IP レンジのリストについては、AWS IP JSON ドキュメントを参照することで、必要に応じて IP アドレスのリストを更新することができます。
・必要最小限の権限に制限された Lambda 実行ロール
・この Lambda 関数は、AWS IP のリストの変更を監視する役割を持つ、AmazonIPSpaceChanged SNS トピックに自動的にサブスクライブされます。
・先に作成した SNSトピックがテンプレートの Lambda 関数を呼び出すことを許可する Lambda 許可リソース。
コンソールによるソリューションの展開
ソリューションの GitHub ページから、template.yml という CloudFormation テンプレートをダウンロードできます。
テンプレートをダウンロードした後、CloudFormation コンソールにアクセスしてスタックを作成します。CloudFormation コンソールでダウンロードしたテンプレートを選択してスタックを展開する手順については、ユーザーガイドを参照してください。
注:テンプレートをデプロイする際に指定したリージョンにリソースが作成されます。
Specify stack details ページでは図 2 に示すように、テンプレートで作成されたリソースの参照先として使用される名前であるスタック名のほか、6 つのスタックパラメータを入力できます。
図 2 : テンプレートのパラメータ
パラメータは以下の通りです。
・EC2REGIONS – ソリューションが IP のリストを更新する際に参照するリージョンです。すべてのリージョンに対しては all を選択しますが、対象となるリージョンを指定することもできます。
・IPV4SetNameSuffix – ソリューションは、スタック名を名前として AWS WAF IPv4 IP セットを作成しますが、名前に任意のサフィックスを追加することができます。
・IPV6SetNameSuffix – AWS WAF の IPv4 IP セットと同様に、IPv6 IP セットにもお好みのサフィックスを付けることができます。
・LambdaCodeS3Bucket – 「前提条件」セクションで述べたように、スタックをデプロイするのと同じリージョンの Amazon S3 のバケットに、Lambda 関数の Python コードをあらかじめアップロードしておく必要があります。ここにはバケット名を入力します。
・LambdaCodeS3Object – S3 バケット内の圧縮された Lambda 関数の .zip ファイルの名前を入力します。(例:myfunction.zip)
・SERVICES – IP セットに IP アドレスを入力したい AWS サービスのリストを入力します。デフォルトでは、このソリューションは ROUTE53_HEALTHCHECKS と CLOUDFRONT を使用していますが、AWS IP ranges JSON 内のリストに従って、このパラメータに任意のサービス名を追加することができます。
テンプレートをデプロイすると、そのステータスは CREATE_COMPLETE に変わります。
AWS CLI によるソリューションのデプロイ
ソリューションテンプレートは AWS コマンドラインインターフェイス (AWS CLI) を使ってデプロイすることもできます。ソリューションの GitHub ページ の Setup セクションで、AWS CLI コマンドを使用してソリューションをデプロイするための指示に従います。
注:AWS CLI を使用するには、自分の環境で AWS CLI をセットアップしておく必要があります。AWS CLI をセットアップするには、AWS CLI のインストールドキュメント の指示に従います。
Lambda関数を初めて起動する
CloudFormation スタックのデプロイに成功した後、AWS WAF の IP セットが AWS サービスの IP で更新されるように Lambda 関数を呼び出します。この呼び出しは初回だけ必要で最初の呼び出しの後は、ソリューションがあなたに代わり更新を行います。
AWS マネジメントコンソールでこの Lambda 関数を呼び出すには、Lambda コンソールを開き、テンプレートで作成された Lambda 関数を選択し、以下のイベントを使用してテストイベントを作成します。テストイベントを実行するための手順は、AWS Lambda Developer Guide の Invoke the Lambda function を参照してください。
{
"Records": [
{
"EventVersion": "1.0",
"EventSubscriptionArn": "arn:aws:sns:EXAMPLE",
"EventSource": "aws:sns",
"Sns": {
"SignatureVersion": "1",
"Timestamp": "1970-01-01T00:00:00.000Z",
"Signature": "EXAMPLE",
"SigningCertUrl": "EXAMPLE",
"MessageId": "12345678-1234-1234-1234-123456789012",
"Message": "{\"create-time\": \"yyyy-mm-ddThh:mm:ss+00:00\", \"synctoken\": \"0123456789\", \"md5\": \"test-hash\", \"url\": \"https://ip-ranges.amazonaws.com/ip-ranges.json\"}",
"Type": "Notification",
"UnsubscribeUrl": "EXAMPLE",
"TopicArn": "arn:aws:sns:EXAMPLE",
"Subject": "TestInvoke"
}
}
]
}
このイベントが成功すると、新しく作成された AWS WAF の IP セットに、連携しているサービスの IP リストが更新されたことになります。
また、以下のコマンドで AWS CLI から Lambda 関数を呼び出すことができます。test_event.json は先ほどのテストイベントです。
aws lambda invoke \
--function-name $CFN_STACK_NAME-UpdateWAFIPSets \
--region $REGION \
--payload file://lambda/test_event.json lambda_return.json
AWS CLIでLambda関数を起動するためのドキュメントを使用して、このコマンドとそのパラメータを調べることができます。
呼び出しが成功すると AWS CLI でステータスコード 200が返され、呼び出しが期待通りにできたことを示します。この時点で AWS WAF の IP セットが更新されます。
ソリューションのIPセットを AWS WAF の Web ACL で使用する
これで AWS WAF の IPv4 と IPv6 の IP セットが登録され、AWS WAF コンソールを使用するか AWS CLI コマンドの get-ip-set を通じて GetIPSet API を呼び出すことで、IP リストを取得できます。
AWS WAF IP セットを Web ACL で使用するには、AWS WAF Developer Guide の Creating and managing an IP set を参照してください。これらの IP セットは AWS Managed Rules Anonymous IP リストを含み、AWS WAF が保護している AWS リソースに関連付けられている、同じ Web ACL またはルールグループで使用できます。AWS WAF の評価順序と WebACL 内のソリューションの位置付けについては、後のセクションで説明します。
Web ACLをAWSリソースに関連付けるには、Associating or disassociating a web ACL with an AWS resource を参照してください。
ソリューションの検証
ソリューションを検証するために CloudFront からのリクエストを許可して、他の匿名やホスティングプロバイダーの送信元 IP をブロックするシナリオを考えてみましょう。このシナリオでは、AWS WAF によってフィルタリングされる以下のリクエストを考えます。
最初のケースでは AWSManagedRulesAnonymousIpList ルールグループによって、ホスティングプロバイダーの IP から来たリクエストがブロックされています。
{
"timestamp": 1619175030566,
"formatVersion": 1,
"webaclId": "arn:aws:wafv2:eu-west-1:111122223333:regional/webacl/managedRuleValidation/11fd1e32-ae25-45f8-811f-3c1485f76ceb",
"terminatingRuleId": "AWS-AWSManagedRulesAnonymousIpList",
"terminatingRuleType": "MANAGED_RULE_GROUP",
"action": "BLOCK",
(...)
"ruleGroupList": [
{
"ruleGroupId": "AWS#AWSManagedRulesAnonymousIpList",
"terminatingRule": {
"ruleId": "HostingProviderIPList",
"action": "BLOCK",
"ruleMatchDetails": null
},
(...)
],
(...)
"httpRequest": {
"clientIp": "203.0.113.176",
(...)
}
}
2 つ目のリクエストは CloudFront から来たトラフィックを、AWS WAF がブロックしなかったことが分かります。
{
"timestamp": 1619175149405,
"formatVersion": 1,
"webaclId": "arn:aws:wafv2:eu-west-1:111122223333:regional/webacl/managedRuleValidation/11fd1e32-ae25-45f8-811f-3c1485f76ceb",
"terminatingRuleId": "Default_Action",
"terminatingRuleType": "REGULAR",
"action": "ALLOW",
"terminatingRuleMatchDetails": [],
(...)
"httpRequest": {
"clientIp": "130.176.96.86",
(...)
}
}
この結果を得るためには AWSManagedRulesAnonymousIpList にスコープダウンステートメントを追加して、このソリューションの IPv4 および IPv6 IP セット内の送信元から送信されていないリクエストのみをブロックするようにする必要があります。
AWSManagedRulesAnonymousIpList のスコープダウンステートメントを作成するには
1. AWS WAF コンソールで Web ACL にアクセスします。
2. Rules タブを開きます。
3. AWSManagedRulesAnonymousIpList ルールセットを選択して、Edit を選択します。
4. Scope-down statement – optional の横にある矢印を選択します。Rule visual editor と Rule JSON editor という 2 つのオプションが表示されます。
5. Rule JSON editor を選択し、次の JSON を入力します。<IPv4-IPSET-ARN> および <IPv6-IPSET-ARN> をそれぞれの IP セットの Amazon Resource Numbers (ARN) に置き換えます。
注:AWS WAF ListIPSets アクションまたは list-ip-sets CLI コマンドを使用して IP セットの Amazon Resource Numbers (ARN) を取得し、その情報を提供された JSON に入力することができます。
{
"NotStatement": {
"Statement": {
"OrStatement": {
"Statements": [
{
"IPSetReferenceStatement": {
"ARN": "<IPv4-IPSET-ARN>"
}
},
{
"IPSetReferenceStatement": {
"ARN": "<IPv6-IPSET-ARN>"
}
}
]
}
}
}
}
この変更を行うと、ルール編集画面は以下のようになります。
図 3 : AWSManagedRulesAnonymousIpList スコープダウンステートメント
ルールの優先順位を設定する際には AWSManagedRulesAnonymousIpList ルールグループを Web ACL 内の他のルールよりも低い優先順位で使用することを検討してください。これにより、そのルールグループは、終了アクション(つまり Allow および Block アクション)で構成されたルールよりも先に評価されます。スコープダウンステートメントは、IP セット内の IP アドレスからのリクエストと許可トラフィックにマッチし、他のすべての IP を次のルールに渡してさらに評価します。図 4 は優先順位の設定例を示しています。
図 4 : Web ACL におけるルールの優先順位の設定例
まとめ
このブログでは、単一または複数の AWS サービスの現在の IP レンジのリストで、AWS WAF IP セットを自動的に更新するソリューションを紹介しました。このソリューションは AWS Managed Rules Anonymous IP List を使用している際に Amazon CloudFront からのリクエストを許可するなど、さまざまな方法で使用できます。
AWS WAFの実装に関するベストプラクティスについては、「Guidelines for Implementing AWS WAF」を参照してください。AWS WAFの詳細については、「AWS WAF Developer Guide」を参照してください。
原文はこちらです。翻訳はソリューションアーキテクトの森が担当しました。