AWS Network Firewallを本番導入で失敗した話と実装のコツ

Network Firewallの設計で実際にハマった落とし穴。AZ間の冗長化ミスから、IDS統合の癖、コスト最適化までの6ヶ月の経験談です。

AWS Network Firewall設計で学んだ実装トラブルと運用のコツ

うちのチームが先月、本番環境のNetwork Firewallを導入してから半年が経った。正直、最初は「AWS管理型なら簡単だろう」と舐めてました。実際に運用してみると、アーキテクチャ設計の落とし穴、IDS統合時の癖、そしてコスト最適化との戦いが続いています。今日は、その過程で学んだ実装のコツと、やってはいけないことを共有したいと思います。

Network Firewallを導入したきっかけ

去年の秋、セキュリティ監査で「ネットワークレイヤーの可視化が足りない」と指摘されたんです。うちのVPCは複数のAZに分散していて、NAT Gatewayを通るトラフィックは把握できるけど、VPC内のマイクロサービス間の通信は完全にブラックボックスだった。

そこで検討したのがNetwork Firewallです。従来のセキュリティグループやNACLと違って、ステートフル検査とIDS/IPS機能がネイティブに組み込まれている。何より、AWS側で管理されるから、パッチ当てや機能追加が勝手に行われる。これは大きい。

ただし、「AWS管理型 = 導入したら終わり」ではなかった。むしろ逆です。

アーキテクチャ設計で最初にハマったこと

最初は以下のような単純な構成を考えてました。

Internet → IGW → Network Firewall → VPC(AZ-1, AZ-2)

素人的には「Firewallを入口に置いとけばいい」と思ってたんですが、実際には大間違いでした。

問題1: AZ間の高可用性を考えてなかった

Network Firewallは、AZ単位でデプロイする必要があります。複数AZで構成しても、各AZのサブネットが異なるIGWを通ると、Firewallをスキップされるリスクがある。

うちのチームが実装したのはこういう構成です:

graph TB
    subgraph "Internet"
        IGW["Internet Gateway"]
    end
    
    subgraph "Network Firewall Layer"
        NFW1["Network Firewall<br/>AZ-1a"]
        NFW2["Network Firewall<br/>AZ-1b"]
    end
    
    subgraph "VPC (10.0.0.0/16)"
        subgraph "AZ-1a"
            PubSub1["Public Subnet<br/>10.0.1.0/24"]
            AppSub1["App Subnet<br/>10.0.10.0/24"]
            DBSub1["DB Subnet<br/>10.0.20.0/24"]
        end
        
        subgraph "AZ-1b"
            PubSub2["Public Subnet<br/>10.0.2.0/24"]
            AppSub2["App Subnet<br/>10.0.11.0/24"]
            DBSub2["DB Subnet<br/>10.0.21.0/24"]
        end
    end
    
    IGW -->|Route to NFW| NFW1
    IGW -->|Route to NFW| NFW2
    NFW1 -->|Inspect| PubSub1
    NFW1 -->|Inspect| AppSub1
    NFW1 -->|Inspect| DBSub1
    NFW2 -->|Inspect| PubSub2
    NFW2 -->|Inspect| AppSub2
    NFW2 -->|Inspect| DBSub2
    
    AppSub1 ---|"VPC Peering<br/>Route via NFW?"| AppSub2
    DBSub1 ---|"East-West<br/>不検査!"|  DBSub2

この図の問題が分かりますか?AZ-1aのDBが、AZ-1bのDBと通信するとき、Network Firewallを通らないんです。East-West Trafficが完全にスキップされてる

最初の実装では、Central Firewallアプローチで「すべてのトラフィックを中央の検査ポイント通す」を目指してたんですが、VPC内通信の場合、それが困難だということに気づきました。

問題2: ルーティングループの危険性

Network Firewallの前に、トランジットゲートウェイ(TGW)を置いて「全トラフィックを集約する」という設計も検討しました。でも、これもうっかりするとループします。

実際、初期実装時に、某開発環境でこれが発生しました:

VPC-A → TGW → Network Firewall → TGW → VPC-A... (ループ)

IAM権限で許可していたため、本番直前にテストで発見できました。危ない。

対策: Network Firewallが配置されるのは「Firewall Subnet」と呼ぶ専用サブネットで、そこに入ってくるトラフィックは 二度とTGWに返さない ルーティングにしました。

IDS統合でぶつかった落とし穴

