Azure Private Link の NSG, UDR

Azure Private Link の NSG, UDR は ~~現在パブリックプレビューの機能となります。~~ <font color="Red">20220816 に GA となりました。</font> NSG, UDR のそれぞれについて検証した内容、留意点を以下にまとめました。NSG については従来のもの (L4 での通信制御) と同じであるため、利用シナリオのイメージがつきやすいですが、UDR はより広いアドレス空間を適用するための機能となっており、利用用途がわかりにくいためこの内容で理解を深めていただけたら幸いです。

https://azure.microsoft.com/ja-jp/updates/public-preview-of-private-link-network-security-group-support/

https://azure.microsoft.com/ja-jp/updates/public-preview-of-private-link-udr-support/

https://docs.microsoft.com/en-us/azure/private-link/disable-private-endpoint-network-policy

## NSG の利用シナリオ、構成イメージ
- Private Endpoint と NSG (PrivateEndpointNetworkPolicies) を利用するシナリオ
    - 送信元 IP アドレス、宛先 IP アドレスを制御したい (Azure Firewall でも実現可能)　
    - 追加の費用なしで通信制御を行いたい
    - NSG フローログは試してみましたが取れなかったため、現状未対応であるようです。通信のログ出力が必須である場合、Azure Firewall をお勧めします。

- 構成イメージ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2266293/f10ae18e-30a3-a0c0-e0b4-5a55b622b226.png)

## UDR の利用シナリオ、構成イメージ
- Private Endpoint と UDR (PrivateEndpointNetworkPolicies) を利用するシナリオ
    - Private Endpoint 用の経路は /32 で広報されるため、/32 をより広い範囲で上書きたい場合
    (**UDR のルートの上限は 400 です。/32 のままだと大規模な構成の場合、すぐに上限に達してしまう可能性があります。またプライベートエンドポイントを作成するたびに UDR の設定変更をするのは手間です。**)

- 構成イメージ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2266293/8dfde51c-0f5c-1f4e-3e62-19064a5c85a7.png)

<font color="Red">Private Endpoint 用の UDR の機能 (PrivateEndpointNetworkPolicies) は PaaS からの戻りのトラフィックを上書きするための機能ではありません。</font>
例えば Azure Firewall 利用時に以下のような戻りのトラフィックを上書きする用途で UDR を使いたいと考えることはあるかもしれませんが、PrivateEndpointNetworkPolicies の UDR の機能はそのような機能ではありません。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2266293/ff448779-6db8-6662-5101-1d7ab440e847.png)


[公開ドキュメント1](https://docs.microsoft.com/ja-jp/azure/private-link/inspect-traffic-with-azure-firewall) [公開ドキュメント2](https://docs.microsoft.com/ja-jp/azure/private-link/private-endpoint-overview#limitations) にある通り、Azure Firewall (仮想アプライアンスも同様) と Private Endpoint を併用する際は Azure Firewall (仮想アプライアンス) 側に SNAT の設定が推奨されます。SNAT を利用することで Private Endpoint へアクセスする際の送信元が Azure Firewall (仮想アプライアンス) のプライベート IP アドレスとなり、戻りのトラフィックが必ず Azure Firewall を経由するような構成にすることができます。
Azure Firewall でアプリケーションルールを利用する場合、既定で SNAT されるので追加の設定は不要です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2266293/b3c31e57-75f3-0c86-5d21-98330310d88f.png)


## 前提
- VPN Gateway, VM, VNet, Storage, Private Endpoint, Azure Firewall 等の以下の図の各リソースは作成済み、設定済みでクライアントから Private Endpoint にアクセス可能となっていること

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2266293/d709b962-90ec-fe80-ef12-c39dec41e687.png)

Azure Firewall には以下のような設定でアプリケーションルールで BLOB の FQDN を許可しています。(アプリケーションルールでは暗黙的に SNAT が行われます。)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2266293/377ec27e-fb88-edc5-7037-f71ff1a0dd91.png)

## 1.PrivateEndpointNetworkPolicies の有効化

Private Endpoint が配置されているサブネットの *PrivateEndpointNetworkPolicies* を有効化することで、NSG, UDR の機能が利用できるようになります。*PrivateEndpointNetworkPolicies* はポータルからプライベートエンドポイントを作成すると既定で無効化されます。

Powershell を起動し、Azure Powershell にてプレビュー機能の登録を行います。20 分程度かかります。
```
Register-AzProviderFeature -FeatureName "AllowPrivateEndpointNSG" -ProviderNamespace "Microsoft.Network"
```

以下のコマンドで対象のリソースグループ、VNet、Private Endpoint が配置されているサブネットを指定して、*PrivateEndpointNetworkPolicies* を有効化します。
```
$SubnetName = "サブネット名"
$VnetName = "VNet 名"
$RGName = "リソースグループ名"

$virtualNetwork= Get-AzVirtualNetwork -Name $VnetName -ResourceGroupName $RGName
($virtualNetwork | Select -ExpandProperty subnets | Where-Object  {$_.Name -eq $SubnetName}).PrivateEndpointNetworkPolicies = "Enabled"  
$virtualNetwork | Set-AzVirtualNetwork
```

現在は Azure ポータルからサブネットを選択し、以下の様にネットワークポリシーが設定できるようになっています。また、有効にする機能も選ぶことができます (20230222 追記)
'Disabled' (無効) , 'NetworkSecurityGroupEnabled' (NSG のみ有効), 'RouteTableEnabled' (ルートテーブルのみ有効), 
'Enabled' (両方有効) 
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2266293/b0fb45e1-e7bc-f5a7-e444-6ac4745d9cf1.png)
https://learn.microsoft.com/en-us/azure/private-link/disable-private-endpoint-network-policy?tabs=network-policy-portal#enable-network-policy

