SSH 免密登录与安全加固实战:从密钥原理到排障

一次例行的安全扫描结果里,我管理的服务器 22 端口一周被尝试登录几十万次——虽然都没有成功,但一直在”裸奔”也知道迟早出事。那周做了一次全量加固:密钥认证替代密码、sshd 参数收紧、加上 fail2ban。这篇把过程和踩过的坑记录下来。

一、SSH 登录到底是怎么完成的

SSH 用”非对称加密做身份认证 + 对称加密传数据”:

  1. 建立连接时双方协商出临时会话密钥(Diffie-Hellman),之后的数据都用对称加密(AES/ChaCha20)传;
  2. 认证阶段二选一:密码认证,或者密钥认证——客户端持有私钥,服务端存公钥,登录时用私钥对随机挑战签名;
  3. 首次连接会提示指纹(host key),用来确认连的是目标机器,防中间人。

密钥认证为什么比密码安全:私钥不出本机,服务端只存公钥,没有”密码在网络上传递”这个环节,也就不怕暴力猜解。

二、免密登录:三个文件两个权限

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 1) 生成密钥对(ed25519 比 rsa 更短更安全)
ssh-keygen -t ed25519 -C "ops@ops-master" -N "" -f ~/.ssh/id_ed25519
# id_ed25519 私钥:绝不出本机,权限 600
# id_ed25519.pub 公钥:分发到所有目标服务器

# 2) 分发公钥
ssh-copy-id -i ~/.ssh/id_ed25519.pub 172.18.1.7

# 手动方式(图形客户端或没装 ssh-copy-id 时)
mkdir -p ~/.ssh && chmod 700 ~/.ssh
vim ~/.ssh/authorized_keys # 粘贴公钥
chmod 600 ~/.ssh/authorized_keys

# 3) 验证
ssh 172.18.1.7 'hostname'

权限:免密失败的 90% 原因

sshd 开了严格模式后,对”家目录 → .ssh → authorized_keys”整条链路的权限非常敏感:

对象 要求 说明
~/.ssh/ 700 其他人不可进入
~/.ssh/authorized_keys 600 只有属主能读写
家目录 ~ 组/其他用户不可写 750 或 700 都行,禁止 775/777
私钥 600 被读出等于裸奔
1
2
3
4
5
# 逐层检查修复(千万别对 /root 用 -R 递归,会把系统文件权限改坏)
ls -ld /root /root/.ssh
chmod 700 /root/.ssh
chmod 600 /root/.ssh/authorized_keys
chown root:root /root /root/.ssh /root/.ssh/authorized_keys

顺带纠正一个流传很广的”加固建议”:把家目录改成 550/555。这是过度加固——官方只要求”组/其他用户不可写”,改成 550 连 root 自己都建不了文件,是纯粹的自找麻烦。

三、免密失败排查:别瞎试,按顺序来

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 1) 确认服务端开着密钥认证
grep -i "pubkeyauthentication" /etc/ssh/sshd_config

# 2) 客户端 -vvv 看握手全过程(最有效的一步)
ssh -vvv 172.18.1.7
# Offering public key: ... 提交了哪把公钥
# Authentications that can continue: password 服务端拒绝了密钥
# Server accepts key 认证通过

# 3) 服务端日志(免密失败时的关键证据)
tail -50 /var/log/secure # RHEL/CentOS
journalctl -u sshd -n 50 # systemd 系统
# Authentication refused: bad ownership or modes ... → 权限问题
# No supported authentication methods available → 没配密钥/客户端没带对私钥

# 4) 网络层
nc -vz 172.18.1.7 22

按”权限 → 配置 → 日志 → 网络”的顺序走,四步之内基本都能定位,比反复重试 ssh-copy-id 高效得多。

四、sshd 加固:逐条说明

服务器只要开着 22 端口,被扫描是持续的。加固项一次配齐:

