Hyper-VのWinNATを使い倒す!1つのNATで複数の仮想サブネットをインターネット接続&隔離するテクニック

Hyper-V
記事内に広告が含まれています。
スポンサーリンク
スポンサーリンク

はじめに

今回は、Hyper-V環境で使う仮想ネットワークとNAT(WinNAT)を使って複数のセグメントからホストを経由してインターネットに繋ぐと同時に各セグメントを完全に隔離しつつもインターネット接続を実現する方法についてご紹介します。
構成のポイントを説明する前に、 まず全体像を確認しておきましょう。

ポイントとしては、仮想スイッチに複数のIPアドレスを設定して、それぞれをゲートウェイとして設定することにより各セグメント間の通信を許可したり、完全に分離することがサブネットマスクとデフォルトゲートウェイの変更だけで切り替えられる構成となります。

Hyper-Vホスト側のネットワーク設定

仮想スイッチの作成

(以降、PowerShellは管理者として実行します。)
まずは、Hyper-Vで使用する仮想スイッチを作成します。
ポイントとしては、SwitchType Internal(内部スイッチ)として作成すること。
コマンド

New-VMSwitch -SwitchName "HyperV-Internal" -SwitchType Internal

実行例:

PS C:\Users\user> New-VMSwitch -SwitchName "HyperV-Internal" -SwitchType Internal

Name            SwitchType NetAdapterInterfaceDescription
----            ---------- ------------------------------
HyperV-Internal Internal

仮想スイッチが出来たら、ネットワークアダプタのifIndexを確認しておきます。

コマンド:

Get-NetAdapter

実行例:

PS C:\Users\user> Get-NetAdapter

Name                      InterfaceDescription                    ifIndex Statu
                                                                          s
