Azure VM の SNAT の話

Azure VM は NAT Gateway のようなリソースがなくても勝手に SNAT して Internet 接続ができるようになっています。
ただ、勝手にいい感じにやってくれる親切設計な反面、仕組みを理解せずに使ってる人が多すぎるので、整理しておこうかなと。

SNAT に使用される Public IP

一応ドキュメントに記載はあるんですが、わかりづらいのでシンプルにまとめると以下のような感じになります。

  • VM に Public IP がある場合: VM (NIC) に紐づいた Public IP で SNAT
  • VM に Public IP がなく、外部 LB に紐づいている場合: LB の Public IP で SNAT
  • VM に Public IP も外部 LB にも紐づいていない場合: 当該リージョンの任意の Public IP で SNAT

したがって、送信元 IP を固定したい (対向側の Firewall 等で IP 指定で許可設定をしたい) 場合には、前者 2 点のいずれかになります。

SNAT Port の割り当て

VM が Public IP を持たないシナリオの場合、個々の VM に割り当てられる SNAT Port の数に注意が必要です。

  • VM に Public IP がある場合: 64k Port
  • VM に Public IP がなく、外部 LB に紐づいている場合: 最大で 1024 Port
  • VM にも外部 LB にも紐づいていない場合: 1024 Port

1 つの Public IP では TCP / UDP それぞれ 64k Port しか使えませんので、外部 LB の Public IP を使用する (複数の VM で Public IP を共有する) 場合 は、1 台あたりに割り当てられる Port が 1024 までに限られる点に気を付けましょう。(なお、LB 配下の VM が 1 台だけであっても、1024 Port しか使えません)
なお、LB のバックエンド プールが 50 台を超える場合には、割り当て Port がさらに少なくなります。(参考)

SNAT Port を使い果たした場合

LB の SNAT Port が使い果たした場合、当然ですが SNAT できないため Public IP 宛の通信ができなくなります。
Azure VM から Public IP あてに多数のセッションを張る場合や、Docker などのコンテナを利用するような場合は、1024 Port を早々に食いつぶす可能性もあるので、きちんと見積もりや設計を行いましょう。(Azure 基盤側としては、VM 上で何台コンテナが起動しているかは把握できないので、同一 VM 上で稼働するすべてのコンテナで 1024 Port を共有することになります。)

ちなみに、負荷テストや再現試験などで SNAT Port を意図的に枯渇させたい場合、Linux で hping3 を使うのがお手軽です。
例えば、以下のような形で実行することで、SYN パケットを100 マイクロ秒ごとに任意の Public IP (xx.xx.xx.xx) の 65000 番の Port に対して、ひたすら投げつけることができます。(一歩間違うと SYN Flood 攻撃になるので、やり方と通信先は慎重に検討しましょう。)

apt install hping3 -y
hping3 -S -i u100 -p 65000 <xx.xx.xx.xx>

外部 LB 配下の VM (すなわち 1024 Port しか使えない環境) で上記を実行すると、1 秒ほどで大量の応答が返ったのち、突然出力が止まるかと思います。Ctrl + C で hping3 を終了すると、以下のように 1024 packets までは応答が得られたものの、あとはパケロスしていたことが確認できます。

