mcpField
S
ssh-doctor

ssh-doctor

github.com/steipete云与 DevOps7.4k154 次安装1 周前更新
代码仓库

关于 ssh-doctor

SSH Doctor 是用于诊断认证前断开等 SSH 问题的技能,重点涵盖本地和远程 Mac。它指导检查 sshd、launchd、配置、日志和网络过滤,并要求谨慎处理秘密信息及配置更改。

主要功能

  1. 01检查 SSH 服务和配置
  2. 02区分本地与网络故障
  3. 03检查 sshd 和 launchd 日志
  4. 04识别过期 SSH 会话
  5. 05诊断时保护秘密信息

适用场景

  1. 01诊断认证前 SSH 连接断开
  2. 02排查 Mac 远程登录不可用
  3. 03查找阻碍新连接的过期会话

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/steipete/agent-scripts --skill ssh-doctor

安装后在 Claude Code 中输入/ssh-doctor确认已加载。

详细介绍

SSH 诊断

适用于 SSH 连接后在认证前关闭、远程登录似乎已启用却无法使用,或需要诊断本地/远程 Mac SSH 的情况。

规则

  • 不要打印机密、令牌、完整环境变量或大量机密 grep 输出。
  • 先在本地验证:环回连接失败意味着服务端 sshd/launchd/配置有问题;环回连接成功但远程连接失败意味着网络/防火墙/过滤器/监听路径有问题。
  • 更改 /etc/ssh/sshd_config 前,先报告可疑配置行。
  • 优先使用非交互式 SSH:
ssh -o RequestTTY=no -o RemoteCommand=none HOST 'hostname; id -un'

基线

hostname; id -un; sw_vers
ipconfig getifaddr en0
ipconfig getifaddr en1 2>/dev/null || true
ipconfig getifaddr en7 2>/dev/null || true
sudo systemsetup -getremotelogin
sudo systemsetup -setremotelogin on
sudo launchctl print system/com.openssh.sshd 2>&1 | head -80
sudo launchctl kickstart -k system/com.openssh.sshd
sudo lsof -nP -iTCP:22 -sTCP:LISTEN
nc -vz 127.0.0.1 22
ssh -4 -F /dev/null -o RequestTTY=no -o RemoteCommand=none [email protected] 'hostname; id -un'

仅在密码回退会导致命令挂起或出现提示时,才使用 BatchMode=yes。

配置

sudo sshd -T 2>&1 | egrep -i '^(allowusers|denyusers|allowgroups|denygroups|listenaddress|maxstartups|logingracetime|usepam|passwordauthentication|pubkeyauthentication|authenticationmethods)'
sudo egrep -n '^[[:space:]]*(AllowUsers|DenyUsers|AllowGroups|DenyGroups|Match|MaxStartups|LoginGraceTime|ListenAddress|AuthenticationMethods|UsePAM|PasswordAuthentication|PubkeyAuthentication)\b' /etc/ssh/sshd_config /etc/ssh/sshd_config.d/* 2>/dev/null || true

可疑项:

  • DenyUsers 匹配目标用户
  • 限制性 AllowUsers/AllowGroups
  • Match 块意外生效
  • MaxStartups 值过小
  • LoginGraceTime 值过小
  • ListenAddress 未包含目标接口

日志

sudo log show --last 30m --predicate 'process == "sshd" OR process == "launchd"' --style compact | tail -160

Mac 上的重要症状:

  • 客户端:kex_exchange_identification: Connection closed by remote host
  • 服务端日志:Could not create new instance of inetd service: 67: Too many processes
  • launchctl print system/com.openssh.sshd:copy count 值偏高
  • 许多父进程为 PID 1 的 sshd-session: USER 进程

这意味着 launchd 已接受 TCP 连接,但拒绝再启动更多 sshd inetd 副本。

过期 sshd-session 修复

先检查:

sudo launchctl print system/com.openssh.sshd 2>&1 | egrep 'active count|copy count|state =|last exit code|runs ='
ps -axo pid,ppid,uid,user,state,lstart,etime,comm,args | awk '/sshd-session:/ && !/awk/ {print}'
sudo lsof -nP -c sshd-session -iTCP 2>/dev/null | head -120

如果确认过期会话已滞留并阻止新的 SSH 连接,请按选定的命令行匹配项终止进程:

ps -axo pid=,args= | awk '/sshd-session: / && !/awk/ {print $1}' | xargs sudo kill -TERM
sleep 2
ps -axo pid=,args= | awk '/sshd-session: / && !/awk/ {print}'

如果 TERM 后仍有阻塞进程,请在使用 KILL 前重新检查进程所有者和活动 shell。

防火墙

仅在环回连接正常但远程连接失败后执行:

sudo /usr/libexec/ApplicationFirewall/socketfilterfw --getglobalstate
sudo /usr/libexec/ApplicationFirewall/socketfilterfw --listapps | grep -i ssh -A2 -B2 || true
sudo pfctl -sr 2>/dev/null | head -80
sudo pfctl -si 2>/dev/null | head -80

同时检查监听地址和目标接口:

ifconfig | awk '/^[a-z0-9]+:/{iface=$1; sub(":","",iface)} iface ~ /^en[0-9]+$/ && /inet / {print iface, $2}'
sudo lsof -nP -iTCP:22 -sTCP:LISTEN

OP 配置文件块

如果要求确保 ~/.profile 中包含从另一台主机复制的、由 Codex 管理的 OP_SERVICE_ACCOUNT_TOKEN:

  • 验证变量名/标记是否完全匹配,但不要打印值
  • 只复制匹配的行/块
  • 重定向到权限为 chmod 600 的临时文件
  • 切勿回显令牌

检查是否存在:

awk 'BEGIN{b=0;e=0;x=0} /BEGIN Codex-managed OP_SERVICE_ACCOUNT_TOKEN/ {b=1} /END Codex-managed OP_SERVICE_ACCOUNT_TOKEN/ {e=1} /^[[:space:]]*(export[[:space:]]+)?OP_SERVICE_ACCOUNT_TOKEN=/ {x=1} END{print "marker_begin", b; print "marker_end", e; print "exact_var", x}' ~/.profile

从远程主机追加:

tmpfile=$(mktemp /tmp/codex-op-token.XXXXXX)
chmod 600 "$tmpfile"
ssh -o RequestTTY=no -o RemoteCommand=none HOST 'awk '\''/^[[:space:]]*(export[[:space:]]+)?OP_SERVICE_ACCOUNT_TOKEN=/ {print; exit}'\'' ~/.profile' > "$tmpfile"
if [ -s "$tmpfile" ]; then
  {
    printf '\n# BEGIN Codex-managed OP_SERVICE_ACCOUNT_TOKEN\n'
    sed -n '1p' "$tmpfile"
    printf '# END Codex-managed OP_SERVICE_ACCOUNT_TOKEN\n'
  } >> ~/.profile
fi
rm -f "$tmpfile"

结束处理

报告:

  • 根本原因
  • 更改的确切命令
  • 验证输出,必要时进行脱敏
  • 远程端是否应重试

评论

支持 Markdown