概要
Azure Standard Load Balancer(以下 SLB)の インバウンドNATルール(DNAT) を使って、パブリックIPを持たないプライベートサブネット上のVMに対して、外部からSSH/HTTPでアクセスできる構成をラボ環境で組んだので、その構築メモになります。
外部からプライベートVMに入る用途であればAzure Bastionや踏み台VMが一般的ですが、SLBのDNAT機能でも同様にポート単位でのアクセスを実現できます。今回は構成と挙動を理解するためにこのパターンを試しました。
構成のポイントは大きく次の3つです。バックエンドVMにはパブリックIPを付与せず、LBのフロントエンドIP(パブリックIP)に対してポートを宛てて入る形にする。インバウンドはDNATでVMの内部ポートへ転送され、アウトバウンドは同じフロントエンドIPでSNATされる、というLB1本でinbound/outboundを面倒見るシンプルな構成です。
通信の流れをざっくり書くと、Client → LB(Frontend PIP) → DNAT → VM(Private IP) という経路になります。
環境
- Azure リソース:仮想ネットワーク / NSG / パブリックIP / 仮想マシン / Standard Load Balancer
- バックエンドVMのOS:Red Hat Enterprise Linux 9.4
- リージョン:Japan East
- IPアドレスは記事用のサンプル値(パブリック側:
20.10.10.10、プライベート側:10.0.1.4)
構成図は以下のとおりです。①インバウンドはLBのDNATでVMの内部ポートへ転送され、②アウトバウンドは同じLBのフロントエンドIPでSNATされます。
構築の流れ
LBから先に作るとバックエンド/フロントエンドの紐づけ先がなくて二度手間になるので、下位レイヤから順に作っていくのが楽です。今回は以下の順で構築しました。
- リソースグループ
- 仮想ネットワーク(VNet・サブネット)
- ネットワークセキュリティグループ(NSG)
- パブリックIP
- 仮想マシン
- Load Balancer(フロントエンドIP構成・バックエンドプール・DNATルール・アウトバウンドルール)
- 動作確認
各リソースの設定値は以下のとおりです。
1. リソースグループ
全リソースを束ねるリソースグループを最初に作成しておきます。
| 項目 | 設定値 |
|---|---|
| 名前 | rg-lab-alb |
| リージョン | Japan East |
2. 仮想ネットワーク(VNet・サブネット)
VMを収容するVNetとサブネットを作成します。アドレス空間は 10.0.0.0/16 で取り、その中の 10.0.1.0/24 をプライベートサブネットとして切り出しました。今回はサブネット1つの最小構成ですが、必要に応じて踏み台用や管理用のサブネットを分けても構いません。
| 項目 | 設定値 |
|---|---|
| VNet名 | vnet-lab |
| アドレス空間 | 10.0.0.0/16 |
| サブネット名 | snet-private |
| サブネットアドレス範囲 | 10.0.1.0/24 |
3. ネットワークセキュリティグループ(NSG)
VMのNICに紐づけるNSGを作成し、SSH/HTTP/ICMPを許可するインバウンドルールを入れておきます。ラボなので送信元はAnyにしていますが、本来であればDNAT経由のアクセスのみを通すように送信元を絞るのが望ましいです。
| 優先度 | 名前 | ポート | プロトコル | 送信元 | 宛先 | アクション |
|---|---|---|---|---|---|---|
| 300 | allow-ssh | 22 | TCP | Any | Any | Allow |
| 310 | allow-http | 80 | TCP | Any | Any | Allow |
| 320 | allow-icmp | Any | ICMP | Any | Any | Allow |
NSG名は nsg-vm-backend-01 とし、後ほどVM作成時にNICへ紐づけます(サブネット単位での適用も可能ですが、今回は1台のVMだけなのでNIC側に付けました)。
⚠ 送信元Anyだとインターネットから22/80に直接到達できてしまうので、本番運用では送信元を
AzureLoadBalancerサービスタグや管理元IPに限定してください。
4. パブリックIP
LBのフロントエンドに割り当てるパブリックIPを作成します。Standard LB に紐づけるパブリックIPは Standard SKU でなければならない ので、ここはBasicと間違えないよう注意が必要です。割り当てはStandard SKUでは静的(Static)のみです。
| 項目 | 設定値 |
|---|---|
| 名前 | pip-lb-01 |
| SKU | Standard |
| 割り当て | 静的 |
| IPアドレス | 20.10.10.10 |
5. 仮想マシン
バックエンドのVMを作成します。プライベートサブネット(snet-private)に配置し、パブリックIPは付与しません。プライベートIPは静的にしてあります(DNAT先のIPが動くと面倒なので)。
| 項目 | 設定値 |
|---|---|
| VM名 | vm-backend-01 |
| OS | Red Hat Enterprise Linux 8.10 |
| VNet / サブネット | vnet-lab / snet-private |
| NIC名 | nic-vm-backend-01 |
| プライベートIP | 10.0.1.4(静的) |
| パブリックIP | なし |
| NSG | nsg-vm-backend-01(NICに適用) |
VM作成ウィザードからNSGを「なし」にして作り、後からNICに nsg-vm-backend-01 を関連付けると意図どおりの構成になります(ウィザードのまま進めると勝手にBasic NSGが作られることがあるため)。
6. Load Balancer
ここまで来てようやくLBを組み立てます。LB本体・フロントエンドIP構成・バックエンドプール・DNATルールの順に設定していきます。
6-1. LB本体
| 項目 | 設定値 |
|---|---|
| 名前 | lb-standard-01 |
| SKU | Standard |
| 種類 | パブリック |
| 階層 | Regional |
6-2. フロントエンドIP構成
先に作成した pip-lb-01 をフロントエンドIP構成として登録します。フロントエンドIP構成名(リソース上の名前)とパブリックIPリソース名は別物 なので、混同しないよう命名で揃えておくと運用しやすいです。
| 項目 | 設定値 |
|---|---|
| フロントエンドIP構成名 | feip-lb-01 |
| 紐づくパブリックIP | pip-lb-01(20.10.10.10) |
6-3. バックエンドプール
バックエンドプールには vm-backend-01 のNICを登録します。VM自身はパブリックIPを持たず、外部とのやり取りはすべてLB経由になります。
| 項目 | 設定値 |
|---|---|
| バックエンドプール名 | bep-vm-01 |
| 登録NIC | nic-vm-backend-01 |
| 登録VMのプライベートIP | 10.0.1.4 |
6-4. インバウンドNATルール(DNAT)
外部からのアクセスをVMに転送するDNATルールを設定します。SSHは外部の222ポートで受けてVMの22に、HTTPは外部の8080で受けてVMの80に転送する形にしました。標準ポートをそのまま晒すよりは多少安全になる、というラボ用の割り切りです。
| ルール名 | フロントエンド IP:Port | バックエンド VM:Port | プロトコル |
|---|---|---|---|
| nat-rule-ssh | 20.10.10.10:222 | 10.0.1.4:22 | TCP |
| nat-rule-http | 20.10.10.10:8080 | 10.0.1.4:80 | TCP |
DNAT(インバウンドNATルール)の場合は 正常性プローブ(Health Probe)の設定は不要 で、ルールを作るだけで動作します。負荷分散ルール(Load balancing rule)を追加するときは別途プローブが必須になるので、ここは混同しやすいポイントです。
6-5. アウトバウンドルール
ここがハマりやすいポイントなのですが、インバウンドNATルール(DNAT)だけではVMから外への通信はSNATされません。Standard LBがバックエンドVMにアウトバウンドSNATを提供するのは、次のいずれかが満たされる場合だけです。
- 負荷分散ルール(Load balancing rule)がバックエンドプールに紐づいている(既定でアウトバウンドSNATが有効)
- アウトバウンドルール(Outbound rule)が明示的に設定されている
- VMにインスタンスレベルのパブリックIPが付いている
- サブネットにNAT Gatewayが紐づいている
以前は Default outbound access という暗黙的なSNATが効いて偶然外に出られていたケースが多かったのですが、これは段階的廃止が進んでおり、新規VNetでは既定で無効化されています。これからAzure上に組む環境では、明示的なアウトバウンド経路を設定しないとVMから外へ出られない、というのが現在の前提になります。
今回はDNAT専用構成で負荷分散ルールは作っていないので、シンプルにアウトバウンドルールを1本追加してSNATを通します。
| 項目 | 設定値 |
|---|---|
| ルール名 | out-rule-01 |
| IPバージョン | IPv4 |
| プロトコル | TCP |
| フロントエンドIP構成 | feip-lb-01 |
| バックエンドプール | bep-vm-01 |
| アイドルタイムアウト | 4分(既定) |
| TCPリセット | 無効 |
| 割り当てられた送信ポート | 1024(任意の8の倍数) |
⚠ ポータルで設定する場合、「割り当てられた送信ポート」を
0のまま保存しようとするとエラーになります。「既定のポート割り当てを有効にする(推奨されません)」のチェックは外したまま、SNATポート数を手動で1以上指定してください。SNAT送信ポートはフロントエンドIPアドレス数 × 64000のプールをバックエンドVM台数で割り振る仕組みで、1IP × 1VMなら最大64000まで割り当てられます。ラボ用途なら1024もあれば十分ですが、VM台数を増やす想定があるなら最大VM台数を見越して逆算しておくと後で詰みません。
7. 動作確認
構築後、以下のコマンドで一通り確認します。
SSH接続(DNAT経由)
ssh <user>@20.10.10.10 -p 222
外部の222番に対してSSHすると、LBがDNATでVMの22番に転送してログインできます。
HTTP疎通確認(DNAT経由)
curl http://20.10.10.10:8080
8080→80のDNATが効いて、VM上のWebサーバの応答が返ってきます。
VMからの外部通信(SNAT確認)
6-5で作成したアウトバウンドルールによって、VMからインターネット側に出る通信はLBのフロントエンドIPを送信元としてSNATされます。VM内から以下を実行してフロントエンドIPが返ってくれば、想定どおりLB経由で出ています。
curl -s ifconfig.me
# → 20.10.10.10 が返ればOK
外部HTTPS疎通
curl -s https://www.google.com | head -5
なお、Standard LB のアウトバウンドルール(および暗黙的なoutbound)は ICMPに対応していない ので、外部への疎通確認に ping は使えません。curl などTCPベースのコマンドで代替する必要があります。ここはハマりやすいので注意です。
まとめ
Standard Load Balancer のDNAT機能を使うことで、踏み台VMやBastionを別途用意しなくても、プライベートサブネットのVMに対してポートベースで外部アクセスができることが確認できました。VMにパブリックIPを付与しなくて済むため、外部公開面のIP管理がLBのフロントエンドIPに集約できるのもメリットです。
構築上のポイントを振り返ると、下位リソース(VNet→NSG→PIP→VM)から積み上げていくのが手戻りなく進めやすいこと、PIPはStandard SKU必須・静的のみであること、DNATだけではSNATされないのでアウトバウンドルールを別途設定する必要があること(以前はDefault outbound accessで偶然動いていただけ)、DNATルール自体はプローブ不要で動く点、アウトバウンドがSNATで自動的にフロントエンドIP経由になる点、ICMPが通らない点あたりが今回触ってみての気付きでした。ラボ用途のごく簡単な構成ですが、踏み台パターンの検証やポートフォワーディングを使った最小構成のWeb公開などには手軽で便利だと感じます。
