Application Gateway で Private Link を使った場合の送信元 IP アドレスを確認する
TL;DR
2023/5/12 時点でまだプレビューの Application Gateway + Private Link ですが、実際のシナリオでありそうな構成でどのように送信元 IP アドレスが確認できるか試してみました。
Application Gateway with Private Link ってなに?どういうメリットがあるの?どうやって構成するの?という内容はこちらの記事が参考になります。
構成のパターン
今回は以下のようなパターンを試します。
- パターン1: Application Gateway + Private Link
- パターン2: Application Gateway + Private Link + フロントに Azure Firewall を配置
また、以下を前提とします。
- Web サーバーは、8080 番ポートで nginx が起動している
- Application Gateway は WAF を有効にしている
パターン1: Application Gateway + Private Link
このパターンはシンプルにクライアント 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_s
や hostname_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 /*....
パターン2: Application Gateway + Private Link + フロントに Azure Firewall を配置
このパターンでは、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 はこちらです。
肝は Application Gateway の Private Link を構成する部分と、Private Endpoint を作る部分でしょうか。
こちらは Private Link を構成する部分です。
以下のようにフロントエンド IP 構成に対して Private Link の構成を指定するようなイメージです。
Private Endpoint は以下のように設定します。ドキュメントにも書いてあるのですが、リソース ID で指定します。
リソース ID として Application Gateway、サブリソースとしてフロントエンド IP 構成を指定します。
別のテナント内から プライベート エンドポイント をプロビジョニングする場合は、Azure Application Gateway リソース ID とフロントエンド IP 構成の 名前 をターゲット サブリソースとして使用する必要があります。たとえば、Application Gateway に関連付けられているプライベート IP があり、プライベート IP のポータルのフロントエンド IP 構成に一覧表示されている [名前] が PrivateFrontendIp の場合、ターゲットサブリソースの値は PrivateFrontendIp になります。
Discussion