セキュリティグループとネットワークACL
戻りの宛先ポートと状態追跡を踏まえて、どの制御が通信を止めたかを規則とパケットから説明します。
説明
通信の許可を確認するときは、要求だけでなく応答も調べます。AWSのSecurity Groupは許可した通信の応答を追跡する一方、Network ACLは往路と復路をそれぞれの方向の規則で評価します。
10.0.1.10:49152から198.51.100.20:443へのTCPのSYNとSYN+ACKを使います。パケットの送信元・宛先ポートを読み、戻りを443宛てとして設定した誤りを直します。その後、ACLの番号順と、Security Groupの許可がない場合を確認します。
戻りの宛先ポートと状態追跡を踏まえて、どの制御が通信を止めたかを規則とパケットから説明します。
1要求と応答の四つのポート
固定パケットを数値で表示します。Flags [S]はSYN、[S.]はSYNとACKです。
tcpdump -nn -tt -Sアドレス・ポート・TCP番号を数値で表示する -r flow.pcap同じTCP通信の往復を読む 2>/dev/null往路は10.0.1.10:49152→198.51.100.20:443、戻りは逆向きの443→49152です。戻りの送信元ポートは443ですが、宛先ポートはクライアント側の49152です。
2現在の許可規則
SGは外向き443を許可し、内向きの新規通信の許可はありません。ACLは両方向とも宛先443を許可した状態です。
cat sg.json状態追跡を行うSGの許可を読む
cat acl.json方向別のACL規則を読むSGとACLの両方に外向き443の許可がありますが、ACLの内向き規則が応答の宛先49152に合っていません。lowとhighは宛先ポートの範囲です。
3往路は許可されても戻りが拒否される
二つのパケットを順に規則へ照合します。SGは先の許可済み通信に対する逆方向を追跡します。
python3 filter.py往路から順にSGとACLを評価する往路はSGとACLの両方でallowです。戻りのSGはtracked returnですが、ACLは一致する規則がなく*:denyになります。状態追跡でSGを通れても、独立したACLの拒否は残ります。
4戻りの宛先ポートを許可する
この例のクライアントが使う49152を含む、49152〜65535宛てを内向きに許可する案へ替えます。
cp acl-return.json acl.json戻りの宛先ポートに合うACLへ替える
python3 filter.py同じパケットで再評価する戻りはACL100:allowとなり、往復とも許可されます。応答を許可する範囲は、クライアント側が実際に使う一時ポートの範囲に合わせて決めます。
5追跡情報のない着信を確認
往路を処理せず、戻りと同じ形のパケットだけを評価します。
python3 filter.py --unsolicited先行する往路の記録なしで着信を評価するACLはallowでも、SGに既存の追跡情報と新規着信の許可がないためfinal=denyです。逆向きのIPとポートを持つことだけで、無条件に応答として通すわけではありません。
6先に一致したACLの拒否
内向きに番号90の拒否を加えた案を適用します。既存の許可は番号100です。
cp acl-deny-first.json acl.json許可より小さい番号の拒否を加える
python3 filter.py最初に一致する番号を確認する戻りは90:denyで拒否されます。ACLは小さい番号から評価し、最初に一致した規則を使うため、その後の100の許可には進みません。
7同じ拒否を許可の後ろへ置く
拒否の番号だけを110にした案へ替えます。
cp acl-deny-later.json acl.json拒否を番号100の許可より後ろへ移す
python3 filter.py順序による結果の変化を確認する戻りは100:allowとなり、110の拒否まで進みません。規則の存在だけでなく、番号と一致する範囲を合わせて確認する必要があります。
8ACLだけの許可では通信は成立しない
SGの外向き許可を取り除いた条件で、同じ往復を評価します。
cp sg-no-egress.json sg.jsonSGの新規外向き通信の許可を除く
python3 filter.pySGとACLの両方の結果を読む往路はSG=no allowで拒否され、戻りにも追跡情報がありません。SGとACLは別々の制御で、経路上で必要な許可をすべて満たす必要があります。
まとめ
戻りの宛先ポートは、要求時のクライアント側ポートです。
SGの状態追跡とACLの方向別評価を分けて確認します。
ACLでは最初の一致規則が適用され、他の制御の拒否をACLの許可で打ち消すことはできません。
操作履歴と解説を振り返れます。
シェル / Python 3
tcpdump -nn -tt -S -r flow.pcap 2>/dev/null0.000000 IP 10.0.1.10.49152 > 198.51.100.20.443: Flags [S], seq 1000, win 64240, length 0 0.010000 IP 198.51.100.20.443 > 10.0.1.10.49152: Flags [S.], seq 9000, ack 1001, win 64240, length 0
cat sg.json
cat acl.json{
"out": [
{
"cidr": "198.51.100.20/32",
"low": 443,
"high": 443
}
],
"in": []
}
{
"out": [
{
"number": 100,
"cidr": "198.51.100.20/32",
"low": 443,
"high": 443,
"effect": "allow"
}
],
"in": [
{
"number": 100,
"cidr": "198.51.100.20/32",
"low": 443,
"high": 443,
"effect": "allow"
}
]
}python3 filter.pyout 10.0.1.10:49152 -> 198.51.100.20:443 SG=rule allow ACL=100:allow final=allow in 198.51.100.20:443 -> 10.0.1.10:49152 SG=tracked return ACL=*:deny final=deny
cp acl-return.json acl.json
python3 filter.pyout 10.0.1.10:49152 -> 198.51.100.20:443 SG=rule allow ACL=100:allow final=allow in 198.51.100.20:443 -> 10.0.1.10:49152 SG=tracked return ACL=100:allow final=allow
python3 filter.py --unsolicitedin 198.51.100.20:443 -> 10.0.1.10:49152 SG=no allow ACL=100:allow final=deny
cp acl-deny-first.json acl.json
python3 filter.pyout 10.0.1.10:49152 -> 198.51.100.20:443 SG=rule allow ACL=100:allow final=allow in 198.51.100.20:443 -> 10.0.1.10:49152 SG=tracked return ACL=90:deny final=deny
cp acl-deny-later.json acl.json
python3 filter.pyout 10.0.1.10:49152 -> 198.51.100.20:443 SG=rule allow ACL=100:allow final=allow in 198.51.100.20:443 -> 10.0.1.10:49152 SG=tracked return ACL=100:allow final=allow
cp sg-no-egress.json sg.json
python3 filter.pyout 10.0.1.10:49152 -> 198.51.100.20:443 SG=no allow ACL=100:allow final=deny in 198.51.100.20:443 -> 10.0.1.10:49152 SG=no allow ACL=100:allow final=deny
演習完了