😎

Application Gateway で Private Link を使った場合の送信元 IP アドレスを確認する

2023/05/12に公開

TL;DR

2023/5/12 時点でまだプレビューの Application Gateway + Private Link ですが、実際のシナリオでありそうな構成でどのように送信元 IP アドレスが確認できるか試してみました。

Application Gateway with Private Link ってなに?どういうメリットがあるの?どうやって構成するの?という内容はこちらの記事が参考になります。

https://qiita.com/Isato-Hiyama/items/b83812ec6389d6f2647c

構成のパターン

今回は以下のようなパターンを試します。

  • パターン1: Application Gateway + Private Link
  • パターン2: Application Gateway + Private Link + フロントに Azure Firewall を配置

また、以下を前提とします。

  • Web サーバーは、8080 番ポートで nginx が起動している
  • Application Gateway は WAF を有効にしている

このパターンはシンプルにクライアント VM から Private Endpoint を通して Application Gateway にアクセスします。
このパターンにおいては、Application Gateway にロギングされる送信元 IP アドレスは、クライアント VM のプライベート IP アドレスになります。
バックエンドの Web サーバーでは、Application Gateway のプライベート IP アドレスが送信元 IP アドレスになるため、X-Forwarded-For ヘッダーを見る必要があります。

クライアントからの curl

$ curl "http://10.254.2.4:8080/?a=%3Cscript%3Ealert(%22Hello%22);%3C/script%3E&d=$(date '+%Y%m%d%H%M%S')"
<h1>vmappgw0</h1>

Application Gateway のログ

WAF を有効にしているので 2つのログが記録されます。

こちらはアクセスログです。

Private Link Service の IP アドレスになるかと思いましたが、clientIP_s はクライアント VM のプライベート IP アドレスになっています。
originalHost_s は、Application Gateway 宛の Private Endpoint のプライベート IP アドレスです。

続いて WAF のログです。

clientIp_s は、Application Gateway のプライベート リンクサービスのプライベート IP アドレスです。
details_data_shostname_s には、Private Endpoint の IP アドレス(=アクセス時のURL)が入っています。

WAF のログでは、送信元がプライベート リンクサービスになっているため、アクセスログと紐づけるには transactionId_g でマッピングする必要があります。

Web サーバーの tcpdump

Web サーバー側を見ると、送信元は Application Gateway のサブネット(=Application Gateway のホスト)になっています。
HTTP のヘッダーをみると、X-Forwarded-For ヘッダーがついており、10.254.0.4(クライアント VM) が含まれています。

