SGのルール削除をしても通信が切れない!Connection Trackingを検証してみた

SGのルール削除をしても通信が切れない!Connection Trackingを検証してみた

AWS環境でセキュリティグループのルール削除後も既存接続が残ってしまう現象に遭遇しました。その原因であるConnection Trackingの仕組みを検証し、インシデント対応時に確実に通信を遮断する方法をお伝えします。
2026.10.06

はじめに

おはようございます、みつぐちです。🙋

先日ジョインブログを投稿しましたが、技術関連では初の投稿となります。
気合い入れていきましょう。うおー。

ジョインしたばかりで案件にがっつり参画していないので、前職での経験ベースでつらつらと書いていきます。
あと今回の記事では、なぜその構成??という疑問がしばしば出てくると思いますが、
諸事情により今回はコストを最大級に抑えた構成にしているからです。悪しからず。

(あ、あと最後の章に趣味紹介をする構成にしたので、良かったらそちらだけでも覗いてください^ ^)

検証内容

今回は前職における経験で、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月に横浜にある三溪園に行きました〜
桜がとっても綺麗でしたので、来春は是非足を運んでみてください🌸

ここまで見てくださりありがとうございました〜

参考資料

この記事をシェアする

AWSのお困り事はクラスメソッドへ

関連記事