Application Gateway の Private Link が GA となりました。(20230607) パブリックプレビューで利用できるようになっています。 作成済みの Application Gateway v2 にも "プライベートリング" の UI が追加されていました。
利用するシナリオや実際に検証した内容をまとめてみました。
活用できるシナリオ
- 強制トンネリングの環境になっているが何らかの理由により UDR を適用できない。
- VNet を独立して構成し、Private Link 経由でアクセスさせることができる。VNet ピアリングも不要。
-
プライベートエンドポイントのプライベート IP をパブリック IP のリスナーに紐づけることができるため、パブリック IP、プライベート IP あての通信で同じポートが利用可能となる。Application Gateway の制限事項の回避が可能。機能追加あり
パブリック側のリスナーとプライベート側のリスナーの両方に同じポートを使用することはできますか? - アドレス空間が重複している VNet 上のリソースにアクセスが可能となる。
- PLS + ILB + VM の構成と似たような構成が可能。Standard SKU の ILB の場合、IP ベースのバックエンドは利用不可だが、Application Gateway の場合は問題ないため、Private Link の Web アクセスの自由度が上がる。(オンプレへのアクセスにも利用可能)
- それなりのインスタンス数が必要になるが、ApplicationGateway 用のサブネットを /24 のような大きな範囲で確保できない。プライベートエンドポイント用のサブネットのみでアドレス空間を確保。
考慮事項
- Application Gateway サブネットは専用サブネットであるため、Private Link 用のサブネットは分けて構成する必要がある。
-
Private Link 用のサブネットにて SNAT されるため、クライアント IP アドレスが不明となる。X-Forwarded-For も Private Link 用サブネットの IP アドレスになる。現状、Application Gateway には TCP Proxy v2 の実装はない。-
ログ取得が必須である場合、Private Endpoint への通信で Azure Firewall を経由させたり、Application Gateway を多段にして X-Forwarded-For を挿入するなど、何らかの仕組みが必要となる。Azure Firewall で XFF を挿入する方法は後述。SNAT されない動作になりました。
- 多数のアクセスが見込まれる場合、Private Link で利用される NAT IP も考慮する必要あり (インスタンスあたり 64,000 個)。
Private Link サービスで使用される NAT (ネットワーク アドレス変換) IP 構成とは何ですか。 - プライベート エンドポイント経由の送受信のデータ処理量の料金が別途かかる。
Azure Private Link の価格 - 利用可能な Application Gateway の SKU は Standard v2, WAF v2 (v1 はサポートされてない)。
- Azure Front Door のバックエンドとして Application Gateway Private Link を利用することはサポートされてない。
- その他の制限事項
構成図
検証のためにあえてそれぞれの仮想ネットワークのアドレス空間を合わせました。
前提
- 構成図内のリソースのうち、Private Link Service, Private Endpoint 以外のリソースは作成済みであること
- Application Gateway v2、バックエンド VM を適切に構成し、http にてアクセス可能な状態となっていること
Private Link, Private Endpoint の追加 (Private IP のリスナー)
Application Gateway のリソースをクリックし、"プライベート リンク" > "追加" をクリックします。
"名前" を入力し、"プライベート リンクのサブネット"、"フロントエンド IP 構成" を選択して、"追加" をクリック後、"追加" をクリックします。
プライベート リンクの設定が完了したら、"プライベート エンドポイント接続" のタブをクリックし、"+ プライベート エンドポイント" をクリックします。
プライベート エンドポイントを作成するサブスクリプション、リソースグループ、プライベート エンドポイントの名前、リージョンを選択し、次に進みます。
対象のサブリソースが Application Gateway のプライベート IP 用の名前 (appGwPrivateFrontendIp) になっていることを確認し、次に進みます。
対象サブリソースが出てこない場合、リスナーが構成されているかや Private Link の設定が完了しているかを確認してください。リスナーが Public IP アドレスで構成されており、Private Link を Private IP に紐づけてしまったりすると以下の様に対象サブリソースが表示されません。構成を変更する際は Private Link を削除してからリスナーの構成変更をします。
プライベート エンドポイントを作成する仮想ネットワーク、サブネットを選択します。ネットワークポリシーを有効化する場合、オプションのチェックを入れます。
ネットワークポリシーの詳細は Azure Private Link の NSG, UDR を参照ください。
作成が完了したらプライベート エンドポイントのリソースに移動し、"DNS の構成" からプライベート エンドポイントの IP アドレスを確認します。
この IP アドレスは動的に割り当てが行われますが、リソースが削除されない限り IP アドレスが変わることはありません。公開情報でもライフサイクル全体で変わらない旨が記載されています。 プライベート エンドポイントのプロパティ
クライアントの VM からプライベート エンドポイントの IP アドレスに curl コマンドでアクセスしてみます。
問題なくアクセスできます。
$ curl -v http://10.10.2.5
* Rebuilt URL to: http://10.10.2.5/
* Trying 10.10.2.5...
* TCP_NODELAY set
* Connected to 10.10.2.5 (10.10.2.5) port 80 (#0)
> GET / HTTP/1.1
> Host: 10.10.2.5
> User-Agent: curl/7.61.1
> Accept: */*
>
< HTTP/1.1 200 OK
< Date: Thu, 08 Jun 2023 08:26:55 GMT
< Content-Type: text/html; charset=UTF-8
< Content-Length: 9
< Connection: keep-alive
< Server: Apache/2.4.37 (centos)
< Last-Modified: Thu, 08 Jun 2023 02:26:10 GMT
< ETag: "9-5fd94f9b834d6"
< Accept-Ranges: bytes
<
ServerVM
* Connection #0 to host 10.10.2.5 left intact
バックエンドの VM 側でパケットキャプチャを見てみます。"X-Forwarded-For" が クライアントの IP アドレスとなっていることがわかります。プレビュー時は Private Link 用サブネットで SNAT されてましたが、GA で動作が変わってます。 "10.10.3.4:1026" となっており、Private Link 用のサブネット (10.10.3.0/24) にて SNAT されていることがわかります。
10.10.1.8.35544 > 10.10.2.4.80: Flags [P.], cksum 0xdc60 (correct), seq 1:278, ack 1, win 63, options [nop,nop,TS val 3257560129 ecr 1787253117], length 277: HTTP, length: 277
GET / HTTP/1.1
X-FORWARDED-PROTO: http
X-FORWARDED-PORT: 80
X-Forwarded-For: 10.10.2.4:41814
X-Original-URL: /
Connection: keep-alive
X-AppGW-Trace-Id: 6ca3db1933e226ffcb5e58174f783d03
Host: 10.10.2.5
X-ORIGINAL-HOST: 10.10.2.5
User-Agent: curl/7.61.1
Accept: */*
08:26:55.450226 IP (tos 0x0, ttl 64, id 12252, offset 0, flags [DF], proto TCP (6), length 52)
10.10.2.4.80 > 10.10.1.8.35544: Flags [.], cksum 0x1746 (incorrect -> 0xffae), seq 1, ack 278, win 235, options [nop,nop,TS val 1787253119 ecr 3257560129], length 0
08:26:55.450660 IP (tos 0x0, ttl 64, id 12253, offset 0, flags [DF], proto TCP (6), length 357)
10.10.2.4.80 > 10.10.1.8.35544: Flags [P.], cksum 0x1877 (incorrect -> 0x2352), seq 1:306, ack 278, win 235, options [nop,nop,TS val 1787253119 ecr 3257560129], length 305: HTTP, length: 305
HTTP/1.1 200 OK
Date: Thu, 08 Jun 2023 08:26:55 GMT
Server: Apache/2.4.37 (centos)
Last-Modified: Thu, 08 Jun 2023 02:26:10 GMT
ETag: "9-5fd94f9b834d6"
Accept-Ranges: bytes
Content-Length: 9
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive
Content-Type: text/html; charset=UTF-8
ServerVM
08:26:55.451857 IP (tos 0x0, ttl 64, id 64390, offset 0, flags [DF], proto TCP (6), length 52)
以下は Application Gateway の診断ログですが、こちらも同様に clientIP_s (クライアント IP アドレス) は "10.10.2.4" となってます。
Private Link, Private Endpoint の追加 (Public IP のリスナー)
プライベート エンドポイント, プライベート リンクの順でそれぞれの設定を削除します。
Application Gateway のリスナーに移動し、フロントエンド IP をパブリックに変更し、保存します。
再度、同じ手順でプライベート リンク、プライベート エンドポイントを作成します。
プライベート エンドポイントが作成できたら、プライベート エンドポイントのリソースに移動し、"DNS の構成" から IP アドレスを確認します。
クライアントの VM から curl コマンドにてプライベート エンドポイントにアクセスします。
問題なくアクセス可能でした。
$ curl -v http://10.10.2.6
* Rebuilt URL to: http://10.10.2.6/
* Trying 10.10.2.6...
* TCP_NODELAY set
* Connected to 10.10.2.6 (10.10.2.6) port 80 (#0)
> GET / HTTP/1.1
> Host: 10.10.2.6
> User-Agent: curl/7.61.1
> Accept: */*
>
< HTTP/1.1 200 OK
< Date: Thu, 08 Jun 2023 08:44:06 GMT
< Content-Type: text/html; charset=UTF-8
< Content-Length: 9
< Connection: keep-alive
< Server: Apache/2.4.37 (centos)
< Last-Modified: Thu, 08 Jun 2023 02:26:10 GMT
< ETag: "9-5fd94f9b834d6"
< Accept-Ranges: bytes
<
ServerVM
* Connection #0 to host 10.10.2.6 left intact
Application Gateway に割り当てられているパブリック IP アドレスにも同じ http でアクセス可能です。
$ curl -v http://4.216.93.20
* Rebuilt URL to: http://4.216.93.20/
* Trying 4.216.93.20...
* TCP_NODELAY set
* Connected to 4.216.93.20 (4.216.93.20) port 80 (#0)
> GET / HTTP/1.1
> Host: 4.216.93.20
> User-Agent: curl/7.61.1
> Accept: */*
>
< HTTP/1.1 200 OK
< Date: Thu, 08 Jun 2023 08:46:39 GMT
< Content-Type: text/html; charset=UTF-8
< Content-Length: 9
< Connection: keep-alive
< Server: Apache/2.4.37 (centos)
< Last-Modified: Thu, 08 Jun 2023 02:26:10 GMT
< ETag: "9-5fd94f9b834d6"
< Accept-Ranges: bytes
<
ServerVM
* Connection #0 to host 4.216.93.20 left intact
診断ログはプライベート IP 用のリスナーと同じ表記になるので、ログからパブリック IP 用のリスナーかどうかを判定するためにはリスナー名から判定する必要があります。
複数のリスナー
異なるポート (TCP 8080) を利用するリスナーを追加します。
プライベート リンク、プライベート エンドポイントの追加の設定は不要で、アクセス可能でした。
この動作からプライベート リンクはリスナーではなくフロントエンドの IP アドレスに紐づく形で構成されていると考えられます。
$ curl -v http://10.10.2.6:8080
* Rebuilt URL to: http://10.10.2.6:8080/
* Trying 10.10.2.6...
* TCP_NODELAY set
* Connected to 10.10.2.6 (10.10.2.6) port 8080 (#0)
> GET / HTTP/1.1
> Host: 10.10.2.6:8080
> User-Agent: curl/7.61.1
> Accept: */*
>
< HTTP/1.1 200 OK
< Date: Thu, 08 Jun 2023 08:56:30 GMT
< Content-Type: text/html; charset=UTF-8
< Content-Length: 9
< Connection: keep-alive
< Server: Apache/2.4.37 (centos)
< Last-Modified: Thu, 08 Jun 2023 02:26:10 GMT
< ETag: "9-5fd94f9b834d6"
< Accept-Ranges: bytes
<
ServerVM
* Connection #0 to host 10.10.2.6 left intact
検証時の注意点
NSG 設定
Application Gateway 用のサブネットに Application Gateway の必須要件以外は受信接続を拒否する NSG を付けたところ、通信ができなくなりました。
以下の様に NSG の受信規則を追加して Private Link Subnet からの通信を許可したころ問題なく通信ができるようになりましたので、必須要件のようです。
Private 構成
こちらで紹介しているプライベート構成では Private Link はサポートされてないので、EnableApplicationGatewayNetworkIsolation
をプレビュー登録したままだと以下の様にエラーになります。
サブスクリプションのプレビュー登録の画面から EnableApplicationGatewayNetworkIsolation
を登録解除して、Application Gateway を再作成してからお試しください。プライベート構成の場合、EnhancedNetworkControl (拡張ネットワーク制御) が有効になり、その影響でエラーとなるようです。
Azure Firewall と組み合わせて X-Forwarded-For を挿入する (20230607 以前のプレビュー時の情報)
Azure Firewall のアプリケーションルールの評価では XFF (X-Forward-For) を挿入する機能が備わっています。(HTTPS の場合、Premium SKU の TLS インスペクション必須)
以下の構成において XFF が想定通りに挿入されているかを確認してみます。あとから AzureFirewallSubnet を追加したため、サブネットの構成がおかしくなっていますが、目をつぶっていただけたらと、、
以下の作業を追加で実施しておきます。
- Azure Firewall / Firewall ポリシーを Premium で作成。
- Firewall ポリシーで TLS 検査を有効化。
- Firewall ポリシーでアプリケーションルールを作成し、http, https で testfw.hiyamanet2.com のトラフィックを許可。TLS 検査も有効化。
- Private DNS Zone を Client-VNet に紐づけ、FQDN (testfw.hiyamanet2.com) で Private Link 経由で Application Gateway にアクセスできるように構成。
- UDR を Client-Subnet に紐づけ、Private Endpoint (10.10.2.6/32) へのトラフィックが Azure Firewall になるように構成。
- Application Gateway 側に HTTPS のリスナー、ルールを作成し、URL "https://testfw.hiyamanet2.com" でアクセスできるように構成。
以下の様に http://FQDN で curl コマンドでアクセスします。
$ curl -v http://testfw.hiyamanet2.com
* Rebuilt URL to: http://testfw.hiyamanet2.com/
* Trying 10.10.2.6...
* TCP_NODELAY set
* Connected to testfw.hiyamanet2.com (10.10.2.6) port 80 (#0)
> GET / HTTP/1.1
> Host: testfw.hiyamanet2.com
> User-Agent: curl/7.61.1
> Accept: */*
>
< HTTP/1.1 200 OK
< Accept-Ranges: bytes
< Content-Length: 10
< Content-Type: text/html; charset=UTF-8
< Date: Wed, 01 Jun 2022 02:44:07 GMT
< Etag: "a-5e044fad1d00e"
< Last-Modified: Tue, 31 May 2022 01:49:14 GMT
< Server: Apache/2.4.37 (centos)
<
Server-VM
* Connection #0 to host testfw.hiyamanet2.com left intact
以下はバックエンドのパケットキャプチャとなりますが、"X-Forwarded-For: 10.10.2.4:59330,10.10.3.4:1026" となっており、クライアントの IP アドレス 10.10.2.4 が含まれていることを確認できます。
10.10.1.7.38222 > 10.10.2.4.80: Flags [P.], cksum 0x0a85 (correct), seq 1:358, ack 1, win 63, options [nop,nop,TS val 1926600497 ecr 904018018], length 357: HTTP, length: 357
GET / HTTP/1.1
Connection: keep-alive
X-AppGW-Trace-Id: f32a898954de466f34596e2ec214e995
Host: testfw.hiyamanet2.com
X-ORIGINAL-HOST: testfw.hiyamanet2.com
User-Agent: curl/7.61.1
Accept: */*
X-Forwarded-For: 10.10.2.4:59330,10.10.3.4:1026
Accept-Encoding: gzip
X-FORWARDED-PROTO: http
X-FORWARDED-PORT: 80
X-Original-URL: /
RemoteAddr: 1755
02:44:07.173599 IP (tos 0x0, ttl 64, id 55011, offset 0, flags [DF], proto TCP (6), length 52)
10.10.2.4.80 > 10.10.1.7.38222: Flags [.], cksum 0x1745 (incorrect -> 0x591e), seq 1, ack 358, win 235, options [nop,nop,TS val 904018019 ecr 1926600497], length 0
02:44:07.173929 IP (tos 0x0, ttl 64, id 55012, offset 0, flags [DF], proto TCP (6), length 359)
10.10.2.4.80 > 10.10.1.7.38222: Flags [P.], cksum 0x1878 (incorrect -> 0xcb0a), seq 1:308, ack 358, win 235, options [nop,nop,TS val 904018019 ecr 1926600497], length 307: HTTP, length: 307
HTTP/1.1 200 OK
Date: Wed, 01 Jun 2022 02:44:07 GMT
Server: Apache/2.4.37 (centos)
Last-Modified: Tue, 31 May 2022 01:49:14 GMT
ETag: "a-5e044fad1d00e"
Accept-Ranges: bytes
Content-Length: 10
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive
Content-Type: text/html; charset=UTF-8
Server-VM
以下の様に https://FQDN で curl コマンドでアクセスします。
$ curl -v https://testfw.hiyamanet2.com
* Rebuilt URL to: https://testfw.hiyamanet2.com/
* Trying 10.10.2.6...
* TCP_NODELAY set
* Connected to testfw.hiyamanet2.com (10.10.2.6) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
* CAfile: /etc/pki/tls/certs/ca-bundle.crt
CApath: none
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / ECDHE-RSA-AES256-GCM-SHA384
* ALPN, server did not agree to a protocol
* Server certificate:
* subject: CN=Azure Firewall Manager CA; CN=testfw.hiyamanet2.com
* start date: May 31 04:12:16 2022 GMT
* expire date: Jun 1 04:12:16 2023 GMT
* subjectAltName: host "testfw.hiyamanet2.com" matched cert's "testfw.hiyamanet2.com"
* issuer: CN=Azure Firewall Manager CA
* SSL certificate verify ok.
> GET / HTTP/1.1
> Host: testfw.hiyamanet2.com
> User-Agent: curl/7.61.1
> Accept: */*
>
< HTTP/1.1 200 OK
< accept-ranges: bytes
< content-length: 10
< content-type: text/html; charset=UTF-8
< date: Wed, 01 Jun 2022 04:13:07 GMT
< etag: "a-5e044fad1d00e"
< last-modified: Tue, 31 May 2022 01:49:14 GMT
< server: Apache/2.4.37 (centos)
<
Server-VM
* Connection #0 to host testfw.hiyamanet2.com left intact
以下はバックエンドのパケットキャプチャとなりますが、"X-Forwarded-For: 10.10.2.4:36974, 10.10.3.4:1025" となっており、クライアントの IP アドレス 10.10.2.4 が含まれていることを確認できます。
10.10.1.4.46982 > 10.10.2.4.80: Flags [P.], cksum 0x3d22 (correct), seq 1:343, ack 1, win 63, options [nop,nop,TS val 3516704903 ecr 4243681630], length 342: HTTP, length: 342
GET / HTTP/1.1
X-FORWARDED-PROTO: https
X-FORWARDED-PORT: 443
X-Forwarded-For: 10.10.2.4:36974, 10.10.3.4:1025
X-Original-URL: /
Connection: keep-alive
X-AppGW-Trace-Id: 4049e634739b96ac5527dc8edb58dfdd
Host: testfw.hiyamanet2.com
X-ORIGINAL-HOST: testfw.hiyamanet2.com
User-Agent: curl/7.61.1
Accept: */*
Accept-Encoding: gzip
04:13:07.780232 IP (tos 0x0, ttl 64, id 60681, offset 0, flags [DF], proto TCP (6), length 52)
10.10.2.4.80 > 10.10.1.4.46982: Flags [.], cksum 0x1742 (incorrect -> 0xa1f9), seq 1, ack 343, win 235, options [nop,nop,TS val 4243681631 ecr 3516704903], length 0
04:13:07.780561 IP (tos 0x0, ttl 64, id 60682, offset 0, flags [DF], proto TCP (6), length 359)
10.10.2.4.80 > 10.10.1.4.46982: Flags [P.], cksum 0x1875 (incorrect -> 0x14e7), seq 1:308, ack 343, win 235, options [nop,nop,TS val 4243681631 ecr 3516704903], length 307: HTTP, length: 307
HTTP/1.1 200 OK
Date: Wed, 01 Jun 2022 04:13:07 GMT
Server: Apache/2.4.37 (centos)
Last-Modified: Tue, 31 May 2022 01:49:14 GMT
ETag: "a-5e044fad1d00e"
Accept-Ranges: bytes
Content-Length: 10
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive
Content-Type: text/html; charset=UTF-8
Server-VM