10:01:07.395032 IP 172.16.4.5.48890 > 172.16.0.4.80: Flags [P.], seq 1:419, ack 1, win 63, options [nop,nop,TS val 523667012 ecr 625700619], length 418: HTTP: GET /?a=%3Cscript%3Ealert(%22Hello%22);%3C/scrip
t%3E&d=20230512100107 HTTP/1.1
0x0000:  4500 01d6 45ef 4000 4006 9709 ac10 0405  E...E.@.@.......
0x0010:  ac10 0004 befa 0050 d136 91ef 9b05 009d  .......P.6......
0x0020:  8018 003f d2e5 0000 0101 080a 1f36 8644  ...?.........6.D
0x0030:  254b 6f0b 4745 5420 2f3f 613d 2533 4373  %Ko.GET./?a=%3Cs
0x0040:  6372 6970 7425 3345 616c 6572 7428 2532  cript%3Ealert(%2
0x0050:  3248 656c 6c6f 2532 3229 3b25 3343 2f73  2Hello%22);%3C/s
0x0060:  6372 6970 7425 3345 2664 3d32 3032 3330  cript%3E&d=20230
0x0070:  3531 3231 3030 3130 3720 4854 5450 2f31  512100107.HTTP/1
0x0080:  2e31 0d0a 582d 464f 5257 4152 4445 442d  .1..X-FORWARDED-
0x0090:  5052 4f54 4f3a 2068 7474 700d 0a58 2d46  PROTO:.http..X-F
0x00a0:  4f52 5741 5244 4544 2d50 4f52 543a 2038  ORWARDED-PORT:.8
0x00b0:  3038 300d 0a58 2d46 6f72 7761 7264 6564  080..X-Forwarded
0x00c0:  2d46 6f72 3a20 3130 2e32 3534 2e30 2e34  -For:.10.254.0.4
0x00d0:  3a36 3032 3134 0d0a 582d 4f72 6967 696e  :60214..X-Origin
0x00e0:  616c 2d55 524c 3a20 2f3f 613d 2533 4373  al-URL:./?a=%3Cs
0x00f0:  6372 6970 7425 3345 616c 6572 7428 2532  cript%3Ealert(%2
0x0100:  3248 656c 6c6f 2532 3229 3b25 3343 2f73  2Hello%22);%3C/s
0x0110:  6372 6970 7425 3345 2664 3d32 3032 3330  cript%3E&d=20230
0x0120:  3531 3231 3030 3130 370d 0a43 6f6e 6e65  512100107..Conne
0x0130:  6374 696f 6e3a 206b 6565 702d 616c 6976  ction:.keep-aliv
0x0140:  650d 0a58 2d41 7070 4757 2d54 7261 6365  e..X-AppGW-Trace
0x0150:  2d49 643a 2063 3765 3639 3338 6538 6233  -Id:.c7e6938e8b3
0x0160:  3864 6264 6230 3466 3233 6661 6435 6364  8dbdb04f23fad5cd
0x0170:  3430 3530 660d 0a48 6f73 743a 2031 3732  4050f..Host:.172
0x0180:  2e31 362e 302e 343a 3830 0d0a 582d 4f52  .16.0.4:80..X-OR
0x0190:  4947 494e 414c 2d48 4f53 543a 2031 302e  IGINAL-HOST:.10.
0x01a0:  3235 342e 322e 343a 3830 3830 0d0a 5573  254.2.4:8080..Us
0x01b0:  6572 2d41 6765 6e74 3a20 6375 726c 2f37  er-Agent:.curl/7
0x01c0:  2e36 382e 300d 0a41 6363 6570 743a 202a  .68.0..Accept:.*
0x01d0:  2f2a 0d0a 0d0a                           /*....

このパターンでは、Application Gateway のフロント(Private Endpoint がある VNet)に Azure Firewall を配置してみます。
Azure Firewall のアプリケーションルールで処理することで、X-Forwarded-For ヘッダーが付きます。
ただし、送信元が Azure Firewall になるため、Application Gateway のログではクライアントの送信元 IP アドレスは確認できません。

クライアントからの curl

$ curl 'http://10.254.2.4:8080/?a=%3Cscript%3Ealert(%22Hello%22);%3C/script%3E'
<h1>vmappgw0</h1>

Azure Firewall のログ

SourceIP はもちろん、クライアント VM のプライベート IP アドレス(10.254.0.4)です。
Target(10.254.2.4)は、Application Gateway 宛の Private Endpoint のプライベート IP アドレスです。

Application Gateway のログ

まずはアクセスログを見てみましょう。

clientIP_s は Azure Firewall のプライベート IP アドレス、つまり、送信元 IP アドレスは、Azure Firewall になります。
originalHost_s には、Application Gateway の Private Endpoint の IP アドレスが入ってきます。

次に、WAF のログを見てみましょう。

パターン 1 と同じですね。

Web サーバーの tcpdump

送信元はこちらのパターンも Application Gateway になっています。
HTTP のヘッダーをみると、X-Forwarded-For ヘッダーがついており、10.254.0.4(クライアント VM)10.254.1.5(Azure Firewall)が含まれています。

08:12:01.257664 IP 172.16.4.5.35576 > 172.16.0.4.80: Flags [P.], seq 1:419, ack 1, win 63, options [nop,nop,TS val 517120875 ecr 619154482], length 418: HTTP: GET /?a=%3Cscript%3Ealert(%22Hello%22);%3C/scrip
t%3E HTTP/1.1
0x0000:  4500 01d6 270f 4000 4006 b5e9 ac10 0405  E...'.@.@.......
0x0010:  ac10 0004 8af8 0050 26cc a440 451c 2ef1  .......P&..@E...
0x0020:  8018 003f 206b 0000 0101 080a 1ed2 a36b  ...?.k.........k
0x0030:  24e7 8c32 4745 5420 2f3f 613d 2533 4373  $..2GET./?a=%3Cs
0x0040:  6372 6970 7425 3345 616c 6572 7428 2532  cript%3Ealert(%2
0x0050:  3248 656c 6c6f 2532 3229 3b25 3343 2f73  2Hello%22);%3C/s
0x0060:  6372 6970 7425 3345 2048 5454 502f 312e  cript%3E.HTTP/1.
0x0070:  310d 0a58 2d46 4f52 5741 5244 4544 2d50  1..X-FORWARDED-P
0x0080:  524f 544f 3a20 6874 7470 0d0a 582d 464f  ROTO:.http..X-FO
0x0090:  5257 4152 4445 442d 504f 5254 3a20 3830  RWARDED-PORT:.80
0x00a0:  3830 0d0a 582d 466f 7277 6172 6465 642d  80..X-Forwarded-
0x00b0:  466f 723a 2031 302e 3235 342e 302e 342c  For:.10.254.0.4,
0x00c0:  3130 2e32 3534 2e31 2e35 3a35 3231 3630  10.254.1.5:52160
0x00d0:  0d0a 582d 4f72 6967 696e 616c 2d55 524c  ..X-Original-URL
0x00e0:  3a20 2f3f 613d 2533 4373 6372 6970 7425  :./?a=%3Cscript%
0x00f0:  3345 616c 6572 7428 2532 3248 656c 6c6f  3Ealert(%22Hello
0x0100:  2532 3229 3b25 3343 2f73 6372 6970 7425  %22);%3C/script%
0x0110:  3345 0d0a 436f 6e6e 6563 7469 6f6e 3a20  3E..Connection:.
0x0120:  6b65 6570 2d61 6c69 7665 0d0a 582d 4170  keep-alive..X-Ap
0x0130:  7047 572d 5472 6163 652d 4964 3a20 6236  pGW-Trace-Id:.b6
0x0140:  6435 3136 3031 3539 6561 6664 3535 6461  d5160159eafd55da
0x0150:  6634 6130 6562 6466 6666 3037 3331 0d0a  f4a0ebdfff0731..
0x0160:  486f 7374 3a20 3137 322e 3136 2e30 2e34  Host:.172.16.0.4
0x0170:  3a38 300d 0a58 2d4f 5249 4749 4e41 4c2d  :80..X-ORIGINAL-
0x0180:  484f 5354 3a20 3130 2e32 3534 2e32 2e34  HOST:.10.254.2.4
0x0190:  3a38 3038 300d 0a55 7365 722d 4167 656e  :8080..User-Agen
0x01a0:  743a 2063 7572 6c2f 372e 3638 2e30 0d0a  t:.curl/7.68.0..
0x01b0:  4163 6365 7074 3a20 2a2f 2a0d 0a41 6363  Accept:.*/*..Acc
0x01c0:  6570 742d 456e 636f 6469 6e67 3a20 677a  ept-Encoding:.gz
0x01d0:  6970 0d0a 0d0a                           ip....