1
vim /etc/ssh/sshd_config
1
2
3
4
5
6
7
8
9
10
11
12
Port 1022                       # 1) 改默认端口,避开批量扫描(防火墙要放行新端口)
PermitRootLogin no # 2) 禁止 root 直连,强制走普通用户+sudo
PasswordAuthentication no # 3) 关密码认证,只允许密钥
PubkeyAuthentication yes
MaxAuthTries 3 # 4) 最多试 3 次,防爆破
MaxSessions 10 # 5) 单连接会话上限
AllowUsers ops01 ops02 # 6) 只允许白名单用户登录
ClientAliveInterval 300 # 7) 300 秒无活动发心跳
ClientAliveCountMax 2 # 8) 连续 2 次无响应踢下线,防僵尸会话
LoginGraceTime 30 # 9) 30 秒内必须完成认证
UseDNS no # 10) 不反查 DNS,登录不再转圈十几秒
GSSAPIAuthentication no # 11) 关 GSSAPI,同样为提速

改完先校验再平滑重载——千万不要直接 restart,断线就可能把自己锁在外面:

1
2
sshd -t && systemctl reload sshd
systemctl status sshd

加固翻车自救:真把自己锁了,用云控制台 VNC 进去改回来。规避办法是改 sshd 前先开一个 tmux/screen 会话保活,这样即使新连接失败,旧会话还在。

另外建议顺手起一个 fail2ban:PasswordAuthentication no 之后爆破实际打不进来,但 fail2ban 能把持续扫描的 IP 直接封掉,日志和告警都清净很多。

五、日常连接技巧

环境稳定之后,用 ~/.ssh/config 把常用连接固化成别名:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Host web01
HostName 172.18.1.7
User ops
Port 1022

# 连接复用:第一次认证后,后续连接走同一隧道(跳板场景提速明显)
Host 10.0.8.*
ControlMaster auto
ControlPath ~/.ssh/cm-%r@%h:%p
ControlPersist 4h

# 跳板直连:一条命令穿透跳板到内网机器
Host 172.18.1.7
ProxyJump ops@10.0.8.31
1
2
3
4
5
ssh web01                                   # 少打一半字
ssh -J ops@10.0.8.31 root@172.18.1.7 # 不走配置文件时的跳板直连
eval "$(ssh-agent -s)" && ssh-add ~/.ssh/id_ed25519 # 私钥有口令时只用输一次
scp app.tar.gz web01:/data/app/ # 传单个文件
rsync -avz --progress /data/app/ web01:/data/app/ # 增量同步用 rsync

多台机器批量执行命令时用循环顶一下可以,真到几十台就该上 Ansible:

1
for ip in 10.0.8.{7..10}; do ssh $ip 'uptime'; done

注意:for + ssh 这种写法没有并发控制,机器一多就会把本机的连接数打满,正式批量操作还是交给 Ansible。

六、常见故障对照

现象 原因 处理
Permission denied (publickey,password) 密钥/权限/配置之一有问题 按”四步排查”走一遍
Host key verification failed 目标机重装/换密钥,指纹变了 确认后删除 known_hosts 中对应条目重连
Too many authentication failures 客户端一次带出太多私钥,超过 MaxAuthTries ssh -o IdentitiesOnly=yes -i 指定私钥
Connection refused 端口没开/服务没起/防火墙 nc -vz 测端口,查 sshd 状态
登录很慢(十几秒) DNS 反查 / GSSAPI 两端都关 UseDNS、GSSAPIAuthentication
Connection reset by peer 被 fail2ban/安全组拦截 查 fail2ban 日志、云控制台安全组
登录后频繁断线 心跳配置太激进/链路抖动 调大 ClientAliveInterval

七、小结

SSH 加固做完之后有两个立竿见影的效果:爆破日志量直接归零,日常登录速度也快了一截(关掉 DNS 反查的功劳)。几条经验总结:密钥体系要尽早建立,别等被扫了才动手;改 sshd 配置永远”先保活会话再动刀”;免密问题先看权限再看日志,九成问题在权限上。