【実務・中級編】 ネットワークアクセステーブル(NACL)のステートレス動作とルール評価 – クラウド&コンテナネットワーク実践ガイド

こんにちは。クラウドの底なし沼……いや、深遠なネットワークの世界へようこそ。第一線でインフラと向き合うSREの私だ。

君は今、Web APIの設計やマイクロサービスの構築において、セキュリティグループ(SG)だけでは満たせない、より厳格なパケット制御を求められていることだろう。あるいは、「セキュリティグループはステートフルなのに、なぜかNACL(ネットワークアクセステーブル)はステートレスなんだっけ?」という疑問から、本番障害のトラウマを呼び起こされているかもしれない。

クラウドネットワークの現場において、NACLのステートレス動作とルール評価の仕組みを誤解していると、「あれ、リクエストは通るのにレスポンスが返ってこない……?」という泥沼のデバッグに数時間を費やすことになる。

今回は、パケットがサブネットの境界をどう駆け抜けるのか、そのリアルな挙動と、実務で絶対に外せないNACLの設計・デバッグ手法を叩き込む。しっかりついてきてほしい。

—

1. NACLの「ステートレス」とは何か? パケットの往復運動から理解する

まず大前提として、AWSのVPCやGCPのVPCネットワーク(ファイアウォールルール)における基本的なフィルター機構と、NACLの決定的な違いを押さえておこう。

セキュリティグループは「ステートフル(Stateful)」だ。つまり、クライアントからバックエンドのAPIサーバーへ向けて外向き(アウトバウンド)の通信を許可すれば、サーバーからの返り値(インバウンド)は、コネクションの状態を自動で追跡(トラッキング)して許可してくれる。

しかし、サブネット境界の番人であるNACLは「ステートレス(Stateless)」だ。
ステートレスとは、文字通り「文脈を一切覚えない」ということ。NACLは、目の前にやってきたパケット単体を、過去の通信の流れなどお構いなしに、ただルールに照らし合わせて機械的に裁く。

通信フロー(シーケンス)のリアルな挙動

ここで、開発環境のクライアントから、パブリックサブネットにあるWeb APIサーバー(10.0.1.10)へ、curlでHTTPSリクエストを投げるシーンを想像してほしい。

[クライアント] ---> (Internet Gateway) ---> [NACL (Inbound)] ---> [APIサーバー]
[クライアント] <--- (Ephemeral Port)   <--- [NACL (Outbound)] <--- [APIサーバー]

1. インバウンド(リクエストの往路)
クライアントからHTTPS(ポート443)のパケットがサブネットに飛び込んできた。NACLのインバウンドルールは、このリクエストを評価し、許可(Allow)すればパケットはAPIサーバーに届く。
2. アウトバウンド(レスポンスの復路)
APIサーバーが処理を終え、クライアントへレスポンスを返す。ここで、NACLは「おっ、さっきの往路の返事だな」なんて気を利かせてくれない。
レスポンスパケットは、宛先ポートとしてクライアント側の一時ポート(エフェメラルポート: 1024-65535)を指定して外に出ていこうとする。
つまり、NACLのアウトバウンドルール側で、このエフェメラルポート宛ての通信を明示的に許可(Allow)しておかないと、パケットはサブネットの外へ持ち出せず、コネクションはタイムアウトで沈黙する。

この「往路と復路の両方でルールが必要」という事実を忘れるのが、若手エンジニアが最初に踏む典型的な地雷だ。

—

2. ルール評価のプロセス:番号順(Low-to-High)の冷徹なルール

NACLのもう一つの特徴は、ルールが番号順(Rule Number)に上から評価される点だ。

セキュリティグループは、すべてのルールが評価された上で(あるいは任意の優先順位で)「どれか一つにマッチすれば許可」という評価モデルをとることが多いが、NACLは違う。

  • 番号が小さい順(例: 100, 200, 300…)に評価される。
  • 最初にマッチしたルールが適用され、その時点で評価は終了する。
  • デフォルトでは、一番最後にすべての通信を拒否する暗黙のルール(* Deny)が待ち構えている。

したがって、NACLの設定ファイルを記述、あるいはTerraformなどのIaCで構築する際は、ルールの順序と番号の採番に細心の注意を払わなければならない。

—

3. 実践:Web APIサーバーを守るNACLの設定例

では、実務でよくあるシナリオを考えてみよう。
「プライベートサブネットに配置されたWeb APIサーバー(10.0.1.50)に対し、特定の踏み台(203.0.113.10)からのSSH(ポート22)と、VPC内からのHTTPS(ポート443)のみを許可したい」というケースだ。

これをAWSのNACL(JSON形式に近い抽象設定、またはTerraform風の概念)で表現すると、インバウンドとアウトバウンドはそれぞれ以下のようになる。

インバウンドルール(Inbound Rules)

