
SGのルール削除をしても通信が切れない!Connection Trackingを検証してみた
はじめに
おはようございます、みつぐちです。🙋
先日ジョインブログを投稿しましたが、技術関連では初の投稿となります。
気合い入れていきましょう。うおー。
ジョインしたばかりで案件にがっつり参画していないので、前職での経験ベースでつらつらと書いていきます。
あと今回の記事では、なぜその構成??という疑問がしばしば出てくると思いますが、
諸事情により今回はコストを最大級に抑えた構成にしているからです。悪しからず。
(あ、あと最後の章に趣味紹介をする構成にしたので、良かったらそちらだけでも覗いてください^ ^)
検証内容
今回は前職における経験で、AWS環境における通信断をSG制御にてしようとしたが、既存コネクションが残ってしまい切れない、、という経験に基づいています。
インシデント対応とかではなかったので大ごとにはなりませんが、インシデントの初動対応として確実に最速で通信断したい場合とかには役立つと思います。
SGの『Connection Tracking(接続追跡)』という仕組みを理解し、再現と検証をしていきます〜🧑💻
先に結論
結論ファーストでいきます。
・SGのルールを削除しても、追跡中(Tracked)の既存接続は切れない。 新規接続のみがブロックされる
・ただし、0.0.0.0/0 を許可するルールなど、追跡されない(Untracked)接続は即座に切れる
・既存の接続を確実に遮断したいなら、NACLを使う
最速で確実に通信断をしたい場合はSG制御ではなくNACLを使いましょうという話です。
今回の検証環境
| 項目 | 内容 |
|---|---|
| リージョン | 東京リージョン(ap-northeast-1) |
| OS | Amazon Linux 2023 |
| インスタンスタイプ | t3.micro |
| リソースデプロイ | ClooudFomation |
Connection Trackingとは
まずは仕様を整理します。
Tracked connection(追跡される接続)
SGは、通過した通信をConnection Trackingのテーブルに記録しています。戻りの通信を自動で許可できるのは、この記録があるためです。
公式ドキュメントでは、ルールを削除しても、追跡中の既存の接続は中断されないとされています。
Untracked connection(追跡されない接続)
すべての通信が追跡されるわけではありません。次の両方を満たすTCP/UDPのフローは追跡されません。
ある方向のルールが、0.0.0.0/0(または ::/0)からの通信を許可している
逆方向のルールが、0.0.0.0/0(または ::/0)へのすべてのポート(0-65535)の通信を許可している
追跡されていない通信は、ルールを削除すると即座に遮断されます。
ここで注意したいのが、SGのデフォルトのアウトバウンドルールは「すべてのトラフィック → 0.0.0.0/0」だという点です。つまり、アウトバウンドがデフォルトのままの場合、次のようになります。
| インバウンドルール | 追跡 | ルール削除時の既存接続 |
|---|---|---|
| TCP 5000 from 0.0.0.0/0 | されない | 即座に切れる |
| TCP 5000 from 特定のIP / SG参照 | される | 切れない |
送信元を絞ったセキュアな設定の方が、ルールを削除しても切れない ことになります。
なお、ICMPや、NLB・NAT Gateway・Transit Gatewayなどを経由する通信は、ルールに関係なく自動で追跡されます。
検証構成
冒頭にもあるとおり、諸事情によりコストを最大限に抑えた構成にしています。
インターフェース型エンドポイントを作成しなかったり、AZを跨いだ構成にしておらず、最小限構成で検証します。
CFnのyamlファイルも置いておくので、コスト最小限で再現していただけます!

