【実務・中級編】 AWS Network FirewallのステートフルインスペクションとIDS/IPSルールセット – クラウド&コンテナネットワーク実践ガイド

こんにちは。シニアネットワークエンジニアの私だ。これまで数々の修羅場――夜中に突如発生した原因不明のパケットロスや、本番リリース直前のセキュリティ監査での痛い指摘など――をくぐり抜けてきた。

クラウドネイティブなインフラストラクチャを構築するうえで、VPCの境界防御やマイクロセグメンテーションは避けて通れない。特に、AWS環境でゼロトラストアーキテクチャを志向する場合、単なるIPアドレスやポート番号に基づくセキュリティグループ(SG)やネットワークACLだけでは、コンプライアンス要件を満たせないフェーズが必ずやってくる。

「L7レイヤーで不正なペイロードを検知・ブロックしたい」「特定の外部APIエンドポイントへの通信であっても、不審なURIやシグネチャを持つものは遮断したい」。そんな現場の切実な要求に応えるのが、今回解説する AWS Network Firewall だ。

今回は、AWS Network Firewallの心臓部であるステートフルインスペクションとSuricata互換のIDS/IPSルールセットに焦点を当て、パケットがVPCの境界をどう駆け抜け、どのようにDPI(ディープパケットインスペクション)の洗礼を受けるのか、実務で即座に使える設定例とともに紐解いていこう。

—

1. AWS Network Firewallのアーキテクチャとステートフルインスペクションの正体

まず、AWS Network Firewall(以下、ANFW)の位置づけをアーキテクチャの観点から整理しておこう。

ANFWは、VPCのルートテーブルからトラフィックを強制的に引き込むための「ファイアウォールエンドポイント(ENI)」を専用のサブネットに配置して動作する。従来のEC2ベースのサードパーティ製次世代ファイアウォール(NGFW)とは異なり、完全マネージドなステートレス/ステートフルエンジンであり、トラフィックの増減に応じてスケールする。

ここで重要なのが、ANFWのステートフルインスペクションの挙動だ。

[クライアントVPC] 
       ↓ (ルートテーブル: 0.0.0.0/0 -> ANFWエンドポイント)
[ANFW専用サブネット (ファイアウォールENI)] 
       ↓ (ステートレスルール評価 ➔ ステートフルルール評価)
[インターネット / 外部API / 他VPC]

ステートレスルールがパケット単体を静的に評価(5タプル等)するのに対し、ステートフルルールはセッションの文脈(State)を維持しながら、L7レイヤーのペイロードまで踏み込んで検査する。Suricataエンジンをベースにしているため、オープンソースのIDS/IPS界隈でデファクトとなっているSuricataルール構文をそのままAWS上に持ち込むことができる。

実務において、ANFWのルール評価順序でハマりがちなポイントがある。ANFWのステートフルルールグループには、主に以下の評価アクションが存在する。

  • aws:pass: 一致したパケットを以降のルール評価からバイパスし、即座に許可する。
  • aws:drop: 一致したパケットを即座に破棄し、必要に応じてTCP RSTを返す(またはサイレントドロップ)。
  • aws:reject: 拒否パケットを送信元に送り返し、通信を強制切断する。
  • aws:alert: パケットを通すが、CloudWatch LogsやKinesisにアラートログを出力する(IDSモード)。

この挙動を理解していないと、「ルールを書いたのにブロックされない」「意図せず通信が全ロストした」というインシデントを踏み抜くことになる。

—

2. Suricata互換ルールセットの設計と実践的な書き方

では、実際に具体的なユースペースを想定したSuricataルールを書いてみよう。

例えば、社内のWeb APIクライアント(PythonスクリプトやLambda等)が、特定の外部決済API(api.example-payment.com)と通信しているとする。このとき、インジェクション攻撃や、既知の脆弱性を狙ったシグネチャを含むHTTPリクエスト、あるいは指定外の怪しいUser-Agentを持つトラフィックをDPIで検知・ブロックしたいケースを考える。

以下は、AWS Network Firewallにインポート(またはTerraform等で定義)するSuricataルールの実例だ。

# 1. 脆弱性を突く可能性のある特定のSQLインジェクションパターンの検知とドロップ
drop tcp any any -> $HOME_NET 443 (msg:"SQL Injection Attempt Detected in TLS Payload"; content:"UNION SELECT"; nocase; sid:1000001; rev:1;)

# 2. 許可されていない不審なUser-Agentを持つHTTP/HTTPSリクエストのブロック
# ※TLS復号をしていない場合でも、SNIや初期ハンドシェイク、あるいは非暗号化通信のHTTPであればパケット内の文字列をスキャン可能
drop http any any -> any any (msg:"Forbidden User-Agent Access Blocked"; http.user_agent; content:"BadScannerBot"; sid:1000002; rev:1;)

# 3. 許可リスト以外の外部ドメインへのデータ流出(Exfiltration)を警戒するためのアラート
alert tls any any -> any 443 (msg:"Suspicious External TLS Connection Alert"; tls.sni; content:!".example-payment.com"; sid:1000003; rev:1;)

パラメーターとルールの詳細解説

  • drop / alert: アクション。dropはパケット破棄、alertは検知してログ記録(IDS)。
  • tcp / http / tls: プロトコル。SuricataのL7パーサーが有効な場合、httpやtlsキーワードを用いることで、HTTPヘッダーやTLSのSNI(Server Name Indication)を精密に指定できる。
  • msg: ログ出力時に表示されるメッセージ。CloudWatch Logsでアラートを解析する際、このメッセージが運命の分かれ道になるため、人間が読んで一発で意図が伝わる文字列にすること。
  • content: ペイロード内に含まれるべき文字列を指定する。nocaseは大文字小文字を区別しないオプション。
  • sid (Rule ID): ルールを一意に識別するID。AWS Network Firewallでは、カスタムルールグループ内で重複しない番号を振る必要がある。