| ルール番号 | プロトコル | ポート範囲 | 送信元 (CIDR) | アクション | 意図・コメント |
| :— | :— | :— | :— | :— | :— |
| 100 | TCP | 22 | 203.0.113.10/32 | ALLOW | 信頼された踏み台からのSSH接続を許可 |
| 200 | TCP | 443 | 10.0.0.0/16 | ALLOW | 同一VPC内からのAPIリクエスト(HTTPS)を許可 |
| 300 | TCP | 1024-65535 | 10.0.0.0/16 | ALLOW | 内部サービスからのパッシブ通信等の受け入れ |
| 32767 | すべて | すべて | 0.0.0.0/0 | DENY | デフォルトの拒否ルール(明示・暗黙) |

アウトバウンドルール(Outbound Rules)

ステートレスであることを思い出してほしい。APIサーバーが外部(例えば外部の決済APIやDB)に通信したり、クライアントにレスポンスを返すためには、アウトバウンド側でもルールが必須だ。

| ルール番号 | プロトコル | ポート範囲 | 宛先 (CIDR) | アクション | 意図・コメント |
| :— | :— | :— | :— | :— | :— |
| 100 | TCP | 443 | 0.0.0.0/0 | ALLOW | 外部APIへのHTTPSアウトバウンド通信を許可 |
| 200 | TCP | 1024-65535 | 0.0.0.0/0 | ALLOW | クライアントのエフェメラルポートへの返信を許可 |
| 32767 | すべて | すべて | 0.0.0.0/0 | DENY | デフォルトの拒否ルール |

ここで特に重要なのが、アウトバウンドのポート 1024-65535 だ。これを忘れると、インバウンドでAPIリクエストを受け付けたはいいが、クライアントへレスポンスを返せずにサーバー側でパケットがドロップされる。

—

4. コードからの検証とデバッグTips

もし君が構築したWeb APIに対して、次のようなPythonスクリプトやcurlコマンドでリクエストを投げた際、原因不明のタイムアウトに直面したとしたらどうデバッグすべきか。

デバッグ用のPythonスクリプト(Requests)

import requests
from requests.exceptions import RequestException

# ターゲットのWeb APIエンドポイント
API_URL = "https://10.0.1.50/api/v1/health"

def check_api_connectivity():
    try:
        # タイムアウトを3秒に設定し、NACLやSGのミロードを即座に検知する
        response = requests.get(API_URL, timeout=3, verify=False)
        print(f"[SUCCESS] ステータスコード: {response.status_code}")
        print(f"[RESPONSE] レスポンスボディ: {response.text}")
    except RequestException as e:
        # ここでタイムアウトが発生する場合、NACLのアウトバウンド(エフェメラルポート)が
        # 塞がれているか、インバウンドのルール順序ミスが強く疑われる
        print(f"[ERROR] 接続に失敗しました: {e}")

if __name__ == "__main__":
    check_api_connectivity()

シニアSREが教える、現場のトラブルシューティング手順

1. 「セキュリティグループ」を疑う前に「VPC フローログ(VPC Flow Logs)」を見ろ
NACLによるドロップは、VPCフローログの action カラムに REJECT として記録される。どのルール番号(srcaddr, dstaddr, dstport)で弾かれたのかをログから逆引きするのが一番の近道だ。
2. エフェメラルポートの範囲を確認する
OSやクライアント環境によって、動的ポート(エフェメラルポート)の範囲が異なる場合がある(Linuxカーネルのデフォルトは 32768-60999 だが、WindowsやAWSのロードバランサーなどは 1024-65535 を使うことが多い)。NACLのアウトバウンドルールでは、安全のために 1024-65535 をまるっと許可しておくのが実務上の定石だ。
3. 極力NACLを細かく書きすぎない
現代のクラウドインフラストラクチャにおいて、サブネットレベルの細かいアクセス制御はセキュリティグループに任せ、NACLは「特定の悪意あるIPレンジ(CIDR)からのブロック」や「サブネット間の大枠の隔離」といった、要塞の「外堀」としての用途にとどめるのがモダンな設計思想である。細かく書きすぎると、ルールの管理コストと障害時の切り分け難易度が跳ね上がる。

—

まとめ

NACLのステートレス動作とルール評価は、一見するとレガシーで面倒くさい仕組みに思えるかもしれない。しかし、パケットの往復というネットワークの基本原則に忠実であるがゆえに、動作原理さえ頭に叩き込んでおけば、これほど予測可能で強力なフィルタリング機構はない。

「往路があれば復路がある。番号順に上から評価される。」
この2つを呪文のように唱えながら、次回のVPC設計やAPIインフラの構築に臨んでほしい。君のネットワークが、無数のパケットを正確無比に導き、一切の障害を起こさないことを祈っている。

コメント

タイトルとURLをコピーしました