AWS Network ACLとセキュリティグループで実装するVPCセキュリティ設計|ステートレス・ステートフルの違いと2層防御の実践パターン

宮崎智広 この記事の監修:宮崎智広(Linux実務・教育歴20年以上・受講者3,100名超)
HOMELinux技術 リナックスマスター.JP(Linuxマスター.JP)AWS(Amazon Linux) > AWS Network ACLとセキュリティグループで実装するVPCセキュリティ設計|ステートレス・ステートフルの違いと2層防御の実践パターン
「セキュリティグループでHTTPSの443番ポートを許可したのに、EC2にアクセスできない。」
AWSのVPC設計に取り組み始めたエンジニアが最初にぶつかる壁のひとつです。原因を調べていくと、セキュリティグループとは別に「Network ACL」というファイアウォールが存在することに気づきます。

この記事では、VPCのセキュリティを支えるNetwork ACL(NACL)とセキュリティグループ(SG)の違いと、2層防御として組み合わせる設計パターンを解説します。Amazon Linux 2023 / RHEL 9.4での実機確認コマンドも交え、「SGで許可したのに繋がらない」エラーの切り分け手順まで説明します。

この記事のポイント

・NACLはサブネット単位のステートレスFW、SGはインスタンス単位のステートフルFWで役割が異なる
・ステートレスのNACLはインバウンド許可だけでは不十分、アウトバウンドも明示的に設定が必要
・「NACL=サブネット境界の入口制限」「SG=インスタンス間のきめ細かい通信制御」で役割を分ける
・「SGで許可したのに繋がらない」の原因はNACLのエフェメラルポート未許可であることが多い


「このままじゃマズい」と感じていませんか?
参考書を開く気力もない、同年代に取り残される不安——
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら

なぜVPCには2種類のファイアウォールが必要なのか

VPCのセキュリティを理解するには、「どのレイヤーでトラフィックを制御するか」という視点が重要です。

AWSのVPCには2つの独立したファイアウォールが存在します。

Network ACL(NACL):サブネット単位で働く。サブネットに出入りするトラフィックをルール番号順に評価する
セキュリティグループ(SG):EC2インスタンス(正確にはENI)単位で働く。「許可したトラフィックの戻りは自動で許可」するステートフルな動作

この2層構造は意図的な設計です。大企業の物理ネットワークでは「フロアごとのFW」と「端末ごとのFW」を組み合わせるのと同じ発想です。NACLが「建物の受付」なら、SGは「各部屋の鍵」です。

VPCの基本設計についてはAWSのLinux環境構築とVPC設計入門で解説しています。本記事では、そのセキュリティ設計層を深掘りします。

セキュリティグループ(SG)の設計──ステートフルの意味を理解する

セキュリティグループの最大の特徴は「ステートフル」であることです。インバウンドルールで許可したトラフィックに対する戻りのパケットは、アウトバウンドルールを設定していなくても自動的に許可されます。

1. インバウンドルールの設計

SGのインバウンドルールは「このポートへの通信を誰から許可するか」を定義します。

# セキュリティグループのインバウンドルールを確認する $ aws ec2 describe-security-groups \ --group-ids sg-0123456789abcdef0 \ --query 'SecurityGroups[*].IpPermissions' [ { "FromPort": 443, "IpProtocol": "tcp", "IpRanges": [ { "CidrIp": "0.0.0.0/0", "Description": "HTTPS from anywhere" } ], "ToPort": 443 }, { "FromPort": 22, "IpProtocol": "tcp", "IpRanges": [ { "CidrIp": "10.0.0.0/8", "Description": "SSH from private network only" } ], "ToPort": 22 } ]

ポート22(SSH)をCIDR 0.0.0.0/0で開けるのは危険です。踏み台サーバー(Bastion)経由のアクセスに限定するか、AWS Systems Manager Session Managerの使用を検討してください。

2. アウトバウンドルールとSGのチェーン設計

SGのアウトバウンドルールのデフォルトは「全て許可(0.0.0.0/0)」です。セキュリティを強化する場合は、アウトバウンドを「必要な宛先のみ許可」に絞ります。

SGの強力な機能のひとつが「SGを参照したルール(SGチェーン)」です。CIDRではなく別のSG IDを参照して許可を設定できます。