—

3. Terraformによるインフラコード化の実装例

現場のSREとしては、マネジメントコンソールをポチポチ叩いて構築するような泥臭い作業は避けたい。Terraformを使って、ANFWのステートフルルールグループとファイアウォールポリシーをコード化してみよう。

以下のHCLスニペットは、先ほど解説したSuricataルールをAWS Network Firewallに適用するための実用的な構成だ。

# ステートフルルールグループの定義(Suricataフォーマット)
resource "aws_networkfirewall_rule_group" "custom_suricata_rules" {
  capacity    = 100
  name        = "production-api-suricata-rule-group"
  type        = "STATEFUL"
  description = "Web API保護のためのカスタムSuricataルールセット"

  rule_group {
    rules_source {
      rules_string = <<-EOF
        drop tcp any any -> $HOME_NET 443 (msg:"SQL Injection Attempt Detected"; content:"UNION SELECT"; nocase; sid:1000001; rev:1;)
        drop http any any -> any any (msg:"Forbidden User-Agent Blocked"; http.user_agent; content:"BadScannerBot"; sid:1000002; rev:1;)
      EOF
    }

    stateful_rule_options {
      rule_order = "DEFAULT_ACTION_ORDER"
    }
  }

  tags = {
    Environment = "Production"
    ManagedBy   = "Terraform"
  }
}

# ファイアウォールポリシーの定義
resource "aws_networkfirewall_firewall_policy" "main_policy" {
  name = "production-vpc-firewall-policy"

  firewall_policy {
    # ステートレスルールのデフォルトアクション(今回はパケットをステートフルエンジンへ転送)
    stateless_default_actions          = ["aws:forward_to_stateful"]
    stateless_fragment_default_actions = ["aws:forward_to_stateful"]

    # ステートフルエンジンでどのルールグループを使うかを紐づけ
    stateful_rule_group_reference {
      resource_arn = aws_networkfirewall_rule_group.custom_suricata_rules.arn
    }

    # ステートフルルールのデフォルトアクション(マッチしなかった場合の挙動:ここでは許可)
    stateful_default_actions = ["aws:default_allow_fulfil_established"]
  }

  tags = {
    Environment = "Production"
  }
}

このコードを適用することで、VPCを通過するすべてのパケットが指定したSuricataエンジンの監視下に入る。

—

4. 現場のトラブルシューティングとデバッグ手法

「ルールを適用した途端、社内システムから外部APIへの疎通が急に切れた」「どこでパケットがドロップされているのか分からない」。こんな障害に直面したとき、シニアエンジニアが真っ先に取るべきデバッグ手順を伝授しよう。

ステップ 1: AWS Network Firewallのログ出力を有効化する

ANFWは、パケットのドロップ理由やアラートをAmazon CloudWatch Logs、Amazon S3、またはAmazon Kinesis Data Firehoseに送信できる。まずはロググループを確認せよ。
特に aws-netfw-flow(フローログ)と aws-netfw-alert(アラートログ)を有効にしておき、CloudWatch Insightsで以下のようなクエリを流すのが定石だ。

fields @timestamp, @message
| filter event_type = "alert"
| sort @timestamp desc
| limit 20

ここで、先ほど設定した msg:"SQL Injection Attempt Detected" などのメッセージが出力されていないか確認する。もし意図しないルール(誤検知:False Positive)でドロップしている場合、Suricataの content マッチが広すぎることが原因のほとんどだ。

ステップ 2: クライアント側からのパケット検証(curl / Pythonによるテスト)

実際にアプリケーションがどのようなHTTPリクエストを送っているのか、手元や踏み台EC2から curl を使ってシミュレートしてみる。

例えば、意図したカスタムUser-Agentでアクセスしてみて、通信がブロックされる(Connection reset by peerなどが返る)ことを確認するスクリプト(Pythonの requests ライブラリ)の例を挙げる。

import requests
from requests.exceptions import RequestException

# テスト対象のエンドポイント
target_url = "https://api.example-payment.com/v1/status"

# 故意にブロックされるべき不審なUser-Agentを設定
headers = {
    "User-Agent": "BadScannerBot/1.0",
    "Content-Type": "application/json"
}

try:
    print(f"[*] 接続テスト開始: {target_url}")
    response = requests.get(target_url, headers=headers, timeout=5)
    
    # ステータスコードの確認
    print(f"[+] レスポンス受信: ステータスコード {response.status_code}")
    print(response.text)

except RequestException as e:
    # ネットワークレベルでドロップ(Connection Reset等)された場合のハンドリング
    print(f"[-] 通信エラーが発生しました(ファイアウォールによるブロックの可能性): {e}")

もしこのスクリプトを実行して Connection Reset や Connection Refused が即座に返ってくるようであれば、ANFWのステートフルルール(今回の例では sid:1000002)が正常に機能してパケットを屠っている証拠だ。

—

5. おわりに

AWS Network Firewallを用いたステートフルインスペクションとSuricataルールセットの運用は、クラウドネットワークセキュリティの引き出しを劇的に広げてくれる強力な武器だ。

しかし、強力なDPIエンジンゆえに、不適切なルール定義は「動くはずのシステムが動かない」という深刻な障害を引き起こす諸刃の剣でもある。まずはステージング環境で入念なトラフィックテストを行い、CloudWatch Logsでアパレートやフローを丹念に観察しながら、ルールを徐々に洗練させていく――この泥臭いアプローチこそが、安定稼働するインフラストラクチャを守り抜く唯一の道である。

君たちのアーキテクチャに、確実な堅牢性を。健闘を祈る。

コメント

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