CodlessCode
AWS

CloudFront と EC2 を 直接 つないで WAF でIP制限する

CloudFront と EC2 を 直接 つないで WAF でIP制限します。
普通は取らない構成ですが、興味があったので、自分用のEC2ひとつを実験台にしてやってみました。
こんな方に向けて発信しています。

  • CloudFrontとEC2を直接つなぎたい
  • EC2は1台しか使わない
  • 柔軟にIP制限したい
  • ドメイン持ってないけどHTTPSアクセスしたい
  • なるべく安くおさめたい
 

概要

全体図

メインの内容はEC2とCloudFrontを直接つなぐことです。
結論から言うと、EC2のパブリックIPv4 DNSをCloudFrontのオリジンドメイン名に指定します。

作業としてすることは3つです。

  1. EC2準備
  2. CloudFront準備
  3. WAF定義

   
前提として、東京リージョンで作業します。

 

EC2 準備

 

インスタンス起動

以下のようにEC2を起動しました。

  1. Amazon Linux 2 – 64ビット (x86)
  2. t3.micro ( 無料枠ある方は t2.micro でも )
  3. セキュリティグループ名cloudfront-ec2とし、2つルールを追加
    • SSH・マイIP
    • HTTP・Anywhere-IPv4(任意の場所)
  4. 任意のキーペア指定

    
他の項目はデフォルトのままです。
セキュリティグループに HTTP・Anywhere-IPv4 を追加しないと、CloudFrontからのアクセスを弾いてしまいます。
EC2が野ざらしになるのですが、ここの修正は最後にやります。

 

Webサーバーインストール

適当なWebページが開けるようにApache2.4をインストールしておきます。
SSHして以下を実行します。

sudo yum -y update
sudo yum -y upgrade
sudo yum -y install httpd
sudo systemctl start httpd

# サービスが自動起動するように設定する
sudo systemctl enable httpd

# Apacheのバージョン確認
httpd -v
# Server version: Apache/2.4.52 ()
# Server built:   Dec 30 2021 21:40:08

http://{インスタンスのパブリックIPv4 DNS}にアクセスして、Apacheのテストページが表示されればOKです。
※http://でアクセスします。

 

CloudFront 準備

私は以下のように作成しました。

  1. オリジンドメイン:EC2のパブリック IPv4 DNS
  2. プロトコル:HTTPのみ
  3. ビューワープロトコルポリシー:HTTP and HTTPS
  4. 許可された HTTP メソッド:GET, HEAD, OPTIONS, PUT, POST, PATCH, DELETE
  5. Cache policy and origin request policy (recommended) を選択
  6. キャッシュポリシー:CachingOptimized

他はデフォルトのままです。
ちなみに「AWS WAF ウェブ ACL – オプション」は後で設定します。

(私みたいな)せっかちな人は気をつけて。
CloudFrontはすぐに使えるようになりません
作成後にディストリビューション一覧を見ると、最終変更日が「デプロイ」になっているかもしれません。
「有効」になるまで待ちませう。

CloudFrontが有効になったら、https://{CloudFrontのドメイン名}でブラウザからアクセスしてみます。
※HTTPSでアクセスします。

Apacheのテストページが表示されればOKです。

Apacheテストページ
 

WAF でIP制限

 

IPセット定義

最後にWAFを使ってホワイトリスト形式でIP制限します。
ここでは自分のIPのみ許可するようにしてみます。

  1. WAFのホームへアクセス
  2. サイドメニューからIP setクリック
  3. Create IP setの左隣のプルダウンからGlobal (CloudFront)を選択してCreate IP set
  4. IP set name:適宜入力(例:developer-ipset)
  5. Region:Global (CloudFront)
  6. IP version:IPv4
  7. IP addresses:アクセスを許可したいIPを入力(例:自宅のIP/32)
IPセット定義
 

Web ACL作成

作成したIPセットを使ってWebACLを作成します。

  1. サイドメニューから「Web ACLs」クリック
  2. プルダウンからGlobal (CloudFront)を選択してCreate web ACL
  3. 先にフォームの上から4番目のResource type → CloudFront distributions
  4. Resorce:適宜入力(例:developer-web-acl)
  5. CloudWatch metric name:リソースと同じ
  6. Associated AWS resources で Add AWS resources
  7. 先ほど作成したCloudFrontを選択して Add して Next
  8. Add Rules から Add my own rules and rule groups を選択
  9. IP set を選択
  10. Rule:適宜入力(例:allow-ipset)
  11. IP set:先ほど作成したIPセットを選択
  12. Source IP Address・Allowを選択してAdd rule
  13. Default action:Block して Next
  14. Set rule priorityはデフォルトのままで
  15. Configure metricsはデフォルトのままで
  16. Review and create web ACLで内容を確認してCreate web ACL
WebACL作成
 

CloudFront との紐づけを確認

CloudFrontのディストリビューション詳細の 一般 → 設定 で
WAFのWebACLが適用されているのがわかります。
これで準備ができたので、CloudFront経由でEC2にアクセスして確認しましょう。

これで当初の目的が達成できました🎉
これから野ざらしになっているEC2のアクセス制御をしていきます!

 

WAFが適用されているのを確認(おまけ)

さっきからずっとアクセスできていたので、WAFがちゃんと機能しているかわかりにくいですね。
そこで、先ほどWEbACLで定義したIPセットのアクションをBlockに変更してみます。
   
WebACL → 作成したACL → Rulesタブ → 該当のIPセット → 右上のEdit
Action を BlockにしてSaveします。
  
自分のIPをブロックしたので、再びアクセスすると「403」エラーでアクセスできなくなりました。
   

アクセスがブロックされることの確認

   
WAFがちゃんと機能していることがわかりました。
これでばっちりなので、BlockしたのをAllowに戻しておきましょう。

 

EC2のアクセス制御

最後に、野ざらしになっているEC2を何とかします。

具体的にすることは
CloudFrontにカスタムヘッダーを追加し、Apache側でカスタムヘッダーが確認できたらアクセスを許可
するように設定します。

 

CloudFrontにカスタムヘッダー追加

CloudFrontのディストリビューション詳細を開き、オリジンタブから該当のオリジンを編集します。
カスタムヘッダーのキーと値は内緒です。
あとで使うので忘れないでください。
   

CloudFrontにカスタムヘッダーを追加
 

Apacheのバーチャルホストの設定を追加

既存の設定ファイルはそのままにして、以下のような内容で/etc/httpd/conf.d/vhost.confを作成します。
作成済みの方は、一番上に追加します。
2.2では書き方が違うかもしれないので注意です。

<VirtualHost *:80>
  <Location '/'>
    SetEnvIf カスタムヘッダ "カスタムヘッダの値" ALLOW_ACCESS
    Require all denied
    Require env ALLOW_ACCESS
  </Location>
</VirtualHost>

Apacheの設定ファイルに大事な値を直書きしてるので、気持ち悪い人は工夫してください💦
設定ファイルを変更したら、Apacheを再起動します。

sudo service httpd restart
 

アクセス確認

http://{EC2のIP}の場合

アクセスNG
 

https://{CloudFrontのドメイン}の場合

アクセスOK
 
 
 

まとめ

CloudFrontのオリジンドメイン名にEC2のパブリックIPv4 DNSを指定して、直接つなげることができました。

最後までお読みいただきありがとうございました。
参考になれば幸いです。