上个月凌晨两点多,一个运维朋友给我发了条消息,说他管的一台云主机 CPU 曲线飘成一条直线,登录上去一看,/var/log/auth.log 已经堆到 60 多 MB,全是同一个 IP 反复尝试不同密码的记录。那台机器上跑着一个没人管的小站点,密码还是三个月前设的,说不上复杂但绝对算不上强。
他把日志翻给我看,我数了一下:不到 9 个小时,失败记录 31247 条,来源 IP 有 940 多个,遍布十几个国家。也就是说,全世界的扫描器一天绕地球好几圈,只为试一下你 root 的密码是不是 123456。这不是针对他的攻击,这是自动化扫描 indiscriminately 扫全网,他只是刚好在线上。
更让人后背发凉的是,那台机器的 SSH 端口是默认的 22,密码登录开着,root 允许直接远程登,Fail2ban 没装,密钥登录也没配。所有的口子都对着外面敞开,只差别人运气好一点、字典刚好里有他的密码。
后来我把这套东西重新捋了一遍,帮他把口子一个一个堵上,也顺手在自己几台机器上复用了。这篇就把整个过程写成一份可以照着做的清单,主要说四件事:怎么从日志里看出机器有没有被盯上、怎么把密码登录彻底关掉换成密钥、Fail2ban 到底怎么配才不误伤自己、以及那些「防住了 SSH 却漏了后门」的收尾动作。
如果你正在给服务器做安全加固、或者想找一台不用一直操心被扫描的机器,把这两家先放心里:狗云DOGYUN(高性价比云服务器,多机房可选)、RackNerd(全球VPS,特价常推)。机器本身性能是一回事,能不能在你半夜睡着的时候替你把第一道门守住,是另一回事,下面这几步比选机器更关键。

一、先看日志:三个数字就能判断机器有没有被盯上
第一步别急着改配置,先搞清楚现状。登录服务器之后先做三件事。
第一,看失败次数和来源分布:
grep "Failed password" /var/log/auth.log | wc -l
再看这些失败的来源有多少个不同 IP:
grep "Failed password" /var/log/auth.log | awk '{print $9}' | sort | uniq -c | sort -rn | head -20
如果单个 IP 的失败次数在几千以上,那是有人在专门针对你这台机器,字典攻击或者针对具体业务的组合爆破。这时候光装 Fail2ban 是不够的,得先确认密码没被撞开。第三,看有没有成功的登录记录混在里面:
grep "Accepted password\|Accepted publickey" /var/log/auth.log | tail -50
这一步是我最看重的地方。Failed 多不可怕,全世界都在扫你;可怕的是 Failed 里面夹着 Accepted,尤其是 Accepted password 说明有密码真的被猜中并登录成功了。如果看到陌生的 Accepted password,配合下面几条命令确认一下是不是留了后门:
last -a | head -30
cat /etc/passwd | awk -F: '$3==0 {print $1":"$7}'
第二条命令会列出所有 UID 为 0 的账号,正常情况下只有 root 一个。如果多出别的,那基本可以确定机器已经被拿下了,接下来要做的不是加固而是隔离、取证、重装。

二、第一步防线:把密码登录关掉,换成密钥登录
这是整套方案里收益最大的一步。密码爆破的本质是猜一个有限的字符组合,密钥认证的本质是证明你持有一把没被泄露的私钥 攻击者没有对应私钥,字典再大也没用。这一步做对了,后面 Fail2ban 那些配置其实都只是降低噪音的辅助手段。
生成密钥对。别在服务器上生成,在你自己机器上生成,私钥永远不要上传到任何服务器:
ssh-keygen -t ed25519 -C "prod-web-01"
把公钥传上去(把 root@改成你自己的 IP):
ssh-copy-id -i ~/.ssh/id_ed25519.pub root@your.server.ip
或者手动把公钥内容追加到服务器的 /root/.ssh/authorized_keys 里,注意权限必须是 700 / 600,不然 sshd 会直接拒绝这把钥匙。
确认能用密钥登进去之后,再去改 sshd 配置:
vi /etc/ssh/sshd_config
把这几项改掉:
PermitRootLogin prohibit-password
PasswordAuthentication no
PubkeyAuthentication yes
第一行的意思是 root 依然可以登录,但只能用密钥,不能用密码。第二行彻底关掉密码认证,第三行确保密钥认证开着。改完 systemctl reload sshd 生效,然后不要关掉当前这个 SSH 窗口,另开一个新终端试一次能不能登进去,确认成功了再关旧窗口。这是我给所有人提的醒:改 SSH 配置最怕的就是把自己锁在外面,等到需要重启机器才发现自己进不去。
还有个细节:sshd_config 里如果写了 UsePAM yes,那它会覆盖后面的部分配置;反过来写 UsePAM no 又会让密钥和某些认证方式失效。改之前先备份一份 cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak,并且用 sshd -t 检查语法,语法错了 reload 是不会成功的。