構築
CFnにて、IaCで環境構築していきます。
今回はゼロベースで環境構築しているので、VPCやサブネット、RT等も全て作成スコープです。
参考までに、CFnのyamlファイルを置いておきます。
sg-conntrack-test.yamlの中身
AWSTemplateFormatVersion: "2010-09-09"
Description: >
SG Connection Tracking verification environment.
No public IP, no NAT Gateway, no IGW.
Access via EC2 Instance Connect Endpoint / dnf via S3 Gateway Endpoint.
Parameters:
LatestAmiId:
Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>
Default: /aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64
InstanceType:
Type: String
Default: t3.micro
TestPort:
Type: Number
Default: 5000
Resources:
VPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: 10.0.0.0/16
EnableDnsSupport: true
EnableDnsHostnames: true
Tags:
- Key: Name
Value: conntrack-vpc
SubnetClient:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
CidrBlock: 10.0.1.0/24
AvailabilityZone: !Select [0, !GetAZs ""]
MapPublicIpOnLaunch: false
Tags:
- Key: Name
Value: conntrack-subnet-client
SubnetServer:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
CidrBlock: 10.0.2.0/24
AvailabilityZone: !Select [0, !GetAZs ""]
MapPublicIpOnLaunch: false
Tags:
- Key: Name
Value: conntrack-subnet-server
SubnetMgmt:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
CidrBlock: 10.0.3.0/24
AvailabilityZone: !Select [0, !GetAZs ""]
MapPublicIpOnLaunch: false
Tags:
- Key: Name
Value: conntrack-subnet-mgmt
RouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: conntrack-rtb
RtbAssocClient:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref SubnetClient
RouteTableId: !Ref RouteTable
RtbAssocServer:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref SubnetServer
RouteTableId: !Ref RouteTable
RtbAssocMgmt:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref SubnetMgmt
RouteTableId: !Ref RouteTable
S3GatewayEndpoint:
Type: AWS::EC2::VPCEndpoint
Properties:
VpcId: !Ref VPC
ServiceName: !Sub com.amazonaws.${AWS::Region}.s3
VpcEndpointType: Gateway
RouteTableIds:
- !Ref RouteTable
NaclServer:
Type: AWS::EC2::NetworkAcl
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: conntrack-nacl-server
NaclServerInboundAllowAll:
Type: AWS::EC2::NetworkAclEntry
Properties:
NetworkAclId: !Ref NaclServer
RuleNumber: 100
Protocol: -1
RuleAction: allow
Egress: false
CidrBlock: 0.0.0.0/0
NaclServerOutboundAllowAll:
Type: AWS::EC2::NetworkAclEntry
Properties:
NetworkAclId: !Ref NaclServer
RuleNumber: 100
Protocol: -1
RuleAction: allow
Egress: true
CidrBlock: 0.0.0.0/0
NaclAssocServer:
Type: AWS::EC2::SubnetNetworkAclAssociation
Properties:
SubnetId: !Ref SubnetServer
NetworkAclId: !Ref NaclServer
SgEic:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: EC2 Instance Connect Endpoint
VpcId: !Ref VPC
SecurityGroupEgress:
- IpProtocol: tcp
FromPort: 22
ToPort: 22
CidrIp: 10.0.0.0/16
Description: SSH to instances in VPC
Tags:
- Key: Name
Value: conntrack-sg-eic
SgClient:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Client instance
VpcId: !Ref VPC
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 22
ToPort: 22
SourceSecurityGroupId: !Ref SgEic
Description: SSH from EIC Endpoint
Tags:
- Key: Name
Value: conntrack-sg-client
SgServer:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Server instance (verification target)
VpcId: !Ref VPC
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 22
ToPort: 22
SourceSecurityGroupId: !Ref SgEic
Description: SSH from EIC Endpoint
- IpProtocol: tcp
FromPort: !Ref TestPort
ToPort: !Ref TestPort
SourceSecurityGroupId: !Ref SgClient
Description: Test traffic from client
Tags:
- Key: Name
Value: conntrack-sg-server
EicEndpoint:
Type: AWS::EC2::InstanceConnectEndpoint
Properties:
SubnetId: !Ref SubnetMgmt
SecurityGroupIds:
- !Ref SgEic
PreserveClientIp: false
Tags:
- Key: Name
Value: conntrack-eic
ClientInstance:
Type: AWS::EC2::Instance
DependsOn:
- S3GatewayEndpoint
- RtbAssocClient
Properties:
ImageId: !Ref LatestAmiId
InstanceType: !Ref InstanceType
SubnetId: !Ref SubnetClient
SecurityGroupIds:
- !Ref SgClient
CreditSpecification:
CPUCredits: standard
UserData:
Fn::Base64: |
#!/bin/bash
dnf install -y nmap-ncat tcpdump
Tags:
- Key: Name
Value: conntrack-client
ServerInstance:
Type: AWS::EC2::Instance
DependsOn:
- S3GatewayEndpoint
- RtbAssocServer
- NaclAssocServer
Properties:
ImageId: !Ref LatestAmiId
InstanceType: !Ref InstanceType
SubnetId: !Ref SubnetServer
SecurityGroupIds:
- !Ref SgServer
CreditSpecification:
CPUCredits: standard
UserData:
Fn::Base64: |
#!/bin/bash
dnf install -y nmap-ncat tcpdump
Tags:
- Key: Name
Value: conntrack-server
Outputs:
ClientInstanceId:
Value: !Ref ClientInstance
ServerInstanceId:
Value: !Ref ServerInstance
ServerPrivateIp:
Value: !GetAtt ServerInstance.PrivateIp
ClientPrivateIp:
Value: !GetAtt ClientInstance.PrivateIp
ServerSecurityGroupId:
Value: !Ref SgServer
ServerNaclId:
Value: !Ref NaclServer
このテンプレートからCFn環境構築を実行し、ステータスが CREATE_COMPLETE になるまで待ちます。

