[アップデート] Amazon VPC Reachability AnalyzerがAWS Network Firewallを含む3つのネットワークサービスをサポートしました

待望のAWS Network Firewallに対応しました
2023.03.29

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

AWS Network Firewallを経由して通信できるか気になるな

こんにちは、のんピ(@non____97)です。

皆さんはAWS Network Firewallを経由して通信できるか気になるなと思ったことはありますか? 私はあります。

通信経路のチェックと言えばAmazon VPC Reachability Analyzer(以降Reachability Analyzer)です。

しかし、Reachability AnalyzerはNetwork Firewallを通る経路をチェックすることができませんでした。そのため、通信ができない場合はルートテーブルやNetwork Firewallのメトリクス、ログから原因を調査する必要があります。

今回、Reachability AnalyzerがNetwork Firewall、Gateway Load BalancerとAWS PrivateLinkをサポートしました。

これはありがたいですね。

また、Gateway Load BalancerのターゲットのファイアウォールアプライアンスやNetwork Firewallのルールによって通信がブロックされているかも確認できるようになったようです。

試してみたので紹介します。

いきなりまとめ

  • Amazon VPC Reachability Analyzerが以下サービスをサポート
    • AWS Network Firewall
    • Gateway Load Balancer
    • AWS PrivateLink
  • Gateway Load BalancerのターゲットのファイアウォールアプライアンスやNetwork Firewallのルールによって通信がブロックされているかも確認
  • Reachability AnalyzerはTCP/UDPしかサポートしていない
    • HTTPなど上位のレイヤーでルールを定義している場合は、意図した結果とならない
  • Network Firewallにおいて、いずれのルールにもマッチしない場合はnetwork-firewall-policy-defaultという設定していないファイアウォールポリシーで評価される
    • デフォルトアクションでドロップするようなものを指定している場合は、このファイアウォールポリシーによってドロップされたと表示される
  • Network Firewallのルールにおいて、msgrevはサポートされていないルールオプションとして認識される
    • これらルールオプションを指定していてもパスの分析結果には影響を与えない
  • Network Firewallにおいて、戻りの通信のルートがない場合は到達可能となるが、UNIDIRECTIONAL_PATH_ANALYSIS_ONLYと戻りの通信で何かしら不具合があったことが示唆される
    • 可達のステータスのみで判断するではなく、パス内で表示されるメッセージも確認する必要

検証環境

検証環境は以下の通りです。

Amazon VPC Reachability AnalyzerがAWS Network Firewallを含む3つのネットワークサービスをサポートしました検証環境構成図

ルールグループにはTLS/443を許可するルールと、HTTP/80を拒否するルールを定義しています。

また、ファイアウォールポリシーは厳格(Strict)にしており、デフォルトアクションは何も選択していない状態です。

検証環境はAWS CDKでデプロイします。使用したコードは以下リポジトリに保存しています。

やってみる

TCP/443の通信経路チェック

早速やってみます。

Reachability AnalyzerがTCP/443の通信経路を認識してくれるかチェックします。

まず、VPCエンドポイントがない状態でもEC2インスタンスにSSMセッションマネージャーで接続できることや、EC2インスタンスからHTTPSで通信できることを確認しました。

