TGW 越しのセキュリティグループはなぜ CIDR ベースになるのか — SG・NACL・経路を多層で理解する

Transit Gateway でアカウントをまたいで通信させると、セキュリティグループの「SG 参照」が効かず CIDR ベースになる。その理由を前提から解き、NACL のステートレス性、パケットが通る順序、dev と SRE の責任分担まで整理します。

前提: 何の話か

マルチアカウント構成で、サービス間を Transit Gateway (TGW) で繋ぐ。DB 接続のような非 HTTP の通信も含む。CIDR は重複しないように採番している — そんな状況を想定します。

このとき、開発チームがよくつまずくのが「TGW 越しだとセキュリティグループの SG 参照が効かない。CIDR で書くしかない」という挙動です。これは仕様であって、理由を知らないと「なぜか繋がらない」で時間を溶かします。前提から順に解きます。

前提 1: セキュリティグループのソースは 2 通りで書ける

SG のインバウンドルールの「ソース」には、2 種類の書き方があります。

# CIDR ベース
ingress 5432 from 10.20.0.0/16

# SG 参照
ingress 5432 from sg-app    ← 「sg-app が付いたリソースからなら許可」

前提 2: SG 参照の「正体」は動的なメンバーシップ解決

from sg-app は名前マッチではありません。AWS が裏で「sg-app が付いている ENI の集合 = その時点の全プライベート IP」を動的に解決して許可しています。

インスタンスがスケールして IP が変わっても、AWS がメンバーシップを追跡して自動で効かせてくれる。つまり SG 参照は「IP の集合」を表す便利な抽象であり、AWS の管理機能に依存しています。ここが効くか効かないかの分かれ目です。

前提 3: メンバーシップ解決が効く「範囲」がある

SG 参照が成立するには、両端が「SG 参照できる関係」にいる必要があります。具体的には次の範囲です。

この範囲では、AWS が両 VPC 間で SG のメンバーシップ情報を共有しているので from sg-xxx が機能します。

本題: なぜ TGW 越しはダメなのか

TGW は純粋な L3 ルーターです。ルートテーブルを見て IP パケットを転送するだけ。設計として「スケールするシンプルな転送装置」であることを優先していて、送信元 ENI にどの SG が付いているかというメンバーシップ情報を運びません

そのため、TGW を通って宛先 VPC に届いたパケットについて、宛先側の SG が知れるのは 送信元の IP アドレスだけです。from sg-app と書いても、sg-app のメンバーが誰なのかを宛先側から解決できない。結果、そのルールはマッチしようがなく、エラーにはならず、ただ通らない

だから TGW 越しは、送信元を IP (CIDR) で書くしかありません。

# 宛先 (DB) 側の SG
ingress 5432 from 10.20.0.0/16    ← アプリ VPC の CIDR を許可(効く)
ingress 5432 from sg-app          ← TGW 越しなので効かない

対比でまとめると、こうなります。

接続方法SG 参照 (from sg-xxx)備考
同一 VPC使える
VPC ピアリング (同一リージョン)使えるピア先 SG を参照可
Transit GatewayCIDR のみL3 ルーターで SG 情報を運ばない
VPN / Direct Connect / Cloud WANCIDR のみ同じく L3 経由

「TGW 越し = CIDR ベース」の根っこは、TGW が SG のメンバーシップ情報を運ばない L3 ルーターだから、の一点に尽きます。

もう一段外側の関所: Network ACL

ここまでは SG (ENI 単位) の話でした。TGW 構成ではもう一段外側、サブネット境界の NACL も効いてきます。SG との違いを押さえます。

Security GroupNetwork ACL
適用単位ENI (インスタンス)サブネット境界
状態ステートフル (戻りは自動許可)ステートレス (戻りも明示許可が要る)
ルールallow のみallow + deny 両方
評価全ルールまとめて判定番号順に上から、最初の一致で確定
ソース指定CIDR または SG 参照CIDR のみ (常に)

NACL は元々 CIDR ベースなので、TGW 越しの CIDR の世界と素直に噛み合います。そして SG にできない deny が書けるのが NACL の存在意義です。

最大のハマりどころ: ステートレス

SG は行きを許可すれば戻りは自動。でも NACL は戻りトラフィックを自分で許可しないと通りません。DB (5432) 接続ならこうなります。

# DB サブネットの NACL
Inbound  : allow TCP 5432       from 10.20.0.0/16   (アプリ VPC)
Outbound : allow TCP 1024-65535 to   10.20.0.0/16   (★戻りのエフェメラルポート)

この Outbound のエフェメラル許可 (1024–65535) を忘れて接続がハングする、が定番事故です。NACL を触るならセットで必ず書きます。

使いすぎ注意

デフォルト NACL は全許可。多くの現場は NACL を開けたまま SG で制御しています。NACL を使う価値があるのは、SG で表現できない「粗い deny」「サブネット境界の防御」です。

逆に、SG のロジックを NACL に二重実装するのは避けます。メンテ地獄になる。細かい許可は SG、粗い遮断は NACL と粒度を分けるのがコツです。

パケットが通る順を一枚で

DB 宛のパケットが TGW 経由で届くとき、何が順番にチェックされるかを並べると、疎通トラブルの切り分けが一気に楽になります。

[アプリ ENI] --(SG outbound: 自動/ステートフル)


[アプリサブネット ルートテーブル]  10.30.0.0/16 → tgw-xxxx ?


[Transit Gateway ルートテーブル]   宛先 VPC への経路がある ?


[DB サブネット ルートテーブル]      戻り経路 (アプリ VPC) がある ?


[DB サブネット Inbound NACL]        allow 5432 from アプリ CIDR ?


[DB の SG inbound]                  allow 5432 from アプリ CIDR ?   ← SG 参照は不可


[DB 到達]
     │  戻り: SG=自動 / NACL Outbound=エフェメラル(1024-65535)を明示許可

繋がらないときは、上から「ルート → NACL → SG」の順に潰します。AWS の Reachability Analyzer はこの経路を自動で辿ってどこで落ちるか教えてくれるので、最初に投げると速い。

dev と SRE の責任分担にきれいに乗る

この多層構造は、開発チームと SRE の役割分担にそのまま対応させられます。

こうすると「開発が SG を緩めても、NACL とルートの境界は破れない」という二段構えになります。認証系のように爆発半径が大きい領域ほど、この分担が効きます。

開発チームが SRE に渡すべきは、実装可能な形の接続コントラクトです。

service: orders-api (prod, account 1234)
  → needs: payments-db (account 5678) : TCP 5432
    direction: outbound only
    source CIDR: 10.20.0.0/16
    throughput: ~50GB/月   (TGW 処理コスト見積り用)
    why: 注文確定時に決済台帳を参照

送信元 CIDR・宛先・ポート・方向・想定量・理由が揃っていれば、SRE は TGW ルート・NACL・SG を機械的に引けます。

CIDR 設計が効いてくる

「TGW 越し = CIDR ベース」ということは、CIDR 設計の質がそのまま SG / NACL ルールの質になるということです。CIDR が重複していたり雑だと、許可範囲も粗くなる。逆に綺麗に採番されていれば、SG はそのまま綺麗な許可リストになります。

許可元が増えて CIDR を並べるのが辛くなったら、マネージドプレフィックスリストに CIDR 群をまとめ、SG ではプレフィックスリストを 1 個参照します。さらにプレフィックスリストは AWS RAM で共有できるので、「許可対象 VPC の CIDR 一覧」を中央で管理し、各 DB の SG から参照する運用が組めます。これがマルチアカウントでの現実的なスケール方法です。

まとめ

← Back to all posts