WSL2からHyper-Vの仮想マシンにSSHする
August 15, 2023
はじめに
WindowsでLinux系システムの開発構築を行う場合の手段としては、WSL2にてDockerを利用する方法が手っ取り早いと思いますが、コンテナでの開発ではなく、Linux環境丸ごと利用して開発を行いたい場合は、仮想環境にて仮想マシンにLinuxを導入して開発環境を構築するのが手っ取り早いと思います。
仮想環境といえば、WindowsにははじめからHyper-Vが同梱されているので、そちらで仮想マシンは作成できます。Hyper-Vマネージャーのコンソールを使用すると日本語が使用できません(日本語表示が化ける)が、sshdを起動させて、Windows上のSSHクライアントからSSHでアクセスするようにすれば問題ありません。
Hyper-Vの仮想マシンにSSHアクセスしたい場合、PowerShellを利用する方法がありますが、WSL2上で何か作業しながら、PowerShellを起動するのも面倒なので、どうせならWSL2上からSSHでHyper-Vの仮想マシンにアクセスできるようにしたいものです。
WSL2もHyper-Vのハイパーバイザー上、つまり仮想マシンとして動いているはずですが、WSL2とHyper-Vマネージャーで個々に作成した仮想マシンとでは、それぞれネットワークが異なるので通信できません。それを何とかして、WSL2からHyper-Vの仮想マシンにSSHさせてみようと思います。
Hyper-V仮想マシンとWSL2のネットワーク
Hyper-Vを有効にすると、「vEthernet(Default Switch)
」の仮想ネットワークアダプタ(NIC)が追加されます。
Hyper-Vマネージャーから、仮想スイッチマネージャーを確認すると、仮想スイッチとして「Default Switch
」が存在しています。
一方、WSL2のネットワークは、Hyper-Vの仮想スイッチである「WSL
」を使用します。
そして、ホスト側との接続は、「vEthernet(WSL)
」の仮想NICで接続されます。
つまり、ホスト側(Windows OS側)には、仮想NICとして「vEthernet(Default Switch)
」と「vEthernet(WSL)
」が存在します。
図のように、
- Hyper-V仮想マシンが接続しているネットワークは、172.27.224.0/20
- WSL2(Ubuntu)が接続しているネットワークは、172.25.208.0/20
で、それぞれ異なります。それぞれ、ホスト内部のNATの機能(Windows NAT, WinNAT)を経由して外部ネットワークにアクセスすることは可能ですが、次の図のように、WSL2からHyper-V上の仮想マシンにアクセスすることはできません。
PowerShellからのHyper-V上の仮想マシンへのSSHアクセスは可能です。
次のサイトにあるように、それぞれネットワークインターフェース間のForwardingをEnabledに設定すると、Hyper-Vの仮想マシンとWSL2が互いに通信できるようになり、WSL2からのSSHアクセスも可能になります。
参考 WSL2とHyper-V仮想マシンの間で通信ができるようにする - メモの日々(2020-12-08)
ですが、大きな問題として、ホストであるWindowsの再起動の度に、これら仮想スイッチ(vSwitch)が再構築(削除と作成)されるので、ネットワークアドレスが変わり、DHCPのIPアドレスレンジも変わり、それぞれ割り当てられるIPアドレスが変わわってしまいます。それゆえSSHする際は、その都度、Hyper-Vマネージャーのコンソールから仮想マシンにアクセスし、仮想マシンのIPアドレスを確認する必要があります。
参考 WSL バージョンの比較 - Microsoft Learn
直接ホストの物理NICにブリッジする「外部ネットワーク」タイプの仮想スイッチに変更すれば、固定のIPアドレスを振れそうですが、開発環境の仮想マシンを社内LAN上に晒したくないですし、Windowsが用意した仮想スイッチを色々いじるのもしたくはないですね。
では、どうしたらよいか、その一解決方法をご紹介する前に、現状のWindows仮想ネットワークを確認します。
Windows仮想ネットワークアダプタ
ネットワークアダプタの一覧を表示させるには、設定からでは、
「ネットワークとインターネット」>「ネットワークの詳細設定」>「ネットワークアダプターオプションの詳細」
また、コントロールパネルからでは、
「ネットワークとインターネット」>「ネットワークと共有センター」>「アダプターの設定の変更」
にて確認できますが、いつしか仮想ネットワークアダプタ(vEthernet)が非表示になりました。
「ネットワークとインターネット」>「ネットワークの詳細設定」>「ハードウェアと接続のプロパティ」
で確認はできますが、有効/無効化はできないようです。
PowerShellで確認してみましょう。Get-NetAdapter
コマンドで情報が取得できます。
PS C:\Users\hoge> Get-NetAdapter
Name InterfaceDescription ifIndex Status MacAddress
---- -------------------- ------- ------ ----------
Bluetooth ネットワーク… Bluetooth Device (Pers… 23 Disconnected 0C-96-E6-XX-XX-XX
イーサネット Intel(R) Ethernet Conn… 20 Disconnected C8-F7-50-XX-XX-XX
Wi-Fi Qualcomm QCA61x4A 802.… 18 Up 0C-96-E6-XX-XX-XX
仮想ネットワークアダプタ(vEthernet)を表示させるためには「-IncludeHidden
」オプションを付加します。
PS C:\Users\hoge> Get-NetAdapter -IncludeHidden
Name InterfaceDescription ifIndex Status MacAddress
---- -------------------- ------- ------ ----------
Bluetooth ネットワーク… Bluetooth Device (Personal Area … 23 Disconnected 0C-96-E6-XX-XX-XX
ローカル エリア接続* 2 Microsoft Wi-Fi Direct Virtua...#2 22 Disconnected 1E-96-E6-XX-XX-XX
ローカル エリア接続* 9 WAN Miniport (PPPOE) 21 Disconnected
vSwitch (Default Switch) Hyper-V Virtual Switch Extension… 17 Up
vSwitch (WSL) Hyper-V Virtual Switch Extens...#2 43 Up
イーサネット Intel(R) Ethernet Connection (4)… 20 Disconnected C8-F7-50-XX-XX-XX
ローカル エリア接続* 6 WAN Miniport (IKEv2) 19 Disconnected
Wi-Fi Qualcomm QCA61x4A 802.11ac Wirel… 18 Up 0C-96-E6-XX-XX-XX
vEthernet (Default Swit… Hyper-V Virtual Ethernet Adapter 26 Up 00-15-5D-XX-XX-XX
Teredo Tunneling Pseudo… 15 Not Present
なんかいっぱい出てきました。Nameに「WSL
」と付くものには、「vSwitch」と「vEthernet」と2種類あります。
PS C:\Users\hoge> Get-NetAdapter -IncludeHidden | Where-Object {$_.Name -like '*WSL*'} | Format-List
Name : vSwitch (WSL)
InterfaceDescription : Hyper-V Virtual Switch Extension Adapter #2
InterfaceIndex : 49
MacAddress :
MediaType : 802.3
PhysicalMediaType : Unspecified
InterfaceOperationalStatus : Up
AdminStatus : Up
LinkSpeed(Gbps) : 10
MediaConnectionState : Connected
ConnectorPresent : False
DriverInformation : Driver Date 2006-06-21 Version 10.0.22621.1 NDIS 6.83
Name : vEthernet (WSL)
InterfaceDescription : Hyper-V Virtual Ethernet Adapter #2
InterfaceIndex : 51
MacAddress : 00-15-5D-XX-XX-XX
MediaType : 802.3
PhysicalMediaType : Unspecified
InterfaceOperationalStatus : Up
AdminStatus : Up
LinkSpeed(Gbps) : 10
MediaConnectionState : Connected
ConnectorPresent : False
DriverInformation : Driver Date Version
「vEthernet」の方は「MacAddress」があるので仮想NICであることが分かります。「vSwitch」の方は、「Hyper-V仮想スイッチ拡張アダプタ (Hyper-V Virtual Switch Extension Adapter)」とありますが、仮想スイッチということで良いのかな。
続いて、Get-NetIpInterface
コマンドで、各ネットワークインターフェース情報を見てみます。
Where-Object
でパイプして IPv4 の情報だけを抽出してみます。
PS C:\Users\hoge> Get-NetIPInterface | Where-Object {$_.AddressFamily -eq 'IPv4'}
ifIndex InterfaceAlias AddressFamily NlMtu(Bytes) InterfaceMetric Dhcp ConnectionState
------- -------------- ------------- ------------ --------------- ---- ---------------
45 vEthernet (WSL) IPv4 1500 5000 Disabled Connected
26 vEthernet (Default Switch) IPv4 1500 5000 Disabled Connected
22 ローカル エリア接続* 2 IPv4 1500 25 Enabled Disconnected
23 Bluetooth ネットワーク接続 IPv4 1500 65 Enabled Disconnected
13 ローカル エリア接続* 1 IPv4 1500 25 Enabled Disconnected
20 イーサネット IPv4 1500 5 Enabled Disconnected
1 Loopback Pseudo-Interface 1 IPv4 4294967295 75 Disabled Connected
18 Wi-Fi IPv4 1500 45 Enabled Connected
Get-NetAdapter
コマンドがL2レベルで、Get-NetIPInterface
コマンドがL3レベルの情報を取得する、ということでしょうか。
新規に仮想スイッチを作成する
それでは、WSL2からHyper-Vの仮想マシンにSSHアクセス可能にするための解決策として、「内部ネットワーク」タイプの仮想スイッチを利用した方法をご紹介します。
Hyper-Vマネージャーの仮想スイッチマネージャーを開いて、新しい仮想ネットワークスイッチ「Host-Only Switch
」を作成してみます。接続の種類を「内部ネットワーク」します。
- 「外部ネットワーク」
- 上で説明した通り、ホストの物理NICに直接ブリッジする
- 「内部ネットワーク」
- Hyper-V上の仮想マシン同士、仮想マシンとホストとのアクセスは可、外部ネットワークとのアクセスは不可
- 「プライベートネットワーク」
- Hyper-V上の仮想マシン同士のみアクセスか、ホストや外部ネットワークとのアクセスは不可
参考 仮想スイッチの種別と用途:Windows 10 Hyper-V入門 - @IT
次に、仮想マシンの設定を開き、「ハードウェアの追加」で「ネットワークアダプター」を1つ追加します。
追加した「ネットワークアダプター」の設定にて、仮想スイッチを作成した「Host-Only Switch」に指定します。
これで、仮想マシンにNICが1つ追加(eth1)されたことになります。
Get-NetIPInterface
コマンドにて確認してみます。
PS C:\Users\hoge> Get-NetIPInterface | Where-Object {$_.InterfaceAlias -like 'vEthernet*'}
ifIndex InterfaceAlias AddressFamily NlMtu(Bytes) InterfaceMetric Dhcp ConnectionState PolicyStore
------- -------------- ------------- ------------ --------------- ---- --------------- -----------
57 vEthernet (Host-Only Switch) IPv6 1500 15 Enabled Connected ActiveStore
45 vEthernet (WSL) IPv6 1500 5000 Enabled Connected ActiveStore
26 vEthernet (Default Switch) IPv6 1500 5000 Enabled Connected ActiveStore
57 vEthernet (Host-Only Switch) IPv4 1500 15 Enabled Connected ActiveStore
45 vEthernet (WSL) IPv4 1500 5000 Disabled Connected ActiveStore
26 vEthernet (Default Switch) IPv4 1500 5000 Disabled Connected ActiveStore
さらに、IPv4の「vEthernet (Host-Only Switch)
」に限定して、Get-NetIPInterface
コマンドにて確認します。
PS C:\Users\hoge> Get-NetIPAddress -InterfaceAlias "vEthernet (Host-Only Switch)" | Where-Object {$_.AddressFamily -eq 'IPv4'}
IPAddress : 169.254.188.169
InterfaceIndex : 57
InterfaceAlias : vEthernet (Host-Only Switch)
AddressFamily : IPv4
Type : Unicast
PrefixLength : 16
PrefixOrigin : WellKnown
SuffixOrigin : Link
AddressState : Preferred
ValidLifetime : Infinite ([TimeSpan]::MaxValue)
PreferredLifetime : Infinite ([TimeSpan]::MaxValue)
SkipAsSource : False
PolicyStore : ActiveStore
リンクローカルアドレスが割り振られているようです。
「vEthernet (Host-Only Switch)
」は、「設定」>「ネットワークとインターネット」>「ネットワークの詳細設定」>「ネットワークアダプターオプションの詳細」でもプロパティが表示されるので、そこで設定変更しても良いのですが、せっかくなので、PowerShellにて New-NetIPAddress
コマンドで設定してみましょう。
- IPアドレス(ホスト側):
10.0.0.1
- ネットマスク:
255.255.255.0
とします。
PS C:\Users\hoge> New-NetIPAddress 10.0.0.1 -PrefixLength 24 -ifIndex 57
再度、Get-NetIPInterface
コマンドにて確認します。
PS C:\Users\hoge> Get-NetIPAddress -InterfaceAlias "vEthernet (Host-Only Switch)" | Where-Object {$_.AddressFamily -eq 'IPv4'}
IPAddress : 10.0.0.1
InterfaceIndex : 57
InterfaceAlias : vEthernet (Host-Only Switch)
AddressFamily : IPv4
Type : Unicast
PrefixLength : 24
PrefixOrigin : Manual
SuffixOrigin : Manual
AddressState : Preferred
ValidLifetime : Infinite ([TimeSpan]::MaxValue)
PreferredLifetime : Infinite ([TimeSpan]::MaxValue)
SkipAsSource : False
PolicyStore : ActiveStore
仮想マシンのほうに追加したNIC(eth1)については、Linux上で設定します。ここでは、10.0.0.10
で設定します。設定方法については割愛あいます。
一方、デフォルトの eth0 については動的IPアドレス割り当てのままにしておきます。
仮想マシン(Linux)で、 ip
コマンドで確認するとこんな感じ。
[hoge@centos6 ~]$ ip -4 a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN
inet 127.0.0.1/8 scope host lo
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP qlen 1000
inet 172.27.237.23/20 brd 172.27.239.255 scope global eth0
3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP qlen 1000
inet 10.0.0.10/24 brd 10.0.0.255 scope global eth1
現在のネットワーク構成はこのようになっています。
仮想マシン(Linux)にて、ルーティングテーブルを確認します。
[hoge@centos6 ~]$ route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
10.0.0.0 0.0.0.0 255.255.255.0 U 0 0 0 eth1
172.27.224.0 0.0.0.0 255.255.240.0 U 0 0 0 eth0
169.254.0.0 0.0.0.0 255.255.0.0 U 1002 0 0 eth0
169.254.0.0 0.0.0.0 255.255.0.0 U 1003 0 0 eth1
0.0.0.0 172.27.224.1 0.0.0.0 UG 0 0 0 eth0
デフォルトゲートウェイは「172.27.224.1
」、即ち「vEthernet(Default Switch)
」のIPアドレスになっています。
NICの eth1(10.0.0.1
)はホストとのアクセスが可能ですので、結果として、WSL2側からSSHが可能になるはずです。
では、WSL2側にて、ssh 10.0.0.10
コマンドをたたいてみます。
hoge@slinky:~$ ssh 10.0.0.10
Unable to negotiate with 10.0.0.10 port 22: no matching host key type found. Their offer: ssh-rsa,ssh-dss
もし、このようなメッセージが表示されて、結果接続できなかった場合は、クライアント側、即ちWSL2側のOpenSSHのバージョンがサーバ側に比べ新しい場合にこのようなことになるようです。クライアント側にて、古いタイプの鍵でも使えるように、.ssh/config
にて次のように設定しておきます。
hoge@slinky:~$ cat .ssh/config
Host 10.0.0.10
HostKeyAlgorithms ssh-rsa,ssh-dss
PubkeyAcceptedAlgorithms +ssh-rsa
ついに、WSL2からHyper-Vの仮想マシンにSSHアクセスできるようになりました。
hoge@slinky:~$ ssh 10.0.0.10
hoge@10.0.0.10s password:
Last login: Tue Aug 15 13:39:46 2023 from 172.27.224.1
[hoge@centos6 ~]$
ホスト(Windows)の再起動による仮想スイッチ「vSwitch(Default Switch)
」の再構築によって、仮想マシン(Linux)の eth0 側のIPアドレスが変わったとしても、eth1 側のIPアドレスは固定なので影響ありません。
違う方法が他にあると思いますが、自分の場合はこれで満足しています。