Azure Application Gateway TCP/TLS Proxy

Application Gateway v2 の TCP/TLS Proxy がパブリックプレビューとなりました。この機能を利用することで、TCP の通信を転送、負荷分散したり、TLS の通信で SNI を書き換えてバックエンドに転送できるようになります。
元々、HTTPS と HTTP の通信をバックエンドに転送する機能はありましたが、Web トラフィック (HTTP, Websocket, HTTP/2) が前提となっており、他のプロトコルは対応しておりませんでした。

利用できそうなシナリオ、ロードバランサーとの違い、実際に検証した内容等をまとめてみました。

https://azure.microsoft.com/en-us/updates/tls-tcp-proxy-preview/

## 利用できそうなシナリオ
-  Azure からオンプレ、他の VNet への接続で利用する。
    -  TCP であれば任意のプロトコルを IP Reachable な環境へ転送できるようになるので、Private IP の DNAT のような使い方が可能。Application Gateway の [Private Link](https://qiita.com/Isato-Hiyama/items/b83812ec6389d6f2647c) との併用も可能なので、以下のように Azure 上の任意の VNet からオンプレに TCP で接続するようなデザインも可能。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2266293/41790751-5c28-da04-d6a1-e272c81e4865.png)

- PaaS の既定ドメインではなくカスタムドメインを使ってアクセスできるようにしたい
    - Event Hub のような非 HTTP のサービスも使えるはずだが、動作確認できたものなし。TLS を使ってる Azure SQLDB, Postgres Flexible Server, Event Grid, Event Hub あたりは試してみたが、うまくいかず (2024/02/28)

## Azure Load Balancer, Azure Firewall (DNAT) との違い

### Azure Load Balancer

- LB ではバックエンドの IP アドレスは特定の VNet 上に VM としてアサインされているリソースのみしか指定できない。オンプレミスの IP アドレスや、VNet ピアリングされている環境の IP アドレス、パブリック IP アドレスは指定不可。
- Application Gateway ではバックエンドを FQDN で指定可能。
- LB の負荷分散ポリシーは IP,Port,Protocol によるハッシュベースとなるが、Application Gateway はラウンドロビンでの振り分けとなる。送信元が同一 IP かつ同一のポートで振り分けが偏る場合などに有効な可能性あり。
- Application Gateway では SSL オフロードの構成が可能。(HTTP 以外は未確認)

### Azure Firewall (DNAT)

- Application Gateway では Private IP で接続し、任意の IP アドレスに転送が可能。Azure Firewall は Public IP のみサポート。
- Application Gateway では正常性プローブと負荷分散の機能もある。

## 考慮事項
- Application Gateway v2 は設定変更時に切断が発生する場合があるため、長時間接続するようなシナリオの場合は注意が必要。[参考: Application Gateway 設定変更の影響](https://jpaztech.github.io/blog/network/appgw-configchange/)
- Windows の RDP のように常時通信が発生するようなプロトコルは問題ないが、Linux の SSH のように操作時のみ通信が発生するようなプロトコルの場合、バックエンド設定のタイムアウトの調整が必要。無通信の場合、タイムアウトを迎えると切断される。
- FTP (Active Mode) のような双方向で TCP 通信が発生するようなサービスは動かない。
- TCP/TLS プロキシーは WAF と併用できないため、通信制御は NSG によって行う必要あり。
- HTTP のヘッダー書き換えは利用できない。

## 検証の構成図
IaaS ベースのシンプルな構成です。TCP Proxy テストのために SSH/RDP で接続しています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2266293/64049a03-7cd2-3597-cf3d-426b2516be1d.png)

## 環境作成
プレビュー登録以外は通常の Application Gateway 作成の手順と大差はありません。

### プレビュー登録
サブスクリプションのプレビュー登録を行います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2266293/3be076be-de19-2eff-5955-e7e9bff8e99c.png)

### Application Gateway の作成
公開ドキュメントと同じよな手順で作成します。
https://learn.microsoft.com/ja-jp/azure/application-gateway/quick-create-portal