## 2.NSG の動作確認

以下の構成で NSG にて通信制御ができるかを試します。(NSG の検証ため、あえて Azure Firewall は非経由になるように構成してます)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2266293/f10ae18e-30a3-a0c0-e0b4-5a55b622b226.png)

Private Endpoint のサブネットに適用した NSG には以下を設定した状態で、*PrivateEndpointNetworkPolicies* を有効化し、VPN 経由で BLOB にアクセスしてみます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2266293/3a59894c-62f5-6380-7371-fc25382530df.png)

Windows の WSL から curl コマンドでアクセスしてみましたが、想定通りアクセスが拒否されています。(hosts を使って名前解決してます。)
```
curl -v https://testhiyamablob.blob.core.windows.net/testblob/index.html
*   Trying 10.1.0.4:443...
* TCP_NODELAY set
* connect to 10.1.0.4 port 443 failed: Connection refused
* Failed to connect to testhiyamablob.blob.core.windows.net port 443: Connection refused
* Closing connection 0
curl: (7) Failed to connect to testhiyamablob.blob.core.windows.net port 443: Connection refused
```

P2S 用のアドレス空間からの接続のみ許可するように NSG にルールを追加します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2266293/df13662a-f8bb-ed4e-8ae8-d3668f9ee0e2.png)

Windows の WSL から curl コマンドでアクセスしてみましたが、想定通りアクセスできました。
```
curl -v https://testhiyamablob.blob.core.windows.net/testblob/index.html
*   Trying 10.1.0.4:443...
* TCP_NODELAY set
* Connected to testhiyamablob.blob.core.windows.net (10.1.0.4) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
*   CAfile: /etc/ssl/certs/ca-certificates.crt
  CApath: /etc/ssl/certs
* 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=*.blob.core.windows.net
*  start date: Feb 11 16:07:58 2022 GMT
*  expire date: Feb 11 16:07:58 2023 GMT
*  subjectAltName: host "testhiyamablob.blob.core.windows.net" matched cert's "*.blob.core.windows.net"
*  issuer: C=US; O=Microsoft Corporation; CN=Microsoft RSA TLS CA 01
*  SSL certificate verify ok.
> GET /testblob/index.html HTTP/1.1
> Host: testhiyamablob.blob.core.windows.net
> User-Agent: curl/7.68.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Content-Length: 12
< Content-Type: text/html
< Content-MD5: bLL+2vK0uXm1HPdaivMY1w==
< Last-Modified: Mon, 28 Mar 2022 06:05:41 GMT
< ETag: 0x8DA1080FD1973BB
< Server: Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0
< x-ms-request-id: 202823d7-e01e-005d-30cf-435971000000
< x-ms-version: 2009-09-19
< x-ms-lease-status: unlocked
< x-ms-blob-type: BlockBlob
< Date: Wed, 30 Mar 2022 00:48:25 GMT
<
* Connection #0 to host testhiyamablob.blob.core.windows.net left intact
﻿Test BLOB
```

同様の構成で、NSG で許可されていない他のサブネットからアクセスを試みます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2266293/50020978-d57b-76c4-1f84-3c6b61f87a12.png)

想定通りアクセスが拒否されています。
```
$ curl -v https://testhiyamablob.blob.core.windows.net/testblob/index.html
*   Trying 10.1.0.4:443...
* TCP_NODELAY set
* connect to 10.1.0.4 port 443 failed: Connection refused
* Failed to connect to testhiyamablob.blob.core.windows.net port 443: Connection refused
* Closing connection 0
curl: (7) Failed to connect to testhiyamablob.blob.core.windows.net port 443: Connection refused
```

## 3.UDR の動作確認

まず UDR を適用していないサブネットに配置されている VM (ClientSubnet) の有効なルートを確認してみます。Private Endpoint 用の NSG は割り当て解除しておきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2266293/95581c99-0422-df91-4baa-406c4b544847.png)

VM のネットワークインタフェースの画面から有効なルートをクリックします。VNet ピアリングの 10.1.0.0/16 と Private Endpoint の IP アドレス (10.1.0.4/32) が表示されていることが確認できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2266293/e35853c6-8d87-13a9-f230-33eaf200ab7c.png)

以下のようなルートテーブル (宛先：Private Endpoint 用の VNet のアドレス空間、次ホップ：Azure Firewall のプライベート IP アドレス) を ClientSubnet に関連付けます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2266293/66ac75bf-ded5-748a-d898-da0017cc1501.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2266293/f1c24ef2-b4bc-05b1-e58b-d133fcf8e19f.png)

Azure では該当するルートが複数ある場合、ロンゲストマッチ (アドレス空間がより狭い方を優先する仕組み) によりルートが決まりますが、PrivateEndpointNetworkPolicies の機能により Private Endpoint の IP アドレス (10.1.0.4/32) が無効化され、より広いアドレス空間の NextHop が Azure Firewall の IP アドレスとなっているルートがアクティブになりました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2266293/30da5351-17d1-ad56-b04e-38431bfadc48.png)

この構成で VPN、Azure Firewall を経由して BLOB にアクセスしてみます。
アクセスは成功し、Azure Firewall の診断ログにもアクセスを許可した旨のログが出力されていました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2266293/8c0593c3-9e8a-90d8-27f5-d2c5df894df7.png)

この機能がどの範囲のアドレス空間まで有効かというと Private Endpoint が配置されている VNet のアドレス空間までのようです。試しにさらに広い範囲である 10.0.0.0/8 を UDR で設定してみましたが、Private Endpoint の IP アドレス (10.1.0.4/32) はアクティブのままでした。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2266293/61522bda-9e15-243c-e057-6503d2d46d72.png)