# DBサーバーのSGに「AppサーバーのSGからの接続のみ許可」を設定する例 $ aws ec2 authorize-security-group-ingress \ --group-id sg-db-xxxxxxxx \ --protocol tcp \ --port 3306 \ --source-group sg-app-xxxxxxxx # SGチェーンの確認 $ aws ec2 describe-security-groups \ --group-ids sg-db-xxxxxxxx \ --query 'SecurityGroups[*].IpPermissions[*].UserIdGroupPairs' [ [ { "GroupId": "sg-app-xxxxxxxx", "UserId": "123456789012" } ] ]

SGチェーンのメリットは、EC2のIPアドレスが変わっても設定変更不要な点です。AppサーバーSGに属するインスタンス全てが自動的にDBに接続できます。

Network ACL(NACL)の設計──ステートレスの落とし穴を理解する

NACLはSGと大きく異なる「ステートレス」な動作をします。ステートレスとは「各パケットを独立して評価する」ということです。インバウンドでHTTPSを許可しても、それに対する応答(アウトバウンド)は別途許可が必要です。

1. ルール番号と評価順序

NACLのルールは「番号の小さい順に評価」され、最初にマッチしたルールで通過・拒否が確定します。

# NACLのルール一覧を確認する $ aws ec2 describe-network-acls \ --network-acl-ids acl-0123456789abcdef0 \ --query 'NetworkAcls[*].Entries[*].[RuleNumber,RuleAction,Protocol,PortRange,CidrBlock,Egress]' \ --output table ------------------------------------------------------------------- | Rule# | Action | Protocol | PortRange | CidrBlock | Egress | |-------|--------|----------|-----------|-----------|------------| | 100 | allow | 6(TCP) | 443-443 | 0.0.0.0/0 | False(IN) | | 32767 | deny | -1(ALL) | - | 0.0.0.0/0 | False(IN) | | 32767 | deny | -1(ALL) | - | 0.0.0.0/0 | True(OUT) | ------------------------------------------------------------------- # アウトバウンドルールが暗黙DENYのみ → レスポンスが返せない

ルール番号は100単位(100, 200, 300...)で設定するのが慣例です。後から中間に挿入できる余地を持たせます。最後の32767は「暗黙のDENY」で変更できません。

2. エフェメラルポートを必ず許可する

NACLを使う際に最も多いトラブルが「エフェメラルポート(一時ポート)の未許可」です。

クライアントがWebサーバーに接続するとき、クライアントは送信元ポートとして1024~65535番のランダムなポート(エフェメラルポート)を使います。NACLのアウトバウンドルールでこの範囲を許可していないと、WebサーバーからクライアントへのHTTPSレスポンスが届きません。

# エフェメラルポートの範囲をOS別に確認する # Amazon Linux 2023 / RHEL 9.4 $ cat /proc/sys/net/ipv4/ip_local_port_range 32768 60999 # NACLのアウトバウンドルールに追加する(安全側で1024-65535を許可) $ aws ec2 create-network-acl-entry \ --network-acl-id acl-0123456789abcdef0 \ --no-ingress \ --rule-number 100 \ --protocol tcp \ --rule-action allow \ --cidr-block 0.0.0.0/0 \ --port-range From=1024,To=65535

Amazon Linux 2023のカーネルでは32768~60999がエフェメラルポートとして使われます。NACLでは余裕を持って1024~65535を許可するのが実践的です。

3. サブネット単位で適用する

NACLはサブネットに紐づきます。1つのNACLを複数のサブネットに関連付けることも、サブネットごとに独立したNACLを作ることも可能です。

# サブネットとNACLの関連付けを確認する $ aws ec2 describe-subnets \ --subnet-ids subnet-0123456789abcdef0 \ --query 'Subnets[*].[SubnetId,Tags[?Key==`Name`].Value|[0]]' [ [ "subnet-0123456789abcdef0", "public-subnet-1a" ] ] # そのサブネットに紐づくNACLを確認する $ aws ec2 describe-network-acls \ --filters "Name=association.subnet-id,Values=subnet-0123456789abcdef0" \ --query 'NetworkAcls[*].NetworkAclId' [ "acl-0123456789abcdef0" ]