Network Firewallは独自のルールエンジンを持ってますが、より詳細な脅威検知には、Suricataベースのルールセットが使えます。AWS側で管理されるルールも用意されてます。

ただし、本番運用で「IDS検知が多すぎる」という別の問題が発生しました。

アラートの嵐をいかに整理するか

デフォルトルールセットを有効にすると、毎日1万件以上のアラートが出ます。Slack通知設定してた当初は、Slackが埋もれて、本当に重要な脅威を見落としてました。

[2026-04-15 10:34] 🚨 Trojan.Generic detected
[2026-04-15 10:35] ⚠️  Suspicious DNS query
[2026-04-15 10:36] ⚠️  Unusual port scan
... 9997件省略

これではダメだと思って、CloudWatch Logsで条件フィルタリング設定を強化しました。

実装例

{
  "MetricTransformations": [
    {
      "MetricName": "CriticalThreat",
      "MetricValue": "1",
      "DefaultValue": 0,
      "MetricNamespace": "NetworkFirewall"
    }
  ],
  "LogGroupName": "/aws/networkfirewall/alert",
  "FilterPattern": "[... action \"alert\", ... severity = 1 || severity = 2 ]"
}

重大度(Severity)で1, 2だけフィルタして、メトリクスに送るようにしました。その後、低優先度のルールはそもそも無効化しました。

重大度レベル対応時間具体例
1Critical即座Ransomware, RCE, Exploitation攻撃
2High1時間以内SQL Injection, XSS試行
3以下Lowログのみアノマリ検知(ホワイトリスト除外)

この体制に切り替えてから、誤検知対応の時間が 70% 削減されました。正直、ここが本当に大事だなと実感した部分ですね。

ホワイトリスト管理の工夫

セキュリティとビジネス要件は相反することが多い。特に、「某SaaSサービスへのAPI呼び出し」がIDS検知に引っかかる問題が出ました。

当該ルールを丸ごと無効化するのはリスク高いので、うちのチームは アドレスグループ を使ってきめ細かく除外しました:

# CloudFormation/CDKで管理
from aws_cdk import aws_networkfirewall as nfw

address_group = nfw.CfnAddressGroup(
    self, "TrustedSaaS",
    capacity=100,
    tags=[{"key": "Name", "value": "trusted-api-ips"}]
)

# Rule Groupで参照
rule_definition = nfw.CfnRuleGroup.RuleGroupDefinitionProperty(
    stateful_rule_options={
        "rule_order": "STRICT_ORDER"
    },
    stateful_rules=[
        nfw.CfnRuleGroup.StatefulRuleProperty(
            action="PASS",
            header=nfw.CfnRuleGroup.RuleHeaderProperty(
                protocol="TCP",
                destination="$TRUSTED_SAAS_IPS",
                destination_port="443",
                direction="ANY",
                source="10.0.0.0/16",
                source_port="ANY"
            ),
            rule_options=[
                {
                    "keyword": "sid",
                    "settings": ["1000001"]
                }
            ]
        )
    ]
)

ポイント: アドレスグループは動的に更新できるので、SaaSプロバイダのIPが変わっても、Lambda + EventBridge で自動更新するようにしました。管理の手間が減ったし、ミスも減りました。

実運用で見えたコスト最適化のツボ

Network Firewallは、処理するトラフィック量検査ルール数 に応じて課金されます。

初月の請求を見て、チーム全員で絶句しました。

処理量: 500GB × $0.65/GB = $325
ルール: 5,000 rules × $0.01/rule/day × 30day = $1,500
合計: $1,825/月

これは予想の3倍でした。

トラフィック量削減戦略

すべてのトラフィックをNetwork Firewallに通すのではなく、信頼できるサブネット間はスキップ する戦略を取りました。

例えば、内部通信のうち:

  • RDS(DB層)への接続 → DB Security Groupで十分なので、Firewallをスキップ
  • ElastiCache → App間 → 同一VPC、内部通信なので除外
  • CloudFront → ALB → CloudFront IPレンジはホワイトリスト化
# ルーティングテーブルで条件分岐
route_table_for_db = ec2.RouteTable(self, "DBRouteTable",
    vpc=vpc
)

# DB通信は Firewall をスキップ
route_table_for_db.add_route(
    "RouteToPrivateSubnet",
    destination_cidr_block="10.0.20.0/24",
    gateway=local_gateway  # VPC Local
)

