context-infra 检查与复盘infra.guiming.net · 全内容自包含呈现 · 生成于 2026-07-21 16:28 UTC

Connect Windows / WSL — Mac 上从 SSH 连 Windows 和 WSL 的操作手册

Z3 全文↑ Z2 条目

术-操作 · 术层 skill 全文

← 返回术层 skill 索引 · 返回方法论区

本页是 <code>rules/skills/connect_windows.md</code> 的逐字投影(仅隐私清洗,零改写)。

时点提示:本页是仓内文件 rules/skills/connect_windows.md 的逐字投影(仅做隐私清洗:仓库根绝对路径→相对路径、家目录→~/;除此零改写)。若源文件后续有修订,以仓内真源为准。

Connect Windows / WSL — Mac 上从 SSH 连 Windows 和 WSL 的操作手册

元数据

目标

在 Tailscale 网络可达时,Mac 上一条命令即可连到 Windows(ssh win)或 WSL(ssh wsl)。不需要先去 Windows 上人工登录、不需要操心 WSL 内网 IP、不需要刷新 portproxy。

验收标准

任一条不通走下面的「诊断决策树」。

不做什么

可用资源

Mac 端

Windows 端(通过 ssh win 后可用)

WSL 端(通过 ssh wslssh win 'wsl -- ...'

跨 Windows shell 的引号陷阱

复杂 PowerShell 命令通过 ssh win 'powershell -Command "..."' 调用经常被 cmd 解析吞引号导致中文乱码错误。可靠方式:

PS='Get-Service sshd | Format-List'
ENC=$(printf '%s' "$PS" | iconv -t UTF-16LE | base64)
ssh win "powershell -EncodedCommand $ENC"

WSL 内复杂 bash 命令同理用 base64:

CMD='set -x
ss -tln
ps -ef | grep sshd'
B64=$(printf '%s' "$CMD" | base64)
ssh win "wsl -- bash -c \"echo $B64 | base64 -d | bash\""

诊断决策树

按链路上游到下游逐段诊断,先确认上游通了再看下游。每一步都有可观测的成功/失败信号,不要跳过验证直接猜。

第 0 段:Mac 自身能 ping 通 Tailscale 节点

tailscale status                       # 自己是否在线、win 节点是否 active 或 idle
tailscale ping 100.79.139.116          # 双向应答;如果只单向到达说明 NAT 穿透没完成

不通:tailscale up 重登。tailscale up --reset 清掉本地状态再登。

第 1 段:Mac → Windows sshd(22022)

nc -zv 100.79.139.116 22022            # 应 succeeded
ssh -v -o ConnectTimeout=10 win 'echo ok'   # 看 banner + 认证

nc 通但 ssh 不通 → 钥匙问题(-vdebug1: Offering public key)。

nc 不通:去 win 上查 sshd 监听

Get-NetTCPConnection -LocalPort 22022 -State Listen

如果连 ssh win 都进不去(key 失效、防火墙断了),只能本机或 RDP 处理。

第 2 段:Windows → WSL(wsl.exe lazy 调用)

ssh win 'wsl -- bash -c "ss -tln | grep :22"'

期望看到 0.0.0.0:22 LISTEN

不通:

第 3 段:完整 ssh wsl

ssh -v -o ConnectTimeout=20 wsl 'whoami; hostname; uname -r'

-v 关键 debug 信号:

已知陷阱(按踩坑频率从高到低)

#现象真原因处置
1ssh -W <WSL_IP>:22 win ProxyCommand 失败WSL2 默认 NAT 网络,Windows 主机到 WSL IP TCP 22 被 vmswitch 拒(ping 通但 TCP 拒);且 WSL 重启 IP 漂移改用 ssh win "wsl -- nc -q0 127.0.0.1 22"。已固化在 config 中
2Get-Service sshd 显示 Stopped 但 ssh 能连上sshd 由 startup folder sshd.vbs 拉起,进程独立于 Service Control Manager。Get-Service 看的是 SCM 视角Get-NetTCPConnection -LocalPort 22022 -State Listen 才是真相
3试图 Start-Service sshd 报 1067sshd_configListenAddress 100.79.139.116,service 启动顺序早于 Tailscale 拿到 IP,bind 失败退出维持 sshd.vbs 方案,autologon 解决"必须登录"依赖
4重启后 ssh win 不通,本机却一切正常sshd.vbs 在用户启动文件夹,需要用户登录后才执行;如果机器在锁屏未登录,sshd 不会起启用 Windows autologon(sysinternals AutoLogon,LSA Secret 加密存密码)。本机 2026-05-21 起已启用,可用上面"验证 autologon"命令确认
5WSL 内 systemctl status ssh.socket 显示 failed (Address already in use)wsl.conf[boot] command = service ssh start 先启动了 sshd 占 22;systemd 的 socket-activated ssh.socket 然后启动失败。实际 sshd 在跑,不影响功能忽略 status 看 ss -tln 真相
6netsh interface portproxy show all 是空的但历史文档说应该有规则portproxy 方案已弃用(WSL IP 漂移导致规则失效,且 wsl nc 方案不依赖 portproxy)不要重建 portproxy 规则;按新方案
7ssh win 'powershell -Command "..."' 出现中文乱码 / cmd 错误cmd 解析 PowerShell 嵌套引号失败powershell -EncodedCommand <base64>(UTF-16LE 编码)
8wsl -l -v 显示 distro Stopped 担心是不是挂了distro 进入 idle 后 hibernation 是正常行为;下次 wsl.exe 调用会自动唤醒别动;下次 ssh wsl 会自动 lazy 启动,第一次连慢 3-10s
9Mac 上 ssh wsl Permission deniedid_wsl.pub 没在 WSL ~/.ssh/authorized_keys把公钥 append 进去(可经 ssh win 'wsl -- bash -c "..."' 间接写)
1091201 不是 administrator,写错位置91201 不在 Administrators 组,所以 SSH key 走 C:\Users\91201\.ssh\authorized_keys;若用户是 admin 则走 C:\ProgramData\ssh\administrators_authorized_keys。先用 ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) 判断写到对应位置
11ssh win 'echo a; whoami; hostname' 输出字面 "a; whoami; hostname"ssh win 默认进入 cmd.exe,分号 ; 不是 cmd 的命令分隔符(cmd 用 & / &&)。被当成 echo 的字面参数打印单条命令直接跑;多条用 & 分隔;复杂逻辑跑 PowerShell:ssh win 'powershell -NoProfile -Command "..."',复杂 PowerShell 用 -EncodedCommand(见上方"跨 Windows shell 的引号陷阱")