$ curl -m 5 https://dev.classmethod.jp -I
HTTP/2 200
date: Tue, 28 Mar 2023 02:18:22 GMT
content-type: text/html; charset=utf-8
content-length: 168287
server: nginx/1.22.1
vary: Accept-Encoding
x-amzn-requestid: b5c41faf-0c1f-4cde-9312-29f4961b4880
x-amzn-remapped-content-length: 168287
x-amz-apigw-id: CeErKENDNjMFi-A=
cache-control: max-age=300
etag: "2915f-vD1WK4HW3441WF3fChXfmHeR8uI"
x-powered-by: Express
x-amzn-trace-id: Root=1-64224e46-1055f5985e2d9be633dcf0c4;Sampled=0
via: 1.1 4e4278a2778e72cc34feef6db603088c.cloudfront.net (CloudFront), 1.1 8731d2a1a7d15f67b588bf58f652f9f0.cloudfront.net (CloudFront)
x-amz-cf-pop: HIO52-P1
vary: Accept-Encoding
x-cache: Hit from cloudfront
x-amz-cf-pop: HIO52-P1
x-amz-cf-id: MmbUrYZ5rmCu2CUm4L_OyZw2pOxNaCMvnadOKADxAvEHc1-gV01BQA==
age: 38
expires: Tue, 28 Mar 2023 02:23:22 GMT
strict-transport-security: max-age=31536000; includeSubDomains
accept-ranges: bytes

それではReachability Analyzerのパスを作成します。

Reachability Analyzer

送信元をEC2インスタンス、送信先をdev.classmethod.jpのIPアドレスを指定します。また、送信先ポートは443とします。プロトコルはTLS or HTTPSにしたいところですが、Reachability AnalyzerがTCP or UDPしかサポートしていないのでTCPを選択しました。

パスの作成と分析

パスの作成と分析をクリックしてしばらく待つと可達のステータス到達可能となりました。

分析の実行リクエストが正常に完了しました。

詳細な通信経路も確認できます。Network Firewallをしっかり通っていますね。

パスの詳細

戻りの経路(リバースパス)も認識されています。

リバースパスの確認

しかし、よくよく見るとFIREWALL_UNSUPPORTED_HIGHER_PRIORITY_RULESと共にこのパスのトラフィックに一致する可能性がある優先度がより高いルールが少なくとも 1 つありますが、これにはサポートされていないルールタイプが含まれているため、無視されました。「documentation」を参照してください。とメッセージが出力されています。

FIREWALL_UNSUPPORTED_HIGHER_PRIORITY_RULES

確かにnetwork-firewall-policy-defaultという設定していないファイアウォールポリシーで評価されているようです。

FIREWALL_UNSUPPORTED_HIGHER_PRIORITY_RULESにカーソルを合わせると以下のようなJSONのポップアップが表示されました。

{
  "additionalDetailType": "FIREWALL_UNSUPPORTED_HIGHER_PRIORITY_RULES",
  "ruleOptions": [],
  "ruleGroupTypePairs": [],
  "ruleGroupRuleOptionsPairs": [
    {
      "ruleGroupArn": "arn:aws:network-firewall:us-east-1:<AWSアカウントID>:stateful-rulegroup/network-firewall-rule-group-5-tuple",
      "ruleOptions": [
        {
          "keyword": "msg",
          "settings": [
            "\"TLS/443 pass\""
          ]
        },
        {
          "keyword": "rev",
          "settings": [
            "1"
          ]
        }
      ]
    },
    {
      "ruleGroupArn": "arn:aws:network-firewall:us-east-1:<AWSアカウントID>:stateful-rulegroup/network-firewall-rule-group-5-tuple",
      "ruleOptions": [
        {
          "keyword": "msg",
          "settings": [
            "\"HTTP/80 reject\""
          ]
        },
        {
          "keyword": "rev",
          "settings": [
            "1"
          ]
        }
      ]
    }
  ],
  "loadBalancers": []
}

これはルールにmsgrevなど無効なルールオプションを指定すると、ルールが評価されないということでしょうか。

試しにルールオプションからmsgrevを削除します。