NACLとSGを組み合わせた2層防御パターン

NACLとSGをどう使い分けるか──実際の3層構成(パブリック・アプリ・DBサブネット)で設計します。

1. 3層サブネット構成でのNACL設計方針

サブネット NACLの役割 SGの役割
パブリック(ALB配置) インターネットからHTTPS(443)のみ許可、それ以外はDENY ALBからEC2のHTTP 8080のみ許可(SGチェーン)
プライベート(Appサーバー) パブリックサブネットCIDRからの通信のみ許可 AppサーバーSGからDBへのMySQL 3306のみ許可
プライベート(DB) Appサブネット以外からの全通信をDENY AppサーバーSGからの3306のみ許可(SGチェーン)
NACLで「どのサブネットから来るか」を制限し、SGで「そのサブネット内のどのインスタンスが接続できるか」を制限する。この2段階が2層防御の本質です。

2. NACL設計で実際に使うルールセット例

パブリックサブネット(ALB用)のNACL設定例です。

# パブリックサブネットNACL(インバウンド) Rule 100: ALLOW TCP 443 from 0.0.0.0/0 # HTTPS(インターネット→ALB) Rule 200: ALLOW TCP 80 from 0.0.0.0/0 # HTTP(ALBでHTTPSへリダイレクト) Rule 32767: DENY ALL # 暗黙のDENY # パブリックサブネットNACL(アウトバウンド) Rule 100: ALLOW TCP 1024-65535 to 0.0.0.0/0 # エフェメラルポート(レスポンス) Rule 200: ALLOW ALL to 10.0.0.0/8 # VPC内部通信 Rule 32767: DENY ALL # 暗黙のDENY # DBサブネットNACL(インバウンド) Rule 100: ALLOW TCP 3306 from 10.0.2.0/24 # Appサブネット(10.0.2.0/24)のみ Rule 32767: DENY ALL # 他は全てDENY # DBサブネットNACL(アウトバウンド) Rule 100: ALLOW TCP 1024-65535 to 10.0.2.0/24 # エフェメラルポートでの応答 Rule 32767: DENY ALL

冗長設計やマルチAZ構成の詳細についてはAWS冗長設計パターン入門も参考にしてください。

「SGで許可したのに繋がらない」エラーの切り分け手順

「SGの設定は正しいはずなのに繋がらない」という状況は、多くの場合NACLの設定が原因です。以下の手順で切り分けてください。

1. NACLのルールを確認する

# 問題のサブネットに紐づくNACLを確認する $ aws ec2 describe-network-acls \ --filters "Name=association.subnet-id,Values=subnet-0123456789abcdef0" \ --query 'NetworkAcls[*].Entries[?Egress==`false`].[RuleNumber,RuleAction,Protocol,PortRange.From,PortRange.To,CidrBlock]' \ --output table # インバウンドの許可ルールにアクセスしたいポートが含まれているか確認 # アウトバウンドのエフェメラルポート(1024-65535)許可があるか確認 $ aws ec2 describe-network-acls \ --filters "Name=association.subnet-id,Values=subnet-0123456789abcdef0" \ --query 'NetworkAcls[*].Entries[?Egress==`true`].[RuleNumber,RuleAction,PortRange.From,PortRange.To]' \ --output table | Rule# | Action | PortFrom | PortTo | |-------|--------|----------|--------| | 32767 | deny | None | None | # アウトバウンドが暗黙DENYのみ → エフェメラルポート許可を追加する

2. エフェメラルポート(1024~65535)の許可を追加する

# アウトバウンドにエフェメラルポート許可ルールを追加する $ aws ec2 create-network-acl-entry \ --network-acl-id acl-0123456789abcdef0 \ --no-ingress \ --rule-number 100 \ --protocol tcp \ --rule-action allow \ --cidr-block 0.0.0.0/0 \ --port-range From=1024,To=65535 # 追加後の確認 $ aws ec2 describe-network-acls \ --network-acl-ids acl-0123456789abcdef0 \ --query 'NetworkAcls[*].Entries[?Egress==`true`].[RuleNumber,RuleAction,PortRange.From,PortRange.To]' \ --output table | Rule# | Action | PortFrom | PortTo | |-------|--------|----------|--------| | 100 | allow | 1024 | 65535 | | 32767 | deny | None | None | # ルール100でエフェメラルポートが許可された

