Last active
September 6, 2024 02:12
-
-
Save da-moon/51f23971feb005d5389642e56ce33bff to your computer and use it in GitHub Desktop.
Powershell script to setup SSH server in WSL so that Windows users can SSH into WSL. It can be useful for "remote" development with Editors such as Lapce or Zed
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Flexible WSL2 SSH Setup Script with Distribution Detection | |
# Ensure running as Administrator | |
if (-NOT ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) { | |
Write-Warning "Please run this script as Administrator!" | |
Exit | |
} | |
# Function to get the default WSL distribution | |
function Get-DefaultWSLDistribution { | |
$wslOutput = wsl -l -v | Out-String | |
$defaultDistroLine = ($wslOutput -split "`n" | Where-Object { $_ -match '\*' }).Trim() | |
if ($defaultDistroLine) { | |
$distroName = ($defaultDistroLine -split '\s+')[1] | |
$distroName = $distroName -replace "`0", "" | |
return $distroName | |
} | |
return $null | |
} | |
# # Alternative function. I kept it just so that I have different approaches written down | |
# function Get-DefaultWSLDistribution { | |
# $wslOutput = wsl -l -v | |
# $defaultDistroLine = $wslOutput | Select-String -Pattern '\*' | |
# if ($defaultDistroLine) { | |
# # Convert the matched line to a string and trim it | |
# $defaultDistroLine = $defaultDistroLine.ToString().Trim() | |
# # Split the line by spaces and select the second element which should be the distro name | |
# $distroName = $defaultDistroLine -split '\s+' | Select-Object -Index 1 | |
# # handle null characters | |
# $distroName = $wslDistro -replace "`0", "" | |
# return $distroName | |
# } else { | |
# Write-Error "No default WSL distribution found." | |
# return $null | |
# } | |
# } | |
# Function to get WSL2 IP address | |
function Get-WSL2IPAddress($distroName) { | |
wsl -d $distroName -e bash -c "ip addr show eth0 | grep -oP '(?<=inet\s)\d+(\.\d+){3}'" | |
} | |
# Get the default WSL distribution or prompt user to enter one | |
$wslDistro = Get-DefaultWSLDistribution | |
if (-not $wslDistro) { | |
$wslDistro = Read-Host "Enter the name of your WSL distribution" | |
} | |
Write-Host "Using WSL distribution: $wslDistro" | |
wsl -u "root" -d "$($wslDistro)" /bin/bash -lc (@' | |
set -ex; | |
if command -v apt-get &> /dev/null; then | |
apt-get update -q && apt-get install -yq openssh-server | |
elif command -v pacman &> /dev/null; then | |
pacman -Syu --noconfirm && pacman -S --needed --noconfirm openssh | |
elif command -v dnf &> /dev/null; then | |
dnf update -y && dnf install -y openssh-server | |
else | |
echo 'Unsupported package manager. Please install OpenSSH server manually.' | |
exit 1 | |
fi | |
ssh-keygen -A | |
sed -i -E 's,^#?Port.*$,Port 2022,' /etc/ssh/sshd_config | |
echo "PasswordAuthentication no" | tee -a /etc/ssh/sshd_config | |
sed -i "/.*PermitRootLogin.*/d" /etc/ssh/sshd_config | |
echo "PermitRootLogin no" | tee -a /etc/ssh/sshd_config | |
sed -i "/.*UsePAM.*/d" /etc/ssh/sshd_config | |
"UsePAM no" | tee -a /etc/ssh/sshd_config | |
sed -i "/.*PubkeyAuthentication.*/d" /etc/ssh/sshd_config | |
echo "PubkeyAuthentication yes" | tee -a /etc/ssh/sshd_config | |
sed -i -e '/^AcceptEnv/!{$s/$/\nAcceptEnv TERM_PROGRAM COLORTERM/}' -e '/^AcceptEnv/{ /TERM_PROGRAM/!s/$/ TERM_PROGRAM/; /COLORTERM/!s/$/ COLORTERM/ }' -e '/^AcceptEnv/h;/^AcceptEnv/d;$G' /etc/ssh/sshd_config | |
if command -v systemctl &> /dev/null; then | |
systemctl enable sshd || systemctl enable ssh | |
systemctl start sshd || systemctl start ssh | |
else | |
service sshd start || service ssh start | |
fi | |
'@) ; | |
# Get WSL2 IP Address | |
$wslIP = Get-WSL2IPAddress $wslDistro | |
# Configure Windows Firewall | |
New-NetFirewallRule -Name "WSL2 SSH" -DisplayName "WSL2 SSH" -Direction Inbound -Protocol TCP -LocalPort 2022 -Action Allow -RemoteAddress 127.0.0.1 | |
# Set up port forwarding | |
netsh interface portproxy delete v4tov4 listenport=2022 listenaddress=127.0.0.1 | |
netsh interface portproxy add v4tov4 listenport=2022 listenaddress=127.0.0.1 connectport=2022 connectaddress=$wslIP | |
# Verify the port proxy settings | |
netsh interface portproxy show v4tov4 | |
# Display the WSL IP for verification | |
Write-Host "WSL IP: $wslIP" | |
# Create a startup script for persistent port forwarding | |
$scriptContent = @" | |
`$wslDistro = '$wslDistro' | |
`$wslIP = wsl -d `$wslDistro -e bash -c "ip addr show eth0 | grep -oP '(?<=inet\s)\d+(\.\d+){3}'" | |
netsh interface portproxy delete v4tov4 listenport=2022 listenaddress=127.0.0.1 | Out-Null | |
Write-Host -f darkgreen "[INFO] Refreshing Windows Firewall config for WSL [`$wslDistro] with IP [`$wslIP]" | |
netsh interface portproxy add v4tov4 listenport=2022 listenaddress=127.0.0.1 connectport=2022 connectaddress=`$wslIP | |
"@ | |
$scriptContent | Out-File -FilePath "$env:USERPROFILE\ConfigureWSLSSHForwarding.ps1" -Encoding ASCII | |
# Create a scheduled task to run the script at startup | |
$action = New-ScheduledTaskAction -Execute "powershell.exe" -Argument "-ExecutionPolicy Bypass -File $env:USERPROFILE\ConfigureWSLSSHForwarding.ps1" | |
$trigger = New-ScheduledTaskTrigger -AtStartup | |
$principal = New-ScheduledTaskPrincipal -UserId (whoami) -RunLevel Highest | |
Register-ScheduledTask -TaskName "WSL2 SSH Setup" -Action $action -Trigger $trigger -Principal $principal -Force | |
# Optional: Set up SSH key authentication | |
$KEY_TYPE="ed25519" ; | |
$KEY_NAME="wsl_$($wslDistro)" | |
$sshKeyPath = "$env:USERPROFILE\.ssh\id_$($KEY_TYPE)_$($KEY_NAME)" | |
if (-not (Test-Path $sshKeyPath)) { | |
ssh-keygen -q -N '""' -t "$KEY_TYPE" -f "$sshKeyPath" -C "$($ENV:USERNAME)@$($ENV:COMPUTERNAME)" | |
} | |
$PUB_KEY = Get-Content "$sshKeyPath.pub" | |
wsl /bin/bash -c "$( | |
@' | |
set -x | |
mkdir -p "$HOME/.ssh" | |
printenv "PUB_KEY" >> "$HOME/.ssh/authorized_keys" | |
chmod 644 "$HOME/.ssh/authorized_keys" | |
cat "$HOME/.ssh/authorized_keys" | |
'@ -f ($PUB_KEY -replace "'", "''") | |
)" | |
$PUB_KEY = Get-Content "$sshKeyPath.pub" | |
wsl -- /bin/bash -c ( | |
(@' | |
PUB_KEY='{0}'; | |
if [ -n "${PUB_KEY}" ]; then | |
echo 'Public Key: ${PUB_KEY}' | |
mkdir -p '$HOME/.ssh' | |
echo 'Adding key to authorized_keys...' | |
echo "${PUB_KEY}" >> "${HOME}/.ssh/authorized_keys" | |
echo 'Setting permissions...' | |
chmod 644 "${HOME}/.ssh/authorized_keys" | |
echo 'Contents of authorized_keys:' | |
cat "${HOME}/.ssh/authorized_keys" | |
else | |
echo 'PUB_KEY is empty or not set' | |
exit 1; | |
fi | |
'@ -replace '\$\{(\w+)\}','$$${1}' -replace '"', '\"' -replace '\$(?!{)', '\$' -replace '\$\{(\w+)\}','\${${1}}' | |
) -f $PUB_KEY | |
) | |
$DEFAULT_WSL_USER=$(wsl /bin/bash -c "id -un") | |
$sshConfigPath = "$env:USERPROFILE\.ssh\config" | |
$wslHostEntry = @" | |
Host wsl_$($wslDistro) | |
HostName localhost | |
User $DEFAULT_WSL_USER | |
IdentityFile $sshKeyPath | |
Port 2022 | |
RequestTTY yes | |
IdentitiesOnly yes | |
StrictHostKeyChecking no | |
CheckHostIP no | |
MACs hmac-sha2-256 | |
UserKnownHostsFile /dev/null | |
"@ | |
if (Test-Path $sshConfigPath) { | |
$configContent = Get-Content $sshConfigPath -Raw | |
if ($configContent -notmatch "Host wsl_$($wslDistro)") { | |
Add-Content $sshConfigPath "`n$wslHostEntry" | |
Write-Host "Added WSL host to SSH config." | |
} else { | |
Write-Host "WSL host already exists in SSH config. No changes made." | |
} | |
} else { | |
New-Item -Path $sshConfigPath -ItemType File -Force | Out-Null | |
Set-Content $sshConfigPath $wslHostEntry | |
Write-Host "Created SSH config with WSL host." | |
} | |
Write-Host "Setup complete. You can now SSH into your $wslDistro WSL2 instance using:" | |
Write-Host "ssh wsl_$($wslDistro)" | |
Write-Host "" | |
Write-Host -f darkcyan '[NOTE] you must run "powershell $env:USERPROFILE\ConfigureWSLSSHForwarding.ps1" after every WSL restart' | |
Write-Host "" | |
Write-Host -f darkyellow ' | |
[WARN] Additionally, depending on your WSL/WSLg version, | |
there might be issues with your wayland/x11 session which can impact your SSH experience. | |
refer to the following issue for a solution: | |
https://github.com/microsoft/wslg/issues/1032#issuecomment-2310369848 | |
' | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment