尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

WSL2 Ubuntu 24.04 配置局域网与远程 SSH 访问的完整实践

WSL2 Ubuntu 24.04 配置局域网与远程 SSH 访问的完整实践 我前后配置过不少 Windows 开发机上的 WSL2每次换新机器、装新版 Ubuntu 时SSH 远程登录这块总有几个坑要重新踩一遍。尤其到了 WSL2 Ubuntu 24.04OpenSSH 的配置路径、默认权限策略都有了一些细节变化网上很多教程还停留在旧版写法照着抄很容易连不上。这篇就把我这段时间从“WSL2 内网能通”到“局域网/远程都能稳定 SSH”的完整配置过程、踩坑记录和最终可复现的脚本整理出来给同样在 WSL2 里跑 SSH 服务的你一个可以直接抄作业的版本。1. 在 WSL2 Ubuntu 里装 SSH 服务前先把网络模型看清楚1.1 WSL2 和传统 Linux 服务器的本质区别很多人习惯了一套动作装系统、配 IP、开 sshd、端口暴露然后就等着外网访问。这套流程放在 WSL2 Ubuntu 24.04 上大概率会直接失败因为 WSL2 的网络模型跟传统 Linux 服务器根本不是一回事。WSL2 现在的实现是基于 Hyper-V 的轻量级虚拟机。你这个 Ubuntu 24.04 不是一个挂在物理网卡上的实体系统它跑在一个虚拟交换机后面由 Windows 主机分配一个独立的内网 IP通常是172.x.x.x段。外面的人看到的“这台机器”是 Windows 宿主机的 IP而不是 WSL 里的 Linux IP。这是第一个关键认知别人连接的是 Windows 主机你真正想访问的是 Windows 后面的 WSL 虚拟机。中间天然隔着一层 NAT。用大白话讲WSL2 像是你家小区里的一栋楼Windows 是小区大门别人想找住在楼里的你得先让门卫登记、再让楼栋的门禁放行。你要做的就是三件事楼里开好门SSH 服务、小区门口挂好门牌端口映射、门卫同意放人防火墙规则。1.2 跟 WSL1 的差异为什么会导致配置思路完全不同再补充一个历史包袱WSL1 不是虚拟机它是在 Windows 内核上做了一层 Linux 系统调用转换网络直接共享 Windows 的 IP。那时你只要在 WSL1 里启动 sshd局域网里别人就能直接连 Windows 的 IP端口完全通透。WSL2 不可能这么干因为它是虚拟机。但微软也做了一件好事从 Windows 访问 WSL 里的服务默认有 localhost 转发。也就是说你在 Windows 本机浏览器打开http://localhost:8080能直接访问到 WSL 里跑的 web 服务。这就是为什么很多人装完 WSL2 后以为局域网访问也没问题——其实那是错觉localhost 转发只对宿主机生效不对外网网卡生效。所以这篇文章真正要解决的就是把“宿主机 localhost 能通”升级成“局域网其他机器也能通”顺便再把开机自启、密钥登录、远程开发工具的适配一起处理掉。1.3 哪些场景真的值得把 WSL2 当 SSH 服务器用有人会问既然这么绕为什么不直接装个真正的 Linux 服务器或者用 VMware/VirtualBox 开一个我自己的经验是WSL2 当 SSH 服务器有它不可替代的价值日常开发机的随身环境你不需要额外维护一台常开的虚拟机WSL2 资源占用低启动快跟 Windows 文件互通完全可以当开发服务器用。局域网内临时服务比如给同事提供一个 Git 仓库、跑一个内网测试脚本、临时开个 SFTP 传文件一台跑着 WSL2 的 Windows 机器就是现成的服务器。云端 Windows 服务器的桥接如果你的线上环境是 Windows Server WSL远程 SSH 进去操作 Linux 工具链非常顺手比远程桌面轻量得多。但也有不适合的场景如果你的服务需要 7×24 小时对外提供、需要固定 IP、需要 root 权限完全隔离那还是老老实实上独立 Linux 服务器。WSL2 的宿主机一关机服务就没了这是从设计上就改变不了的。2. 安装、配置、自检先在 WSL2 内部把 SSH 服务跑通2.1 安装 openssh-server 和创建登录用户进入你的 WSL2 Ubuntu 24.04第一步先更新软件源sudo apt update sudo apt upgrade -y然后安装 SSH 服务端sudo apt install -y openssh-server装完先别急着启动。Ubuntu 24.04 的 sshd 默认配置里有些项目需要确认sudo systemctl status ssh如果提示Unit ssh.service could not be found不用慌这是因为 WSL2 里的 systemd 默认不是开启的。你直接手动启动服务就行sudo service ssh start这里插一个很多人会忽略的事WSL2 现在虽然支持 systemd但 Ubuntu 24.04 默认新装的 WSL 实例不一定启用了它。我自己的验证结果是Win11 上如果创建 WSL 实例时是全新安装的 24.04/etc/wsl.conf里[boot] systemdtrue通常已经配好但如果你是从旧版升级上来的很可能没有。判断方法很简单cat /etc/wsl.conf如果看到systemdtrue就可以直接用systemctl。没有的话建议加上这段配置然后wsl --shutdown再重进[boot] systemdtrue启用 systemd 的好处是服务管理更标准之后配置开机自启也更省心。再检查一下登录用户。WSL 安装时创建的默认用户一般带 sudo 权限直接用它登录 SSH 完全没有问题。但如果你不清楚自己的用户密码WSL 的默认机制是用户密码可能和你输入的 UNIX 密码一致也可能是空密码状态我建议直接重设sudo passwd yourusername这一步很关键。很多人 SSH 连不上不是因为 sshd 没起来而是因为用户在 Linux 侧根本没有一个可用的登录密码。2.2 sshd_config 里必须调整的几个配置项打开配置文件sudo vim /etc/ssh/sshd_config我推荐至少确认这几个项Port 22 PermitRootLogin no PubkeyAuthentication yes PasswordAuthentication yes AllowUsers yourusername逐条说明我为什么这么设Port 22WSL2 内部的 sshd 端口默认 22 就行真正的端口变化可以放在 Windows 侧的转发环节去做。这里保持默认最简单后面你会看到我把外网端口映射成了 2222两层端口分离更安全。PermitRootLogin no禁止 root 直接 SSH 登录。WSL2 里 root 权限太好拿了wsl -u root直接能进如果你又开了密码登录等于给局域网里所有人留了一把明晃晃的钥匙。用普通用户 sudo 就够。PasswordAuthentication yes先用密码把链路打通后面再切密钥登录。一步到位直接上密钥对新手不友好出了问题很难区分是密码问题还是密钥格式问题。所以我建议先 yes跑通了再改 no。AllowUsers白名单。WSL2 里默认可能只有你一个用户但养成写白名单的习惯能避免以后装了别的服务创建了多余用户、又不小心开放了登录权限的问题。改完配置后先做一次语法检查sudo sshd -tsshd -t只检查配置语法不会启动服务它是我每次改完配置必跑的一步。有报错会直接告诉你哪一行有问题省得反复重启服务试错。然后重启服务sudo service ssh restart2.3 自检链路本地先证明 sshd 真的在工作服务启动后我习惯先在 WSL 内部自测一遍ssh -p 22 yourusernamelocalhost如果第一次登录提示确认 host key输yes然后输入密码能进入 shell 就说明 sshd 本身没问题。再检查一下监听地址sudo ss -tlnp | grep sshd正常会看到类似LISTEN 0 128 0.0.0.0:22 0.0.0.0:* users:((sshd,pidxxx,fd3))注意0.0.0.0:22这行它代表 sshd 监听在所有网络接口上。如果你在这里看到127.0.0.1:22说明你的ListenAddress配成了仅本机回环那后面一切转发都白搭WSL 之外根本连不进来。到这里WSL2 内部这一环就算闭环了。接下来才进入真正容易卡壳的环节让 Windows 外面的设备找到你。3. 让局域网其他机器连上你端口转发与防火墙的正确配置3.1 为什么你在 Windows 上直接连 WSL 没问题别人就连不上前面提到过WSL2 有个宿主机 localhost 转发机制。你在 Windows 的 CMD 里执行ssh -p 22 yourusernamelocalhost能通是因为 Windows 检测到了 WSL 的 sshd 在监听 22 端口自动帮你把 localhost 请求转进去了。这个转发是单向的、仅限本机的。但局域网里另一台电脑访问你它的请求到达的是你的 Windows 网卡 IP而 Windows 不会像处理 localhost 那样自动帮它转发到 WSL。所以你需要手动建立一个转发规则告诉 Windows当收到发往某端口的外部连接请把它转交给 WSL 里的那个 IP 的 22 端口。3.2 用 netsh interface portproxy 做端口映射步骤一查 WSL 当前的 IP。在 WSL Ubuntu 里执行hostname -I比如你得到172.20.148.214记下这个地址。步骤二在 Windows 上用管理员权限打开 PowerShell 或 CMD添加端口转发规则netsh interface portproxy add v4tov4 listenaddress0.0.0.0 listenport2222 connectaddress172.20.148.214 connectport22参数解释一下listenaddress0.0.0.0表示监听 Windows 主机所有网络接口上的 2222 端口。如果只想让局域网访问不想暴露在公网也可以写成 Windows 的内网 IP比如192.168.1.100。但写0.0.0.0更通用配合防火墙规则比细抠监听地址更可控。listenport2222外部访问时使用的端口我故意不用 22。原因是 22 端口太容易被各类扫描器盯上而且很多 Windows 环境里 22 端口可能已被其他软件占用。用高位随机端口能减少大部分无差别扫描。connectaddress172.20.148.214WSL2 里 sshd 实际监听的 IP。connectport22WSL2 里 sshd 实际监听的端口。步骤三验证转发规则是否生效netsh interface portproxy show all你会看到类似Listen on ipv4: Connect to ipv4: Address Port Address Port -------------- ---------- -------------- ---------- 0.0.0.0 2222 172.20.148.214 22然后在 Windows 本机测试外部入口Test-NetConnection localhost -Port 2222如果显示TcpTestSucceeded : True映射关系基本没问题。3.3 防火墙规则怎么加才不会被自己坑到端口映射配好了但如果你没有在 Windows 防火墙里放行 2222 端口局域网其他机器连接时会被直接丢弃表现为“连接超时”。我加规则的习惯是只放行 TCP 入站并限定到端口netsh advfirewall firewall add rule nameWSL2 SSH Forward dirin actionallow protocolTCP localport2222这里说一个容易被忽略的细节如果你只想让局域网内网段访问而不是让整个公网都扫到你的端口我建议这条防火墙规则的作用域限制一下。可以在防火墙高级设置里把规则范围限制为本地子网或者干脆用 PowerShell 加 remote address 参数netsh advfirewall firewall add rule nameWSL2 SSH Forward dirin actionallow protocolTCP localport2222 remoteip192.168.1.0/24根据自己的网段去写别图省事完全放行Any。3.4 从另一台机器实测连上才算数在局域网另一台电脑上执行ssh -p 2222 yourusername你的Windows主机IP第一次连接同样会有 host key 确认提示。输入密码后如果进入了 Ubuntu shell恭喜链路已经打通。这一步如果失败常见的表现有两种我分别说连接超时Connection timed out请求根本没到达 sshd大概率是防火墙没放行或者端口转发规则没生效。先检查netsh interface portproxy show all是否还在再看 Windows 防火墙里规则是否启用。连接拒绝Connection refused请求到了但 Windows 转不进去或者目标端口不对。检查connectaddress是不是 WSL2 当前最新的 IP以及 WSL 里 sshd 是否还在监听。我第一次配置时就被坑过一次WSL 重启后 IP 变了portproxy 还指向旧 IP结果 Windows 本机测试localhost:2222都失败。这个问题的根治办法就是下一章要聊的自动刷新机制。4. 重启之后不手忙脚乱把自动启动和 IP 刷新脚本配好4.1 WSL2 的 IP 为什么会变以及怎么应对WSL2 的虚拟网卡是动态的。每次wsl --shutdown后重新进入或者 Windows 重启、物理网卡断线重连WSL 的 IP 都可能变化。这一点和真实 Linux 服务器完全不同也是 WSL2 做 SSH 服务最烦人的地方。我的应对思路是不要把 IP 写死而是靠脚本每次启动时自动获取最新 IP并用它刷新 portproxy 规则。4.2 一个 PowerShell 脚本启动服务 刷新端口映射在 Windows 上建一个脚本文件比如C:\WSL2AutoSSH\setup-wsl-ssh.ps1内容如下$wslSshPort 22 $listenPort 2222 # 1. 确保 WSL2 里的 ssh 服务启动 wsl -d Ubuntu-24.04 -u root -- sh -c service ssh start # 2. 获取 WSL2 当前的 IP $wslIp (wsl -d Ubuntu-24.04 -u root -- hostname -I).trim().split( )[0] Write-Host WSL2 IP: $wslIp # 3. 删除旧的 portproxy 规则全部删再重建避免残留脏数据 netsh interface portproxy reset Start-Sleep -Seconds 1 # 4. 添加新的转发规则 netsh interface portproxy add v4tov4 listenaddress0.0.0.0 listenport$listenPort connectaddress$wslIp connectport$wslSshPort # 5. 确保防火墙规则存在幂等追加已存在会报错但没关系 netsh advfirewall firewall add rule nameWSL2 SSH Forward dirin actionallow protocolTCP localport$listenPort | Out-Null Write-Host Done. SSH listening on port $listenPort - $wslIp : $wslSshPort这里解释几个细节wsl -d Ubuntu-24.04 -u root -- sh -c service ssh start是让 WSL 里的 root 用户去执行命令即使你当前没有登录 WSL 也能把服务拉起来。-d Ubuntu-24.04指定发行版名称用wsl -l -v可以查到准确的名称。netsh interface portproxy reset会清掉所有现有转发规则。如果同一台机器上还有其他 portproxy 用途的话建议不要用 reset而是先按旧规则逐条删除。我这台机器是专用开发机reset 最干净。脚本里加了输出方便之后排查时看 WSL IP 到底是多少。4.3 用任务计划程序实现开机自启把这个 PowerShell 脚本挂到 Windows 任务计划程序让它开机后自动跑。步骤是Win R 输入taskschd.msc打开任务计划程序。右侧“创建任务”。常规选项卡里填写名称比如WSL2 Setup SSH勾选“使用最高权限运行”。触发器选项卡新增一个触发器“开始任务”选“登录时”。如果想让它在开机后未登录状态也运行就选“启动时”但需要把脚本放入系统账户上下文我这里为了方便调试选了登录时。操作选项卡新增操作程序填powershell.exe参数填-ExecutionPolicy Bypass -File C:\WSL2AutoSSH\setup-wsl-ssh.ps1。条件选项卡取消勾选“只有在计算机使用交流电源时才启动此任务”。笔记本用户尤其要注意这个不然插着电才能自启。确定保存会要求输入 Windows 登录密码。保存后可以先手动右键“运行”一次看脚本是否能正常执行。4.4 WSL 默认用户的另一个自启方案如果你的场景是“打开 WSL 终端就自动启动 SSH”而不要求 Windows 一开机就跑那更轻量的做法是在 WSL 的~/.bashrc里加if ! pgrep -x sshd /dev/null; then sudo service ssh start fi这个方案适合你把 WSL 当开发终端用、偶尔需要被连的场景省掉了 Windows 侧任务计划程序的配置。但缺点也很明显如果没有人打开 WSLsshd 就不会启动外部连接依然失败。所以严格来说Windows 计划任务 PowerShell 脚本才是自动化的正解bashrc 里那套只能当辅助。5. 进阶使用密钥登录、VS Code Remote-SSH 和 SFTP 传输5.1 从密码登录切换到密钥登录密码登录跑通后我强烈建议尽快切换到密钥认证。这既是为了安全——WSL2 暴露到局域网后22/2222 端口会不断收到扫描密码登录的暴力破解风险是真实存在的——也能让之后的 VS Code Remote-SSH、脚本访问省去反复输密码的麻烦。在本地机器Windows 或 Mac上生成密钥ssh-keygen -t ed25519 -C wsl2-ubuntu-24.04一路回车即可除非你想给私钥设置 passphrase。生成的公钥在~/.ssh/id_ed25519.pub。然后把公钥复制到 WSL 里。最省事的是用ssh-copy-id但 Windows 自带 OpenSSH 客户端不一定有这个命令所以我一般手动拷# 在本地机器上 type $env:USERPROFILE\.ssh\id_ed25519.pub | ssh -p 2222 yourusernameWindows主机IP mkdir -p ~/.ssh cat ~/.ssh/authorized_keys chmod 700 ~/.ssh chmod 600 ~/.ssh/authorized_keys命令稍长但一次搞定创建目录、追加公钥、调整权限三个动作。重点说下权限。authorized_keys 或整个 .ssh 目录权限过宽时sshd 会直接拒绝使用这个文件这是 Linux 下的安全策略。目录必须 700文件必须 600别多也别少。完成后先测试ssh -p 2222 yourusernameWindows主机IP如果不再提示输入密码说明密钥登录生效。此时再回 WSL 修改 sshd_configPasswordAuthentication no把密码登录关掉重启 sshd。以后这台机器就只能通过密钥访问爆破风险基本归零。5.2 VS Code Remote-SSH 连接 WSL2 的两个细节VS Code 的 Remote-SSH 扩展是很多人用 WSL2 做远程开发的核心工具。理论上密钥配好后它自然就能连上只要在~/.ssh/config里加一条Host wsl2-dev HostName 192.168.1.100 Port 2222 User yourusername IdentityFile ~/.ssh/id_ed25519然后 VS Code 远程资源管理器里点连接、选wsl2-dev就行。我自己用下来有两个细节值得提第一VS Code 连接的“主机”是 Windows但打开目录要选 WSL 里的 Linux 路径。连接成功后 VS Code 会弹出选择文件夹的界面如果你直接进C:\下的目录就是在用 Remote-SSH 编辑 Windows 文件这没问题但体验和直接本地编辑一样想真正发挥 WSL 环境应该打开/home/yourusername/这类路径。第二如果连接很慢检查一下 WSL2 里是不是有其他服务占住了 CPU。Remote-SSH 第一次连接需要安装 VS Code Server会花一点时间但如果每次都卡在“Setting up SSH Host”超过一分钟多半是 WSL 里网络或磁盘有问题。这时可以直连看输出ssh -v -p 2222 yourusernameWindows主机IP用 verbose 输出确认连接路径上没有反复重试的迹象。5.3 SFTP 传文件时最容易忽略的权限问题远程开发免不了传文件SFTP 顺手就能用。WinSCP、FileZilla、或者 VS Code 的 SFTP 插件连接参数和 SSH 一致主机填 Windows IP端口 2222用户名和密码/密钥照旧。这里我要提醒一个坑WSL2 的文件系统和 Windows 的文件系统是两套。你用 SFTP 登录后看到的/home/yourusername是 WSL 的虚拟磁盘访问速度正常但如果你切到/mnt/c/下的 Windows 文件目录会明显感觉到慢因为那要走 9P 协议转换。传大文件时尤其明显。我实际传输时还遇到过一个更隐蔽的问题从 Windows 拖文件到 WSL 目录经常遇到“Permission denied”。原因是 Windows 侧拷贝过来时文件权限可能继承出错。经验解法是传完后在 WSL 里统一修正sudo chown -R yourusername:yourusername /home/yourusername/target_dir如果只是临时传文件scp也够用scp -P 2222 ./local-file yourusernameWindows主机IP:/home/yourusername/注意scp用大写-P指定端口小写-p是保留文件时间属性这俩大写小写拼错是新手常碰到的报错来源。6. 高频故障排查认证失败、连不上、服务悄悄退出6.1 “Permission denied, please try again” 到底是哪里错了这个报错看起来像密码输错但其实一半以上的情况不是密码问题。我按频率排一下我遇到过的原因sshd_config 里PasswordAuthentication no已经关了但你还想用密码登录。此时报错依然是 Permission denied不会提示你“密码认证已禁用”。解决办法是确认配置或者改用密钥。WSL 用户没有设置密码。很多人装完 WSL 从没执行过passwd系统里用户密码是锁定状态SSH 密码登录自然失败。用wsl -u root进入 root执行passwd yourusername重设。AllowUsers 里没写这个用户。sshd 会静默拒绝同样表现为 Permission denied。把用户名加进 AllowUsers重启服务。公钥文件的权限不对。.ssh 目录或 authorized_keys 权限过宽sshd 直接略过密钥认证并继续要求密码。修正命令我在上面写过目录 700、文件 600。排查这类问题最直接的方法是看日志。在 WSL2 里执行sudo tail -f /var/log/auth.log然后另开一个终端做连接尝试日志里会明确告诉你“Failed password for ...”“User ... from ... not allowed because listed in AllowUsers”“Authentication refused: bad ownership or modes”之类的具体原因。我几乎每次都靠这个方法定位比自己瞎猜配置快得多。6.2 连接超时 vs 连接拒绝分别该查哪里这是我最想让读者分清楚的一组概念因为它们指向完全不同的故障层现象含义优先排查项Connection timed out包没到达目标或被丢弃Windows 防火墙规则、物理网络连通性、端口监听缺失Connection refused包到了但没人接portproxy 规则是否指向正确的 WSL IP、sshd 是否在运行Connection closed by remote host连接建立后被远端主动断开sshd 配置、认证失败次数过多、AllowUsers 设置如果外部连接超时先在 Windows 本机测Test-NetConnection localhost -Port 2222本机都通说明是防火墙或路由问题本机不通说明 portproxy 或 WSL 侧 sshd 已经失效。一层层缩小范围很快能找到断点。6.3 WSL2 休眠/空闲后服务失联的坑我自己遇到过最神出鬼没的故障是WSL 用着用着外部 SSH 突然连不上了但 Windows 本机localhost:2222也失败。查了半天发现WSL2 在空闲一段时间后Windows 会自动把整个虚拟机的内存回收sshd 进程还在但网络栈已经处于一种半冻结状态。这时候最简单的恢复办法就是主动唤醒wsl --shutdown然后重新进入 WSL再跑一次自启脚本。如果希望减少这种“突然失联”的频率有两个调整方向在 WSL 里保持一个常驻进程让它不至于彻底空闲。比如我习惯在 tmux 里挂着htop之类的东西算是个偷懒办法。调整 Windows 的 WSL 空闲回收策略。新版 Windows 有相关全局设置Windows Terminal 文档里有说明但这个因版本而异我不展开细说。另一个相关的经典坑是Windows 重启后端口映射规则还在但 WSL IP 变了你不刷脚本的话netsh就指向一个幽灵地址表现为怎么连都不通。所以我的 4.2 节那个自动刷脚本是必须的不是可选项。6.4 “REMOTE HOST IDENTIFICATION HAS CHANGED” 的处理当你重装 WSL、重建发行版或者多次清理 WSL 网络配置后本机的 known_hosts 里还留着旧的 host key 指纹连接时会爆出这个警告并拒绝继续。处理方法很直接找到本机~/.ssh/known_hosts里对应 IP 和端口的那一行删除即可。如果嫌麻烦直接整个删掉 known_hosts 也让报错消失代价是以后首次连接都要重新确认 host key。有人会问能不能固定 WSL 的 host key 让它不变化可以只要把/etc/ssh/ssh_host_*密钥文件备份下来重建后放回去就行。不过考虑到 WSL2 本质是开发环境不是生产服务器我一般懒得做这件事删掉旧指纹重新信任就行。六章内容写下来主线其实就一条WSL2 的 SSH 服务本身不难难的是把 Windows 和 Linux 之间的这层 NAT 关系理顺。内部启动、端口转发、防火墙放行、自动刷新 IP四件事环环相扣漏了任何一环外部都连不上。我个人实测下来最稳定、也最推荐的做法就是配好 4.2 节那个 PowerShell 自启脚本然后把密钥登录一开、密码登录一关。这套方案跑了几周局域网和远程访问都稳得很唯一的日常维护就是把脚本和备份的 WSL 密钥文件一起放进自己的 dotfiles 仓库换新机器时几分钟就能原地复现一套一模一样的 SSH 环境。如果你也在 WSL2 里折腾 SSH照着上面的顺序走一遍应该能少走不少弯路。
返回列表