Azure Firewall の SNAT と X-Forwarded-For の挙動

azure
Published: 2022-06-17

Note

2024/10/11 Azure Firewall の DNAT の場合の動作を追記しました

はじめに

Azure Firewall のドキュメントには SNAT と X-Forwarded-For について次の記載があります。

指定したプライベート アドレス範囲は、ネットワーク規則にのみ適用されます。 現時点では、アプリケーション ルールでは常に SNAT が使用されます。

Azure Firewall の SNAT プライベート IP アドレス範囲

さらに、アプリケーション ルールによって処理されたトラフィックでは、常に SNAT が実行されます。 FQDN トラフィックのログに元の送信元 IP アドレスを表示する場合は、宛先 FQDN でネットワーク規則を使用できます。

Azure Firewall で、プライベート ネットワーク間のアウトバウンド SNAT は実行されますか?

HTTP および HTTPS プロトコル (TLS インスペクションあり) には常に、元の送信元 IP アドレスに相当する XFF (X-Forward-For) ヘッダーが Azure Firewall によって付けられます。 

ネットワーク ルールとアプリケーション ルール

つまり…

  • ネットワークルールで許可されたプライベート IP アドレス宛ての通信は SNAT されない
    • ただし、SNAT する宛先をカスタマイズできる
  • アプリケーションルールで許可された通信は宛先に関わらず SNAT される、さらに X-Forwarded-For が付与される

ということです。「百聞は一見に如かず」ということで、実際に環境を作って試してみました。

構成その1

まずはシンプルなサーバ2台の通信を Azure Firewall で制御する構成で試します。

構成その1

172.16.1.100 のサーバには、受信したリクエストの RemoteAddr と X-Forwarded-For だけを表示するシンプルな Web サーバ(kongou-ae/light-ip-echo)が動作しています。

192.168.4.100 のサーバから 172.16.1.100 のサーバへの通信をネットワークルールで許可した場合、次のような出力になります。Azure Firewall は通信を SNAT しないので、RemoteAddr には192.168.4.100 のサーバの IP アドレスが入ります。またネットワークルールの場合 X-Forwarded-For を付与しないので、X-Forwarded-For は空になります。

hubTestVM:~$ curl http://spksv01.aznet.test
{"RemoteAddr":"192.168.4.100:52822","RequestURI":"/","Xforwardedfor":""}

SNAT の宛先に 172.16.0.0/16 を含めるように設定を変更すると、プライベート IP アドレス宛ての通信をネットワークルールで許可した場合であっても SNAT されます。その結果、RemoteAddr が Azure Firewall の IP アドレスになります。

hubTestVM:~$ curl http://spksv01.aznet.test
{"RemoteAddr":"192.168.2.7:52824","RequestURI":"/","Xforwardedfor":""}

同様の通信をアプリケーションルールで許可した場合、次のような出力になります。アプリケーションルールは通信を SNAT するので、RemoteAddr が Azure Firewall の IP になっています、また、アプリケーションルールは X-Forwarded-For を付与するので、X-Forwarded-For に192.168.4.100 のサーバの IP アドレスが入ります。

hubTestVM:~$ curl http://spksv01.aznet.test
{"RemoteAddr":"192.168.2.6:61776","RequestURI":"/","Xforwardedfor":"192.168.4.100:37776"}

構成その2

インターネットへのアウトバウンド通信を試します。

構成その2

ifconfig.me への通信をネットワークルールで許可した場合、Azure Firewall は 192.168.4.100 のサーバの情報を X-Forwarded-For に付与しません。

hubTestVM:~$ curl ifconfig.me/forwarded
104.41.179.235, 34.117.59.81,35.191.10.86

一方で ifconfig.me への通信をアプリケーションルールで許可した場合、Azure Firewall は 192.168.4.100 のサーバの情報を X-Forwarded-For に付与します。

hubTestVM:~$ curl ifconfig.me/forwarded
192.168.4.100:41556, 104.41.179.235, 34.117.59.81,35.191.11.50

構成その3

Application Gateway を Azure Firewall の前に置いた構成を試します。

構成その3

Application Gateway から172.16.1.100 のサーバへの通信をネットワークルールで許可した場合、Azure Firewall は通信を SNAT しないので、Application Gateway から172.16.1.100 のサーバに到達したリクエストの RemoteAddr は Application Gateway の IP アドレスになります。また、ネットワークルールは X-Forwarded-For を付与しないため、172.16.1.100 のサーバに到達したリクエストの X-Forwarded-For には、前段の Application Gateway が付与した 192.168.4.100 のサーバの IP アドレスだけが含まれます。

hubTestVM:~$ curl http://agw.aznet.test:8080
{"RemoteAddr":"192.168.5.7:35194","RequestURI":"/","Xforwardedfor":"192.168.4.100:45200"}

Application Gateway から172.16.1.100 のサーバへの通信をアプリケーションルールで許可した場合、Azure Firewall は通信を SNAT するので、Application Gateway から172.16.1.100 のサーバに到達したリクエストの RemoteAddr は Azure Firewall の IP アドレスになります。また、アプリケーションルールは X-Forwarded-For を付与するのですが、現在の仕様では、Azure Firewall は X-Forwarded-For を上書きします。そのため、172.16.1.100 のサーバに到達したリクエストの X-Forwarded-For には、Azure Firewall が付与した Application Gateway の IP アドレスだけが含まれます。前段の Application Gateway が X-Forwarded-For に付与した 192.168.4.100 のサーバの IP アドレスは消失します。

hubTestVM:~$ curl http://agw.aznet.test:8080
{"RemoteAddr":"192.168.2.7:45062","RequestURI":"/","Xforwardedfor":"192.168.5.5:55432"}

構成その4

