拓冰建站拓冰建站
首页 / 资讯中心 / 正文

WSL SSH 无限提示密码?从组件检查到端口转发的完整排障指南

前两天一个朋友发来截图FinalShell 连 Windows Server 上的 WSL Ubuntu密码框弹了一遍又一遍输到系统都提示错误次数过多还是进不去。他说了句很经典的话“我密码肯定没错它就是要我输。”我扫了一眼他的配置用户名填 root密码填 Windows 账户密码WSL 是刚装好的 Ubuntu。这几乎是教科书级别的连环误会。先说清楚标题里的 FinallShell 其实是 FinalShell 的手滑拼写网上一搜一堆人打错。这篇文章就围绕这个场景展开Windows Server 上装了 Linux 子系统WSL想用 FinalShell 远程连进去管理 Linux 环境结果无限提示输入密码。文章适合两类人一类是刚把 WSL 部署到 Windows Server 上、准备用 SSH 客户端接管 Linux 的运维另一类是已经能正常进入 WSL但任何 SSH 客户端连上去都反复要密码的人。下面按我实际排查的顺序来写不绕弯子。1. 先搞清楚“无限提示输入密码”到底是谁在拒绝你1.1 三种容易混在一起的表现做排障第一件事不是改配置而是先把现象分清楚。FinalShell 里反复弹密码框看起来都一样但底层原因差别很大。我见过的情况基本就这三种表现常见原因典型提示密码框弹一下马上消失过两秒又弹出来本质是连接层失败sshd 没监听、端口不通、防火墙拦截Connection refused / No route to host密码框输完提示认证失败然后又弹出来本质是认证层被拒PasswordAuthentication 没开、UsePAM 导致验证异常、用户或密码不对Permission denied, please try again能连上但进去之后不是 Linux 环境甚至显示 cmd 或 PowerShell再或者提示“该账户已锁定”22 端口被 Windows 自带 OpenSSH 占用你连的根本不是 WSLShell 显示 Windows 环境第一种和第二种最容易混淆。FinalShell 在连接失败时默认会重新弹密码框于是给人的感觉就是“无限提示输入密码”。很多人以为是密码不对其实服务端压根没起来。1.2 被 22 端口骗了你连的很可能是 Windows 的 OpenSSH而不是 WSL这里必须单独说一个在 Windows Server 上特别常见的坑。Windows Server 本身可以启用 OpenSSH Server很多运维为了远程管理会在服务器上装这个服务并让它监听 22 端口。等你再去 WSL 里装 openssh-server它也想监听 22结果发现端口被占。这时候你用 FinalShell 去连 127.0.0.1:22实际握手的是 Windows 的 sshd.exe而不是 WSL 里的 sshd。即使你密码输对了Windows OpenSSH 也会把你带到 Windows 的命令行环境或者直接因为用户权限问题拒绝表现就是密码不对、反复弹窗。判断方法很简单netstat -ano | findstr :22看监听 22 的 PID再到任务管理器或命令行确认这个 PID 是不是 sshd.exe。如果进程路径在C:\Windows\System32\OpenSSH下那基本可以确定是 Windows 版 SSH 服务。这个问题我在两台 Server 上都碰到过处理方式也很直接要么把 Windows OpenSSH 的端口改成 2222要么在下面做端口转发时避开 22用另一个宿主机端口把流量转到 WSL 内部。2. 先把 WSL 本身跑通组件检查与发行版健康状态2.1 那个著名的 WSL 组件报错有相当一部分人其实是被卡在更前面还没到 SSH 那一步。在 Windows Server 上第一次运行 WSL 相关命令或某些依赖 WSL 的软件时会弹出一段英文提示此应用程序需要适用于 Linux 的 Windows 子系统可选组件。通过运行 wsl.exe --install 进行安装。这不是什么高深错误就是 Windows Server 默认没有启用 WSL 对应的 Windows 功能。Windows Server 不像 Win10/11 家庭版那样默认带 WSL需要手动启用两个功能一个是“适用于 Linux 的 Windows 子系统”另一个是“虚拟机平台”。用管理员 PowerShell 或 CMD 执行dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart dism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norestart然后重启系统。重启完再执行wsl --update wsl --install -d Ubuntu如果是在 Windows Server 2022 上这个流程基本畅通。Server 2019 会麻烦一点WSL2 的内核更新需要手动下载 msi 包安装不过现在大多数场景应该已经升级到 Server 2022 了。2.2 三条命令确认 WSL 版本和发行版状态装完之后先别急着开 FinalShell用下面三条命令确认 WSL 是健康的wsl -l -v wsl --status wsl -d Ubuntu -- whoamiwsl -l -v看发行版列表和版本号VERSION列显示 2 说明是 WSL2 内核模式。wsl --status看默认发行版和内核版本。最后一条wsl -d Ubuntu -- whoami是直接进发行版执行命令能输出用户名说明 WSL 能正常启动这一步能过滤掉大量“WSL 根本没跑起来”的情况。如果你执行wsl -l -v时看到的发行版状态一直是 Stopped这很正常WSL2 是按需启动的不代表有问题。2.3 内核过旧时先升级Windows Server 的 WSL 还有一个特点系统自带的内核版本往往比 Windows 11 客户端要旧。旧内核可能导致 systemd 不起效、localhost 转发不工作、wsl.conf 部分配置项被忽略这些都会间接导致 SSH 相关问题。所以我的建议是任何 Windows Server 上的 WSL 排障第一步都先跑一次wsl --update把内核升到最新。升级后执行wsl --shutdown让虚拟机彻底重启一次再查状态。这个操作的性价比极高很多时候你排查半天发现是内核太老某个配置项没生效。3. 密码明明对的为什么 sshd 不认WSL 内 SSH 服务端的真实状态3.1 sshd 可能根本没起来WSL 装好 Ubuntu 之后默认是不带 openssh-server 的或者带了但没启动。很多人以为装好了 Ubuntu 就等于有了 SSH 服务这是误解。我在排查“无限提示密码”时第一个动作是进 WSL 里看 sshd 状态sudo service ssh status ps aux | grep sshd ss -tlnp | grep :22如果service ssh status显示未运行或者ss -tlnp里没有任何进程监听 22 端口那问题就很清楚了FinalShell 连过去根本没有服务端应答连接层失败导致反复弹密码框。这时候手动启动一次sudo service ssh start启动过程中如果报错最常见的是Missing privilege separation directory: /run/sshd原因是 WSL 的/run目录是临时文件系统重启后/run/sshd就没了。处理方式sudo mkdir -p /run/sshd sudo service ssh start这个mkdir -p /run/sshd不是冷门知识但也确实不在常规 Linux 服务器教程里因为物理机的/run是自动创建好的只有 WSL 会这么别扭。3.2 两个配置项定生死PasswordAuthentication 与 UsePAM启动成功还不算完接下来才是重头戏。编辑 sshd 配置文件sudo nano /etc/ssh/sshd_config这里有两个配置项直接决定你会不会无限提示密码第一个是PasswordAuthentication。很多 Linux 发行版的默认配置是只允许公钥认证PasswordAuthentication可能是no或者被注释掉。如果被注释OpenSSH 默认值是yes问题不大但有些发行版会显式写成no这时候你输密码输到天荒地老也没用。改法PasswordAuthentication yes第二个是UsePAM。这个才是我认为 WSL 场景下最阴间的坑。默认配置里UsePAM yes在普通 Linux 服务器上没问题但在 WSL 里PAM 和 WSL 的用户认证体系有时会打架导致即使密码正确sshd 在做 PAM 验证时也直接拒绝表现就是密码框一直弹、日志里一堆 Failed password。我在 Ubuntu 20.04 的 WSL 上碰到过一次后来把UsePAM yes改成UsePAM no然后重启 ssh立马就好了。原理很简单UsePAM no让 sshd 自己直接用/etc/shadow验证密码绕开 PAM 那套容易出问题的流程。这个操作在普通 VPS 上一般没人动但在 WSL 里值得优先尝试。3.3 用户、密码和 root 登录策略三个来自 WSL 的误区WSL 的用户密码体系和 Windows 完全是两套。第一次启动 Ubuntu 时会让你创建 UNIX 用户名和密码这个密码只对 Linux 子系统有效和你登录 Windows Server 的密码毫无关系。这就引出一个高频误区FinalShell 里填的是 Windows 密码当然进不去。如果你忘了 WSL 里用户的密码用管理员命令行执行wsl -u root -d Ubuntu进入 root 之后用passwd 用户名重设密码或者直接给 root 设置密码然后用 root 登录。另一个误区是默认用 root 去连。Ubuntu 的 sshd 配置里PermitRootLogin默认值是prohibit-password意思是 root 不允许用密码登录只能用密钥。如果你 FinalShell 里用户名填的是 root密码填得再对也会被拒。排障阶段可以临时改PermitRootLogin yes改完重启 ssh。不过说实话日常使用我建议用普通用户SSH 的安全风险比你在 FinalShell 里多点几下重要得多。3.4 用日志和 sshd -t 定位真实原因如果你改完配置还是不行别急着再改别的先看日志。运行环境是否启用 systemd决定了日志怎么看。启用 systemd 的情况下sudo journalctl -u ssh -f没启用 systemd 的话直接看tail -50 /var/log/auth.log日志里如果出现Failed password for invalid user说明用户名不对如果出现user ... not allowed because account is locked说明账户被锁如果出现Bad protocol 2 host key formats说明 host key 有问题。这些都比“反复输密码”要明确很多。另外别忘了sshd -t这个命令它会检查 sshd_config 语法并打印错误。这个命令不会启动服务只做校验非常安全。改完配置先跑一遍sudo sshd -t有语法错误它会直接告诉你哪一行有问题没有输出才是正常。然后重启 sshsudo service ssh restart4. 让 WSL 的 sshd 常驻systemd 才是 Windows Server 上的正解4.1 为什么 WSL 一停服务就没人管即使你手动把 ssh 启动了FinalShell 也能连上了事情还没完。WSL2 本质是一个轻量虚拟机它的生命周期规则是当最后一个 WSL 相关进程结束之后虚拟机默认过几秒到几十秒就会自动关闭。你关掉 FinalShell、关掉所有 WSL 窗口过一会儿再去连又连不上了。之前手动启动的 sshd 服务在 WSL 虚拟机重启后不会自动拉起除非它被 systemd 托管。WSL 老版本默认不跑 systemd这也是很多人在 Windows Server 上反复折腾 SSH 的根本原因不是配置错了是服务根本不会自启。4.2 开启 systemd 的完整步骤较新的 WSL 版本支持在/etc/wsl.conf里启用 systemd。操作很简单sudo tee /etc/wsl.conf EOF [boot] systemdtrue EOF然后回到 Windows 侧彻底重启 WSLwsl --shutdown再次进入 Ubuntusystemctl is-system-running如果输出running或degraded说明 systemd 正常工作。然后启用 SSH 服务自启动sudo systemctl enable --now ssh确认一下systemctl status ssh只要显示Active: active (running)以后 WSL 虚拟机每次启动sshd 都会自动起来。这一步做完等于把“无限提示输入密码”里最不可控的那部分直接消除了。4.3 不启用 systemd 时的替代启动方案如果你的 WSL 版本太老或者某些发行版开 systemd 有问题可以退而求其次在 Windows 计划任务里开机时执行一条命令启动 WSL 里的 ssh。wsl -d Ubuntu -u root service ssh start但这里有个细节计划任务的“安全选项”一定要设置成“不管用户是否登录都要运行”否则依赖用户会话服务器重启后它可能不执行。这个方案只负责把 sshd 拉起来它不负责保活后面还得靠保活手段。4.4 保活让 WSL 虚拟机别轻易退出systemd 解决了“服务随系统启动”但没解决“WSL 虚拟机空闲一段时间后被回收”。WSL 2 在 Windows 11 22H2 之后支持一个配置项可以在C:\Users\用户名\.wslconfig里设置空闲超时[wsl2] vmIdleTimeout600000单位是毫秒600000就是 10 分钟。设上之后WSL 虚拟机空闲 10 分钟才会关闭比默认的 8 秒宽裕得多。不过这个配置项在 Windows Server 上未必生效要看 WSL 版本支不支持。更暴力但可靠的保活方式是用计划任务或启动项里跑一条wsl -d Ubuntu -- sleep infinity这条命令会让 WSL 里一直有一个sleep infinity进程占着虚拟机永远不会空闲关闭。代价是内存占用会长期存在但对 Windows Server 来说如果你确实需要随时 SSH 进去这个代价值得付。5. FinalShell 该连哪个地址Windows Server 与 WSL 之间的网络最后一公里5.1 WSL 的 IP 在 NAT 后面能不能直连看版本现在 SSH 服务起来了但你还得让 FinalShell 能连到它。这里有个经常被忽略的网络问题。WSL2 默认的网络模式是 NATWSL 里的 Ubuntu 有自己的私有 IP通常是172.x.x.x和 Windows Server 宿主机的 IP 不在同一个网段。如果 WSL 版本较新且支持 localhost 自动转发你从 Windows 上直接访问127.0.0.1:22是有可能通到 WSL 的但前提是 WSL 虚拟机正在运行而且转发功能没被防火墙挡掉。问题在于Windows Server 上的 WSL 版本往往和客户端系统有差异localhost 转发有时并不可靠。更难受的是就算你进入了 WSL 看到 IP 是172.20.x.x也不建议直接在 FinalShell 里填这个地址去连因为从宿主机访问这个 NAT 内部的 IP 不一定通而且 WSL 每次重启 IP 都可能变。5.2 用端口转发把 WSL 的 22 端口暴露到宿主机我在 Windows Server 上的做法是在宿主机上做一个端口转发把宿主机的某个端口比如 2222转发到 WSL 的 22 端口。Windows 自带 netsh 就能做。首先拿到 WSL 当前 IPwsl -d Ubuntu -- hostname -I假设输出是172.20.10.5在管理员 CMD 里执行netsh interface portproxy add v4tov4 listenport2222 listenaddress0.0.0.0 connectport22 connectaddress172.20.10.5这样宿主机上的2222端口就等价于 WSL 里的22端口。你从 FinalShell 连服务器IP:2222流量会被 Windows 转发到 WSL 的 sshd。注意我这里特意选了2222而不是22就是为了避开 Windows OpenSSH 或其它服务占用 22 端口的情况。既然要做转发就选一个绝对不被占用的高位端口。因为 WSL 的 IP 会变我每次 WSL 重启之后会重新跑一遍。这个步骤我写成了脚本$wslIp (wsl.exe -d Ubuntu -- hostname -I).Trim().Split( )[0] netsh interface portproxy delete v4tov4 listenport2222 listenaddress0.0.0.0 | Out-Null netsh interface portproxy add v4tov4 listenport2222 listenaddress0.0.0.0 connectport22 connectaddress$wslIp | Out-Null Write-Host WSL IP updated to $wslIp在 PowerShell 里以管理员身份放行。跑完一次再执行netsh interface portproxy show all确认转发条目还在。5.3 防火墙放行端口转发配好了Windows 防火墙默认还是会拦外部的 2222 入站连接。需要加一条入站规则netsh advfirewall firewall add rule nameWSL SSH 2222 dirin actionallow protocolTCP localport2222如果在公司内网环境你还需要确认 Windows Server 自己的防火墙没有拦截 FinalShell 所在机器到宿主机的访问。如果你是从跳板机或公网 SSH 进来管理这台 Windows Server那建议只对必要的来源 IP 开放 2222别对全网段放开。5.4 FinalShell 会话参数的推荐配置走到这一步FinalShell 新会话可以这样填配置项推荐值说明协议SSHFinalShell 默认支持主机Windows Server 的 IP 或域名不是 WSL 的 172 IP端口2222对应端口转发监听的端口用户名WSL 里的 Linux 用户名不是 Windows 账户名认证方式密码如果配了密钥可以选密钥密码WSL 用户对应的密码非 Windows 登录密码如果你 FinalShell 里之前填过 22 端口、root 用户、Windows 密码那现在应该明白为什么无限提示输密码了每一项都踩在坑上。照上表改完一次就能通。6. 排完之后按这份清单验证一遍再收工6.1 先用命令行 SSH 打通配置完后别直接打开 FinalShell先在你本机命令行验证链路是否真的通了。Windows 自带 OpenSSH 客户端直接执行ssh 你的用户名WindowsServerIP -p 2222比如ssh admin192.168.1.100 -p 2222。如果这里能正常进入 Ubuntu 的 shell说明端口转发、防火墙、ssh 服务、密码认证全部正常。这时候再去 FinalShell 里连成功率几乎是百分之百。命令行能通但 FinalShell 不能通那问题就主要出在 FinalShell 本身而不是服务器。FinalShell 在某些版本对 SSH key 格式支持不好但密码认证很少出岔子大概率是你某个字段填错了。6.2 再 FinalShell 连一次注意指纹提示连上去之后FinalShell 会弹一次“确认主机指纹”之类的提示确认后它会把这个指纹记录在本地。这个和“无限提示输入密码”没关系别被吓到直接接受就行。如果 FinalShell 还是弹密码点开下方输出区域看具体错误码Connection refused说明端口不通Authentication failed说明用户名密码或 SSH 配置还有问题。结合错误码再回到第 3 章排查不用瞎猜。6.3 模拟最残酷的场景重启以后还能不能连能连一次不代表问题结束。建议你模拟一次最严酷的场景重启 Windows Server然后等系统起来再过一两分钟打开 FinalShell 去连。这个场景能验证三件事systemd 是否真的把 ssh 拉起来了、portproxy 的转发条目是否还在、防火墙规则是否持久化生效。如果你用的是固定 IP 的 WSL 配置和脚本更新重启后 WSL 的 IP 可能变了所以第一条验证命令应该先在服务器上执行wsl -d Ubuntu -- hostname -I拿到新 IP 后重跑一次上面那个 PowerShell 脚本更新 portproxy再让 FinalShell 去连。这就是为什么我建议把这个脚本存起来因为 WSL 只要重启一次IP 就换一次。6.4 我在 Windows Server 上的固定套路这套组合拳我后来沉淀成一个固定套路Windows Server 2022 WSL2 Ubuntu 22.04systemd 托管 ssh宿主机 2222 端口转发到 WSL 22端口转发更新脚本放在桌面重启后执行一次。这套配置跑了几个月没再出现过“无限提示输入密码”的情况。最后分享一个排障顺序的总结先把 WSL 组件确认好再确认 sshd 真的在跑然后检查 PasswordAuthentication 和 UsePAM接着用端口转发把网络打通最后才轮到 FinalShell 会话配置。大多数人是倒着排查的一上来就改 FinalShell改半天没用越弄越火。记住这句话能帮你少走至少一小时弯路。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门