LoginSignup
5
2

Azure Application Gateway Private Link を試す

Last updated at Posted at 2022-05-31

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 を利用することはサポートされてない。
  • その他の制限事項

構成図

検証のためにあえてそれぞれの仮想ネットワークのアドレス空間を合わせました。
image.png

前提

  • 構成図内のリソースのうち、Private Link Service, Private Endpoint 以外のリソースは作成済みであること
  • Application Gateway v2、バックエンド VM を適切に構成し、http にてアクセス可能な状態となっていること

Private Link, Private Endpoint の追加 (Private IP のリスナー)

Application Gateway のリソースをクリックし、"プライベート リンク" > "追加" をクリックします。
"名前" を入力し、"プライベート リンクのサブネット"、"フロントエンド IP 構成" を選択して、"追加" をクリック後、"追加" をクリックします。
image.png

プライベート リンクの設定が完了したら、"プライベート エンドポイント接続" のタブをクリックし、"+ プライベート エンドポイント" をクリックします。
image.png

プライベート エンドポイントを作成するサブスクリプション、リソースグループ、プライベート エンドポイントの名前、リージョンを選択し、次に進みます。
image.png

対象のサブリソースが Application Gateway のプライベート IP 用の名前 (appGwPrivateFrontendIp) になっていることを確認し、次に進みます。

image.png

対象サブリソースが出てこない場合、リスナーが構成されているかや Private Link の設定が完了しているかを確認してください。リスナーが Public IP アドレスで構成されており、Private Link を Private IP に紐づけてしまったりすると以下の様に対象サブリソースが表示されません。構成を変更する際は Private Link を削除してからリスナーの構成変更をします。

image.png

プライベート エンドポイントを作成する仮想ネットワーク、サブネットを選択します。ネットワークポリシーを有効化する場合、オプションのチェックを入れます。
ネットワークポリシーの詳細は Azure Private Link の NSG, UDR を参照ください。
image.png

設定内容を確認し、作成をクリックします。
image.png

作成が完了したらプライベート エンドポイントのリソースに移動し、"DNS の構成" からプライベート エンドポイントの IP アドレスを確認します。
この IP アドレスは動的に割り当てが行われますが、リソースが削除されない限り IP アドレスが変わることはありません。公開情報でもライフサイクル全体で変わらない旨が記載されています。 プライベート エンドポイントのプロパティ
image.png

クライアントの 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" となってます。
image.png

Private Link, Private Endpoint の追加 (Public IP のリスナー)

プライベート エンドポイント, プライベート リンクの順でそれぞれの設定を削除します。
image.png

Application Gateway のリスナーに移動し、フロントエンド IP をパブリックに変更し、保存します。
image.png

再度、同じ手順でプライベート リンク、プライベート エンドポイントを作成します。
プライベート エンドポイントが作成できたら、プライベート エンドポイントのリソースに移動し、"DNS の構成" から IP アドレスを確認します。
image.png

クライアントの 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 用のリスナーかどうかを判定するためにはリスナー名から判定する必要があります。
image.png

複数のリスナー

異なるポート (TCP 8080) を利用するリスナーを追加します。
image.png

追加したリスナー用のルールも追加します。
image.png

プライベート リンク、プライベート エンドポイントの追加の設定は不要で、アクセス可能でした。
この動作からプライベート リンクはリスナーではなくフロントエンドの 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 からの通信を許可したころ問題なく通信ができるようになりましたので、必須要件のようです。

image.png

Private 構成

こちらで紹介しているプライベート構成では Private Link はサポートされてないので、EnableApplicationGatewayNetworkIsolation をプレビュー登録したままだと以下の様にエラーになります。

image.png

サブスクリプションのプレビュー登録の画面から EnableApplicationGatewayNetworkIsolation を登録解除して、Application Gateway を再作成してからお試しください。プライベート構成の場合、EnhancedNetworkControl (拡張ネットワーク制御) が有効になり、その影響でエラーとなるようです。

image.png

Azure Firewall と組み合わせて X-Forwarded-For を挿入する (20230607 以前のプレビュー時の情報)

Azure Firewall のアプリケーションルールの評価では XFF (X-Forward-For) を挿入する機能が備わっています。(HTTPS の場合、Premium SKU の TLS インスペクション必須)

以下の構成において XFF が想定通りに挿入されているかを確認してみます。あとから AzureFirewallSubnet を追加したため、サブネットの構成がおかしくなっていますが、目をつぶっていただけたらと、、
image.png

以下の作業を追加で実施しておきます。

  • 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
5
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
2