你刚装好一台服务器,兴致勃勃开始部署。此刻可能已经有几十个扫描器知道了它的地址——因为从它装好那一刻起,它就是敞开的。
现象
一台新装好的服务器,SSH 服务默认监听在所有接口上,包括公网 IPv6;同时允许密码登录;机器本身有公网 IPv6 地址;而且没有启用防火墙。
结果是只要有人扫到,就可以对着 22 端口做密码爆破。翻一下认证日志,能看到成片来自世界各地的失败登录尝试:
grep "Failed password" /var/log/auth.log | wc -l
# 可能已经有几千条
日志里的条目通常长这样,来源地址不停变化:
Failed password for root from <IP> port 51234 ssh2
Failed password for invalid user admin from <IP> port 51235 ssh2
注意 invalid user 那种——攻击者不只是在试 root,还在试 admin、ubuntu、test 这类常见用户名。如果一直没看过日志,说不定已经被试出弱口令了。
根因
这是四个默认值叠加的结果,每一个单看都"合理",合起来就是一个敞开的门:
- SSH 默认监听
[::]:22,也就是 IPv6 通配地址,不只监听内网; - OpenSSH 默认允许
PasswordAuthentication yes,只要密码对就能进; - 机器有公网 IPv6,而 IPv6 是"默认可达"的——不像 IPv4 时代需要手动做端口映射,有地址就通;
- 发行版通常默认不开防火墙,规则链是空的。
这四个默认值之所以危险,是因为它们互相叠加、彼此放大。如果只是监听在通配地址但防火墙拦住了,或者监听在公网但强制密钥登录,都不至于这么被动;而当四者同时成立时,攻击者连额外门槛都不需要——一串自动化脚本轮流试口令就行,成本几乎为零。
这里要特别强调"默认可达"的含义。IPv4 时代,机器即使有公网地址,也常常在 NAT 后面,外部要先做端口映射才能连;而 IPv6 是端到端的,地址分配出去就直连,中间没有那一层"要手动打开"的关卡。很多人对 IPv4 的"安全默认"有肌肉记忆,切换到 IPv6 之后这个记忆就失效了。
解决
第一步,让 SSH 只走 IPv4(或只在确定的接口监听)。 在 sshd_config 里加一行:
AddressFamily inet
这会让 sshd 只监听 IPv4。如果本来就没有公网 IPv4,等于彻底断了公网 SSH,而局域网访问完全不受影响。更精细的做法是用 ListenAddress 指定具体地址:
ListenAddress 192.168.0.10
第二步,关掉用不上的服务。 典型的是 rpcbind(NFS 相关),如果没有 NFS 需求,它只会白白多开几个端口:
systemctl disable --now rpcbind rpcbind.socket
第三步,目标是 IPv6 零监听。 改完配置重启 sshd 后用 ss -tlnp 确认没有任何 [::] 的项:
sudo systemctl restart sshd
sudo ss -tlnp | grep ':22 '
第四步,自己测一遍。 这一步非常关键——改 ssh 配置前记得保留一个已登录的会话不要断,然后用新的连接测试能不能进。别把自己关在门外。万一改错了,还能从这个没断的会话里改回去。
顺手可以再做两件事:禁用密码登录改用密钥(PasswordAuthentication no,前提是公钥已经装好),以及装个 fail2ban 之类的工具自动封禁爆破来源。
延伸与预防
建立一份"新机器上线清单",第一条就是:确认有没有公网 IPv6 地址、SSH 监听在哪、防火墙开没开。
这里最需要扭转的认知是:公网 IPv6 是"默认可达"的,别以为地址长、没规律就没人会注意到。 扫描器扫的是整个网段(而且 IPv6 前缀分配是有规律的),不是靠猜地址。
想快速判断自己是不是已经被"重点照顾",可以看失败登录记录:
lastb | head -20 # 最近的失败登录
lastb | wc -l # 总数
如果短时间内就有成百上千条、来源地址各不相同但尝试节奏高度一致,那基本可以确定是自动化爆破。数量本身不吓人,真正需要警惕的是"你的机器已经被扫描器列进清单了"这个事实。
同样的思路可以推广到所有新上线的机器:先去确认暴露面,再开始装东西。顺序反过来的话,你会在一堆敞着的服务上做部署,而暴露面这件事就被忘在脑后了。