三、第二道防线:Fail2ban 到底怎么配才不误伤自己
Fail2ban 的原理不复杂:它盯着日志,一旦某个 IP 在时间窗口内失败次数超过阈值,就通过 iptables 或 firewalld 把这个 IP 的流量直接丢掉一段时间。装起来很简单:
apt update && apt install -y fail2ban
但默认配置有两个坑。第一个是 bantime 默认只有 10 分钟,攻击者等十分钟又能继续试,十小时下来照样试几十万次;第二个是 findtime 默认 10 分钟内失败 5 次就封,这个阈值对正常输错密码的人来说太敏感了 你自己手抖输错三次,再输两次就被自己封在外面了,只能去控制台用 fail2ban-client unban 解救。
我一般这么配,/etc/fail2ban/jail.local:
[DEFAULT]
bantime = 1h
findtime = 10m
maxretry = 6
bantime.increment = true
bantime.factor = 2
bantime.maxtime = 1d
这几行的意思是:10 分钟内错 6 次就封 1 小时;随着同一个 IP 继续作恶,封禁时间每次翻倍(1 小时 → 2 小时 → 4 小时),最多封到 1 天。递增封禁对付那种「被封了就换 IP、隔一小时再来」的持续扫描特别有效,因为它会记住这个 IP 的历史。
然后是 SSH 这个 jail,Debian 系默认 sshd 的日志在 /var/log/auth.log,RHEL 系在 /var/log/secure, jails 文件里默认的 backend = auto 一般能自己认出来,但我在 CentOS 上遇到过认错的情况,日志里明明有记录 jail 却不计数,那就要手动写 backend = systemd。改完执行 systemctl restart fail2ban,然后一定要验证:
fail2ban-client status sshd
能看到 Currently banned 之类的统计就说明生效了。再用 fail2ban-client status sshd 看一眼被封的 IP 列表是不是在增长,如果一直是 0,说明日志路径没认对,回去检查 backend。
白名单别忘了加,否则你要是从固定办公 IP 或者跳板机登服务器,手抖输错几次就被封,很影响心情:
[sshd]
ignoreip = 127.0.0.1/8 ::1 223.104.1.0/24
改完记得重启 fail2ban 让白名单生效,不然你已经被封了的话,ignoreip 救不了当下的你。

四、别只盯着 22 端口:容易被忽略的四个收尾动作
前面三步做完,SSH 这条路基本封死了。但我见过的被入侵案例里,相当一部分不是从 22 端口进来的,而是从别的口子。收尾这部分我一般按这个顺序检查。
第一,改 SSH 端口。改端口不能算真正的安全措施,它只是把扫描器挡在门外、减少日志噪音,作用有限但成本极低,还是值得做。改之前记得同步改安全组和防火墙,只改 sshd_config 会把自己关在外面。
第二,看 crontab 和 systemd 定时任务。攻击者拿到权限后,最喜欢做的事就是写个定时任务保持驻留:
crontab -l
ls -la /etc/cron.*/ /var/spool/cron/
systemctl list-timers --all
第三,检查异常账号和 sudo 权限:
awk -F: '$3>=1000 {print $1}' /etc/passwd
cat /etc/sudoers.d/ 2>/dev/null; grep -v '^#' /etc/sudoers | grep -v '^$'
第四,看有没有新增的账号密钥:
for f in /root/.ssh/authorized_keys /home/*/.ssh/authorized_keys; do echo "== $f"; cat "$f" 2>/dev/null; done
这四条查完,再配合前面看 auth.log 的那几行,基本能把「门是被哪把钥匙打开的」问出个大概。要是发现陌生的 authorized_keys 里的公钥、陌生的定时任务、陌生的账号,那基本可以确认被拿下了 这时候最省事的做法是重装系统并从干净备份恢复数据,而不是在已经被污染的系统上继续加固。
五、把这套动作变成日常习惯
上面这些事一次做完,大概花不了一个小时。但服务器不是装完就完的机器,后面还会加新服务、新端口、新同事的账号,所以最好做成流程。
我的习惯是:新机器上线前先跑一遍上面这四条命令确认基线干净;每周看一眼 fail2ban-client status 的封禁数和 auth.log 的失败曲线;每季度轮换一次 SSH 密钥和服务器账号密码。另外这台机器上跑的到底是什么服务、开放了哪些端口,我都会记在一个 Markdown 清单里放仓库,交给下一个接手的人 交接文档比任何加固工具都管用。
说到底,SSH 爆破这件事防起来并不难,难的是「觉得没人会盯上我」的那份侥幸。全球的扫描器一天能扫几百万台机器,你的机器只要在线,就一定会被扫到。区别只在于被扫到的时候,你是有准备的,还是在等着看运气。


喜欢
顶
难过
囧
围观
无聊



