
AWS Network Firewallのコンテナ関連付け(Container Association)を試してみた
こんにちは、林です。
2026年6月に、AWS Network Firewallにコンテナ関連付け(Container Association)が追加されました。
EKSのPodをnamespaceやラベルで選択し、そのIPアドレスの一覧をNetwork Firewallのルールから参照できる機能です。
どのような機能なのか、EKSのアプリケーションごとに接続先のドメインを制限するルールを構成して試してみました。
コンテナ関連付けとは
Network Firewallのステートフルルールは、「このIPアドレスからこの宛先への通信を許可する」のように、送信元と宛先をIPアドレスで指定します。
EKSでは、アプリケーションはPodという単位で稼働します。PodにはVPCのIPアドレスが1つずつ割り当てられますが、Podを再作成するたびに別のIPアドレスになります。
そのため、「このアプリケーションからの通信のみを許可する」というルールを、IPアドレスでは定義できませんでした。
コンテナ関連付けは、条件に一致するPodのIPアドレスの一覧をNetwork Firewallが自動で保持し、ルールの送信元や宛先として参照できるようにするリソースです。EKSまたはECSのクラスターを指定して作成します。
追加の料金はなく、Network Firewallの利用料金に含まれます。
Podの選択
Podの条件には、namespaceとラベルを使います。
apps namespaceに、ラベル app: ai-agent の生成AIアプリと、ラベル app: web-app のWebアプリが稼働している環境を例にします。
# 生成AIアプリのPod
apiVersion: v1
kind: Pod
metadata:
name: ai-agent
namespace: apps # 条件に使うnamespace
labels:
app: ai-agent # 条件に使うラベル(キー: app、値: ai-agent)
---
# WebアプリのPod
apiVersion: v1
kind: Pod
metadata:
name: web-app
namespace: apps # 同じnamespace
labels:
app: web-app # ラベルの値のみが異なる
コンテナ関連付けの条件を「namespaceが apps、ラベルが app=ai-agent」とすると、生成AIアプリのPodのみが一覧に含まれます。
条件を「namespaceが apps」のみにすると、2つのPodがどちらも一覧に含まれます。
アプリケーションごとに許可する接続先を変えたい場合は、アプリケーションごとにコンテナ関連付けを作成します。
ルールでの使い方
Network Firewallでは、通信を許可・拒否するルールをルールグループにまとめて定義します。
ルールの送信元や宛先にはIPアドレスを直接指定するほか、IPセット参照という機能で、プレフィックスリストなどのIPアドレスの集合を変数名で参照できます。
コンテナ関連付けは、このIPセット参照の参照先として新たに指定できるようになりました。
ai-agentのPodの一覧を AI_AGENT という変数名で登録すると、送信元に @AI_AGENT を指定したルールを定義できます。
ai-agentのPodから api.openai.com へのTLS通信を許可するルールです。
pass tls @AI_AGENT any -> any 443 (tls.sni; content:"api.openai.com"; startswith; endswith; nocase; flow:to_server; sid:1;)
接続先のドメインは、SNIで判定します。SNIは、TLSの接続開始時にクライアントが送る接続先のホスト名です。
PodのIPアドレス一覧の更新
PodのIPアドレスの一覧は、Network Firewallが自動で更新します。
Podが再作成されてIPアドレスが変わると、新しいIPアドレスが一覧に追加され、古いIPアドレスは一覧から削除されます。Podが増えた場合も同様です。
利用者が設定するのは、コンテナ関連付けの作成時に指定するクラスターと条件のみです。
クラスター側に追加する設定はなく、PodのIPアドレスが変わるたびにルールを修正する必要もありません。
制御の対象になる通信
制御できるのは、Network Firewallを通過する通信のみです。
Podから外部へ向かう通信がFirewallのエンドポイントを経由するように、ルートテーブルを構成します。VPC内で完結するPod間の通信などは対象外です。
また、Firewallに届く通信の送信元は、PodのIPアドレスである必要があります。公式ドキュメントのネットワーク要件に次の記載があります。
Disable SNAT (Amazon EKS) – For Amazon EKS pods, disable source NAT (SNAT) so that the firewall sees the pod's original IP address. If SNAT is enabled, traffic arrives at the firewall with the node IP, not the pod IP, and rules referencing the container association do not match.
EKSでPodにIPアドレスを割り当てるVPC CNIは、VPCの外部へ向かう通信の送信元を、既定ではノードのIPアドレスに変換します。この変換を無効にし、PodのIPアドレスのままFirewallへ届くようにします。
検証の構成
確認する内容
apps namespaceに、生成AIアプリのai-agentとWebアプリのweb-appを配置します。
ai-agentは api.openai.com のみ、web-appは api.github.com のみに接続できるルールを定義します。
各Podから curl で両方のAPIに接続し、許可したAPIにのみ接続できることを確認します。
| Pod | ラベル | api.openai.com | api.github.com |
|---|---|---|---|
| ai-agent | app=ai-agent | 接続できる | 接続できない |
| web-app | app=web-app | 接続できない | 接続できる |
環境