発展: Application Gateway のログでクライアントの送信元 IP アドレスを識別するには

前述したように、パターン 2 の構成では、Application Gateway で確認できる送信元 IP アドレスは Azure Firewall になり、クライアント VM の IP アドレスはわかりません。
これがどういう時に困るかというと、WAF のログでアクセス元のクライアントが Azure Firewall によって丸められてしまうため、クライアントが識別できないということです。

そこで、Application Gateway の書き換えルールを使って、X-Forwarded-For を URL のクエリ文字列に含めてログに出力してみます。

以下のように書き換えルールを書いてみます。
URL クエリ文字列の値 には、{var_query_string}&c={http_req_X-Forwarded-For_1} を指定します。

アクセスログ:

WAF ログ:

サーバーの tcpdump:

02:17:56.791484 IP 172.16.4.5.54314 > 172.16.0.4.80: Flags [P.], seq 1:465, ack 1, win 63, options [nop,nop,TS val 841476403 ecr 4281729927], length 464: HTTP: GET /?a=%3Cscript%3Ealert(%22Hello%22);%3C/script%3E&d=20230516021756&c=10.254.0.4 HTTP/1.1
0x0000:  4500 0204 01ef 4000 4006 dadb ac10 0405  E.....@.@.......
0x0010:  ac10 0004 d42a 0050 a234 8dcb e7c0 c36b  .....*.P.4.....k
0x0020:  8018 003f 1c40 0000 0101 080a 3227 e933  ...?.@......2'.3
0x0030:  ff36 0387 4745 5420 2f3f 613d 2533 4373  .6..GET./?a=%3Cs
0x0040:  6372 6970 7425 3345 616c 6572 7428 2532  cript%3Ealert(%2
0x0050:  3248 656c 6c6f 2532 3229 3b25 3343 2f73  2Hello%22);%3C/s
0x0060:  6372 6970 7425 3345 2664 3d32 3032 3330  cript%3E&d=20230
0x0070:  3531 3630 3231 3735 3626 633d 3130 2e32  516021756&c=10.2
0x0080:  3534 2e30 2e34 2048 5454 502f 312e 310d  54.0.4.HTTP/1.1.
0x0090:  0a43 6f6e 6e65 6374 696f 6e3a 206b 6565  .Connection:.kee
0x00a0:  702d 616c 6976 650d 0a58 2d41 7070 4757  p-alive..X-AppGW
0x00b0:  2d54 7261 6365 2d49 643a 2064 6639 3166  -Trace-Id:.df91f
0x00c0:  3431 6231 6466 6236 3030 3565 6466 3134  41b1dfb6005edf14
0x00d0:  6232 6561 6431 3238 6135 320d 0a48 6f73  b2ead128a52..Hos
0x00e0:  743a 2031 3732 2e31 362e 302e 343a 3830  t:.172.16.0.4:80
0x00f0:  0d0a 582d 4f52 4947 494e 414c 2d48 4f53  ..X-ORIGINAL-HOS
0x0100:  543a 2031 302e 3235 342e 322e 343a 3830  T:.10.254.2.4:80
0x0110:  3830 0d0a 5573 6572 2d41 6765 6e74 3a20  80..User-Agent:.
0x0120:  6375 726c 2f37 2e36 382e 300d 0a41 6363  curl/7.68.0..Acc
0x0130:  6570 743a 202a 2f2a 0d0a 582d 466f 7277  ept:.*/*..X-Forw
0x0140:  6172 6465 642d 466f 723a 2031 302e 3235  arded-For:.10.25
0x0150:  342e 302e 342c 3137 322e 3136 2e31 2e34  4.0.4,172.16.1.4
0x0160:  3a31 3032 350d 0a41 6363 6570 742d 456e  :1025..Accept-En
0x0170:  636f 6469 6e67 3a20 677a 6970 0d0a 582d  coding:.gzip..X-
0x0180:  464f 5257 4152 4445 442d 5052 4f54 4f3a  FORWARDED-PROTO:
0x0190:  2068 7474 700d 0a58 2d46 4f52 5741 5244  .http..X-FORWARD
0x01a0:  4544 2d50 4f52 543a 2038 3038 300d 0a58  ED-PORT:.8080..X
0x01b0:  2d4f 7269 6769 6e61 6c2d 5552 4c3a 202f  -Original-URL:./
0x01c0:  3f61 3d25 3343 7363 7269 7074 2533 4561  ?a=%3Cscript%3Ea
0x01d0:  6c65 7274 2825 3232 4865 6c6c 6f25 3232  lert(%22Hello%22
0x01e0:  293b 2533 432f 7363 7269 7074 2533 4526  );%3C/script%3E&
0x01f0:  643d 3230 3233 3035 3136 3032 3137 3536  d=20230516021756
0x0200:  0d0a 0d0a                             

クエリ文字列、Application Gateway のログに、クライアントの送信元 IP アドレスが含まれました。

Terraform

今回の環境を展開するための Terraform はこちらです。

https://github.com/tsubasaxZZZ/terraform-azure/tree/main/appgw-with-privateendpoint

肝は Application Gateway の Private Link を構成する部分と、Private Endpoint を作る部分でしょうか。

こちらは Private Link を構成する部分です。

https://github.com/tsubasaxZZZ/terraform-azure/blob/519b568fc7eae11b78dd8f83eb31642e43ac49d4/appgw-with-privateendpoint/backend_appgw.tf#L196-L205

以下のようにフロントエンド IP 構成に対して Private Link の構成を指定するようなイメージです。

https://github.com/tsubasaxZZZ/terraform-azure/blob/519b568fc7eae11b78dd8f83eb31642e43ac49d4/appgw-with-privateendpoint/backend_appgw.tf#L109-L115

Private Endpoint は以下のように設定します。ドキュメントにも書いてあるのですが、リソース ID で指定します。
リソース ID として Application Gateway、サブリソースとしてフロントエンド IP 構成を指定します。

https://github.com/tsubasaxZZZ/terraform-azure/blob/519b568fc7eae11b78dd8f83eb31642e43ac49d4/appgw-with-privateendpoint/frontend.tf#L166C1-L178

別のテナント内から プライベート エンドポイント をプロビジョニングする場合は、Azure Application Gateway リソース ID とフロントエンド IP 構成の 名前 をターゲット サブリソースとして使用する必要があります。たとえば、Application Gateway に関連付けられているプライベート IP があり、プライベート IP のポータルのフロントエンド IP 構成に一覧表示されている [名前] が PrivateFrontendIp の場合、ターゲットサブリソースの値は PrivateFrontendIp になります。

Microsoft (有志)

Discussion