AWS CLIで構築するAWS WAF環境

2018.03.04

こんにちは、坂巻です。
今回は、AWS WAFをAWS CLIで構築する方法をご紹介します。

前提

本エントリの記載を実施するには、以下が必要となります。

  • ACL CLIでAWSに接続ができること
  • ALBとその配下にWebサーバが存在すること
  • Webサイトにアクセス可能な日本のリージョン以外で稼働するEC2

要件

構成図内の赤枠(破線)をCLIで作成します。
今回のAWS WAFは、日本からのリクエストのみを許可するように設定します。

Web ACL

事前確認

AWS WAFの構築前にWebサーバにアクセスできることを確認します。

curl ALB-1335792166.ap-northeast-1.elb.amazonaws.com

正常にリクエストが通り、Webページが表示されることを確認してスタートです。

AWS WAF test!!

作業の流れ

今回は以下の順でAWS WAFを構築します。

  1. 事前準備
  2. Web ACLの作成
  3. ルールの作成
  4. 地域条件の作成
  5. 地域条件の更新
  6. ルールに地域条件を追加
  7. Web ACLにルールを追加
  8. Web ACLとALBの関連付け
  9. 動作確認

AWS WAF作成

事前準備

AWS WAFの更新系(CREATE/UPDATE/DELETE)コマンドは、コマンド実行時にトークンを含める必要があります。
GetChangeTokenAPIでトークンを取得し、リクエストにこのトークンを含めることで、更新の競合を防いでいます。
詳細は以下のマニュアルをご確認ください。

GetChangeToken

今回はスクリプトを用意してコマンド発行時のトークンの取得及び、オプションでの指定を簡略化したいと思います。
以下のコマンドを実行すると、トークンを取得する「token.sh」が作成されます。

#スクリプト名をセット
TOKEN_SCRIPT=token.sh

cat <${TOKEN_SCRIPT}|chmod 744 ${TOKEN_SCRIPT}
#!/usr/bin/env bash

aws waf-regional get-change-token \
--query 'ChangeToken' \
--output text
EOF

Web ACLの作成

Web ACLはルールを束ね、 CloudFrontやALBと関連付けて使用します。
詳細はこちらをご確認ください。

今回はALBに関連付けるのでwaf-regionalコマンドを利用します。
Web ACLの作成は以下のコマンドを実行します。

#Web ACLの名前を定義
WEB_ACL_NAME=test-web-acl
#AWS WAFによって作成されWeb ACLに関連付けられるCloudWatchメトリクスの名前を定義
WEB_ACL_MET_NAME=TestWebAcl
#Web ACL作成時の実行結果を保存するファイル名を定義
WEB_ACL_LOG=webacl.log

aws waf-regional create-web-acl \
--name ${WEB_ACL_NAME} \
--metric-name ${WEB_ACL_MET_NAME} \
--default-action 'Type=BLOCK' \
--change-token $(./${TOKEN_SCRIPT}) \
> ${WEB_ACL_LOG} 2>&1

実行結果をリダイレクトしているため、結果は${WEB_ACL_LOG}に指定したファイルに出力されます。
後続のコマンドでWeb ACLのIDが必要になりますので、
必要な情報がコマンドで取得できるように、ファイルに実行結果を出力しました。

それでは、Web ACLのIDを以下のコマンドで取得します。

WEB_ACL_ID=`cat ${WEB_ACL_LOG} | jq -r '.WebACL.WebACLId'`

取得結果を確認します。

echo $WEB_ACL_ID

以下のようなIDが表示されれば、Web ACLは正常に作成されています。

8452e0b2-1e08-4526-9c77-a2fba006faa2

Web ACLが作成されていることを、マネジメントコンソールからも確認してみます。
赤枠部分が追加された設定です。

Web ACL

なお、--queryオプションを利用して、必要な情報のみ取得することもできますが、
後から実行結果の全量を確認できるように--queryオプションは利用しない方針としました。

以降、実行結果に後続コマンドで必要となる情報が含まれる場合は、
同様の方法で取得していきます。

ルールの作成

ルールは条件を束ねたもので、条件毎に許可または拒否を設定します。
詳細はこちらをご確認ください。

ルールの作成は以下のコマンドを実行します。

#ルールの名前を定義
RULE_NAME=test-rule
#AWS WAFによって作成されルールに関連付けられるCloudWatchメトリクスの名前を定義
RULE_MET_NAME=TestRule
#ルール作成時の実行結果を保存するファイル名を定義
RULE_LOG=rule.log

aws waf-regional create-rule \
--name ${RULE_NAME} \
--metric-name ${RULE_MET_NAME} \
--change-token $(./${TOKEN_SCRIPT}) \
> ${RULE_LOG} 2>&1

作成したルールのIDが後続のコマンドで必要になるため、IDを取得します。

RULE_ID=`cat ${RULE_LOG} | jq -r '.Rule.RuleId'`

取得結果を確認します。

echo ${RULE_ID}

以下のようなIDが表示されれば、ルールは正常に作成されています。

314ef040-c297-4ba9-875d-23aea7275456

マネジメントコンソールからも確認してみます。
赤枠部分が追加された設定です。

ルール

地域条件の作成

リクエスト送信元の国に基づいてリクエストを許可または拒否する場合に利用する条件です。
地域条件の作成は以下のコマンドを実行します。

#地域条件の名前を定義
GEO_MATCH_CONDITION_NAME=test-geo-match-set
#地域条件作成時の実行結果を保存するファイル名を定義
GEO_MATCH_CONDITION_LOG=geomatchcondition.log

aws waf-regional create-geo-match-set \
--name ${GEO_MATCH_CONDITION_NAME} \
--change-token $(./${TOKEN_SCRIPT}) \
> ${GEO_MATCH_CONDITION_LOG} 2>&1

作成した地域条件のIDが後続のコマンドで必要になるため、IDを取得します。

GEO_MATCH_CONDITION_ID=`cat ${GEO_MATCH_CONDITION_LOG} | jq -r '.GeoMatchSet.GeoMatchSetId'`

取得結果を確認します。

echo ${GEO_MATCH_CONDITION_ID}

以下のようなIDが表示されれば、地域条件は正常に作成されています。

f8dec01d-e0e3-4b8d-81ca-488102fbe84e

マネジメントコンソールからも確認してみます。
赤枠部分が追加された設定です。

ルール

地域条件の更新

日本からのリクエストにのみ制御を行うため、地域条件の更新を行います。
条件の更新は以下のコマンドを実行します。

aws waf-regional update-geo-match-set \
--geo-match-set-id ${GEO_MATCH_CONDITION_ID} \
--updates 'Action=INSERT,GeoMatchConstraint={Type=Country,Value=JP}' \
--change-token $(./${TOKEN_SCRIPT})

正常にコマンドが実行されると、以下のようなトークンが表示されます。

{
"ChangeToken": "82d5dc66-2cd1-4566-845b-4efdca469714"
}

マネジメントコンソールからも確認してみます。
赤枠部分が追加された設定です。

ルール

ルールに地域条件を追加

地域条件をルールに追加するには、以下のコマンドを実行します。

aws waf-regional update-rule \
--rule-id ${RULE_ID} \
--updates 'Action=INSERT,Predicate={Negated=false,Type=GeoMatch,DataId='${GEO_MATCH_CONDITION_ID}'}' \
--change-token $(./${TOKEN_SCRIPT})

正常にコマンドが実行されると、以下のようなトークンが表示されます。

{
"ChangeToken": "703db109-6152-4ae5-b6d6-ab2d7c3fc7b2"
}

マネジメントコンソールからも確認してみます。
赤枠部分が追加された設定です。

ルール

Web ACLにルールを追加

ルールをWeb ACLに追加するには、以下のコマンドを実行します。

aws waf-regional update-web-acl \
--web-acl-id ${WEB_ACL_ID} \
--updates 'Action=INSERT,ActivatedRule={Priority=1,RuleId='${RULE_ID}',Action={Type=ALLOW}}' \
--change-token $(./${TOKEN_SCRIPT})

正常にコマンドが実行されると、以下のようなトークンが表示されます。

{
"ChangeToken": "93e7c3bb-e483-42f0-9b79-c9df238b8269"
}

マネジメントコンソールからも確認してみます。
赤枠部分が追加された設定です。

ルール

Web ACLとALBの関連付け

Web ACLとALBを関連付けします。ここでは既に作成されているALBを利用します。
Web ACLとALBの関連付けは、以下のコマンドを実行します。

#関連付けするALBのARN
ALB_ARN=arn:aws:elasticloadbalancing:ap-northeast-1:XXXXXXXXXXXX:loadbalancer/app/ALB/6ab32c376e483135

aws waf-regional associate-web-acl \
--web-acl-id ${WEB_ACL_ID} \
--resource-arn ${ALB_ARN}

実行結果に何も表示されないため、
以下のコマンドを実行して戻り値が0であることを確認します。

echo $?

マネジメントコンソールからも確認してみます。
赤枠部分が追加された設定です。

ルール

動作確認

AWS WAF設定前と同様のコマンドを日本から実行します。

curl ALB-1335792166.ap-northeast-1.elb.amazonaws.com

正常にリクエストが通り、AWS WAF設定前と同様の結果が返されます。

AWS WAF test!!

次は、日本以外の国(ここではバージニア北部)から、同様のコマンドを実行します。
念の為、リクエスト元が日本のリージョン以外であることを確認します。

curl -s http://169.254.169.254/latest/meta-data/placement/availability-zone | sed -e 's/.$//'

バージニア北部のため、以下の結果が返されます。

us-east-1

それではWebサイトにアクセスしてみます。

curl ALB-1335792166.ap-northeast-1.elb.amazonaws.com

以下の結果が返され、AWS WAFによりリクエストが拒否されていることが確認できました。

403 Forbidden

<center></center> 
<h1>403 Forbidden</h1>

関連記事

過去にAWS WAFを題材にしたエントリは多数ありますので、こちらもあわせてご確認ください。

AWS CLIがAWS WAFとALBの関連付けをサポートしていたので試してみた

AWS CLIからAWS WAFのWeb ACLを定義してみた #reinvent

AWS WAFが正規表現と地域条件をサポートしました