强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

SSH 服务器完全指南 / 第14章 故障排查

第14章 故障排查

14.1 SSH 调试模式

客户端调试

# 一级调试
ssh -v user@server

# 二级调试(更详细)
ssh -vv user@server

# 三级调试(最详细)
ssh -vvv user@server

调试输出关键阶段

# 1. 连接阶段
debug1: Connecting to server [192.168.1.100] port 22.
debug1: Connection established.

# 2. 密钥协商
debug1: SSH2_MSG_KEXINIT sent
debug1: kex: algorithm: curve25519-sha256
debug1: kex: host key algorithm: ssh-ed25519

# 3. 主机验证
debug1: Host 'server' is known and matches the ED25519 host key.
# 或
debug1: Host 'server' is not in the known_hosts list.

# 4. 认证
debug1: Offering public key: /home/user/.ssh/id_ed25519
debug1: Server accepts key: /home/user/.ssh/id_ed25519
# 或
debug1: Trying private key: /home/user/.ssh/id_ed25519
debug1: No more authentication methods to try.

# 5. 会话建立
debug1: Authentication succeeded (publickey).
debug1: channel 0: new [client-session]

服务端调试

# 不使用 systemd,直接前台启动(调试模式)
sudo /usr/sbin/sshd -d

# 或指定配置文件
sudo /usr/sbin/sshd -d -f /etc/ssh/sshd_config.debug

# 端口调试模式(避免占用 22 端口)
sudo /usr/sbin/sshd -d -p 2222

# 然后从客户端连接
ssh -p 2222 user@server

注意: 服务端 -d 模式只处理一个连接然后退出,不会影响正常运行的 sshd 服务。


14.2 常见连接问题

问题一:Connection refused

症状

ssh: connect to host 192.168.1.100 port 22: Connection refused

排查步骤

# 1. 检查目标主机 SSH 服务是否运行
ssh user@server "systemctl status sshd"

# 从本机检查端口是否开放
nc -zv 192.168.1.100 22
nmap -p 22 192.168.1.100

# 2. 检查防火墙
sudo ufw status                          # Ubuntu
sudo firewall-cmd --list-all             # RHEL/CentOS
sudo iptables -L -n | grep 22

# 3. 检查 SSH 是否监听
sudo ss -tlnp | grep :22
sudo netstat -tlnp | grep :22

# 4. 检查 sshd 是否运行
sudo systemctl status sshd
sudo ps aux | grep sshd

# 5. 检查配置文件
sudo sshd -t

# 6. 尝试重启服务
sudo systemctl restart sshd

问题二:Connection timed out

症状

ssh: connect to host 192.168.1.100 port 22: Connection timed out

排查步骤

# 1. 测试网络连通性
ping 192.168.1.100
traceroute 192.168.1.100

# 2. 测试端口连通性
nc -zv 192.168.1.100 22
telnet 192.168.1.100 22

# 3. 检查防火墙规则
sudo iptables -L -n -v
sudo nft list ruleset

# 4. 检查安全组(云服务器)
# 在云平台控制台检查安全组规则是否允许 22 端口

# 5. 检查路由
ip route get 192.168.1.100

问题三:Permission denied (publickey)

症状

user@server: Permission denied (publickey).

排查步骤

# 1. 客户端调试
ssh -vvv user@server 2>&1 | grep -i "permission\|publickey\|offering\|accept"

# 2. 检查服务器端日志
sudo journalctl -u sshd -n 50

# 3. 检查 authorized_keys
ls -la ~/.ssh/authorized_keys
cat ~/.ssh/authorized_keys

# 4. 检查目录权限(服务器端)
ls -la ~/.ssh/
# .ssh 目录应该是 700
# authorized_keys 应该是 600

# 5. 检查私钥权限(客户端)
ls -la ~/.ssh/id_*
# 私钥应该是 600

# 6. 检查 sshd 配置
sudo sshd -T | grep -i "pubkey\|authorized"