常用任务速查

加一个公钥到 WSL authorized_keys(不需要直连 WSL 也能写)

KEY='ssh-ed25519 AAAA... comment'
CMD="mkdir -p ~/.ssh && echo '$KEY' >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys"
B64=$(printf '%s' "$CMD" | base64)
ssh win "wsl -- bash -c \"echo $B64 | base64 -d | bash\""

加一个公钥到 Windows 91201 authorized_keys

PS='$key = "ssh-ed25519 AAAA... comment"; $path = "$env:USERPROFILE\.ssh\authorized_keys"; if (-not (Test-Path (Split-Path $path))) { New-Item -ItemType Directory -Force (Split-Path $path) | Out-Null }; Add-Content -Path $path -Value $key'
ENC=$(printf '%s' "$PS" | iconv -t UTF-16LE | base64)
ssh win "powershell -EncodedCommand $ENC"

一次性健康体检(建议在被卡时跑)

注意 ssh win 默认进入 cmd.exe(不是 bash 也不是 PowerShell),命令分隔符是 & 而不是 ;ssh wsl 走 bash,可用 ;

echo "--- 0. tailscale ---"; tailscale status | grep -E "(self|laptop-s44)"
echo "--- 1. ssh win (cmd 用 &) ---"; ssh -o ConnectTimeout=5 win 'echo ok & whoami & hostname' 2>&1 | head -5
echo "--- 2. win sshd listener (PowerShell) ---"; ssh win 'powershell -NoProfile -Command "(Get-NetTCPConnection -LocalPort 22022 -State Listen) | Select-Object LocalAddress, OwningProcess"' 2>&1 | grep -v -E 'CLIXML|<Objs|<Obj|Props>|progress' | head -5
echo "--- 3. ssh wsl (bash 用 ;) ---"; ssh -o ConnectTimeout=15 wsl 'echo ok; whoami; hostname; uname -r' 2>&1 | head -5
echo "--- 4. wsl sshd ---"; ssh win 'wsl -- bash -c "ss -tln 2>/dev/null | grep :22"' 2>&1 | head -5

期望(autologon 已启用、链路完整时):

验证 autologon 是否启用(决定无人值守路径是否完整)

PS='$p = Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon"
Write-Host "AutoAdminLogon = $($p.AutoAdminLogon) / User = $($p.DefaultUserName) / Domain = $($p.DefaultDomainName)"
if ($p.PSObject.Properties.Name -contains "DefaultPassword") { Write-Host "WARN: 密码明文存在注册表" } else { Write-Host "OK: 密码不在明文注册表(LSA Secret 加密)" }'
ENC=$(printf '%s' "$PS" | iconv -t UTF-16LE | base64)
ssh win "powershell -EncodedCommand $ENC"

期望输出:AutoAdminLogon = 1 / User = 91201 / Domain = LAPTOP-S44RCPHF + OK: 密码不在明文注册表(LSA Secret 加密)

任一项不符(例如 AutoAdminLogon = 0WARN: 密码明文)说明 autologon 没正确配置,开机后 startup folder 的 sshd.vbs / OpenCode.vbs 不会自动跑。重新跑 C:\Users\91201\Tools\AutoLogon\Autologon64.exe GUI 配置一次。

重新配置或撤销 autologon

工具位置:C:\Users\91201\Tools\AutoLogon\Autologon64.exe(64-bit Windows 用这个,32-bit 用 Autologon.exe,ARM64 用 Autologon64a.exe)。

# Windows 本机管理员 PowerShell
& "$env:USERPROFILE\Tools\AutoLogon\Autologon64.exe"

GUI 三栏 Username / Domain / Password 输入。点 Enable 启用、Disable 撤销(Disable 会同时清掉 LSA Secret 中的密码,比注册表单独改 AutoAdminLogon=0 更干净)。

配置变更时的发布纪律

任何对以下任一项的实质修改,都要更新 Windows_SSH_Tailscale_Config.md 的"历史问题与修复"段(追加新日期段,不重写历史),并同步更新 WSL_Mac_Tailscale_SSH_One_Command_Guide.md 如涉及 WSL 通路:

修改后跑一次"健康体检"确认链路完好。

参考


← 返回术层 skill 索引 · 返回方法论区