$ aws network-firewall describe-rule-group \
    --rule-group-name network-firewall-rule-group-5-tuple \
    --type STATEFUL \
    --query "RuleGroup.RulesSource"
{
    "StatefulRules": [
        {
            "Action": "PASS",
            "Header": {
                "Protocol": "TLS",
                "Source": "$HOME_NET",
                "SourcePort": "ANY",
                "Direction": "FORWARD",
                "Destination": "$EXTERNAL_NET",
                "DestinationPort": "443"
            },
            "RuleOptions": [
                {
                    "Keyword": "sid",
                    "Settings": [
                        "1000001"
                    ]
                }
            ]
        },
        {
            "Action": "REJECT",
            "Header": {
                "Protocol": "HTTP",
                "Source": "$HOME_NET",
                "SourcePort": "ANY",
                "Direction": "FORWARD",
                "Destination": "$EXTERNAL_NET",
                "DestinationPort": "80"
            },
            "RuleOptions": [
                {
                    "Keyword": "sid",
                    "Settings": [
                        "1000002"
                    ]
                }
            ]
        }
    ]
}

この状態で再度パスの分析を行います。

パスの分析

結果を確認するとFIREWALL_UNSUPPORTED_HIGHER_PRIORITY_RULESなどのメッセージは出力されなくなりましたが、変わらずnetwork-firewall-policy-defaultという設定していないファイアウォールポリシーで評価されているようです。

msgとrevを削除して再度パス分析した結果

ギブアップです。

TCP/80の通信経路チェック

TCP/443は一旦置いておいて、TCP/80の通信経路チェックを行います。

送信先ポートに80を指定して、TCP/80のパスの作成と分析を行います。

http:80のパスの作成

分析結果を確認すると、HTTP/80でrejectしているはずが、到達可能となっています。

到達可能になっていることを確認

また、こちらもnetwork-firewall-policy-defaultという設定していないファイアウォールポリシーで評価されているようです。

ここで閃きました。

ルールグループで定義しているルールでTCP/UDPよりも上位のレイヤーのプロトコルを使っている場合は、評価されないのでは と。

試しにTLSやHTTPをTCPに変更して再チャレンジします。

$ aws network-firewall describe-rule-group \
    --rule-group-name network-firewall-rule-group-5-tuple \
    --type STATEFUL \
    --query "RuleGroup.RulesSource"
{
    "StatefulRules": [
        {
            "Action": "PASS",
            "Header": {
                "Protocol": "TCP",
                "Source": "$HOME_NET",
                "SourcePort": "ANY",
                "Direction": "FORWARD",
                "Destination": "$EXTERNAL_NET",
                "DestinationPort": "443"
            },
            "RuleOptions": [
                {
                    "Keyword": "sid",
                    "Settings": [
                        "1000001"
                    ]
                }
            ]
        },
        {
            "Action": "REJECT",
            "Header": {
                "Protocol": "TCP",
                "Source": "$HOME_NET",
                "SourcePort": "ANY",
                "Direction": "FORWARD",
                "Destination": "$EXTERNAL_NET",
                "DestinationPort": "80"
            },
            "RuleOptions": [
                {
                    "Keyword": "sid",
                    "Settings": [
                        "1000002"
                    ]
                }
            ]
        }
    ]
}

この状態で再度分析をすると、到達不可能となりました。

rejectされることを確認

rejectのルールグループやルールも表示されていますね。

詳細を展開すると、以下のようにより詳細なルールを確認することができます。$HOME_NET$EXTERNAL_NETは展開されていますね。$EXTERNAL_NET$HOME_NETで定義したネットワークアドレス以外となっていることも分かります。