リスナーのところはプロトコルとして TCP を選びます。リスナーのポートは ["22" が予約 ](https://learn.microsoft.com/en-us/azure/application-gateway/application-gateway-components#ports) されて使えなかったので "2222" を使います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2266293/87908dda-98d6-8b12-4c2e-7d41ddb7be2c.png)

バックエンド設定では Linux に SSH で接続できるようプロトコル TCP で、ポートは 22 を指定します。
<font color="Red">検証後、SSH の場合、20 秒だとすぐにタイムアウトできれてしまうことに気付いたため、600 秒に変更しました。</front>
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2266293/4ef322b1-c7c2-68d5-d30e-18d96c278dde.png)

正常性プローブの作成は Application Gateway 作成画面からはできませんでした。既定のプローブでも動作しますが、もしカスタムプローブ作りたい場合は、以下のように Application Gateway 作成後に作成できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2266293/ead5c0db-8ed9-b723-b63c-bc4917eedfc0.png)

## Public IP 経由で SSH ログイン

Application Gateway の Public IP:2222 に SSH で接続してみます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2266293/039ac99c-bd1a-39e0-ce93-c8a1e741c926.png)

問題なくログインできました。ターミナルをもう一つ追加すると、別の Linux VM にログインしていたため、負荷分散もうまくいってそうです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2266293/7296047d-f6b4-8e5f-d5e1-2e1c46f25113.png)

## Private IP 経由で RDP ログイン

Private IP のリスナー経由で RDP ログインできるように TCP 3389 用のリスナーとバックエンド設定を追加します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2266293/d2a87a3a-4dcd-a966-e75b-73f004a8ca1f.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2266293/39e10627-abeb-f19e-f1e6-fc81e6367b40.png)

問題なく接続できました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2266293/74cf010e-84ae-da1f-d1bc-d79758b38246.png)

## Private Link 経由で RDP ログイン
Private Link、Private Endpoint を作成して、RDP 用につくったリスナーに接続してみます。(Application Gateway の Private Link の用途や手順は[こちら](https://qiita.com/Isato-Hiyama/items/b83812ec6389d6f2647c))

問題なくログインできました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2266293/f1d9dcf4-b024-298c-b2a8-a76e661efa82.png)

## SSL オフロード (TLS リスナー + TCP バックエンド設定)
HTTP プロトコルでの検証なので、意味はありませんが、SSL オフロードできるかを試してみます。

TLS のリスナーを追加します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2266293/6a39835a-507a-d4ee-e57a-bb9990835daf.png)

バックエンド設定で TCP 80 接続用の設定を追加します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2266293/c0c80f4f-dd76-9717-6921-484994eb70a2.png)

Application Gateway に HTTPS でつないでみると、バックエンドの Apache に接続することができました。
php でレスポンスとしてリクエストヘッダを表示してみると、TLS/TCP Proxy のため、HTTP(S) リスナーとは違い X-Forwared-For や X-AppGW-Trace-Id 等の Application Gateway ならではのヘッダーがないことが確認できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2266293/fb7a40e4-5692-3125-d4df-34470df3d07f.png)

通常の HTTPS リスナー + バックエンド設定 (HTTP) の SSL オフロードの場合はこちら。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2266293/1483a6a6-58b3-a0c9-d69e-bb5720d15933.png)


## リスナーとバックエンド設定の組み合わせ

リスナーとバックエンド設定は組み合わせがあるようで、リスナーの設定に応じてバックエンド設定のプロトコルが決まります。(ルールの設定画面でサポートされてないプロトコルを含むバックエンド設定は選べない)

| リスナー | バックエンド設定 |
|:-----------|:------------|
| HTTPS      | HTTPS/HTTP |
| HTTP     | HTTPS/HTTP |
| TCP     | TCP/TLS |
| TLS     | TCP/TLS |

## WAF
Application Gateway への WAF ポリシーの適用自体は可能です。WAF の利用が可能な HTTPS/HTTP リスナーとの共存をサポートするためと考えられます。
IP アドレスや Geo Location であれば動作する可能性があると思い試してみましたが、WAF のルールは動作しませんでした。TCP/TLS Proxy では NSG 以外での制御はできないようです。

また、TLS/TCP のリスナーへポリシーの適用が可能かを確認してみましたが、HTTPS/HTTP リスナーのみが表示されました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2266293/206f968f-b737-4f48-9490-80e645e88d03.png)

## 診断ログ (アクセスログ)

TCP Proxy にて RDP 接続したときのログを見てみましたが、接続元の IP アドレス、接続先の IP アドレスという最低限の情報は出力されていました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2266293/c4718b24-994d-d289-8303-7ed0f8d23d5a.png)