EKSノードはプライベートサブネットに1台あり、2つのPodはこのノードで稼働します。
Podから外部へ向かう通信は、Firewallのエンドポイント、NATゲートウェイ、インターネットゲートウェイの順に通過します。
| 項目 | 内容 |
|---|---|
| Kubernetesバージョン | 1.37 |
| ノード | マネージド型ノードグループ、t3.medium、AL2023、1台 |
| VPC CNIアドオン | v1.22.4-eksbuild.8 |
| コンテナイメージ | public.ecr.aws/amazonlinux/amazonlinux:2023 |
| リージョン | 東京(ap-northeast-1) |
手順
1. コンテナ関連付けを作成する
コンテナ関連付けは、アプリケーションごとの2つと、apps namespace全体の1つを作成します。
| 名前 | 属性フィルター | 一覧に含まれるPod |
|---|---|---|
| ai-agent-pods | namespace=apps、app=ai-agent | ai-agent |
| web-app-pods | namespace=apps、app=web-app | web-app |
| app-pods | namespace=apps | ai-agent、web-app |
執筆時点(2026/10/30)で、CloudFormationの AWS::NetworkFirewall::ContainerAssociation はバージニア北部とオレゴンでは利用でき、東京では利用できませんでした。
そのため、コンソールまたはAWS CLIで作成します。今回はAWS CLIで作成しました。
aws network-firewall create-container-association \
--container-association-name ai-agent-pods \
--type EKS \
--container-monitoring-configurations '[{
"ClusterArn": "arn:aws:eks:ap-northeast-1:123456789012:cluster/sample-cluster",
"AttributeFilters": [
{"Key": "namespace", "Value": "apps"},
{"Key": "app", "Value": "ai-agent"}
]
}]'
作成後、数秒で Status が ACTIVE になります。
describe-container-association の ResolvedCidrCount が、一覧に含まれるIPアドレスの数です。
Podを作成してから約20秒で、ai-agent-podsとweb-app-podsは1、app-podsは2になりました。
{
"ContainerAssociationName": "ai-agent-pods",
"Type": "EKS",
"Status": "ACTIVE",
"ResolvedCidrCount": 1
}
2. ルールグループでコンテナ関連付けを参照する
ルールグループの IPSetReferences にコンテナ関連付けのARNを変数名で登録すると、ルールの送信元に @変数名 で指定できます。変数名は AI_AGENT、WEB_APP、APPS にしました。
ルールグループに設定した内容です。
{
"RulesSource": {
"RulesString": "pass tls @AI_AGENT any -> any 443 (msg:\"ai-agent: allow api.openai.com\"; tls.sni; content:\"api.openai.com\"; startswith; endswith; nocase; flow:to_server; sid:1; rev:1;)\npass tls @WEB_APP any -> any 443 (msg:\"web-app: allow api.github.com\"; tls.sni; content:\"api.github.com\"; startswith; endswith; nocase; flow:to_server; sid:2; rev:1;)\ndrop tcp @APPS any -> any 443 (msg:\"apps: drop TLS to other domains\"; flow:established,to_server; sid:3; rev:1;)\ndrop tcp @APPS any -> any !443 (msg:\"apps: drop non-443 TCP\"; sid:4; rev:1;)\ndrop udp @APPS any -> any any (msg:\"apps: drop UDP\"; sid:5; rev:1;)\npass ip any any -> any any (msg:\"others: pass established\"; flow:established,to_server; sid:6; rev:1;)"
},
"ReferenceSets": {
"IPSetReferences": {
"AI_AGENT": { "ReferenceArn": "arn:aws:network-firewall:ap-northeast-1:123456789012:container-association/ai-agent-pods" },
"WEB_APP": { "ReferenceArn": "arn:aws:network-firewall:ap-northeast-1:123456789012:container-association/web-app-pods" },
"APPS": { "ReferenceArn": "arn:aws:network-firewall:ap-northeast-1:123456789012:container-association/app-pods" }
}
},
"StatefulRuleOptions": { "RuleOrder": "STRICT_ORDER" }
}
RulesString に書いたルールです。
| sid | 送信元 | 通信 | アクション |
|---|---|---|---|
| 1 | @AI_AGENT(ai-agent) | SNIが api.openai.com のTLS |
許可 |
| 2 | @WEB_APP(web-app) | SNIが api.github.com のTLS |
許可 |
| 3 | @APPS(apps namespaceの全Pod) |
443番ポートへのTCP | 拒否 |
| 4 | @APPS | 443番ポート以外へのTCP | 拒否 |
| 5 | @APPS | UDP | 拒否 |
| 6 | 任意(ノード、apps 以外のPod) |
任意 | 許可 |
評価順序はSTRICT_ORDERで、上から順に評価し、最初に一致したルールが適用されます。
ai-agentから api.openai.com への通信はsid 1で許可され、api.github.com への通信はsid 1、2に一致せずsid 3で拒否されます。
describe-rule-group で更新トークンを取得し、update-rule-group でrulegroup.jsonを反映します。
コンソールでは、ルールグループの編集画面の「IPセット参照」で同じ設定ができます。
aws network-firewall describe-rule-group \
--rule-group-arn arn:aws:network-firewall:ap-northeast-1:123456789012:stateful-rulegroup/pod-egress \
--query UpdateToken --output text
aws network-firewall update-rule-group \
--rule-group-arn arn:aws:network-firewall:ap-northeast-1:123456789012:stateful-rulegroup/pod-egress \
--update-token 0a1b2c3d-4e5f-6789-abcd-ef0123456789 \
--rule-group file://rulegroup.json
3. ファイアウォールポリシーを作成する
ファイアウォールポリシーに、手順2のルールグループを関連付けます。
ステートフルルールの既定のアクションは aws:drop_established と aws:alert_established にしました。どのルールにも一致しなかった通信を、TCPの接続が確立した後に拒否してログに記録します。SNIはTCPの接続が確立した後に送られるので、確立前に拒否するとsid 1、2で判定できません。
{
"StatelessDefaultActions": ["aws:forward_to_sfe"],
"StatelessFragmentDefaultActions": ["aws:forward_to_sfe"],
"StatefulEngineOptions": { "RuleOrder": "STRICT_ORDER" },
"StatefulDefaultActions": ["aws:drop_established", "aws:alert_established"],
"StatefulRuleGroupReferences": [
{ "ResourceArn": "arn:aws:network-firewall:ap-northeast-1:123456789012:stateful-rulegroup/pod-egress", "Priority": 10 }
]
}
4. VPC CNIの外部SNATを有効にする
VPC CNIの AWS_VPC_K8S_CNI_EXTERNALSNAT を true にしました。ノードは送信元を変換せず、PodのIPアドレスのままNATゲートウェイへ送ります。
設定はアドオンの設定値で指定します。
aws eks update-addon --cluster-name sample-cluster --addon-name vpc-cni \
--resolve-conflicts OVERWRITE \
--configuration-values '{"env":{"AWS_VPC_K8S_CNI_EXTERNALSNAT":"true"}}'
5. Podを作成する
「Podの選択」のマニフェストで、apps namespaceにai-agentとweb-appを作成しました。
kubectl apply -f pods.yaml
結果
各Podから、https://api.openai.com/v1/models と https://api.github.com/ に curl で接続しました。タイムアウトは10秒です。
kubectl exec -n apps ai-agent -- curl -sS -m 10 -o /dev/null -w '%{http_code}' https://api.openai.com/v1/models
| Pod | api.openai.com | api.github.com |
|---|---|---|
| ai-agent | 401 | タイムアウト |
| web-app | タイムアウト | 200 |
ai-agentとweb-appは、それぞれ許可したAPIにのみ接続できました。
api.openai.com の401は、APIキーを付けずに接続したときの応答で、TLSの接続が成立しAPIサーバーまで到達しています。
拒否した通信は、アラートログに記録されます。
web-appから api.openai.com への接続を拒否したログから、送信元、宛先、一致したルール、SNIの項目を抜粋しました。
{
"event": {
"src_ip": "10.40.10.95",
"dest_ip": "162.159.140.245",
"dest_port": 443,
"alert": {
"signature_id": 3,
"signature": "apps: drop TLS to other domains",
"action": "blocked",
"metadata": {
"container_association": ["APPS"]
}
},
"tls": {
"sni": "api.openai.com",
"version": "TLS 1.3"
},
"verdict": { "action": "drop" }
}
}
alert.metadata.container_association に、一致したルールの変数名が記録されます。
送信元のIPアドレスからPodを調べなくても、どのコンテナ関連付けの通信を拒否したかが分かります。
tls.sni は接続先のドメインです。
外部SNATを既定に戻した場合
AWS_VPC_K8S_CNI_EXTERNALSNAT を false に戻し、同じ接続を試しました。
| Pod | api.openai.com | api.github.com |
|---|---|---|
| ai-agent | 401 | 200 |
| web-app | 401 | 200 |
どちらのPodも、どちらのAPIにも接続できました。アラートログには何も記録されませんでした。
Firewallのフローログで、Firewallに届いた通信の送信元IPアドレスを確認しました。
| 外部SNAT | 送信元IPアドレス |
|---|---|
| 有効(true) | 10.40.10.142(ai-agent)、10.40.10.95(web-app)、10.40.10.195(ノード) |
| 既定(false) | 10.40.10.195(ノード) |
既定では送信元がノードのIPアドレスになり、どのコンテナ関連付けにも一致しません。
すべての通信がsid 6で許可されていました。
注意点
ルールグループを更新してから、Firewallに反映されるまで数分かかりました。反映前に接続すると、更新前のルールで判定されます。反映されたかどうかは、describe-firewall の ConfigurationSyncStateSummary が IN_SYNC になっているかで確認できます。
また、コンテナ関連付けの参照には制約があります。
- 同じルールグループの中で、コンテナ関連付けの参照と、プレフィックスリストなどのIPセット参照は混在できない
- 1つのルールグループから参照できるコンテナ関連付けは30個まで
まとめ
Network Firewallのコンテナ関連付けを試してみました。
- namespaceとラベルで選択したPodのIPアドレスの一覧を、ルールから
@変数名で参照できる - Podが増減しても一覧は自動で更新され、ルールの修正は不要
- アラートログに、一致したルールの変数名が記録される
- VPC CNIの外部SNATを有効にし、PodのIPアドレスのままFirewallに届くようにする必要がある
この記事がどなたかの参考になれば幸いです。最後までご覧いただきありがとうございました!
参考








