はじめに

AWS上でシステムを構築・運用する際、セキュリティは非常に重要な要素です。特に、EC2インスタンスからの意図しない外部への通信(アウトバウンド通信)を制御することは、情報漏洩や不正アクセスのリスクを低減するために不可欠です。

従来、セキュリティグループやネットワークACLでもIPアドレスベースでの制御は可能でしたが、特定のWebサイトやAPIエンドポイントなど、ドメイン名で通信先を制限したいケースも多くあります。
「特定の管理コンソールやAPIにしかアクセスさせたくない」「社内システムへのアクセスのみ許可したい」といった要望です。

この記事では、AWS Network Firewall (NWFW) を利用して、EC2インスタンスからのアウトバウンド通信を「このサイトだけ許可!」という形で指定したドメインのみに制限する方法を、具体的な設定手順とともに解説します。

構成イメージ

今回は、AWS Network Firewall Workshop で紹介されている 分散モデル (Distributed Model) を参考に構築します。
このモデルでは、各アベイラビリティゾーン (AZ) にNetwork Firewallのエンドポイントを配置し、EC2インスタンスからのアウトバウンド通信をそれぞれのAZ内のエンドポイントを経由させる構成となります。

(出典: AWS Network Firewall Workshop – Setup Network Firewall in Distributed Model)

https://catalog.workshops.aws/networkfirewall/en-US/setup/distributedmodel

構築手順

既存の検証用VPCに必要なリソースを追加・設定していきます。

1. Network Firewall用サブネットの追加

既存のVPCに、Network Firewallのエンドポイントを配置するためのパブリックサブネットを異なるAZに作成します。

  • otake-test-nwfw-subnet-public1-ap-northeast-1a
  • otake-test-nwfw-subnet-public2-ap-northeast-1c

2. Firewall Policy の作成

Network Firewallのルールセットを定義するポリシーを作成します。

3. Network Firewall の作成

実際にトラフィックを検査・フィルタリングするNetwork Firewallを作成します。

VPC: 既存の検証用VPCを選択

サブネット: 手順1で作成したNWFW用サブネット (otake-test-nwfw-subnet-public1-ap-northeast-1a, otake-test-nwfw-subnet-public2-ap-northeast-1c) を選択

Firewall Policy: 手順2で作成した otake-test-nwfw-policy を関連付けます。

準備完了!

指定したAZ分のエンドポイントが作成されているのがわかります

ログも有効にします。今回はCloudWatch Logsを使用します。

4. ルートテーブルの設定

EC2インスタンスからの通信がNetwork Firewallを経由するように、ルートテーブルを設定します。

既存パブリックサブネット用ルートテーブルの変更 (otake-test-rtb-public)

EC2インスタンスが配置されているパブリックサブネットに関連付けられているルートテーブルを編集します。
送信先 0.0.0.0/0 (すべてのインターネット向けトラフィック) のターゲットを、同じAZにあるNetwork Firewallのエンドポイントに変更します。

ap-northeast-1c用パブリックサブネットルートテーブルの新規作成と関連付け (otake-test-rtb-public-1c)

別AZ (ap-northeast-1c) のパブリックサブネット用に、新しいルートテーブルを作成します。
otake-test-rtb-public と同様に、送信先 0.0.0.0/0 のターゲットを、ap-northeast-1c のNetwork Firewallのエンドポイントに設定します。
このルートテーブルを ap-northeast-1c のパブリックサブネットに関連付けます。

Network Firewall Ingress ルートテーブルの作成 (otake-test-nwfw-ingress-rtb)

Network Firewallがインターネットゲートウェイからの戻りの通信を受け取れるように、専用のルートテーブルを作成します。

Edgeの関連付け: 作成したルートテーブルの「Edgeの関連付けを編集」から、インターネットゲートウェイ (otake-test-igw) を関連付けます。

ルートの追加
  • 送信先: EC2インスタンスが存在する各パブリックサブネットのCIDR、ターゲット: 対応するAZのNetwork Firewallのエンドポイント
    • 送信先 10.0.0.0/24 (ap-northeast-1aのパブリックサブネット)、ターゲット vpce-xxxx (ap-northeast-1aのNWFWエンドポイント)
    • 送信先 10.0.16.0/24 (ap-northeast-1cのパブリックサブネット)、ターゲット vpce-yyyy (ap-northeast-1cのNWFWエンドポイント)