3. LinuxのポートをSSHログイン後に確認する

NACLとSGの設定を修正しても繋がらない場合、Linux側でプロセスが正常にポートをリッスンしているかを確認します。EC2インスタンスにSSHでログイン後、以下を実行してください。

# Linuxで待ち受けているポートを確認する(Amazon Linux 2023 / RHEL 9.4) $ ss -tlnp State Recv-Q Send-Q Local Address:Port Peer Address:Port Process LISTEN 0 128 0.0.0.0:443 0.0.0.0:* users:(("nginx",pid=1234,fd=6)) LISTEN 0 128 0.0.0.0:22 0.0.0.0:* users:(("sshd",pid=888,fd=3)) # 443番ポートでNginxがリッスンしていることを確認 # ポートがリッスンしていない場合はアプリ側の問題(AWSのFW設定ではない) $ systemctl status nginx Active: active (running) since Thu 2026-07-25 10:00:00 UTC; 1h ago

Linuxのポート確認コマンドの詳細についてはLinuxのポート確認コマンド(ss / lsofの全オプション)も合わせて参照してください。

本記事のまとめ

Network ACLとセキュリティグループの違いと2層防御の設計パターンをまとめます。

比較項目 Network ACL(NACL) セキュリティグループ(SG)
適用単位 サブネット ENI(インスタンス単位)
ステート ステートレス(IN・OUT独立評価) ステートフル(応答は自動許可)
ルール評価 番号順(最初にマッチで確定) 全ルールを評価(DENYルールなし)
DENY設定 可能 不可(許可ルールのみ)
主な用途 サブネット境界の不審IPブロック インスタンス間の通信制御
デフォルト動作 全許可(新規NACL作成時は全DENY) インバウンド全拒否・アウトバウンド全許可
よくある落とし穴 エフェメラルポートのアウトバウンド未許可 SGチェーン参照のSG ID誤り
VPCのセキュリティ設計では「NACLでサブネット境界を守り、SGでインスタンス単位の通信を制御する」という役割分担が基本です。NACLはステートレスのため、エフェメラルポートの許可を忘れないようにしてください。

VPCの基本設計についてはAWSのLinux環境構築とVPC設計入門を、マルチAZ冗長構成の詳細はAWS冗長設計パターン入門も合わせて参照してください。

Network ACLの設定方法だけでなく、AWSを「実務の型」として身につけませんか?

aws ec2 create-network-acl-entry のコマンドは調べれば分かります。でも「NACLとSGをどう組み合わせて設計するのか」「エフェメラルポートをどの範囲で開けるべきか」を、自信を持って答えられますか?
現場で通用する安全なLinuxサーバー構築の「型」を体系的に身につけたい方へ、『Linuxサーバー構築入門マニュアル(図解60P)』を完全無料でプレゼントしています。

「独学の時間がもったいない」「プロから直接、現場の技術を最短で学びたい」という本気の方には、2日で実務レベルのスキルが身につく【初心者向けハンズオンセミナー】も開催しています。

無料メルマガで学習を続ける

Linuxの実践スキルをメールで毎週お届け。
登録は30秒、解除もいつでも可。

登録無料・いつでも解除できます

暗記不要・1時間後にはサーバーが動く

3,100名以上が実践した「型」を無料で公開中

プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。

姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら

Linux無料マニュアル(図解60P) 名前とメールで30秒登録
宮崎 智広

この記事を書いた人

宮崎 智広(みやざき ともひろ)

株式会社イーネットマーキュリー代表。現役のLinuxサーバー管理者として20年以上の実務経験を持ち、これまでに累計3,100名以上のエンジニアを指導してきたLinux教育のプロフェッショナル。「現場で本当に使える技術」を体系的に伝えることをモットーに、実践型のLinuxセミナーの開催や無料マニュアルの配布を通じてLinux人材の育成に取り組んでいる。

趣味は、キャンプにカメラ、トラウト釣り。好きな食べ物は、ラーメンにお酒。休肝日が作れない、酒量を減らせないのが悩み。最近、ドラマ「フライトエンジェル」を観て涙腺が崩壊しました。