X-Forwarded-For の挙動を確認するために、Application Gateway を Azure Firewall の後段に置いた構成も試します。

構成その4

クライアントから Application Gateway への通信をネットワークルールで許可した場合、Azure Firewall は通信を SNAT しないので、Application Gateway に到達するリクエストは「RemoteAddr:192.168.4.100 のサーバの IP アドレス、X-Forwarded-For:なし」になります。

このリクエストをもとに Application Gateway が 172.16.1.100 のサーバにアクセスする際に、Application Gateway はアクセス元である 192.168.4.100 のサーバの情報を X-Forwarded-For に付与します。したがって、172.16.1.100 のサーバに到達するリクエストは「RemoteAddr:Application Gateway の IP アドレス、X-Forwarded-For:192.168.4.100 のサーバの IP アドレス」になります。

hubTestVM:~$ curl http://agwspk.aznet.test:8080
{"RemoteAddr":"172.16.2.4:36142","RequestURI":"/","Xforwardedfor":"192.168.4.100:34234"}

この通信をアプリケーションルールで許可した場合、Azure Firewall は通信を SNAT かつ X-Forwarded-For を付与するので、Application Gateway に到達するリクエストは「RemoteAddr:Azure Firewall の IP アドレス、X-Forwarded-For:192.168.4.100 のサーバの IP アドレス」になります。

このリクエストをもとに Application Gateway が 172.16.1.100 のサーバにアクセスする際に、Application Gateway はアクセス元である 192.168.4.100 のサーバの情報を X-Forwarded-For に追記します。Application Gateway は X-Forwarded-For を上書きしません。したがって、172.16.1.100 のサーバに到達するリクエストは「RemoteAddr:Application Gateway の IP アドレス、X-Forwarded-For:192.168.4.100 のサーバの IP アドレス, Azure Firewall の IP アドレス」になります。

hubTestVM:~$ curl http://agwspk.aznet.test:8080
{"RemoteAddr":"172.16.2.5:32636","RequestURI":"/","Xforwardedfor":"192.168.4.100:34232, 192.168.2.6:51910"}

構成その5

Azure Firewall を DNAT で通過した場合の挙動を確認します。

構成その5

DNAT の設定は次の通りです。Azure Firewall の Public IP アドレスに到達した HTTP のパケットを、仮想マシンのプライベート IP アドレスの HTTP に転送します。

"rules": [
    {
        "ruleType": "NatRule",
        "name": "1",
        "translatedAddress": "10.0.3.4",
        "translatedFqdn": "",
        "translatedPort": "80",
        "ipProtocols": [
            "TCP"
        ],
        "sourceAddresses": [
            "*"
        ],
        "sourceIpGroups": [],
        "destinationAddresses": [
            "48.218.213.236"
        ],
        "destinationPorts": [
            "80"
        ]
    }
],

Azure Firewall の Public IP アドレスにアクセスしたときの応答は次の通りです。

> $res = Invoke-WebRequest http://48.218.213.236/
> $res.Content
{"RemoteAddr":"10.0.4.8:12050","RequestURI":"/","Xforwardedfor":"","Host":"48.218.213.236","XAzureFDID":"","XAzureFDHP":""}

Azure Firewall の DNAT は SNAT するため、RemoteAddr が AzureFirewallSubnet の IP アドレスになっているのは想定の通りです。ですが、Xforwardedfor が空です。アプリケーションルールのように XFF を上書きするのであれば、Azure Firewall の Public IP アドレスにアクセスした端末のグローバル IP アドレスが XFF に記録されます。したがって、DNAT の XFF に対する挙動はアプリケーションルールとは違うことがわかります。

Xforwardedfor が空だと、Azure Firewall が何もしないのか、それとも XFF を消してしまったのかを判別できません。そこで、Azure Firewall の Public IP アドレスにアクセスするときに XFF を明示的に追加します。本当はプロキシサーバを経由させて XFF を足したいのですが、すぐに用意できなかったので手動でリクエストヘッダに追記します。結果は次の通りです。

> $header = @{ "X-Forwarded-For" = "xxx.xxx.xxx.xxx(我が家のグローバル IP アドレス)" }
> $res = Invoke-WebRequest http://48.218.213.236/ -Headers $header
> $res.Content
{"RemoteAddr":"10.0.4.6:24553","RequestURI":"/","Xforwardedfor":"xxx.xxx.xxx.xxx(我が家のグローバル IP アドレス)","Host":"48.218.213.236","XAzureFDID":"","XAzureFDHP":""}

XFF に追加した IP アドレスをそのままサーバ側で確認できます。したがって、DNAT の場合、Azure Firewall は XFF に対して何もしないということになります。

まとめ

様々な構成を組んだうえで、Azure Firewall の SNAT と X-Forwarded-For の動作を確認しました。次のような振る舞いであることを理解したうえで、要件を満たす配置にしましょう。

  • ネットワークルールで許可されたプライベート IP アドレス宛ての通信は SNAT されない
    • ただし、SNAT する宛先をカスタマイズできる
  • アプリケーションルールで許可された通信は宛先に関わらず SNAT される、さらに X-Forwarded-For を上書きする
    • 上書きが望ましくない場合は、上書きされたくない通信をネットワークルールで許可する
  • DNAT で許可された通信は SNAT される。X-Forwarded-For に対しては何もしない

Note

当サイトは個人のブログです。このブログに示されている見解や意見は個人的なものであり、所属組織の見解や意見を表明するものではありません。 公開情報を踏まえて正確な情報を掲載するよう努めますが、その内容の完全性や正確性、有用性、安全性、最新性について一切保証しません。 添付文章やリンク先などを含む本サイトの内容は作成時点でのものであり、予告なく変更される場合があります。