5. Network Firewall ルールグループの作成

いよいよ、通信を許可するドメインを指定するルールグループを作成します。今回はステートレスルールグループを使用します。

  • 名前: otake-test-nwfw-rule-grp
  • タイプ: ステートフル
  • ルールグループ設定:
    • ステートフルルールグループ を選択します。
    • ドメインリスト タイプを選択します。
    • アクション: 許可 (指定したドメインへの通信を許可)
    • プロトコル: HTTP, HTTPS (HTTPS通信を対象とする)
    • ドメインリスト: 許可したいドメインを指定します。今回はAmazon関連のドメインのみを許可します。
.amazon.co.jp
.amazon.com
.aws.amazon.com
# SSM接続に必要なエンドポイントも追加
.ec2messages.ap-northeast-1.amazonaws.com
.ssm.ap-northeast-1.amazonaws.com
.ssmmessages.ap-northeast-1.amazonaws.com

不足があったため後にドメインを追加

作成したルールグループ (otake-test-nwfw-rule-grp) を、Firewall Policy (otake-test-nwfw-policy) のステートフルルールグループに追加します。

動作確認

設定が完了したら、EC2インスタンスにSSMなどで接続し、実際に通信が制御されているか確認します。

  • 許可されていないドメインへのアクセス (Google)
    NWFWによって通信がブロックされ、SSLハンドシェイク中にエラーが発生しました。想定通りの動作です。
[ec2-user@ip-10-0-4-184 ~]$ curl -v https://www.google.com/
* Host www.google.com:443 was resolved.
* IPv6: 2404:6800:4004:820::2004
* IPv4: 142.251.222.36
*   Trying 142.251.222.36:443...
* Connected to www.google.com (142.251.222.36) port 443
* ALPN: curl offers h2,http/1.1
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
*  CAfile: /etc/pki/tls/certs/ca-bundle.crt
*  CApath: none
* OpenSSL SSL_connect: SSL_ERROR_SYSCALL in connection to www.google.com:443
* Closing connection
curl: (35) OpenSSL SSL_connect: SSL_ERROR_SYSCALL in connection to www.google.com:443
[ec2-user@ip-10-0-4-184 ~]$    
  • 許可されたドメインへのアクセス (Amazon)
    許可されたドメインへのアクセスは成功しました。