{
  "addresses": [],
  "availabilityZones": [],
  "cidrs": [],
  "component": {
    "id": "network-firewall",
    "arn": "arn:aws:network-firewall:us-east-1:<AWSアカウントID>:firewall/network-firewall"
  },
  "explanationCode": "FIREWALL_RULES_RESTRICTION",
  "loadBalancerTargetGroups": [],
  "portRanges": [],
  "protocols": [],
  "securityGroups": [],
  "vpc": {
    "id": "vpc-01167e67d44f46d18",
    "arn": "arn:aws:ec2:us-east-1:<AWSアカウントID>:vpc/vpc-01167e67d44f46d18"
  },
  "firewallStatefulRule": {
    "ruleGroupArn": "arn:aws:network-firewall:us-east-1:<AWSアカウントID>:stateful-rulegroup/network-firewall-rule-group-5-tuple",
    "sources": [
      "10.1.1.0/24"
    ],
    "destinations": [
      "128.0.0.0/1",
      "64.0.0.0/2",
      "32.0.0.0/3",
      "16.0.0.0/4",
      "0.0.0.0/5",
      "12.0.0.0/6",
      "8.0.0.0/7",
      "10.1.0.0/24",
      "10.1.2.0/23",
      "11.0.0.0/8",
      "10.1.4.0/22",
      "10.128.0.0/9",
      "10.1.8.0/21",
      "10.64.0.0/10",
      "10.32.0.0/11",
      "10.0.0.0/16",
      "10.1.32.0/19",
      "10.2.0.0/15",
      "10.4.0.0/14",
      "10.1.16.0/20",
      "10.1.64.0/18",
      "10.8.0.0/13",
      "10.16.0.0/12",
      "10.1.128.0/17"
    ],
    "sourcePorts": [
      {
        "from": 0,
        "to": 65535
      }
    ],
    "destinationPorts": [
      {
        "from": 80,
        "to": 80
      }
    ],
    "protocol": "TCP",
    "ruleAction": "reject",
    "direction": "FORWARD"
  }
}

TCP/443の通信経路チェック (2回目)

ということは、TCP/443の通信経路チェックも自分が設定したルールグループで評価されるのようになったのではないでしょうか。

再度実行すると、確かに自分が設定したルールグループのルールでpassされたことが分かります。

passされることを確認

Network FirewallでTCP/UDPよりも上位のレイヤーのプロトコルを使っているルールを使用している場合は注意が必要ですね。

試しにrevというルールオプションを追加して再度実行してみます。

$ aws network-firewall describe-rule-group \
    --rule-group-name network-firewall-rule-group-5-tuple \
    --type STATEFUL \
    --query "RuleGroup.RulesSource"
{
    "StatefulRules": [
        {
            "Action": "PASS",
            "Header": {
                "Protocol": "TCP",
                "Source": "$HOME_NET",
                "SourcePort": "ANY",
                "Direction": "FORWARD",
                "Destination": "$EXTERNAL_NET",
                "DestinationPort": "443"
            },
            "RuleOptions": [
                {
                    "Keyword": "sid",
                    "Settings": [
                        "1000001"
                    ]
                },
                {
                    "Keyword": "rev",
                    "Settings": [
                        "1"
                    ]
                }
            ]
        },
        {
            "Action": "REJECT",
            "Header": {
                "Protocol": "TCP",
                "Source": "$HOME_NET",
                "SourcePort": "ANY",
                "Direction": "FORWARD",
                "Destination": "$EXTERNAL_NET",
                "DestinationPort": "80"
            },
            "RuleOptions": [
                {
                    "Keyword": "msg",
                    "Settings": [
                        "\"HTTP/80 reject\""
                    ]
                },
                {
                    "Keyword": "sid",
                    "Settings": [
                        "1000002"
                    ]
                },
                {
                    "Keyword": "rev",
                    "Settings": [
                        "1"
                    ]
                }
            ]
        }
    ]
}

実行すると意図したルールグループで評価されていますが、FIREWALL_UNSUPPORTED_RULE_OPTIONSでサポートされていないルールオプションがあると表記されました。

msgを削除しても検出される

詳細を確認すると、確かに追加したrevがリストアップされていますね。

{
  "additionalDetailType": "FIREWALL_UNSUPPORTED_RULE_OPTIONS",
  "ruleOptions": [
    {
      "keyword": "rev",
      "settings": [
        "1"
      ]
    }
  ],
  "ruleGroupTypePairs": [],
  "ruleGroupRuleOptionsPairs": [],
  "loadBalancers": []
}