----                      --------------------                    ------- -----
イーサネット              Intel(R) Ethernet Controller (3) I225-V      16 Up
vEthernet (Default Switc… Hyper-V Virtual Ethernet Adapter             45 Up
vEthernet (WSL (Hyper-V … Hyper-V Virtual Ethernet Adapter #3          56 Up
Bluetooth ネットワーク接… Bluetooth Device (Personal Area Networ…      13 Disc…
Wi-Fi                     Intel(R) Wi-Fi 6 AX201 160MHz                 3 Disc…
vEthernet (HyperV-Intern… Hyper-V Virtual Ethernet Adapter #2          22 Up

上の例では新しく作った仮想スイッチのifIndex22であることが確認出来ました。
この値や表示内容は各環境で異なるので、注意してください。

続いて、仮想スイッチのネットワークアダプタにVMで設定するデフォルトゲートウェイとなるIPアドレスを設定します。
今回は対象セグメントが2つあるので、全体を包括するIPアドレス、各セグメント個別で設定するため、3つのIPアドレスを設定。

コマンド:

# セグメント全体で使用出来るIPアドレス(サブネットマスク /16)
New-NetIPAddress -IPAddress 10.0.0.1 -PrefixLength 16 -ifIndex 22
# セグメント10.0.1.0/24で使用するIPアドレス(サブネットマスク /24)
New-NetIPAddress -IPAddress 10.0.1.1 -PrefixLength 24 -ifIndex 22
# セグメント10.0.2.0/24で使用するIPアドレス(サブネットマスク /24)
New-NetIPAddress -IPAddress 10.0.2.1 -PrefixLength 24 -ifIndex 22

実行例:同じような結果が二重に出ているように見えますが、これは正常です。(PolicyStoreが変わってますね)

PS C:\Users\user> New-NetIPAddress -IPAddress 10.0.0.1 -PrefixLength 16 -ifIndex 22

IPAddress         : 10.0.0.1
InterfaceIndex    : 22
InterfaceAlias    : vEthernet (HyperV-Internal)
AddressFamily     : IPv4
Type              : Unicast
PrefixLength      : 16
PrefixOrigin      : Manual
SuffixOrigin      : Manual
AddressState      : Tentative
ValidLifetime     : Infinite ([TimeSpan]::MaxValue)
PreferredLifetime : Infinite ([TimeSpan]::MaxValue)
SkipAsSource      : False
PolicyStore       : ActiveStore

IPAddress         : 10.0.0.1
InterfaceIndex    : 22
InterfaceAlias    : vEthernet (HyperV-Internal)
AddressFamily     : IPv4
Type              : Unicast
PrefixLength      : 16
PrefixOrigin      : Manual
SuffixOrigin      : Manual
AddressState      : Invalid
ValidLifetime     : Infinite ([TimeSpan]::MaxValue)
PreferredLifetime : Infinite ([TimeSpan]::MaxValue)
SkipAsSource      : False
PolicyStore       : PersistentStore

無事に登録出来ているか確認してみましょう。

コマンド:

Get-NetIPAddress -InterfaceIndex 22 | Format-Table IPAddress, AddressState

実行例:

PS C:\Users\user> Get-NetIPAddress -InterfaceIndex 22 | Format-Table IPAddress, AddressState

IPAddress AddressState
--------- ------------
10.0.2.1  Preferred
10.0.1.1  Preferred
10.0.0.1  Preferred

ちなみにいわゆるWindowsのネットワークアダプタのプロパティからも確認できます。
複数のIPアドレスは詳細設定ボタンで確認出来ます。

NATの作成

最後にNATを設定します。
NATはこれまで設定したIPアドレス全体を含んだ、10.0.0.0/16で設定します。

コマンド:

New-NetNat -Name WinNAT-Core -InternalIPInterfaceAddressPrefix 10.0.0.0/16

実行例:

PS C:\Users\user> New-NetNat -Name WinNAT-Core -InternalIPInterfaceAddressPrefix 10.0.0.0/16

Name                             : WinNAT-Core
ExternalIPInterfaceAddressPrefix :
InternalIPInterfaceAddressPrefix : 10.0.0.0/16
IcmpQueryTimeout                 : 30
TcpEstablishedConnectionTimeout  : 1800
TcpTransientConnectionTimeout    : 120
TcpFilteringBehavior             : AddressDependentFiltering
UdpFilteringBehavior             : AddressDependentFiltering
UdpIdleSessionTimeout            : 120
UdpInboundRefresh                : False
Store                            : Local
Active                           : True

ここで、過去にNATを作成してそのまま残っている場合はエラーとなります。
その場合は、NATを削除する必要があるので以下のコマンドで消しましょう。

NATを削除するコマンド:

Get-NetNat | Remove-NetNat

Hyper-V 仮想マシンの設定

Hyper-Vではどうなっているか確認してみましょう
仮想スイッチマネージャーを開いてみるとすでに仮想スイッチが作成されていると思います。

仮想マシンのネットワークアダプタにはこの仮想スイッチを設定してあげましょう。

動作検証

検証0:インターネット接続

まずは肝心のインターネット接続について。
セグメント間通信を許可する設定とセグメント間通信を隔離する設定の2つのパターンがありますが、それぞれ問題無くインターネットへ接続することが出来ています。
DNSはGoogle(8.8.8.8)とCloudflare(1.1.1.1)のパブリックDNSサービスを設定しています。

  • (右)セグメント間通信を許可する設定
  • (左)セグメント間通信を隔離する設定

検証1:サブネットマスク 「/16」でセグメント間の疎通確認

各セグメント間は通信可能とする設定となります。セグメント間でのpingは問題無く通信出来ています。
※事前にWindowsファイアウォールでICMPを解放しておいてください。

各サーバのネットワーク設定

  • サーバ1(左)
    • IPアドレス:10.0.1.21
    • サブネットマスク:255.255.0.0
    • デフォルトゲートウェイ:10.0.0.1
  • サーバ2(右)
    • IPアドレス:10.0.2.11
    • サブネットマスク:255.255.0.0
    • デフォルトゲートウェイ:10.0.0.1

検証2:サブネットマスク 「/24」でインターネット通信を維持したままセグメント間の通信を隔離

今回の目玉、サブネットマスクとデフォルトゲートウェイの設定だけでセグメント間の通信を隔離する設定です。
セグメント間のpingは疎通できず、隔離することができています。

各サーバのネットワーク設定

  • サーバ1(左)
    • IPアドレス:10.0.1.21
    • サブネットマスク:255.255.255.0
    • デフォルトゲートウェイ:10.0.1.1
  • サーバ2(右)
    • IPアドレス:10.0.2.11
    • サブネットマスク:255.255.255.0
    • デフォルトゲートウェイ:10.0.2.1

応用編

セグメントの追加

今回は全体的な疎通の他に2つのセグメント個別に使用出来るゲートウェイIPアドレスを登録しました。
もしさらにセグメントを追加したい場合は、同様に以下のコマンドでセグメントのゲートウェイIPを追加する事ができます。

New-NetIPAddress -IPAddress 10.0.3.1 -PrefixLength 24 -InterfaceAlias "vEthernet (HyperV-Internal)"

一部VMのみの隔離

今回紹介した方式はVM毎のネットワーク設定で柔軟に切り替えられる方式です。
VM毎のネットワーク設定を切り替えるだけでセグメントを分けたVM間で疎通できたり隔離できたりしますので、アプリのリトライロジックやタイムアウトの検証、セグメントまたぎによる名前解決などの検証にも使えたりするかと思います。

(※通信を跨がせたい特定のVM同士は、お互いにサブネットマスクを /16 に合わせておく必要がある点だけ注意してください。片方が /24 のままだと、帰りのパケットがゲートウェイで落とされて片道切符になります)

雑多な検証用「マルチテナント」環境の乱造

例えば、開発チームA用、B用、C用…と複数のVMグループがある時、ホストの足を 10.0.1.110.0.2.110.0.3.1 と増やして、各チームのVMを /24 で閉じ込めておけば、お互いの通信の混線を防ぎつつ、全員が同じホストのWinNATからインターネットに出てライブラリをダウンロードできる、といった「簡易マルチテナント開発環境」が1台のPCの中に一瞬で作れます。

免責事項(念のため)

⚠️ 注意:本構成はあくまで「開発・検証環境向け」のテクニックです VM側のサブネットマスク変更だけで通信を制御する特性上、VMの管理者権限を持つユーザーがマスクを書き換えれば簡単に隔離を突破できてしまいます。本番環境や、厳密なセキュリティ隔離が必要な環境では、おとなしくHyper-Vの公式な仮想LAN(VLAN)機能や、ネットワークセキュリティグループ等を利用しましょう。

今回遭遇したトラブルシュートなど

本筋とはあまり関係が無い部分ですが参考まで。(思い切り基本的な事も含んでおりますw)

1)サーバ間のPingがつながらない!
はい、これは超基本的なファイアウォールの設定です。ICMPを許可してください。
またはpingではなく、arpテーブルを確認して相手のIPが存在していたら、疎通できているということです。

2)VMインポート時のネットワークの確認について
セグメントを分けるためにVMをエクスポートしてインポートしたのですが、インポートした際にネットワークアダプタに仮想スイッチを設定したあとに、「ネットワーク接続を許可しますか?」といった確認が3回発生しました。
どうやら登録したIPアドレスの数だけ確認が発生するようです。全部「はい」で進めましょう。

3)Hyper-V のVMで「構成記憶域に接続出来ない」ゾンビVMの消し方
久しぶりにHyper-Vを確認してみたのですが、↑のようなVMができてしまって、削除する事もできなくなってしまいました。
対処方法:(ただし、何も考えずに削除していいVMの場合に限ります。。。HardDiskファイルがあればなんとかなるかもしれませんが。)

# 1.VMのIDを確認してみる
Get-VM | Select-Object Name, Id, Status
  # 恐らくゾンビVMはエラーメッセージが表示されるので、正常に表示されているIDを控えておく。
# 2.心当たりのあるVM関連ファイルがあるフォルダから正常に表示されたID以外のVirtual Machinesファイルを削除する。
# 3.Hyper-Vサービスを再起動する
Restart-Service vmms
# 4.Hyper-V コンソールからVMを削除できるようになっている「はず」なので、削除する。
# 以 上

まとめ

今回紹介した構成により、ホストでは1つしか作成できないWinNATを使い、VM側の設定だけでインターネット接続をしたままセグメント間の通信を許可・隔離する環境を実現することができました。
複雑なルーター環境を組むことなく、手軽にセキュアなWEB/DB階層などの検証環境をシミュレートしたい場合に、非常に手軽で有効なアプローチになったと思います。

ではでは。