[ec2-user@ip-10-0-4-184 ~]$ curl -v https://www.amazon.co.jp/
* Host www.amazon.co.jp:443 was resolved.
* IPv6: 2600:9000:2142:d800:9:73fc:b895:4201, 2600:9000:2142:800:9:73fc:b895:4201, 2600:9000:2142:3a00:9:73fc:b895:4201, 2600:9000:2142:6600:9:73fc:b895:4201, 2600:9000:2142:9400:9:73fc:b895:4201, 2600:9000:2142:ea00:9:73fc:b895:4201, 2600:9000:2142:c600:9:73fc:b895:4201, 2600:9000:2142:6000:9:73fc:b895:4201
* IPv4: 3.168.249.224
*   Trying 3.168.249.224:443...
* Connected to www.amazon.co.jp (3.168.249.224) port 443
* ALPN: curl offers h2,http/1.1
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
*  CAfile: /etc/pki/tls/certs/ca-bundle.crt
*  CApath: none
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
* TLSv1.3 (IN), TLS handshake, Certificate (11):
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
* TLSv1.3 (IN), TLS handshake, Finished (20):
* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.3 (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / TLS_AES_128_GCM_SHA256 / X25519 / RSASSA-PSS
* ALPN: server accepted h2
* Server certificate:
*  subject: CN=www.amazon.co.jp
*  start date: Mar 15 00:00:00 2025 GMT
*  expire date: Feb 20 23:59:59 2026 GMT
*  subjectAltName: host "www.amazon.co.jp" matched cert's "www.amazon.co.jp"
*  issuer: C=US; O=DigiCert Inc; CN=DigiCert Global CA G2
*  SSL certificate verify ok.
# ... (HTMLコンテンツが表示される) ...
[ec2-user@ip-10-0-4-184 ~]$ curl -v https://www.amazon.com/
* Host www.amazon.com:443 was resolved.
* IPv6: 2600:9000:21c5:a400:7:49a5:5fd4:b121, 2600:9000:21c5:c000:7:49a5:5fd4:b121, 2600:9000:21c5:3200:7:49a5:5fd4:b121, 2600:9000:21c5:dc00:7:49a5:5fd4:b121, 2600:9000:21c5:fa00:7:49a5:5fd4:b121, 2600:9000:21c5:6e00:7:49a5:5fd4:b121, 2600:9000:21c5:ac00:7:49a5:5fd4:b121, 2600:9000:21c5:4600:7:49a5:5fd4:b121
* IPv4: 13.225.166.101
*   Trying 13.225.166.101:443...
* Connected to www.amazon.com (13.225.166.101) port 443
* ALPN: curl offers h2,http/1.1
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
*  CAfile: /etc/pki/tls/certs/ca-bundle.crt
*  CApath: none
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
* TLSv1.3 (IN), TLS handshake, Certificate (11):
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
* TLSv1.3 (IN), TLS handshake, Finished (20):
* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.3 (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / TLS_AES_128_GCM_SHA256 / X25519 / RSASSA-PSS
* ALPN: server accepted h2
* Server certificate:
*  subject: CN=www.amazon.com
*  start date: Sep 13 00:00:00 2024 GMT
*  expire date: Aug 23 23:59:59 2025 GMT
*  subjectAltName: host "www.amazon.com" matched cert's "www.amazon.com"
*  issuer: C=US; O=DigiCert Inc; CN=DigiCert Global CA G2
*  SSL certificate verify ok.
    # ... (HTMLコンテンツが表示される) ...

【注意】SSH接続ができなくなる場合の対処法

上記の設定を行うと、EC2インスタンスからのアウトバウンド通信はNWFWを経由するようになります。
しかし、これによりインターネット側からEC2インスタンスへのインバウンド通信(例えばSSH接続)ができなくなる場合があります。
これは、戻りのトラフィックがNWFWのルールによってブロックされる、またはIngressルートテーブルの設定が不十分な場合に発生します。

発生した問題:

❯ ssh -i .ssh/otake-test-key-sre3.pem ec2-user@XXXX
ssh_dispatch_run_fatal: Connection to 18.183.73.107 port 22: Operation timed out

対策: SSH接続元IPを許可するステートフルルールを追加

この問題を解決するために、特定の送信元IPアドレスからの通信を明示的に許可するステートフルルールグループを作成し、Firewall Policyに追加します。
ステートフルルールは、ステートレスルールよりも先に評価(1)され、戻りの通信も自動的に許可してくれます。
(
1)ファイヤーウォールポリシー作成時に、ステートレスデフォルトアクションを「ステートフルルールに転送」を選択したため

  1. ステートフルルールグループの作成:
    • 名前: otake-test-nwfw-ssh-stateful-grp
    • タイプ: ステートフル
    • キャパシティ: 必要な数値を設定 (例: 10)
    • ルールの追加 (標準ステートフルルール):
      • アクション: パス (通信を許可)
      • プロトコル: TCP
      • 送信元 CIDR: SSH接続元のグローバルIPアドレス/32 (例: xxx.xxx.xxx.xxx/32 のように指定)
      • 送信元ポート: 任意のポート
      • 送信先: すべて(EC2インスタンス配置のサブネットCIDRも可能)
      • 送信先ポート: 任意のポート
      • トラフィックの方向: 転送


2. Firewall Policy への追加:
* 作成したステートフルルールグループ (otake-test-nwfw-ssh-stateful-grp) を Firewall Policy (otake-test-nwfw-policy) のステートフルルールグループセクションに追加します。
* 重要: このSSH許可ルールが他のルール(特に暗黙のDenyや広範なDropルール)よりも先に評価されるように、ルールグループ内の優先度を最も高く 設定することが重要です。

この設定により、指定したIPアドレスからのSSH接続は、ドメインフィルタリングの影響を受けずに許可されるようになります。

再度動作確認

SSH接続

❯ ssh -i .ssh/otake-test-key-sre3.pem ec2-user@XXXX

A newer release of "Amazon Linux" is available.
  Version 2023.6.20250128:
  Version 2023.6.20250203:
  Version 2023.6.20250211:
  Version 2023.6.20250218:
  Version 2023.6.20250303:
  Version 2023.6.20250317:
  Version 2023.7.20250331:
  Version 2023.7.20250414:
Run "/usr/bin/dnf check-release-update" for full release and version update info
   ,     #_
   ~\_  ####_        Amazon Linux 2023
  ~~  \_#####\
  ~~     \###|
  ~~       \#/ ___   https://aws.amazon.com/linux/amazon-linux-2023
   ~~       V~' '->
    ~~~         /
      ~~._.   _/
         _/ _/
       _/m/'
Last login: Wed Apr 23 08:07:42 2025 from XXXX
[ec2-user@ip-10-0-4-184 ~]$ 
[ec2-user@ip-10-0-4-184 ~]$ 

指定したドメインのみ接続が可能!

Googleへは接続不可

[ec2-user@ip-10-0-4-184 ~]$ curl -v https://www.google.com
* Host www.google.com:443 was resolved.
* IPv6: 2404:6800:4004:818::2004
* IPv4: 142.251.42.196
*   Trying 142.251.42.196:443...
* Connected to www.google.com (142.251.42.196) port 443
* ALPN: curl offers h2,http/1.1
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
*  CAfile: /etc/pki/tls/certs/ca-bundle.crt
*  CApath: none
* OpenSSL SSL_connect: SSL_ERROR_SYSCALL in connection to www.google.com:443 
* Closing connection
curl: (35) OpenSSL SSL_connect: SSL_ERROR_SYSCALL in connection to www.google.com:443 
[ec2-user@ip-10-0-4-184 ~]$ 

Amazonは接続が可能!

[ec2-user@ip-10-0-4-184 ~]$ curl -v https://www.amazon.com
* Host www.amazon.com:443 was resolved.
* IPv6: 2600:9000:27b9:f200:7:49a5:5fd4:b121, 2600:9000:27b9:3e00:7:49a5:5fd4:b121, 2600:9000:27b9:2000:7:49a5:5fd4:b121, 2600:9000:27b9:c200:7:49a5:5fd4:b121, 2600:9000:27b9:5800:7:49a5:5fd4:b121, 2600:9000:27b9:5400:7:49a5:5fd4:b121, 2600:9000:27b9:f000:7:49a5:5fd4:b121, 2600:9000:27b9:e800:7:49a5:5fd4:b121
* IPv4: 23.62.102.143
*   Trying 23.62.102.143:443...
* Connected to www.amazon.com (23.62.102.143) port 443
* ALPN: curl offers h2,http/1.1
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
*  CAfile: /etc/pki/tls/certs/ca-bundle.crt
*  CApath: none
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
* TLSv1.3 (IN), TLS handshake, Certificate (11):
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
* TLSv1.3 (IN), TLS handshake, Finished (20):
* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):

まとめ

AWS Network Firewall を利用することで、従来のIPアドレスベースの制御に加え、より柔軟なドメインベースでのアウトバウンド通信制御が可能になります。「このサイトだけにアクセスさせたい」という具体的な要件も、ドメインリスト機能を使えば簡単に実現できます。

今回はステートレスルールグループのドメインリスト機能を使って特定のドメインのみを許可し、さらに予期せぬSSH接続問題を解決するためにステートフルルールを使って特定のIPからの接続を許可する設定を行いました。

Network Firewallは高機能な反面、ルートテーブルの設定(特にIngressルートテーブル)やルール評価順序など、理解しておくべきポイントがいくつかあります。
しかし、これらを適切に設定することで、EC2インスタンスのセキュリティを大幅に向上させることができます。
ぜひ、実際のセキュリティ要件に合わせて活用してみてください!