TCP/587の通信経路チェック

次にTCP/587の通信経路チェックを行います。

現在ファイアウォールポリシーでデフォルトアクションは何も指定していません。そのため、TCP/587などルールにマッチしない通信は通るはずです。

送信先ポートに587を指定して、TCP/587のパスの作成と分析を行います。

tcp:587のパスの作成

分析結果を確認すると、確かに到達可能となっていますね。

tcp:587に到達可能であることを確認

どうやらnetwork-firewall-policy-defaultというファイアウォールポリシーはルールグループに設定したルールにマッチしない場合に表示されるようですね。

TCPのポートを指定しない場合の通信経路チェック

ついでにTCPのポートを指定しない場合の通信経路チェックを行います。

送信先ポートに何も指定せず、パスの作成と分析を行います。

tcp:anyの分析パスの作成

結果を確認すると到達可能となっています。気になるポートを確認すると0-79となっていますね。TCP/0からTCP/79にマッチするルールはないためnetwork-firewall-policy-defaultで評価されています。

tcp:anyの分析パスの結果

TCP/587の通信経路チェック (暗黙的な拒否)

暗黙的な拒否を設定している場合、正しく判定されるのか気になってきました。

ファイアウォールポリシーのステートフルデフォルトアクションにaws:drop_establishedを設定します。

$ aws network-firewall describe-firewall-policy \
    --firewall-policy-name network-firewall-policy \
    --query "FirewallPolicy.{StatefulRuleGroupReferences : StatefulRuleGroupReferences, StatefulDefaultActions : StatefulDefaultActions}"
{
    "StatefulRuleGroupReferences": [
        {
            "ResourceArn": "arn:aws:network-firewall:us-east-1:<AWSアカウントID>:stateful-rulegroup/network-firewall-rule-group-5-tuple",
            "Priority": 1
        }
    ],
    "StatefulDefaultActions": [
        "aws:drop_established"
    ]
}

これにより、TCPのコネクションを確立後ルールにマッチしない通信はドロップされます。

tracerouteは到達可能です。

$ sudo traceroute -T -p 587 email-smtp.us-east-1.amazonaws.com
traceroute to email-smtp.us-east-1.amazonaws.com (34.205.148.75), 30 hops max, 60 byte packets
 1  * * *
 2  * * *
 3  ip-10-1-1-4.ec2.internal (10.1.1.4)  1.950 ms  3.531 ms  3.512 ms
 4  * * *
 5  ec2-34-205-148-75.compute-1.amazonaws.com (34.205.148.75)  3.936 ms  3.608 ms  3.870 ms

Network Firewallにおける暗黙的な拒否の詳細は以下記事を参照ください。

TCP/587の分析パスを実行をすると、到達不可能となりました。network-firewall-policy-default(確立された接続のパケットをドロップ)でdropともなっていますね。

aws-drop_establishedでtcp:587の分析パスを実行した場合

詳細を確認すると以下のようになっていました。

{
  "addresses": [],
  "availabilityZones": [],
  "cidrs": [],
  "component": {
    "id": "network-firewall",
    "arn": "arn:aws:network-firewall:us-east-1:<AWSアカウントID>:firewall/network-firewall"
  },
  "explanationCode": "FIREWALL_RULES_RESTRICTION",
  "loadBalancerTargetGroups": [],
  "portRanges": [],
  "protocols": [],
  "securityGroups": [],
  "vpc": {
    "id": "vpc-01167e67d44f46d18",
    "arn": "arn:aws:ec2:us-east-1:<AWSアカウントID>:vpc/vpc-01167e67d44f46d18"
  },
  "firewallStatefulRule": {
    "ruleGroupArn": "arn:aws:network-firewall:us-east-1:<AWSアカウントID>:firewall-policy/network-firewall-policy-default",
    "sources": [],
    "destinations": [],
    "sourcePorts": [],
    "destinationPorts": [],
    "ruleAction": "drop"
  }
}

TCP/443の通信経路チェック (暗黙的な拒否)

暗黙的な拒否が動作することが分かりました。

次にTCP/443で許可しているルールをTLS/443に変更した場合、暗黙的な拒否でドロップされるか確認します。

以前の検証で「確立された接続のパケットをドロップ」としている場合、HTTPなどTCPよりも上位のレイヤーで許可しているルールがあれば通信できることを確認しました。

同じような挙動をReachability Analyzerがしてくれるのか確認します。

まず、TCP/443で許可しているルールをTLS/443に変更します。

$ aws network-firewall describe-rule-group \
    --rule-group-name network-firewall-rule-group-5-tuple \
    --type STATEFUL \
    --query "RuleGroup.RulesSource"
{
    "StatefulRules": [
        {
            "Action": "PASS",
            "Header": {
                "Protocol": "TLS",
                "Source": "$HOME_NET",
                "SourcePort": "ANY",
                "Direction": "FORWARD",
                "Destination": "$EXTERNAL_NET",
                "DestinationPort": "443"
            },
            "RuleOptions": [
                {
                    "Keyword": "sid",
                    "Settings": [
                        "1000001"
                    ]
                },
                {
                    "Keyword": "rev",
                    "Settings": [
                        "1"
                    ]
                }
            ]
        },
        {
            "Action": "REJECT",
            "Header": {
                "Protocol": "TCP",
                "Source": "$HOME_NET",
                "SourcePort": "ANY",
                "Direction": "FORWARD",
                "Destination": "$EXTERNAL_NET",
                "DestinationPort": "80"
            },
            "RuleOptions": [
                {
                    "Keyword": "msg",
                    "Settings": [
                        "\"HTTP/80 reject\""
                    ]
                },
                {
                    "Keyword": "sid",
                    "Settings": [
                        "1000002"
                    ]
                },
                {
                    "Keyword": "rev",
                    "Settings": [
                        "1"
                    ]
                }
            ]
        }
    ]
}

その後、TCP/443のパスの分析を実行します。

結果を確認すると到達不可能となっていました。

aws-drop_establishedでtls:443の分析パスを実行した場合

やはりTCPよりも上位のレイヤーのプロトコルでルールを定義している場合は、気をつけた方が良さそうです。

Public SubnetからNetwork Firewallへのルートを削除した状態の通信経路チェック

最後にPublic SubnetからNetwork Firewallへのルートを削除した状態の通信経路チェックを行います。

ついつい戻りの通信用のルートの定義を忘れがちなので、それの検知ができるか気になりました。

Public SubnetのルートテーブルからNetwork Firewallへのルートを削除します。

Public SubnetからNetwork Firewallへのルートを削除

ルート削除は以下のようになります。

Public SubnetからNetwork Firewallへのルートを削除2

また、TLS/443で許可していたルールはTCP/443で許可するように変更します。

この状態でパスを分析すると、到達可能になってしまいました。

tcp:443を許可してもUNIDIRECTIONAL_PATH_ANALYSIS_ONLY

しかし、よくよく見るとUNIDIRECTIONAL_PATH_ANALYSIS_ONLYと表示されており、戻りの通信で何かしら不具合があったことが分かります。

可達のステータスのみで判断するではなく、パス内で表示されるメッセージも確認する必要がありますね。

待望のAWS Network Firewallに対応しました

Amazon VPC Reachability AnalyzerがAWS Network Firewallを含む3つのネットワークサービスをサポートしたアップデートを紹介しました。

個人的には待望のAWS Network Firewallの対応です。まさかルールの評価まで確認してくれるとは思いませんでした。少しクセはありますが、使いこなせば通信経路チェックに非常に役立ちそうです。

この記事が誰かの助けになれば幸いです。

以上、AWS事業本部 コンサルティング部の のんピ(@non____97)でした!