len=46 ip=xx.xx.xx.xx ttl=57 DF id=0 sport=65000 flags=SA seq=1011 win=29200 rtt=30.4 ms
len=44 ip=xx.xx.xx.xx ttl=64 DF id=0 sport=65000 flags=SA seq=1016 win=29200 rtt=29.8 ms
len=44 ip=xx.xx.xx.xx ttl=64 DF id=0 sport=65000 flags=SA seq=1007 win=29200 rtt=31.0 ms
len=44 ip=xx.xx.xx.xx ttl=64 DF id=0 sport=65000 flags=SA seq=1013 win=29200 rtt=30.3 ms
len=44 ip=xx.xx.xx.xx ttl=64 DF id=0 sport=65000 flags=SA seq=1012 win=29200 rtt=30.6 ms
len=44 ip=xx.xx.xx.xx ttl=64 DF id=0 sport=65000 flags=SA seq=1006 win=29200 rtt=31.4 ms
len=44 ip=xx.xx.xx.xx ttl=64 DF id=0 sport=65000 flags=SA seq=1009 win=29200 rtt=31.1 ms
len=44 ip=xx.xx.xx.xx ttl=64 DF id=0 sport=65000 flags=SA seq=1008 win=29200 rtt=31.2 ms
len=44 ip=xx.xx.xx.xx ttl=64 DF id=0 sport=65000 flags=SA seq=1014 win=29200 rtt=30.4 ms
len=44 ip=xx.xx.xx.xx ttl=64 DF id=0 sport=65000 flags=SA seq=1021 win=29200 rtt=29.5 ms
len=44 ip=xx.xx.xx.xx ttl=64 DF id=0 sport=65000 flags=SA seq=1019 win=29200 rtt=29.9 ms
len=44 ip=xx.xx.xx.xx ttl=64 DF id=0 sport=65000 flags=SA seq=1022 win=29200 rtt=29.5 ms
len=44 ip=xx.xx.xx.xx ttl=64 DF id=0 sport=65000 flags=SA seq=1018 win=29200 rtt=30.1 ms
len=44 ip=xx.xx.xx.xx ttl=64 DF id=0 sport=65000 flags=SA seq=1017 win=29200 rtt=30.2 ms
len=44 ip=xx.xx.xx.xx ttl=64 DF id=0 sport=65000 flags=SA seq=1020 win=29200 rtt=29.9 ms
len=44 ip=xx.xx.xx.xx ttl=64 DF id=0 sport=65000 flags=SA seq=1023 win=29200 rtt=29.4 ms
^C
--- xx.xx.xx.xx hping statistic ---
21434 packets transmitted, 1024 packets received, 96% packet loss
round-trip min/avg/max = 3.5/9.2/32.9 ms

なお、SNAT で使用した Port はセッションが閉じられたり (FIN/ACK)、強制的に切断されたり (RST)、アイドル タイムアウトに達した場合 (既定で 4 分) に開放されます。

また、外部 LB が Standard SKU の場合に限り、使用済みの SNAT Port をメトリックの機能で確認することが可能です。
先の例のように Ctrl + C で止めずに、長時間 1024 Port を消費させたままの状態にすると、以下のような感じになります。

Basic SKU の外部 LB を使っている場合は、上記のようなメトリックは利用できませんが、たしか診断ログの機能でアラート イベント ログに “Ports exhausted” と記録されたかと思います。 (参考)

SNAT Port の割り当てを増やしたい場合

VM (NIC の ipconfig) あたり 1024 Port では足りないという場合、対処策は以下のいずれかになります。

  • VM に Public IP を直接紐づける
  • Standard SKU の外部 LB で allocatedOutboundPorts を設定して、SNAT Port の割り当てを変更する

後者の allocatedOutboundPorts は Basic SKU では使えず、Standard SKU でのみ提供される機能ですが、1 VM あたりに割り当てる SNAT Port を 10000 Port などと明示的に設定することが可能です。(もちろん、1 台当たりの Port × LB 配下の VM 台数が 64k に収まるように設計する必要があります。)

現状 Portal からは設定ができないため、Azure PowerShell や CLI、REST、JSON テンプレートなどで設定する必要があります。(参考)

# 既存の LB の設定を取得
$LB = Get-AzLoadBalancer -ResourceGroupName SNAT-Test -Name SNAT-Test-StandardLB

# OutboundRule で AllocatedOutboundPort 等のパラメーターを設定
Add-AzLoadBalancerOutboundRuleConfig -LoadBalancer $LB -Name OutboundRule -AllocatedOutboundPort 10000 -Protocol All -EnableTcpReset -IdleTimeoutInMinutes 4 -FrontendIpConfiguration $LB.FrontendIpConfigurations -BackendAddressPool $LB.BackendAddressPools[0]

# LoadBalancingRule による SNAT を無効化 (これを忘れるとエラーになるので注意)
$LB.LoadBalancingRules[0].DisableOutboundSNAT = $true

# 設定を保存
Set-AzLoadBalancer -LoadBalancer $LB

以上、Azure での SNAT でハマりやすいポイントと対処策についてでした。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください