近几年 Windows 上做开发绕不开 WSL2,尤其是 Ubuntu 24.04 更新之后,很多朋友把编译工具链、数据库、Python 环境都塞进了 WSL2 里。但不少人折腾完系统,到了“远程登录”这一步就卡住了——SSH 要么连不上、要么只能在本机敲命令、要么每次重启都得重新查 IP。这篇文章就把 WSL2 Ubuntu 24.04 开启 SSH 远程登录的完整流程捋一遍,从环境准备、sshd 配置、密钥免密登录,到局域网访问和常见故障排查,一次说清楚。适合正在用 Windows 做日常办公、同时需要进入 Linux 环境跑服务的开发者,也适合刚装好 Ubuntu 24.04 还没搞定远程入口的同学。
1. 为什么要在 WSL2 里单独折腾一套 SSH 服务
1.1 默认体验和“真远程”之间的差距
装了 WSL2 Ubuntu 24.04 后,你确实能在 Windows Terminal 里直接敲wsl进入 Linux 环境,日常用完全没问题。但仔细想想,这套默认机制有几个绕不开的痛点:
第一,终端会话绑定在 Windows 的进程上,如果想从局域网内另一台电脑(比如办公室的台式机、家里的笔记本)连进来,根本不可能。第二,像 VS Code Remote-SSH、JetBrains Gateway 这类工具,默认优先走 SSH 协议,哪怕你只在本机操作,也需要有一个标准的 sshd 服务在跑。第三,文件传输、自动化脚本、CI/CD 构建等场景,SSH 是通用语言,几乎不会有人为 WSL 单独写一套适配接口。
所以,“开启 SSH”不是为了装样子,而是为了把 WSL2 变成一个可以被标准工具接入的开发环境。
1.2 SSH 方案和 Windows 自带远程功能的取舍
可能有人会问,Windows 本身有 OpenSSH Server,也可以用远程桌面,为什么非得在 WSL2 里装一套?我的答案是:你的开发环境在哪个系统里,就在哪里开服务。代码、编译产物、数据库都在 Ubuntu 里,直接在 Ubuntu 里监听 22 端口,路径最短、权限模型最干净、和 Linux 生态的运维习惯完全一致。
远程桌面那种方案不适合纯命令行开发,带图形界面的方式资源开销也大。SSH 则天然适配命令行 + 密钥认证 + 端口转发的组合,轻量、可控、易自动化,这也是绝大多数开发者最终选择它的原因。
2. 前期准备:WSL2 和 Ubuntu 24.04 的一次性到位
2.1 确认你的 WSL 版本和系统状态
现在的 Windows 10/11 基本都支持 WSL2,安装方式也比早期简单得多。打开管理员权限的 PowerShell 或 CMD,执行:
wsl --install这条命令会装好 WSL2 平台、虚拟机相关组件,并默认安装 Ubuntu。如果已经装过旧版,建议先升级:
wsl --update查看当前发行版和版本号:
wsl -l -v输出里会看到类似:
NAME STATE VERSION * Ubuntu-24.04 Running 2这里关键是 VERSION 必须为 2。如果显示 1,说明用的老版本,需要转换:
wsl --set-version Ubuntu-24.04 2这一步不复杂,但很多人会漏。VERSION 为 1 时网络行为、文件系统性能和 systemd 支持都会有差异,后面配置 SSH 容易踩坑。
2.2 启用 systemd,否则 SSH 服务管理会别扭
Ubuntu 24.04 在 WSL2 里默认是否启用 systemd,取决于你的/etc/wsl.conf。如果没启用,你会发现systemctl命令报错,SSH 服务只能靠service ssh start来启停,开机自启、进程守护这些基本指望不上。
建议一进入 Ubuntu 就检查并配置好。编辑/etc/wsl.conf:
[boot] systemd=true保存后,在 Windows 侧执行:
wsl --terminate Ubuntu-24.04重新进入 WSL2,然后确认:
ps -p 1 -o comm=如果输出是systemd,就说明成功。这一步属于“磨刀不误砍柴工”,后续所有服务的托管都会省心很多。
2.3 理解 WSL2 的网络模式,这是 SSH 能不能连通的分水岭
WSL2 默认走的是 NAT 模式的虚拟网络,不像桥接网络那样直接占用局域网 IP。直白点说,WSL2 内部有一个网卡,IP 一般类似172.x.x.x,Windows 主机通过 Hyper-V 虚拟交换机和它通信。
这意味着两件事:
- 从 Windows 本机访问 WSL2 内服务,通常可以用
localhost或127.0.0.1,这是 WSL 的 localhost 转发机制在起作用。 - 从局域网其他机器访问 WSL2,不能直接用 WSL 的 IP,必须在 Windows 上做端口转发,并且放行防火墙规则。
这两条是后面所有连接方案的基础,先记住,具体怎么操作后面的章节会展开。
3. 安装并配置 OpenSSH 服务端
3.1 安装 openssh-server 并处理依赖
进入 Ubuntu 24.04 后,先更新软件源和系统包:
sudo apt update && sudo apt upgrade -y然后安装 SSH 服务端:
sudo apt install openssh-server -y安装完成后,先别急着启动,确认一下版本和基本状态:
sshd -V systemctl status ssh正常情况下,openssh-server 装完会自动创建一个sshd服务,但默认可能没有启动,也可能没设置开机自启,需要手动处理。
3.2 sshd_config 里值得改的关键参数
SSH 服务的主配置在/etc/ssh/sshd_config。Ubuntu 24.04 的默认配置安全性比较高,但直接拿来用于 WSL 远程登录,有几个点需要调整。我建议至少过一遍以下参数:
sudo nano /etc/ssh/sshd_config重点检查这几项:
Port 22 PermitRootLogin no PubkeyAuthentication yes PasswordAuthentication yes AllowUsers yourname说下我的经验:
Port 22:如果本机 22 端口没被占,保持默认即可。如果你担心端口冲突或安全扫描,可以改成高位端口,比如2222,但所有客户端连接时都要带上-p 2222。PermitRootLogin no:强烈建议禁止 root 直接登录,日常操作用一个带 sudo 权限的普通用户就够了。允许 root 远程登录等于把系统钥匙挂门口。PasswordAuthentication:考虑使用免密登录时,先保留yes方便调试,等密钥配置好了再关掉更安全。AllowUsers:如果你创建了多个用户,明确指定允许登录的用户名,可以减少无谓的暴力尝试。
改完配置后,用下面的命令检查语法是否有问题:
sudo sshd -t没有输出即表示配置正确。如果报错,它会提示你具体是哪一行有问题,改完再测。
3.3 启动服务并设置开机自启
Ubuntu 24.04 在 WSL2 里启用 systemd 后,可以用 systemctl 管理:
sudo systemctl enable ssh sudo systemctl start ssh查看监听状态:
sudo ss -tlnp | grep ssh看到类似这样的输出就说明 SSH 已经在监听:
LISTEN 0 4096 0.0.0.0:22 0.0.0.0:* users:(("sshd",pid=123,fd=3))这里我要提醒一个容易忽视的点:WSL2 每次冷启动时,systemd 会随发行版一起起来,所以理论上 sshd 也会自动拉起来。但如果你在 Windows 命令行里用wsl --terminate之后再进入,有时候 systemd 服务的启动顺序会有延迟,登录前先systemctl status ssh看一眼,别急着连。
4. 从 Windows 本机登录 WSL2 的几种姿势
4.1 利用 localhost 回环,最简单直接的连接方式
WSL2 从较新版本开始支持 localhost 回环转发。也就是说,如果 sshd 在 WSL2 的 22 端口监听,你在 Windows 的 PowerShell 里直接执行:
ssh yourname@localhost就能进入 Ubuntu 环境,首次连接会提示确认指纹,输入一次密码,之后就可以正常使用。
这种方式对于本机开发场景已经够用,不需要记录动态变化的 IP,不需要配置端口转发,也基本不会遇到防火墙拦截。
但注意,这种方式只对 Windows 本机有效。如果你在另一台局域网电脑上执行ssh yourname@windows-host-ip,大概率会失败,因为 Windows 不会自动把 22 端口透传给 WSL2。
4.2 使用 WSL2 的真实 IP 进行连接
第二个方式是找到 WSL2 当前的 IP,然后从 Windows 本机连接:
wsl hostname -I或者进入 Ubuntu 后:
ip addr show eth0 | grep inet拿到类似172.24.23.10的地址后,在 Windows 上执行:
ssh yourname@172.24.23.10一样能通。这里想说明一个细节:WSL2 的 IP 是动态分配的,每次重启 Windows 或重启 WSL2 都可能变化。如果你依赖这个 IP 远程连接,建议后面配置端口转发脚本时把 IP 联动更新,否则一天到晚改地址会非常恼火。
个人建议:本地调试优先用localhost,需要局域网共享时才去做端口转发和动态更新。
4.3 把 SSH 集成到 Windows Terminal 和 VS Code 里
如果你经常需要进入 WSL,手动ssh yourname@localhost虽然不麻烦,但每次都敲一遍口令确实没必要。Windows Terminal 支持新增配置文件,可以直接调 SSH:
- 打开 Windows Terminal 设置,选择“添加新配置文件”。
- 命令行填
ssh yourname@localhost,名称填WSL SSH。 - 图标、配色按自己喜欢设置。
VS Code 方面,有两种路径:
- 如果代码和编译环境在 WSL2 里,最推荐直接用“WSL 扩展”,它不需要 SSH,VS Code 会通过 WSL 的桥接机制直接打开远程环境。
- 如果所有代码要通过 SSH 协议管理,也可以在 VS Code 里安装 Remote-SSH 插件,配置
~/.ssh/config,增加:
Host wsl-ubuntu HostName localhost User yourname Port 22然后通过 Remote-SSH 打开。两种方式体验都很好,前者更适合纯 WSL 场景,后者更适合你把 WSL 当服务器对待的统一管理场景。
5. 免密登录:配置 SSH 密钥认证
5.1 为什么一定要上密钥
密码登录不是不行,但有两个问题:一是每次连接都要输入,很影响自动化脚本和开发效率;二是密码认证容易被暴力尝试,长时间暴露端口后,日志里能看到大量扫描记录。密钥登录改用公钥加密验证,私钥留在本地,安全性高一个档次,而且连上之后直接进 shell,体验非常顺滑。
5.2 生成密钥对并推送公钥
在 Windows 的 PowerShell 里生成密钥:
ssh-keygen -t ed25519 -C "yourname@windows"一路回车即可,默认生成在C:\Users\你的用户名\.ssh\下。如果你需要更严谨,可以为私钥设置一个 passphrase,防止私钥文件被盗后直接被滥用。
把公钥传到 Ubuntu,方法一:
type $env:USERPROFILE\.ssh\id_ed25519.pub | ssh yourname@localhost "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys"方法二,如果 Ubuntu 里装了ssh-copy-id,更省事:
ssh-copy-id -i ~/.ssh/id_ed25519.pub yourname@localhost完成后,在 Ubuntu 里确认权限是否安全:
chmod 700 ~/.ssh chmod 600 ~/.ssh/authorized_keys权限不对是密钥登录最常见的拦路虎,后面排查部分会再讲。
5.3 验证免密登录并加固配置
从 Windows 重新登录一次,如果不再提示密码,说明密钥生效:
ssh yourname@localhost确认稳定后,建议修改/etc/ssh/sshd_config关闭密码认证:
PasswordAuthentication no重启 SSH 服务:
sudo systemctl restart ssh之后再想登录,就只能靠密钥。这一步做完,SSH 的可用性和安全性就都到位了。如果你担心万一私钥丢了进不去系统,可以先在 Windows 上保留一个正常登录的用户,或者断网前反复测试确认密钥可用,再切PasswordAuthentication no。
6. 局域网/外部设备访问 WSL2 的端口转发配置
6.1 使用 netsh 创建端口转发规则
想让办公室另一台电脑访问这台 Windows 上的 WSL2 SSH,需要做两件事:Windows 防火墙放行、端口转发到 WSL 的 IP。
在管理员权限的 PowerShell 里执行:
netsh interface portproxy add v4tov4 listenport=22 listenaddress=0.0.0.0 connectport=22 connectaddress=<WSL2_IP>其中<WSL2_IP>就是wsl hostname -I得到的地址。这一步做完,访问 Windows 机器的 22 端口,流量就会被转发到 WSL2 的 22 端口。
防火墙放行:
netsh advfirewall firewall add rule name="WSL SSH" dir=in action=allow protocol=TCP localport=22从另一台电脑测试:
ssh yourname@<Windows_IP>如果一切正常,就能看到 Ubuntu 的 shell 提示符。
6.2 自动更新转发规则,解决 IP 漂移问题
WSL2 的 IP 会在每次系统重启或wsl --terminate后重新分配,端口转发规则里的 IP 也就失效了。手动改很烦,建议写一个小脚本,放在开机启动或计划任务里。
思路是:先删除旧规则,再获取新的 WSL IP,然后重新添加规则。示例脚本(PowerShell):
$wslIp = (wsl hostname -I).Trim() netsh interface portproxy delete v4tov4 listenport=22 listenaddress=0.0.0.0 netsh interface portproxy add v4tov4 listenport=22 listenaddress=0.0.0.0 connectport=22 connectaddress=$wslIp如果你希望开机自动执行,可以把脚本存为.ps1,注册一个开机自启任务。每次重启后确认端口转发规则:
netsh interface portproxy show all看到的新 IP 应该是当前 WSL2 的实际 IP。
6.3 注意事项:不要直接暴露到公网
端口转发解决了局域网访问问题,但也能把 SSH 暴露到公网。这里必须泼一盆冷水:当你把 Windows 的 22 端口映射到公网时,你面对的是整个互联网的自动扫描和暴力破解。
如果你确实需要从外网连回家里的 WSL,建议:
- 只启用密钥认证,坚决关闭密码认证,把
PasswordAuthentication no固定下来。 - 更换非默认端口,比如
2222,避免被扫描到。 - 限制来源 IP 白名单,比如只允许公司出口 IP 或自己的宽带 IP。
- 做好日志监控,定期看
/var/log/auth.log。
这些操作在多少台机器上都适用,属于 SSH 远程登录的通用安全底线。
7. 通过 SSH 传输文件:scp / sftp 实战用法
7.1 scp 命令的实际操作
SSH 不只能登录 shell,还能直接干文件传输的活。Windows 到 WSL 推文件:
scp D:\backup\data.tar.gz yourname@localhost:/home/yourname/从 WSL 拉文件回 Windows:
scp yourname@localhost:/home/yourname/data.tar.gz D:\backup\注意路径里的盘符,Windows 路径建议用绝对路径,避免权限问题。目标路径写到 Linux 里时,要用 Linux 的路径格式,别写C:\这样的路径。
7.2 用 sftp 做交互式文件管理
如果你需要在本地和远程之间来回翻目录、批量传文件,sftp 更顺手:
sftp yourname@localhost进入后执行:
put D:\local\file.txt /home/yourname/ get /home/yourname/remote.txt D:\local\ ls cd命令风格和 shell 几乎一致,稍微熟悉一下就能上手。很多开发者把 sftp 当成随身 U 盘,配合免密登录后,两边传文件几乎零成本。
7.3 端口变化、路径空格等细节
如果你改了 SSH 端口,scp 和 sftp 都要带-P参数:
scp -P 2222 D:\backup\data.tar.gz yourname@localhost:/home/yourname/路径中包含空格时,Windows 侧最好用双引号整体包住,Linux 侧用单引号。比如:
scp "C:\My Documents\a.txt" yourname@localhost:"/home/yourname/a file.txt"这类细节平时容易忽略,等真正传文件失败时才发现,提前记住能省不少事。
8. 常见故障与排查手记
8.1 Connection refused:服务没起来或端口不对
症状:ssh: connect to host localhost port 22: Connection refused
排查顺序:
# 在 WSL2 里检查 sshd 是否在跑 systemctl status ssh # 检查监听端口 sudo ss -tlnp | grep :22如果是服务没启动,执行sudo systemctl start ssh,并enable开机自启。如果端口不是 22,那就是把你改的配置忘了,检查/etc/ssh/sshd_config里的Port值,以及客户端连接时是否带上-p。
还有一种情况:你启动的是 ssh 而不是 sshd,或者服务名不同,比如ssh.service在 Ubuntu 里通常都存在,但如果改了别名,注意用systemctl list-units | grep ssh确认准确名字。
8.2 Permission denied:密码对但进不去
症状:输对密码,却提示Permission denied, please try again.,或者在密钥登录时报publickey错误。
常见的几个原因:
PasswordAuthentication已改为no,但客户端还在走密码认证。authorized_keys里的公钥和本机私钥不匹配。~/.ssh目录或authorized_keys文件权限不对。
权限修正命令:
chmod 700 ~/.ssh chmod 600 ~/.ssh/authorized_keys如果用了 SELinux(Ubuntu 默认没有),注意上下文问题;但 Ubuntu 上更可能是权限太宽松导致的拒绝。
还有一种情况是AllowUsers把你当前用户挡在外面了,检查一下 sshd_config 里的用户名单。
8.3 Connection timed out:网络不通或防火墙拦截
症状:ssh: connect to host 192.168.x.x port 22: Connection timed out
这说明你的包根本没到达目标。可能性:
- Windows 防火墙没有放行 22 端口。
- 端口转发规则里 IP 已过期。
- 目标机器和当前主机不在同一网段。
- 路由器/交换机层面拦截了端口。
在 Windows 侧看一下规则:
netsh advfirewall firewall show rule name="WSL SSH" netsh interface portproxy show all如果 IP 变了,重新执行端口转发脚本即可。
8.4 sshd 启动失败时如何定位问题
启动失败通常是因为sshd_config写错了。首推这个命令:
sudo sshd -t它会逐行检查配置语法,并给出错误提示。比如:
/etc/ssh/sshd_config line 32: Bad yes/no argument按提示改正后重启。如果只是临时测试,不重启服务也可以:
sudo sshd -d前台调试模式会输出详细日志,适合快速定位。
8.5 WSL 重启后 SSH 又失效了
遇到这种情况,先别急着删配置,按这个顺序理一遍:
- 确认 WSL2 启动了:
wsl -l -v。 - 确认 sshd 服务状态:
systemctl status ssh。 - 确认 IP 是否变化:
wsl hostname -I。 - 确认 Windows 端口转发规则是否还是旧 IP:
netsh interface portproxy show all。 - 测试 localhost 连接:
ssh yourname@localhost。
大多数“重启后失效”的根因,一个是 sshd 没设为开机自启,另一个是端口转发规则没联动更新。前者一条命令systemctl enable ssh解决;后者需要跑一遍动态更新脚本。做过一遍之后,你会发现整套配置其实非常稳定,所谓的问题都是初始化阶段的坑。
9. 我踩坑后的几点实在建议
整套配置下来,我最想强调的不是某个命令,而是“分层验证”的习惯。先验证 WSL2 本身能进、再验证 sshd 监听、再验证本机连接、最后验证局域网连接,每一步都固定一个检查命令,这样出问题时立刻知道卡在哪一层。
第二个建议是尽早切密钥登录。密码登录在 WSL 里用着好像很方便,但一旦你上了端口转发、被扫描工具盯上,日志里那些暴破记录能让你瞬间没安全感。生成密钥、推送公钥、关闭密码认证,这三步加起来不到十分钟,收益却是长期的。
最后,别把这套配置理解成“一次性动作”。WSL2 的 IP 变化、Windows 更新重置防火墙规则、Ubuntu 大版本升级覆盖 sshd_config,都会让服务中途出毛病。把端口转发脚本、sshd 的备份配置、排查命令记在一个笔记里,比任何教程都管用。我自己的习惯是在~/.ssh/config里把所有连接参数固化下来,换电脑、换环境都能快速恢复。这套方案稳定跑了大半年,平时基本感知不到 SSH 的存在,但需要它的每一刻它都在。