元ネタ
AWSのWell-Architected Frameworkを改めて整理。せっかくなんでハンズオン形式で学べるAWS Well-Architected Labsをやってみよう!ということで、備忘も兼ねて実践&記録をしてみました。
前提条件
- AWSアカウントがあること
ハンズオン開始
0. 概要
このハンズオンでは、Amazon CloudFront と AWS ウェブ アプリケーション ファイアウォール (WAF) を使用してネットワーク ベースの攻撃からワークロードを保護する手順を実施します。 AWS マネジメントコンソールと AWS CloudFormation を使用して、WAF 統合を備えた CloudFront をデプロイして多層防御方法を適用する方法を実施します。
1. EC2インスタンスの作成
WebアプリケーションとなるEC2インスタンスを作成します。
EC2インスタンス作成手順
- EC2コンソールより「インスタンスの起動」を選択する。
- 「名前とタグ」の「名前」に適切な名前を入力する。
- 「Application and OS Images」は「Amazon Linux 2023 AMI」を選択する。(デフォルト)
- 「インスタンスタイプ」は「t2.micro」を選択する。(デフォルト)
- 「キーペア(ログイン)」には、あれば既存のキーペアを選択。なければ「新しいキーペアの作成」を選択する。
- 「キーペアを作成」では、「キーペア名」を入力、「キーペアのタイプ」に「ED25519」を選択、「プライベートキーファイル形式」は使用しているツールによって選択し、「キーペアを作成」を選択する。
- 「ネットワーク設定」では「Allow SSH traffic from」に「自分のIP」を選択し、「インターネットからのHTTPトラフィックを許可」のチェックボックスをオンにする。
- 「ストレージを設定」はデフォルトのまま。
- 「高度な詳細」を開く。
- 「IAMインスタンスプロフィール」で「新しいIAMプロファイルの作成」を選択する。(ブラウザに別タブが開く。
- 「ロールを作成」を選択する。
- 「信頼されたエンティティタイプ」に「AWSのサービス」を、「ユースケース」の「サービスまたはユースケース」に「EC2」を選択した上で、ユースケースは「EC2」を選択し、「次へ」を選択する。
- 「許可ポリシー」の検索入力ボックスに「s3」を入力する。ポリシーの一覧から「AmazonS3ReadOnlyAccess」の右のチェックボックスをオンにし、「次へ」を選択する。
- 「ロールの詳細」の「ロール名」に「ec2-s3-read-only-role」を入力する。
- 「ロールを作成」を選択する。
- 作成完了のメッセージを確認する。
- 元タブのEC2インスタンス作成にて「IAMインスタンスプロフィール」に作成したロール「ec2-s3-read-only-role」を選択する。
- 「ユーザーデータ」に以下を入力する。
#!/bin/bash
yum update -y
yum install -y httpd
service httpd start
chkconfig httpd on
groupadd www
usermod -a -G www ec2-user
chown -R root\:www /var/www
chmod 2775 /var/www
find /var/www -type d -exec chmod 2775 {} +
find /var/www -type f -exec chmod 0664 {} +
- 「インスタンスを起動」を選択する。
- 開始のメッセージを確認する。
- インスタンス一覧より作成したインスタンスのステータスチェックが完了するのを確認する。
- 作成したインスタンスの詳細を開き、概要の右側にある「パブリック IPv4 DNS」のドメイン名を予めコピーする。
2. AWS WAFを設定する
CloudFormationを使ってWAFを構成します。
CloudFormationによるWAF作成手順
- CloudFormationコンソールを開き、「スタックの作成」より「新しいリソースを使用(標準)」を選択する。
- 「前提条件ーテンプレートの準備」の「テンプレートの準備」では「テンプレートの準備完了」、「テンプレートの指定」の「テンプレートソース」に「Amazon S3 URL」を選択の上、「Amazon S3 URL」に以下を貼り付け、「次へ」を選択する。
https://s3-us-west-2.amazonaws.com/aws-well-architected-labs/Security/Code/waf-global.yaml
- 「スタック名」に「waf」を入力する。
- 「パラメータ」は以下の表を参考に入力する。(全てデフォルトのままでOK)
項目名 | 説明 | 入力値(デフォルト) |
---|---|---|
WAFName | このスタックのリソースとエクスポート名に使用するベース名を入力します。たとえば、WAF は Lab1-ManualBlacklistIPRule1 などのベース名と組み合わせたエクスポート名を作成します。 | Lab1 |
WAFCloudWatchPrefix | 各ルールに使用する CloudWatch プレフィックスの名前を入力します (英数字のみ) | Lab1 |
WAFManualBlacklistIPRule1Name | 作成された WAFManualBlacklistIPRule1 リソースの CloudFormation リソース名 | ManualBlacklistIPRule1 |
WAFManualBlacklistIPSet1Name | 作成された WAFManualBlacklistIPSet1 リソースの CloudFormation リソース名 | ManualBlacklistIPSet1 |
WAFUserAgentBlacklistRule1Name | 作成された WAFUserAgentBlacklistRule1 リソースの CloudFormation リソース名 | UserAgentBlacklistRule1 |
WAFUserAgentByteSet1Tuple1String | ユーザー エージェント ブラックリスト ルールに含める文字列を入力し、必要に応じてパディングの例の名前を変更し、カンマ区切りの値が合計 10 個あることを確認します。 | 長いため割愛 |
WAFUserAgentBlacklistSet1Name | 作成された WAFUserAgentBlacklistSet1 リソースの CloudFormation リソース名 | UserAgentBlacklistSet1 |
WAFManualBlacklistIPRule1Name | インポートするManualBlacklistIPRuleの名前を入力してください。値が空の場合はルールは作成されません。 | ManualBlacklistIPRule1 |
WAFManualBlacklistIPRule1Action | COUNTか、BLOCK | COUNT |
WAFManualBlacklistUserAgentRule1Name | インポートするManualBlacklistUserAgentRuleの名前を入力してください。値が空の場合はルールは作成されません。 | UserAgentBlacklistRule1 |
WAFManualBlacklistUserAgentRule1Action | COUNTか、BLOCK | COUNT |
WAFSqlInjectionProtectionParam | 一般的な SQL インジェクション攻撃をブロックするように設計されたコンポーネントを有効にするには、「Yes」を選択します。 | Yes |
WAFSqlInjectionExceptionByteSet1Param | Yesか、No | Yes |
WAFSqlInjectionProtectionAction | COUNTか、BLOCK | COUNT |
WAFSqlInjectionExceptionByteSet1Tuple1Type | HEADER、METHOD、QUERY_STRING、URI、BODYのいずれか | URI |
WAFSqlInjectionExceptionByteSet1Tuple1String | SQL インジェクション ルールから除外する文字列を入力し、必要に応じてパディングの例の名前を変更し、カンマ区切りの値が合計 10 個あることを確認します。 | 長いため割愛 |
WAFSqlInjectionExceptionByteSet1Tuple1Transform | CMD_LINE、COMPRESS_WHITE_SPACE、HTML_ENTITY_DECODE、LOWERCASE、URL_DECODE、NONEのいずれか | NONE |
WAFSqlInjectionExceptionByteSet1Tuple1Position | CONTAINS、CONTAINS_WORD、EXACTLY、STARTS_WITH、ENDS_WITHのいずれか | CONTAINS |
WAFSqlInjectionExceptionByteSet1Tuple1Data | HEADER に設定されている場合のバイト一致タイプの値 | User-Agent |
WAFXssProtectionParam | 一般的な XSS 攻撃をブロックするように設計されたコンポーネントを有効にするには、[Yes] を選択します。 | Yes |
WAFXssExceptionByteSet1Param | Yesか、No | Yes |
WAFXssProtectionAction | COUNTか、BLOCK | COUNT |
WAFXssExceptionByteSet1Tuple1Type | HEADER、METHOD、QUERY_STRING、URI、BODYのいずれか | URI |
WAFXssExceptionByteSet1Tuple1String | Xss ルールから除外する文字列を入力し、必要に応じてパディングの例の名前を変更し、カンマ区切りの値が合計 10 個あることを確認します。 | 長いため割愛 |
WAFXssExceptionByteSet1Tuple1Transform | CMD_LINE、COMPRESS_WHITE_SPACE、HTML_ENTITY_DECODE、LOWERCASE、URL_DECODE、NONEのいずれか | NONE |
WAFXssExceptionByteSet1Tuple1Position | CONTAINS、CONTAINS_WORD、EXACTLY、STARTS_WITH、ENDS_WITHのいずれか | CONTAINS |
WAFXssExceptionByteSet1Tuple1Data | HEADER に設定されている場合のバイト一致タイプの値 | User-Agent |
WAFSizeProtectionParam | URI およびクエリ文字列における一般的なオーバーフロー攻撃をブロックするように設計されたコンポーネントを有効にするには、[Yes] を選択します。 | Yes |
WAFSizeExceptionByteSet1Param | Yesか、No | Yes |
WAFSizeProtectionAction | COUNTか、BLOCK | COUNT |
WAFSizeURI1 | URI の最大サイズ (バイト単位) (最大 8192) | 512 |
WAFSizeQuery1 | クエリ文字列の最大サイズ (バイト単位) (最大 8192) | 512 |
WAFSizeCookie1 | Cookie ヘッダーの最大サイズ (バイト単位) (最大 4096)。 サイズは、Web アプリケーションが Cookie に保存する情報の量によって決まります。 セッション トークンを Cookie 経由でのみ渡す場合は、サイズをセッション トークンと Cookie メタデータのシリアル化されたサイズ以下に設定します。 | 4096 |
WAFSizeExceptionByteSet1Tuple1Type | HEADER、METHOD、QUERY_STRING、URI、BODYのいずれか | URI |
WAFSizeExceptionByteSet1Tuple1String | サイズ制限ルールから除外する文字列を入力し、必要に応じてパディングの例の名前を変更し、カンマ区切りの値が合計 10 個あることを確認します。 | 長いため割愛 |
WAFSizeExceptionByteSet1Tuple1Transform | CMD_LINE、COMPRESS_WHITE_SPACE、HTML_ENTITY_DECODE、LOWERCASE、URL_DECODE、NONEのいずれか | NONE |
WAFSizeExceptionByteSet1Tuple1Position | CONTAINS、CONTAINS_WORD、EXACTLY、STARTS_WITH、ENDS_WITHのいずれか | CONTAINS |
WAFSizeExceptionByteSet1Tuple1Data | HEADER に設定されている場合のバイト一致タイプの値 | User-Agent |
WAFBlacklistByteSet1ProtectionParam | [Yes] を選択すると、コンポーネントが汎用バイト一致文字列をブロックできるようになります。 | Yes |
WAFBlacklistByteSet1ProtectionAction | COUNTか、BLOCK | COUNT |
WAFBlacklistByteSet1Tuple1Type | HEADER、METHOD、QUERY_STRING、URI、BODYのいずれか | HEADER |
WAFBlacklistByteSet1Tuple1String | バイト セット 1 ルールで拒否される文字列を入力します。追加には関連するバイト セットを更新する必要があります。必要に応じてサンプル パディングの名前を変更し、カンマ区切りの値が合計 10 個あることを確認します。 | 長いため割愛 |
WAFBlacklistByteSet1Tuple1Transform | CMD_LINE、COMPRESS_WHITE_SPACE、HTML_ENTITY_DECODE、LOWERCASE、URL_DECODE、NONEのいずれか | LOWERCASE |
WAFBlacklistByteSet1Tuple1Position | CONTAINS、CONTAINS_WORD、EXACTLY、STARTS_WITH、ENDS_WITHのいずれか | CONTAINS |
WAFBlacklistByteSet1Tuple1Data | HEADER に設定されている場合のバイト一致タイプの値 | User-Agent |
- 「次へ」を選択する。
- 「スタックオプションの設定」もデフォルトのままで「次へ」を選択する。
- 「レビュー」にて内容を確認する。
- 「送信」を選択する。
- 「ステータス」が「CREATE_IN_PROGRESS」から「CREATE_COMPLETE」に変わるまで待つ。
3. CloudFrontを構成する
作成したEC2、WAFを元にCloudFrontを構成します。
CloudFront構成手順
- CloudFrontコンソールより「CloudFrontディストリビューションを作成」選択する。
- 「オリジン」の「オリジンドメイン」に作成したEC2の詳細でコピーした「パブリック IPv4 DNS」を貼り付け、「プロトコル」に「HTTPのみ」を選択する。
- 「キャッシュキーとオリジンリクエスト」の「キャッシュポリシー」で「CachingOptimized」を選択する。
- 「ウェブアプリケーションファイアウォール(WAF)」で「セキュリティ保護を有効にする」を選択し、「既存のWAF設定」にチェックを入れ、「ウェブACLを選択」にて作成したWAFを選択する。
- 「ディストリビューションを作成」を選択する。
- 作成のメッセージを確認する。
- 作成したディストリビューションの詳細を確認し、「最終変更日」が「デプロイ」から日時に変わるのを待つ。
- 「ディストリビューションドメイン名」をコピーし、ブラウザのURLに貼り付け、WEBアプリケーションが応答することを確認する。
Appendix. クリーンアップ手順
作成したリソースのクリーンアップ手順です。
CloudFrontの削除手順
- CloudFrontコンソールより「ディストリビューション」を開き、対象のディストリビューションをオンにした上で「無効」を選択する。
- 「ディストリビューションを無効にしますか?」にて「無効」を選択する。
- 「ステータス」が「無効」であることを確認し、最終変更日が「デプロイ」から日時に変わるまで待つ。
- 対象のディストリビューションをオンにした上で「削除」を選択する。
- 「ディストリビューションを削除しますか?」にて「削除」を選択する。
- 削除メッセージを確認する。
WAF(CloudFormation)の削除手順
- CloudFormationコンソールよりスタックを開き、対象スタックをオンにした上で「削除」を選択する。
- 「スタックを削除しますか?」にて「削除」を選択する。
- 削除開始のメッセージを確認する。
- リストから削除されるまで待つ。
EC2インスタンスの削除手順
- EC2コンソールよりインスタンスを開き、対象のインスタンスをオンにした上で、「インスタンスの状態」から「インスタンスの終了」を選択する。
- 「終了インスタンス?」にて「終了」を選択する。
- 終了メッセージを確認する。
- 「終了済み」になるまで待つ。
不要になったインスタンスは削除できます。これは、インスタンスの終了 と呼ばれます。インスタンスの状態が shutting-down
または terminated
に変わったら、そのインスタンスへの課金は停止します。
インスタンスを削除した後に、接続または起動することはできません。ただし、同じ AMI から別のインスタンスを起動することができます。
インスタンスの削除後、インスタンスはしばらくの間コンソールに表示されたままですが、エントリは自動的に削除されます。終了したインスタンスのエントリを自分で削除することはできません。インスタンスを削除すると、タグやボリュームなどのリソースはインスタンスから徐々に関連付けが解除され、しばらくすると削除されたインスタンスでこれらのリソースが表示されなくなる可能性があります。
インスタンスが終了すると、そのインスタンスに関連付けられたすべてのインスタンスストアボリュームのデータが削除されます。
デフォルトでは、インスタンスの削除時に Amazon EBS のルートデバイスボリュームが自動的に削除されます。ただし、起動時にアタッチした追加の EBS ボリューム、または既存のインスタンスにアタッチした EBS ボリュームがある場合、デフォルトでは、インスタンスの削除後もそれらのボリュームは保持されます。この動作はボリュームの DeleteOnTermination
属性によって制御されますが、変更できます。