route_table_for_app = ec2.RouteTable(self, "AppRouteTable",
    vpc=vpc
)

# App通信は Firewall 経由
route_table_for_app.add_route(
    "RouteToDatabase",
    destination_cidr_block="10.0.20.0/24",
    gateway=firewall_gateway
)

結果: トラフィック量を約 40% 削減。月$1,300 → $780に減ったのは大きい。

ルール最適化:不要ルールの刈り込み

5,000ルールって、正直ほぼ使ってないルールが大半でした。

セキュリティチームと相談して、以下の判定基準でルール削減を実施:

# 過去30日間で1回も検知されたことがないルール → 候補
# ただし以下は除外:
# - CIS Benchmarkで推奨される基本ルール
# - PCI-DSS対応に必須なルール
# - Compliance Framework で指定されたルール

rules_to_remove = []
for rule in all_rules:
    if rule.triggered_count == 0 and rule.category not in MUST_HAVE_CATEGORIES:
        rules_to_remove.append(rule.id)

# 段階的に削除(本番環境だから慎重に)
for batch in chunks(rules_to_remove, 100):
    remove_rules_from_firewall(batch)
    wait_and_monitor(hours=24)  # 24時間様子見

最終的に、3,000ルール → 1,500ルールに削減。月$1,500 → $750。

アラート・ロギングの工夫

Network Firewallのアラートを何もしないで溜めておくと、ログだけで毎月テラバイト単位のストレージが消費されます。

うちのチームでは、段階的なログフィルタリング戦略 を取ってます:

xychart-beta
    title "Network Firewall ログ保持戦略(月間推移)"
    x-axis [Week1, Week2, Week3, Week4, Month]
    y-axis "ログ保持期間(日)" 0 --> 90
    line [90, 60, 30, 14, 3]

具体的には:

  • リアルタイム(0-3日): CloudWatch Logs(即座な脅威検知)
  • 短期(3-30日): S3 Standard(調査・フォレンジック)
  • 長期(30-90日): S3 Glacier(コンプライアンス保持)

実装の工夫:

from aws_cdk import aws_logs as logs
from aws_cdk import aws_s3 as s3

log_group = logs.LogGroup(self, "NFWLogs",
    retention=logs.RetentionDays.THREE_DAYS,
    log_group_name="/aws/networkfirewall/alert"
)

# S3へのエクスポート(Kinesis Firehose経由)
firehose_delivery_stream = kinesisfirehose.DeliveryStream(
    self, "NFWToS3",
    source=kinesisfirehose.StreamSource.cloud_watch_logs(
        log_group=log_group
    ),
    destinations=[kinesisfirehose.Destination.s3(
        bucket=s3.Bucket(self, "NFWLogs"),
        data_format_conversion_props={
            "enabled": True,
            "input_format": "JSON",
            "output_format": "PARQUET"
        }
    )]
)

効果: ストレージコスト 60% 削減(約$400/月の削減)

まとめ

AWS Network Firewallを本番導入して半年。学んだことは、「マネージドサービスだから簡単」という過信は禁物ということです。むしろ、ネットワーク設計、IDS運用、コスト最適化の3つのバランスを取ることが、本当の難しさなんだと感じました。

正直、この3つが揃わないと、セキュリティを高めるどころか、システムが不安定になったりコストがオーバーヒートしたりするんですよね。

次のアクション

  1. アーキテクチャ: East-West Trafficの検査方針を明確化する(全検査 vs 除外ホワイトリスト)
  2. IDS運用: 重大度レベルでフィルタ、継続的なルール見直し(月1回は削減候補をチェック)
  3. コスト: トラフィック分散&ルール圧縮で、初期の 40-50% 削減を目指す
  4. ロギング: 保持期間の階層化で、今月さらに $200/月の削減予定

Network Firewallは確かに強力ですが、「導入」ではなく「運用」がすべてだと痛感してます。同じく導入検討中なら、セキュリティチームだけでなく、インフラチームも早めに巻き込んで、設計フェーズから議論することをお勧めします。失敗事例から学べるのは、本当に幸運だと思うので、この記事が少しでも参考になれば幸いです。

U

Untanbaby

ソフトウェアエンジニア|AWS / クラウドアーキテクチャ / DevOps

10年以上のIT実務経験をもとに、現場で使える技術情報を発信しています。 記事の誤りや改善点があればお問い合わせからお気軽にご連絡ください。

関連記事