# 7. 检查 SELinux
sudo ausearch -m avc -ts recent | grep ssh
sudo setenforce 0  # 临时禁用测试

# 8. 修复权限
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
chmod 600 ~/.ssh/id_*

问题四:Permission denied (password)

症状

user@server: Permission denied (password).

排查步骤

# 1. 检查密码是否正确
# 尝试在服务器本地登录
su - user

# 2. 检查密码认证是否启用
sudo sshd -T | grep passwordauthentication

# 3. 检查 PAM 配置
cat /etc/pam.d/sshd

# 4. 检查账户是否被锁定
sudo passwd -S user
sudo faillock --user user

# 5. 检查账户是否过期
sudo chage -l user

# 6. 检查 nologin/nologin shell
grep user /etc/passwd

# 7. 检查 AllowUsers/DenyGroups
sudo sshd -T | grep -i "allow\|deny"

问题五:Host key verification failed

症状

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@    WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!     @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
Host key verification failed.

解决步骤

# 1. 确认服务器身份(通过带外渠道)

# 2. 如果确认是服务器重装/密钥更换
ssh-keygen -R 192.168.1.100

# 3. 重新连接并接受新密钥
ssh user@192.168.1.100

# 4. 如果是 known_hosts 中有错误条目
ssh-keygen -R "[192.168.1.100]:2222"

问题六:Connection reset by peer

症状

Connection reset by 192.168.1.100 port 22

排查步骤

# 1. 检查服务器日志
sudo journalctl -u sshd -n 50

# 2. 可能原因:
# - MaxStartups 限制(并发未认证连接过多)
# - 防火墙重置连接
# - 服务器资源不足
# - sshd 进程崩溃

# 3. 检查 MaxStartups 设置
sudo sshd -T | grep maxstartups

# 4. 检查服务器资源
free -m
df -h
cat /proc/loadavg

问题七:Broken pipe / Write failed

症状

Write failed: Broken pipe

解决步骤

# 客户端配置保活
# ~/.ssh/config
Host *
    ServerAliveInterval 60
    ServerAliveCountMax 3
    TCPKeepAlive yes

# 服务端配置
# /etc/ssh/sshd_config
ClientAliveInterval 60
ClientAliveCountMax 3
TCPKeepAlive yes

# 临时解决方案
# 在当前终端发送保活信号
# 按 Enter 键或 Ctrl+C

14.3 权限问题

常见权限错误

# 错误一:UNPROTECTED PRIVATE KEY FILE
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@         WARNING: UNPROTECTED PRIVATE KEY FILE!          @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
Permissions 0644 for '/home/user/.ssh/id_ed25519' are too open.

# 解决
chmod 600 ~/.ssh/id_ed25519

# 错误二:Authentication refused
Authentication refused: bad ownership or modes for directory /home/user

# 解决
chmod 755 /home/user
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys

# 错误三:bad ownership or modes for file
Authentication refused: bad ownership or modes for file /home/user/.ssh/authorized_keys

# 解决
chmod 600 ~/.ssh/authorized_keys
chown user:user ~/.ssh/authorized_keys

SSH 权限检查脚本

#!/bin/bash
# check-ssh-permissions.sh

echo "=== SSH 权限检查 ==="

# 检查 .ssh 目录
if [ -d ~/.ssh ]; then
    PERM=$(stat -c %a ~/.ssh)
    if [ "$PERM" = "700" ]; then
        echo "✅ ~/.ssh 权限正确 (700)"
    else
        echo "❌ ~/.ssh 权限错误 ($PERM),应该是 700"
    fi
else
    echo "❌ ~/.ssh 目录不存在"
fi

# 检查 authorized_keys
if [ -f ~/.ssh/authorized_keys ]; then
    PERM=$(stat -c %a ~/.ssh/authorized_keys)
    if [ "$PERM" = "600" ]; then
        echo "✅ authorized_keys 权限正确 (600)"
    else
        echo "❌ authorized_keys 权限错误 ($PERM),应该是 600"
    fi
