VirtualBox の NAT とブリッジ:VM の VPN はどこで終わるか
NAT はゲストをホストの背後に隠すので、ホスト側のトンネルがゲストを覆う。ブリッジはゲストに独自のアドレスを与えるので、トンネルは VM の内側に置く必要がある。
仮想マシンが VPN クライアントを動かすたびに、二つの異なる問いが同時に生じるが、答えはそれぞれ違う。一つ目はゲストの内側のトンネルがホストを守るかどうか、二つ目はホスト側のトンネルがゲストを覆うかどうかである。どちらもアダプタの方式が決めるもので、VPN ソフトウェアそのものより影響が大きい。NAT アダプタはホストを可視の端点にし、ブリッジアダプタはゲストを独自のアドレスで網に置く。
暗号化セッションを実際に終わらせているのは誰か
暗号化セッションはそれを終わらせる機械に属し、クライアントが VM の内側で動くときはそれがゲストである。そのトンネルを通るのはゲストの仮想アダプタに入る通信だけであり、ハイパーバイザが自ら他の機械をゲスト経由で通すことはない。だからゲストがトンネルを保ったまま、ホストは平文の網を閲覧できる。逆は対称ではなく、多くの解説が取り違えるのはこの非対称性の部分である。
外の網がどのアドレスを見るかは、アダプタの方式が答える問いである。それが決まれば、被覆は方針の問題ではなくなり、機械的な問題になる。以下のすべてはこの二つの事実から出てくる。
NAT はゲストをホストに見えるまで書き換える
NAT では、ハイパーバイザがゲストのパケットを最初のスイッチに届く前にホストのアドレスへ書き換え、その通信はホスト自身の網スタックに渡されてホストの経路表に従う。ホスト側のトンネルが持つ既定経路もその一部である。したがって NAT のゲストは設定なしでホスト側のトンネルに覆われ、これはゲストからホストという向きが示すものの逆である。
# on the host: adapter mode and NAT DNS behaviour
VBoxManage showvminfo "lab-vm" | grep -i -E "nic|nat"
VBoxManage modifyvm "lab-vm" --natdnshostresolver1 on
自前のクライアントを動かすゲストはそのトンネルをゲストの内側で終わらせ、暗号化された通信は同じ書き換えを通って外へ出るので、ホストのアプリは自前の経路と自前の出口アドレスを保つ。VirtualBox はこの方式で名前解決も担い、--natdnshostresolver1 が有効でない限りゲストの問い合わせを自前の機構で中継する。有効にするとゲストの検索はホストの解決器に渡り、ホスト自身の見え方が正しいときは解決が直るが、同時にトンネルの内側だけに存在する解決器をそのまま渡すことにもなる。設定を変える前に VM の電源を切る必要があり、その設定はアダプタに属し、ゲストのソフトウェアのものではない。
ブリッジはゲストに独自の貸与と露出を与える
ブリッジはゲストのフレームを物理セグメントへ流すので、DHCP が貸与を与え、網はゲストを別のホストとして扱う。ブリッジしたゲストはそれぞれ独自の既定経路を持つ独立したトンネルを保て、ホストの表は変わらない。代価は露出であり、ゲストはブロードキャストに応答し、近隣表に現れ、スイッチのポート保護や無線クライアントの分離の対象になる。ホスト側のトンネルもこの構成を覆わないのは、ゲストのフレームがホストのスタックを通らないからである。
NAT の場合とのこの違いは、含意として残さずにはっきり書く価値がある。ブリッジしたゲストは、通信をトンネルに通す必要があるたびに自前のクライアントと自前の資格情報を要する。ゲストをローカル網から見えなくする必要がある場所では、NAT か、経路を制御した内部専用網のほうが設計として優れている。
ゲストがホストのゲートウェイになれない理由
ゲストのトンネルをホストに貸す仕組みは、二大ハイパーバイザのどちらにも対応する切り替えがない。ホストにはゲストのトンネルへの経路も、そこを指す変換規則もないので、ゲストとホストの通信は別の持ち主を持つ別の流れのままである。この向きはアダプタの方式に関係なく成立しない。
実用的な代替は転送と代理である。SSH のローカル転送を使えば、ゲストが流れをトンネルへ運ぶあいだホストは自分のループバックに接続でき、二つ目のクライアントを入れずに TCP のサービスを覆える。ホスト自身の IP 通信に代わりにゲストのトンネルを使わせるには ゲストの内側に経路器を置き、さらにホスト側に明示的な経路と変換が必要になる。これは小さな網の設計であり、チェック箱ではない。ゲストがクライアントを動かすためだけに存在するなら、戻りの経路の計画を立てたうえで、クライアントをホストに置き、余分な中継を削ればよい。
ssh -N -f -L 8080:internal-host.example:80 user@lab-vm
解決器と経路は境界を越えない
DNS はその選択肢を受け取ったアダプタに従うので、ゲストがホストのトンネル解決器を受け継ぐことはなく、ホストがゲストへ押し込まれたサーバを知ることもない。押し込まれた dhcp-option DNS の値はそれを受け入れたインタフェースに効くので、クライアントを持たないブリッジしたゲストは内部の名前を解決できない。NAT のゲストは通常、代わりにハイパーバイザの DNS 処理を使い、その違いは環境ごとに一つの命令で見える。
# on the host
VBoxManage showvminfo "lab-vm" | grep -i -E "nic|nat"
# inside a Linux guest
ip -4 route
resolvectl status
# inside a Windows guest
Get-DnsClientServerAddress
どちらかがホストに従うと決めつける前に、ゲストの内側で解決器と経路を読む。内部の名前をそこで解決する必要があるなら、トンネルの解決器をゲスト自身のアダプタに加え、そのうえでトンネルの背後にしか存在しない名前で確かめる。VM ごとにアダプタの方式、トンネルの持ち主、解決器の出どころを記録するのは、この三つの事実が後のほとんどの問いに答えるからである。他のハイパーバイザは方式の名を変えるが、原則はそのままである。