無事完了しました!これで環境構築は終わりです。
念の為、サーバにログインしてパッケージがインストールされているか確認します。

大丈夫そうです。
検証
各ケースで、以下の手順を共通で行います。
EC2サーバ側(Server):ポート5000で待ち受ける
nc -lk 5000
EC2クライアント側(Client):1秒ごとに時刻を送信し続ける
1つのTCP接続を維持したまま、データを流し続けます。
while true; do date '+%H:%M:%S'; sleep 1; done | nc <ServerPrivateIp> 5000
EC2クライアント側(Client):新規接続できるか確認する
nc -zv -w 3 <ServerPrivateIp> 5000
Server側に時刻が表示され続けていれば、既存の接続が維持されていると判断します。
(こんな感じ)

▪️ケース1:SG参照のルールを削除する(Tracked)
まずは、テンプレートで作成したルールのままで検証します。
| タイプ | ポート | 送信元 |
|---|---|---|
| カスタムTCP | 5000 | SG-client |
送信元がSG参照なので、Untrackedの条件を満たさず、追跡される接続 になるはずです。

クライアント側から時刻を送信している状態で、セキュリティグループから『conntrack-sg-server』のインバウンドルールを修正し「TCP 5000」のルールを削除しました。
(ここからはエビデンスとして作業時刻も載せています。)

サーバ側の出力結果

SGルール削除後も、時刻が途切れることなく表示され続けました。既存の接続は維持されています。
クライアント側で新規接続を確認
nc -zv -w 3 <ServerPrivateIp> 5000
【実行結果】
一方で、新規接続はタイムアウトしました。

ルールを削除すると、新しい接続はブロックされるが、既存の接続は維持される ことが確認できました。
▪️ケース2:0.0.0.0/0 のルールを削除する(Untracked)
次に、送信元を 0.0.0.0/0 に変更して検証します。
| タイプ | ポート | 送信元 |
|---|---|---|
| カスタムTCP | 5000 | 0.0.0.0/0 |
アウトバウンドルールはデフォルト(すべてのトラフィック → 0.0.0.0/0)のままなので、Untrackedの条件を満たします。
※なお、今回の環境にはIGWがなく、VPCの外から到達する経路がありません。そのため、検証のために一時的に 0.0.0.0/0 を許可しても外部から攻撃を受けるリスクはありませんが、参考にされる場合は十分にご留意ください。
上記ルール設定後、時刻が正常に送信される事を確認したら、追加した上記ルールを削除していきます。

サーバ側の出力結果
【実行結果】
ルールを削除した直後に、時刻の表示が止まりました。

ケース1と同じ「ルールの削除」という操作でも、送信元の指定方法によって、既存の接続への影響が変わることが確認できました。
▪️ケース3:NACLで遮断する
インシデント対応を想定し、追跡中の接続をNACLで遮断できるかを確認します。
SG-serverを、ケース1の状態(TCP 5000、送信元:SG-client)に戻します。
時刻の送信を再開した状態で、ネットワークACLで次のルールを追加しました。
| ルール番号 | タイプ | プロトコル | ポート範囲 | 送信元 | 許可/拒否 |
|---|---|---|---|---|---|
| 50 | カスタムTCP | TCP | 5000 | 10.0.1.0/24 | 拒否 |

