Azure Firewall Private IP DNAT

Azure Firewall の Private IP を使った DNAT が GA となりました。

https://techcommunity.microsoft.com/t5/azure-network-security-blog/private-ip-dnat-support-and-scenarios-with-azure-firewall/ba-p/4230073

今までは Public IP → Public IP か Public IP → Private IP への DNAT しかサポートされていなかったのですが、Private IP → Private IP への DNAT が使えるようになります。
IPv4 のアドレス枯渇への対応やオンプレに公報されていない環境へのルーティングでも使える技術ですので、機能の詳細や留意点をまとめてみたいと思います。

## 類似の機能

| サービス | 機能の差異 |
|-|-|
|Application Gateway TCP Proxy | DNAT と同じような使い方ができる。AppGW の場合、負荷分散の機能、Private Link が使える。|
|Private Link (ALB + VM)|Private Link とロードバランサーの構成が必要。Private Endpoint を LB のバックエンドプールに設定できない。
|Azure Firewall Explicit Proxy| http,https のみが対象|

## 留意点

- Standard と Premium SKU のみで利用可能
- あて先として指定できる Azure Firewall の Private IP は一つのみ
    - Public IP とは違い Private IP は追加できない
- 送信元は Azure Firewall のインスタンスの IP (AzureFirewallSubnet 内の任意の IP) に SNAT される
- DNAT 用の NAT 規則では 1 から 63999 の範囲のポートがサポートされる
    - ネットワーク規則とアプリケーション規則で 1 から 65535 の範囲の任意のポートがサポートされる

## 動作確認

### 事前準備

- Hub&Spoke 構成の仮想ネットワークを作成
- Azure Firewall Standard SKU を作成
- クライアントと Web サーバー用の VM を作成

### 構成図
以下のような構成図で複数のクライアントから Azure Firewall の Private IP へアクセスし、複数のサーバーへ DNAT させます。
DNAT を試すため VNet peering のリモートゲートウェイのオプションはあえて入れておりません。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2266293/eb196397-55c3-b59b-5d05-9eb3ba3a8cb7.png)

### ソース IP を指定

ソース IP に応じて異なる宛先の tcp:80 に転送されるような DNAT ルールを作成します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2266293/3ba6a1dc-53dd-10f2-c067-bad419426392.png)

1 つ目のクライアント (192.168.0.4) でブラウザでアクセスを試してみます。期待通り、linux-vm1 のページが表示されました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2266293/b8b2052c-2e4f-0ba3-5129-4b0183f67405.png)

サーバー側でパケットをみてみると Azure Firewall のレンジ内の 10.10.1.5 の IP アドレスに NAT されていました。
```
10:01:27.588261 IP (tos 0x0, ttl 127, id 56732, offset 0, flags [DF], proto TCP (6), length 521)
    10.10.1.5.65532 > 10.2.1.4.80: Flags [P.], cksum 0xbd42 (correct), seq 1:482, ack 1, win 2052, length 481: HTTP, length: 481
GET / HTTP/1.1
Host: 10.10.1.4
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36 Edg/128.0.0.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9
```

1 つ目のクライアント (192.168.0.5) でブラウザでアクセスを試してみます。
期待通り、linux-vm2 のページが表示されました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2266293/39a3c2a2-0b94-e967-738e-42ab565011c4.png)

ソース IP に応じて転送するあて先を変更することができました。

### Private IP から Pubic IP に DNAT
Private IP を Public IP に変換してアクセスできるかを試してみます。Application Gateway を作成し、パブリックのリスナー経由で Web サーバーにつながるようにしておきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2266293/93c71f59-b4a4-da07-3371-d34861fb8669.png)

ルールの変換後の IP アドレスを Application Gateway の Public IP アドレスに変更します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2266293/373ec03e-38f2-eb54-4448-1366821ec33d.png)

問題なくアクセスできました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2266293/f0afebe4-b48c-8cf1-f4f7-17916e5120e3.png)

バックエンドの Web サーバーのパケットを見てみると Azure Firewall の Public IP アドレスからのアクセスとなっていました。
```
    10.2.0.5.23250 > 10.2.1.4.80: Flags [P.], cksum 0xa5bf (correct), seq 1:735, ack 1, win 63, options [nop,nop,TS val 1754014223 ecr 2451204498], length 734: HTTP, length: 734
        GET / HTTP/1.1
        X-FORWARDED-PROTO: http
        X-FORWARDED-PORT: 80
        X-Forwarded-For: 172.207.157.xxx:8512
        X-Original-URL: /
        Connection: keep-alive
        X-AppGW-Trace-Id: 9dcfdf27c3d5346c060488f70f0963b0
        Host: 10.10.1.4
        X-ORIGINAL-HOST: 10.10.1.4
        Cache-Control: max-age=0
        Upgrade-Insecure-Requests: 1
        User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36 Edg/128.0.0.0
        Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
        Accept-Encoding: gzip, deflate
        Accept-Language: en-US,en;q=0.9
        If-None-Match: "1b-621e907af9f3e"
        If-Modified-Since: Thu, 12 Sep 2024 09:50:00 GMT
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2266293/00073e87-d19d-feff-7f22-3a3f47750341.png)

### FQDN を指定

変換するあて先に FQDN を指定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2266293/d505368c-02a2-8e15-986d-d53723cea551.png)

以下のような構成になるように Private DNS ゾーンを作成し、Hub-VNet にリンクします。
DNAT する FQDN は linux-vm1 の IP アドレスに解決されるようになっています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2266293/2777f59f-ca1c-9b84-a7e7-415392225b02.png)

アクセスしてみると想定通りにアクセスできました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2266293/f682a755-4f76-dac1-4021-2fea22545afc.png)

Private DNS ゾーンを更新し、linux-vm2 の IP アドレスに解決されるようにします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2266293/9d4dac6a-9eb9-d4cb-8c8a-310dca5b3452.png)

このような構成に変更し、アクセスを試みます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2266293/3d84638c-d294-ee2b-68e0-2d94b6c4d68f.png)

ブラウザを更新すると想定通り linux-vm2 の画面が表示されました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2266293/02d551df-896d-f9e6-e8f6-a755823614f4.png)

### 送信元に重複するアドレス空間がある場合

以下のようにクライアントとあて先の VM を追加します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2266293/6cc3f61d-905b-ce9c-6e5c-7418fc7c0ddc.png)

以下のように送信元を Any にした最優先の DNAT ルールコレクションを作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2266293/73c58c9e-ac6b-54b9-e0aa-507fcdae9c99.png)

ClientVM3 からアクセスすると linux-vm3 にアクセスしました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2266293/5095560e-a95b-a8bc-0895-5f2a2e7dd411.png)

この状態で ClientVM1 からアクセスすると上記同様 linux-vm3 にアクセスしました。DNAT のルールの優先度に従って動作していることがわかります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2266293/6b54bce0-6cf4-5b06-0c05-4eb739aa4de9.png)

今度は送信元を Any にした DNAT ルールコレクションの優先度を最低に変更します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2266293/f2f26523-9efb-8bf3-2c47-970546fd6707.png)

ClientVM3 からアクセスすると linux-vm3 にアクセスしました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2266293/cda07bc3-4adf-9365-1c69-ef24ad7dea8a.png)

ClientVM1 からアクセスすると linux-vm1 にアクセスしました。ルールコレクションの優先度に従って DNAT が動作していることがわかります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2266293/098d5342-0cc9-46be-e225-946f50fbd749.png)

## ルールを作成する際の留意点
**Azure Firewall の Private IP は一つのみ**ですので、送信元や DNAT 用の宛先ポートを考慮してルールを適切に作成していく必要があります。

- 優先度に応じて評価されるため、送信元の範囲が広い (Any や /8,/16 等) 場合は他の通信に影響しやすくなるため、独自のポートを利用する等、工夫が必要。
    - 例えば送信元が Any で TCP 443 を最優先のルールで定義してしまうと、他の https 用の DNAT ルールは評価されず、すべてのクライアントからの https 通信が同じあて先に変換さる。
- なるべく狭い範囲での送信元の定義が望ましい
- あて先ポート (Azure Firewall が受け付けるポート) を変更できる場合は独自ポートを利用するのが望ましい

## 診断ログ
Public IP の DNAT の時は Public IP が DestinationIp として表示されていましたが、Private IP が DestinationIp に表示されています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2266293/3ab67fa7-3ed5-6db6-cb2f-2b9c8c3bc8e1.png)