else
    echo "⚠️  authorized_keys 不存在"
fi

# 检查私钥文件
for key in ~/.ssh/id_*; do
    if [[ "$key" == *.pub ]]; then continue; fi
    if [ ! -f "$key" ]; then continue; fi
    
    PERM=$(stat -c %a "$key")
    if [ "$PERM" = "600" ]; then
        echo "✅ $key 权限正确 (600)"
    else
        echo "❌ $key 权限错误 ($PERM),应该是 600"
    fi
done

# 检查 config
if [ -f ~/.ssh/config ]; then
    PERM=$(stat -c %a ~/.ssh/config)
    if [ "$PERM" = "600" ]; then
        echo "✅ config 权限正确 (600)"
    else
        echo "❌ config 权限错误 ($PERM),应该是 600"
    fi
fi

# 检查主目录
HOME_PERM=$(stat -c %a ~)
echo "⚠️  主目录权限: $HOME_PERM(应该是 755 或更严格)"

echo ""
echo "=== 修复命令 ==="
echo "chmod 700 ~/.ssh"
echo "chmod 600 ~/.ssh/authorized_keys ~/.ssh/config ~/.ssh/id_*"
echo "chmod 644 ~/.ssh/*.pub ~/.ssh/known_hosts"

SELinux 相关问题

# 查看 SELinux 是否阻止 SSH
sudo ausearch -m avc -ts recent | grep ssh

# 查看 SELinux 状态
getenforce

# 临时禁用(仅用于排查)
sudo setenforce 0

# 修复 SELinux 上下文
sudo restorecon -Rv ~/.ssh

# 允许 SSH 访问非标准目录
sudo semanage fcontext -a -t ssh_home_t "/custom/ssh/path(/.*)?"
sudo restorecon -Rv /custom/ssh/path

14.4 配置问题排查

检查配置语法

# 检查语法
sudo sshd -t

# 如果有错误,会显示:
# /etc/ssh/sshd_config: line 88: Bad configuration option: invalidoption

# 查看完整生效配置
sudo sshd -T

# 过滤特定配置
sudo sshd -T | grep -i "password\|permit\|allow\|deny"

Match 块问题

# 常见问题:
# Match 块中的配置对所有后续行生效
# Match 块只能放在配置文件末尾

# 调试 Match 块
ssh -vvv user@server 2>&1 | grep -i "matching"

# 查看哪些规则匹配
sudo sshd -T -C user=admin,host=192.168.1.100,addr=192.168.1.100

Include 文件问题

# 检查 Include 文件是否存在
ls -la /etc/ssh/sshd_config.d/

# 检查 Include 语法
grep -n "Include" /etc/ssh/sshd_config

