リモートWindowsのWSLへSSH接続
Table of Contents
クライアントからリモートWindowsのWSLへSSH接続をする場合、以下のどちらかを使用します。
- SSHのJumphost(推奨)
- Windowsのポートフォワーディング機能
前提
この記事でリモートWindowsへのSSH接続ができていること。未設定の場合は、先にこちらを参考にしてリモートWindowsへのSSH接続を確立してください。
リモートのWSLへOpenSSHサーバをインストール
どちらの方法を用いる場合も、リモートWindowsのWSL側でOpenSSHサーバをインストールし、Windows側と被らないポートで待ち受ける必要があります。
sudo apt update
sudo apt upgrade -y
sudo apt install openssh-server -y
Port 22222など、Windows側(22)と被らないポートに変更:
sudo vim /etc/ssh/sshd_config
SSHサーバを再起動:
sudo service ssh restart
sshdが22222で待ち受けているか確認:
ss -tlnp | grep 22222
# 出力例
# LISTEN 0 128 0.0.0.0:22222 0.0.0.0:*
# LISTEN 0 128 [::]:22222 [::]:*
この設定が完了したら、リモートWindowsからWSLへSSH接続できることを確認します。
# WSLのパスワードが必要
ssh -p 22222 wsluser@localhost
Windowsの設定(Jumphostを用いる場合)
リモートWindowsのSSHサーバをJumphostとして利用し、SSHのProxyJump機能を用いてクライアントからリモートのWSLへ接続する方法です。
ポートフォワーディングを用いる方法と比べて構成がシンプルなため、こちらの方法を推奨します。
ミラーモードの前提条件
ミラーモードを使用する場合は以下が必要です:
- ホストOS:Windows 11 22H2 以降
- WSLバージョン:WSL v2.0.4 以上
(wsl --versionで確認可能)
ホスト側のWSL設定
ホストの C:\Users\<username>\.wslconfig へ以下を追加します:
[wsl2]
networkingMode=mirrored
vmIdleTimeout=-1
[experimental]
hostAddressLoopback=true
- networkingMode=mirrored:WSLがホストと同じネットワークに直接接続されるため、WSLのIPアドレスがホストと同じになります。これにより、クライアントから直接WSLへSSH接続できるようになります。
- hostAddressLoopback=true:ミラーモード前提のexperimental設定で、ホストに割り当てられているIPアドレスを使って、コンテナ(WSL)⇔ホスト間で接続できるようにするオプションです。これにより、ホストのIPアドレスを使ってWSLへSSH接続できるようになります。なおサポートされるのはホストに割り当てられた IPv4 アドレスのみで、IPv6はサポートされません。
- vmIdleTimeout=-1:WSLのアイドルタイムアウトを無効化する設定です。これにより、一定時間操作がない場合でもWSLが自動的に停止しないようになります。
保存後、ホストでWSLを再起動します。
wsl --shutdown
さらに、Windows起動時にWSLを自動起動する設定を行います。
Win + R+taskschd.mscでタスクスケジューラを開く- 右側のペインで「タスクの作成」をクリック
- 「全般」タブ:
- 名前:「Start WSL on Login」など
- セキュリティオプション:「ユーザーがログオンしたときに実行する」を選択
- 最上位の特権で実行するにチェック
- 「トリガー」タブ:
- 「新規」をクリック
- タスクの開始:「ログオン時」
- 設定:「特定のユーザー」にチェックを入れ、ユーザーを選択
- 遅延時間:30秒~1分程度(安定化のため)
- 有効:チェック
- 「操作」タブ:
- 「新規」をクリック
- 操作:「プログラムの開始」
- プログラム/スクリプト:
wsl.exe - 引数の追加:
-d Ubuntu --exec dbus-launch true - 開始:空欄
これで、Windows起動時にwsl -d Ubuntu --exec dbus-launch trueコマンドが実行され、WSLが自動的に起動されるようになります。
WSLの設定
/etc/wsl.conf に以下を追加します。これでWSL起動時にsshdが自動起動されます。
[boot]
systemd=true
さらに、以下のコマンドでSSHサーバを自動起動するようにします。
sudo systemctl enable ssh
ホストでWSLを再起動します。
wsl --shutdown
これら一連の設定により、
- 安定してWSLが起動するようになり、
- Windows起動時にWSLが自動的に起動される
- WSL内でsystemdが有効化され、SSHサーバが自動起動される
- 一定時間操作がない場合でもWSLが自動的に停止しない
- クライアントからホストWindowsのWSLへ安定してSSH接続できるようになります
- WSLとホストWindowsのIPアドレスが一致(WSL起動ごとのIPアドレス変動を防ぐ)
- ProxyJumpの際にwslのIPアドレスを127.0.0.1で指定できるようになる
ProxyJumpの設定方法
クライアント側の~/.ssh/configに以下の設定を行います。
注意:remote-wslのHostNameはlocalhostではなく127.0.0.1を指定します。localhostを指定すると、環境によってはIPv6で接続され接続に失敗する可能性があるためです。127.0.0.1を指定することで、明示的にIPv4経由で接続されるようになります。
Host remote-win
HostName <hostname> # or <hostname>.local or <ip-address>
User windowsuser
Host remote-wsl
HostName 127.0.0.1 # 明示的にIPv4を指定
User wsluser
Port 22222
ProxyJump remote-win
これにより、クライアントから以下のコマンドで簡単にSSH接続できるようになります。
ssh remote-wsl
(オプション)ミラーモードを用いない場合のWSL IPアドレスの更新
もしミラーモードを使用せず、WSLがホストとは別のIPアドレスを持つ場合、注意が必要です。
WSLは仮想NIC上で動作し、起動するたびにIPが変わるため、クライアント側の~/.ssh/configのHostNameも更新する必要があります。
クライアント側で更新スクリプトを作成しておくと便利です。
まず、プロファイルのパスを確認します。
$PROFILE
通常は C:\Users\kouki\Documents\PowerShell\Microsoft.PowerShell_profile.ps1 のようなパスになります。
もしファイルが存在しない場合は、以下のコマンドで作成します。
if (-not (Test-Path $PROFILE)) {
New-Item -ItemType File -Path $PROFILE -Force
}
次に、VSCode等でプロファイルを開き、
code $PROFILE
開いたファイルの末尾に以下の関数を追加します。
function Update-WslIp {
[CmdletBinding()]
param(
[string]$JumpHost = 'winusername@hostname', # Jumphostのユーザー名とホスト名
[string]$ConfigPath = "$HOME\.ssh\config",
[string]$TargetHost = 'remote-wsl'
)
Write-Host "Fetching WSL IP via $JumpHost..." -ForegroundColor Cyan
$wslIp = (ssh $JumpHost 'wsl hostname -I' 2>$null).Trim().Split(' ')[0]
if (-not $wslIp) {
Write-Error "Failed to get WSL IP address"
return
}
if (-not (Test-Path $ConfigPath)) {
Write-Error "$ConfigPath not found"
return
}
# Get current IP (early return if no change is needed)
$content = Get-Content $ConfigPath -Raw
$pattern = "(?ms)(Host\s+$TargetHost\s*\r?\n(?:[^\r\n]*\r?\n)*?\s*HostName\s+)(\S+)"
$currentMatch = [regex]::Match($content, $pattern)
$currentIp = if ($currentMatch.Success) { $currentMatch.Groups[2].Value } else { $null }
if ($currentIp -eq $wslIp) {
Write-Host "IP is already up to date: $wslIp" -ForegroundColor Green
return
}
# Back up and replace
Copy-Item $ConfigPath "$ConfigPath.bak" -Force
$updated = $content -replace $pattern, "`${1}$wslIp"
Set-Content -Path $ConfigPath -Value $updated -NoNewline
Write-Host "Updated: $currentIp -> $wslIp" -ForegroundColor Green
}
# Short alias (optional)
Set-Alias uwsl Update-WslIp
PowerShellを再起動するか、プロファイルを再読み込みします。
. $PROFILE
以下のコマンドで使用できます。
# エイリアス
uwsl
# フルコマンド
Update-WslIp -JumpHost 'windowsuser@hostname' -TargetHost 'remote-wsl'
Windowsの設定(ポートフォワーディングを用いる場合)
JumpHostを用いることが出来ない環境の場合、Windowsのportproxy機能を用いて、リモートWindowsの特定のポート(例:22222)への接続をWSLのSSHサーバへ転送する方法もあります。
以下、リモートWindowsの管理者権限のPowerShellで実行します。
portproxyの待ち受けポート設定
WSLのIPアドレスを取得し、Windowsのportproxyでポートフォワーディングを設定します。
WSLはIPアドレスが2つ以上表示される(例:IPv4とIPv6, Docker)ことがあるため、最初のIPアドレス(IPv4)を取得します。
# WSLのIPアドレス(IPv4)を取得
$wslIp = (wsl hostname -I).Trim().Split(' ')[0]
# Windowsのportproxyでポートフォワーディングを設定
netsh interface portproxy add v4tov4 listenport=22222 listenaddress=0.0.0.0 connectport=22222 connectaddress=$wslIp
# 確認
# Listen on ipv4: 0.0.0.0:22222 → Connect to ipv4: 172.x.x.x:22222 のように表示されればOK
netsh interface portproxy show v4tov4
ファイアウォールでport 22222を許可
Windows Defenderファイアウォールでこのポートを開けると、外部からWSLのSSHサーバへ接続できるようになります。
New-NetFirewallRule -DisplayName "WSL SSH" -Direction Inbound -LocalPort 22222 -Protocol TCP -Action Allow
クライアントからリモートのWSLへSSH接続
以下で接続を確認します。
注意:IPv4で接続するための-4オプションを明示的に指定します。無しの場合、環境によってはIPv6が使用され接続に失敗する可能性があります。
ssh -4 -p 22222 wsluser@<hostname>
接続が成功すれば、リモートWindowsのWSLへSSH接続できています。
パスワードなしで接続したい場合は、SSHキーを生成してリモートWindowsのWSLに公開鍵を配置してください。
注意:SSH公開鍵は既に生成済みの前提です。詳細はこの記事を参照。
type $env:USERPROFILE\.ssh\id_ed25519.pub | ssh -4 -p 22222 wsluser@<hostname> "mkdir -p ~/.ssh && chmod 700 ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys"
また、接続簡略化のために、クライアント側の~/.ssh/configに以下の設定を追加することもできます。
Host remote-wsl
HostName <hostname> # or <hostname>.local or <ip-address>
Port 22222
User wsluser
AddressFamily inet # IPv4を強制
これにより、以下のコマンドで簡単にSSH接続できるようになります。
# ssh -4 -p 22222 wsluser@<hostname> の代わりに以下で接続可能
ssh remote-wsl
トラブルシューティング
段階的に切り分けると原因が見つけやすいです:
- リモートWSL:
ss -tlnp | grep 22222でsshdが22222で待ち受けているか確認 - リモートWindows:
netsh interface portproxy show v4tov4で転送設定を確認 - リモートWindows:
ssh -p 22222 wsluser@localhostでWSLへのローカル経由の接続を試す - クライアント:
ssh username@hostnameでリモートWindowsへのSSH接続を試す - クライアント:
ssh -4 -v -p 22222 wsluser@<WindowsのIP>で外部経由を試す (-vで詳細なログを表示)
WSLのIPアドレス自動更新
WSLは仮想NIC上で動作し、起動するたびにIPが変わるため、Windowsのportproxy設定も更新する必要があります。
リモートWindowsで以下の更新用PSスクリプトを作成しておきます。
# update-wsl-portproxy.ps1
$wslIp = (wsl hostname -I).Trim().Split(' ')[0]
netsh interface portproxy delete v4tov4 listenport=22222 listenaddress=0.0.0.0 2>$null
netsh interface portproxy add v4tov4 listenport=22222 listenaddress=0.0.0.0 connectport=22222 connectaddress=$wslIp
Write-Host "Forwarded 22222 -> $wslIp:22222"
スクリプトは netsh を使うため、Windows側で管理者権限のPowerShellから実行する必要があります。WSL内では動きません。実行するタイミングとして、以下の2通りの方法があります。
方法1:Windowsログオン時にタスクスケジューラで自動実行
Windows再起動のたびにWSLのIPが変わる可能性があるため、ログオン時に自動で更新するのが便利です。
-
管理者権限のPowerShellで以下を実行し、タスクを登録します(
C:\Scripts\update-wsl-portproxy.ps1は実際のスクリプト配置パスに置き換え)。$action = New-ScheduledTaskAction -Execute "powershell.exe" -Argument "-NoProfile -ExecutionPolicy Bypass -File C:\Scripts\update-wsl-portproxy.ps1" $trigger = New-ScheduledTaskTrigger -AtLogOn $principal = New-ScheduledTaskPrincipal -UserId "$env:USERDOMAIN\$env:USERNAME" -RunLevel Highest Register-ScheduledTask -TaskName "Update WSL PortProxy" -Action $action -Trigger $trigger -Principal $principal -
登録後、タスクスケジューラのGUI(
taskschd.msc)から「Update WSL PortProxy」を確認できます。 -
動作確認したい場合は、タスクを右クリック →「実行する」で手動起動できます。
注意:スクリプト内で wsl hostname -I を呼ぶため、WSLが停止していても自動で起動されます。
方法2:WSL再起動後に手動実行
wsl --shutdown などでWSLを再起動した直後は、IPが変わっている可能性があります。その場合は、管理者権限のPowerShellで手動実行します。
powershell -NoProfile -ExecutionPolicy Bypass -File C:\Scripts\update-wsl-portproxy.ps1
実行後、netsh interface portproxy show v4tov4 で転送先IPが最新のWSL IPに更新されていれば成功です。