NACLは、ルール番号の小さい順に評価されます。すべてを許可するルール100よりも先に、このルール50が評価されます。
サーバ側の出力結果

EC2クライアント側(Client):新規接続できるか確認する

【実行結果】
ルールを追加した直後に、時刻の表示が止まりました
NACLはステートレスで、Connection Trackingの仕組みを持ちません。そのため、追跡中の接続であっても、パケットごとにルールが評価され、即座に遮断されます。
結果のまとめ
実際に以下の結果になりました。
| ケース | 操作 | 既存の接続 | 新規接続 |
|---|---|---|---|
| 1 | SG参照のルールを削除 | 維持される | 拒否される |
| 2 | 0.0.0.0/0 のルールを削除 | 即座に切れる | 拒否される |
| 3 | NACLで拒否 | 即座に切れる | 拒否される |
想定通り、SG参照のルール削除してもConnection Trackingによりコネクション維持されることが確認できました。
ちなみに維持されたコネクションはデフォルト 5日間 のタイムアウトが設定されているため、実質的に「切れない」と考えた方が安全であることが分かりました。
▪️インシデント対応では、まずNACLを使う
不正な通信を検知してSGのルールを削除しても、攻撃者の既存のセッションは残る可能性があります。今回の検証結果から、インシデント対応の手順書は次の流れにしておくのが安全だと考えます。
1.NACLで該当の通信を拒否し、即座に遮断する
2.SGのルールを見直す
3.必要に応じて、NACLのルールを元に戻す
ただし、NACLはサブネット全体に影響します。正常な通信まで遮断しないよう、ルールの範囲は慎重に決める必要があります。また、NACLはステートレスなので、戻りの通信(エフェメラルポート)の扱いにも注意が必要です。
▪️「セキュアな設定ほど切れにくい」ことを知っておく
送信元を絞ったルールは追跡され、0.0.0.0/0 を許可するルールは追跡されないという結果でした。
本番環境のSGは、ほとんどの場合、送信元を絞っているはずです。
つまり、本番環境ではルールを削除しても既存の接続が切れないケースの方が多いと考えておくべきです。
まとめ
SGのConnection Trackingについて検証しました。🧑💻
・SGのルールを削除しても、追跡中の既存接続は切れない
・0.0.0.0/0 を許可するルールのような 追跡されない接続は、即座に切れる
・既存の接続を確実に遮断したいなら、NACLを使う
・追跡中の接続は、アイドル状態がタイムアウトを超えるまで維持される(TCPのデフォルトは5日間)
「SGはステートフル」という知識は多くの方が持っていると思いますが、それが運用時にどのような影響を与えるかまでは、意外と意識されていないのではないでしょうか。
インシデント対応や変更作業の手順を見直すきっかけになれば幸いです。
また、今回はS3 Gateway EndpointとEC2 Instance Connect Endpointを組み合わせることで、パブリックIPもNAT GatewayもIAMロールも使わずに検証環境を構築しました。
ネットワークの検証をするときの構成として、参考にしてみてください〜〜
最後に
完全に本線とはずれますが、毎投稿の最後に私の趣味である写真(私の中の趣味優先度5位くらいですが、)を見てもらいます(強制)
カメラはsony a7iiiを使ってます。
そんなわけで本日の写真はこちら(ででん)

今年の4月に横浜にある三溪園に行きました〜
桜がとっても綺麗でしたので、来春は是非足を運んでみてください🌸
ここまで見てくださりありがとうございました〜
参考資料
- https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/security-group-connection-tracking.html
- https://docs.aws.amazon.com/vpc/latest/userguide/vpc-network-acls.html
- https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/connect-with-ec2-instance-connect-endpoint.html
- https://docs.aws.amazon.com/vpc/latest/privatelink/vpc-endpoints-s3.html
- https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/monitoring-network-performance-ena.html