# 合并查看所有配置
cat /etc/ssh/sshd_config /etc/ssh/sshd_config.d/*.conf | grep -v '^#' | grep -v '^$'

14.5 认证问题排查

密钥认证调试

# 客户端调试(查看密钥认证过程)
ssh -vvv user@server 2>&1 | grep -A5 -B5 "publickey\|Offering\|accepts\|denied"

# 服务端日志
sudo journalctl -u sshd -n 100 | grep -i "publickey\|key\|denied"

# 检查密钥是否在 authorized_keys 中
ssh-keygen -lf ~/.ssh/id_ed25519.pub  # 获取指纹
grep "$(ssh-keygen -lf ~/.ssh/id_ed25519.pub | awk '{print $2}')" ~/.ssh/authorized_keys

密码认证调试

# 服务端日志
sudo journalctl -u sshd | grep -i "password\|pam\|failed"

# 检查 PAM 配置
cat /etc/pam.d/sshd

# 手动测试 PAM
sudo pamtester sshd user authenticate

证书认证调试

# 查看证书内容
ssh-keygen -Lf ~/.ssh/id_ed25519-cert.pub

# 检查信任的 CA
sudo cat /etc/ssh/ca_user_key.pub

# 检查证书有效期
ssh-keygen -Lf ~/.ssh/id_ed25519-cert.pub | grep -i "valid\|expires"

14.6 网络问题排查

端口检查

# 检查端口是否开放
nc -zv 192.168.1.100 22
nmap -p 22 192.168.1.100

# 检查本机监听
ss -tlnp | grep :22
ss -tlnp | grep sshd

# 检查连接数
ss -tnp | grep :22 | wc -l

防火墙检查

# iptables
sudo iptables -L -n -v | grep 22

# firewalld
sudo firewall-cmd --list-all

# nftables
sudo nft list ruleset | grep 22

# UFW
sudo ufw status verbose

DNS 问题

# 正向解析
nslookup server.example.com
dig server.example.com

# 反向解析
nslookup 192.168.1.100
dig -x 192.168.1.100

# 禁用 DNS 反向解析(加速登录)
# /etc/ssh/sshd_config
UseDNS no

14.7 性能问题排查

连接慢

# 测量连接时间
time ssh user@server "echo connected"

# 详细分析(查看各阶段耗时)
ssh -vvv user@server 2>&1 | ts '[%Y-%m-%d %H:%M:%S]'

# 常见原因和解决方案
# 1. DNS 反向解析慢 → UseDNS no
# 2. GSSAPI 认证超时 → GSSAPIAuthentication no
# 3. 主机密钥检查慢 → 客户端 StrictHostKeyChecking accept-new
# 4. 网络延迟 → 使用 ControlMaster 复用连接

传输慢

# 测试带宽
dd if=/dev/zero bs=1M count=100 | ssh user@server "cat > /dev/null"

# 启用压缩
ssh -C user@server

# 使用更快的加密算法
ssh -c chacha20-poly1305@openssh.com user@server

# 使用 rsync 代替 scp
rsync -avz -e ssh largefile user@server:/path/

14.8 日志分析

关键日志位置

# systemd 系统
sudo journalctl -u sshd
sudo journalctl -u sshd --since "1 hour ago"
sudo journalctl -u sshd --since today

# 传统日志
sudo tail -f /var/log/auth.log     # Debian/Ubuntu
sudo tail -f /var/log/secure       # RHEL/CentOS

常见日志模式

# 认证成功
grep "Accepted" /var/log/auth.log

# 认证失败
grep "Failed" /var/log/auth.log

# 无效用户
grep "Invalid user" /var/log/auth.log

# 会话开始/结束
grep "session opened\|session closed" /var/log/auth.log

# 按 IP 统计失败次数
grep "Failed password" /var/log/auth.log | \
    awk '{print $(NF-3)}' | sort | uniq -c | sort -rn | head -20

# 实时监控攻击
sudo tail -f /var/log/auth.log | grep --line-buffered "Failed\|Invalid"

增强日志配置

# /etc/ssh/sshd_config
LogLevel VERBOSE

# 创建日志轮转配置
# /etc/logrotate.d/sshd-custom
/var/log/sshd/*.log {
    daily
    rotate 30
    compress
    delaycompress
    notifempty
    create 0640 root adm
    sharedscripts
    postrotate
        /bin/systemctl reload sshd > /dev/null 2>&1 || true
    endscript
}

14.9 问题排查速查表

症状可能原因解决方案
Connection refusedSSH 服务未运行systemctl start sshd
Connection timed out防火墙阻止检查安全组/防火墙规则
Permission denied (publickey)权限错误修复 ~/.ssh/ 权限
Permission denied (password)密码错误或 PAM检查密码和 PAM 配置
Host key changed主机密钥变更ssh-keygen -R host
Broken pipe连接超时设置 ServerAliveInterval
Write failed网络中断检查网络和保活设置
Key is too open私钥权限过宽chmod 600 ~/.ssh/id_*
Connection reset服务端限制检查 MaxStartups
No route to host网络不通检查路由和防火墙

扩展阅读


下一章: 第15章 最佳实践与规范 → 学习生产环境的 SSH 密钥管理、审计日志和运维规范。