云服务器防 SSH 暴力破解:Fail2ban + 密钥登录加固实战
一、为什么要做这件事
新买的云服务器开机不到一小时,查看认证日志往往就能看到成百上千条“Failed password for root”。这些都是自动化脚本在撞库。只要密码足够弱,或者某个测试账号忘了删,服务器就可能沦为挖矿机。本文的目标很简单:让暴力破解在技术上没有意义,同时让异常 IP 自动被封。
环境:Ubuntu 22.04,root 或具备 sudo 权限的用户。
二、先看看自己正在被谁攻击
在 Ubuntu 上,SSH 认证日志位于 /var/log 目录下的 auth 日志中,可以直接用 journalctl 查看:
sudo journalctl -u ssh --since "1 hour ago" | grep "Failed password" | head
统计尝试次数最多的 IP:
sudo journalctl -u ssh --since today | grep "Failed password" \
| grep -oE "from [0-9.]+" | awk '{print $2}' | sort | uniq -c | sort -rn | head
如果你看到某个 IP 一天尝试了几千次,就说明加固刻不容缓。
三、第一步:改用密钥登录
在本地电脑生成密钥(推荐 ed25519):
ssh-keygen -t ed25519 -C "my-laptop"
把公钥推到服务器:
ssh-copy-id -i 私钥路径 deploy@服务器IP
推完后务必新开一个终端测试密钥能否登录,确认成功前不要关掉当前会话,避免把自己锁在门外。
四、第二步:关闭密码登录和 root 直登
编辑 SSH 服务端配置(/etc/ssh 目录下的 sshd_config),修改或添加以下几行:
PermitRootLogin no
PasswordAuthentication no
KbdInteractiveAuthentication no
PubkeyAuthentication yes
MaxAuthTries 3
LoginGraceTime 30
检查语法并重载:
sudo sshd -t && sudo systemctl reload ssh
注意:Ubuntu 22.04 的配置目录 sshd_config.d 中可能有云厂商预置的文件把 PasswordAuthentication 又打开了,用下面的命令确认最终生效值:
sudo sshd -T | grep -E "passwordauthentication|permitrootlogin"
五、第三步:安装并配置 Fail2ban
密钥登录已经让暴力破解几乎不可能成功,但扫描流量仍会占用日志和连接。Fail2ban 会读取日志,发现某 IP 在短时间内多次失败后,自动调用防火墙封禁它。
安装:
sudo apt update && sudo apt install -y fail2ban
不要直接修改默认配置,而是在 /etc/fail2ban 目录下新建名为 jail 的 local 本地配置文件,写入:
[DEFAULT]
bantime = 1h
findtime = 10m
maxretry = 5
backend = systemd
ignoreip = 127.0.0.1/8 ::1
[sshd]
enabled = true
port = ssh
mode = aggressive
[recidive]
enabled = true
bantime = 1w
findtime = 1d
maxretry = 3
说明:
1. backend 设为 systemd,直接读 journal,适配新版 Ubuntu。
2. mode = aggressive 会把“无效用户”“预认证断开”等也算作失败。
3. recidive 是“惯犯”规则:一天内被封 3 次的 IP 直接封一周。
4. 如果你有固定办公出口 IP,务必加进 ignoreip,防止误封自己。
启动并设置开机自启:
sudo systemctl enable --now fail2ban
查看状态:
sudo fail2ban-client status sshd
输出中的 Currently banned 和 Banned IP list 就是当前被封的地址。
手动解封某个 IP:
sudo fail2ban-client set sshd unbanip 1.2.3.4
六、第四步:配合云安全组
Fail2ban 是主机层防护,云安全组是网络层防护,两者叠加效果最好:
1. 安全组里 22 端口只对自己的出口 IP 放行,是最彻底的办法。
2. 如果出口 IP 不固定,可以把 SSH 改到高位端口(例如 22022),在 sshd_config 中设置 Port,并同步修改安全组和 jail 本地配置里的 port。改端口不能代替加固,但能大幅减少日志噪音。
七、用 Python 统计攻击来源
下面这个小脚本读取 journal 输出,统计失败次数最多的前 10 个 IP,并给出被 Fail2ban 封禁的数量,适合放进 cron 每天跑一次:
import re
from subprocess import run
from collections import Counter
def failed_ips(since="today"):
out = run(
["journalctl", "-u", "ssh", "--since", since, "--no-pager"],
capture_output=True, text=True, check=False
).stdout
pattern = re.compile(r"(?:Failed password|Invalid user).*?from (\d+\.\d+\.\d+\.\d+)")
return Counter(pattern.findall(out))
def banned_count():
out = run(
["fail2ban-client", "status", "sshd"],
capture_output=True, text=True, check=False
).stdout
m = re.search(r"Currently banned:\s+(\d+)", out)
return int(m[1]) if m else 0
if __name__ == "__main__":
stats = failed_ips()
print(f"今日失败尝试总数: {sum(stats.values())}")
print(f"当前封禁 IP 数: {banned_count()}")
for ip, n in stats.most_common(10):
print(f"{ip:<16} {n}")
把脚本保存为 ssh_report(不带扩展名也可以,加上执行权限并在首行写 python3 解释器声明),用 sudo 运行即可。
八、上线检查清单
1. 密钥登录可用,且已在新终端验证。
2. sshd -T 显示 passwordauthentication no、permitrootlogin no。
3. fail2ban-client status sshd 正常,有封禁记录。
4. ignoreip 已加入自己的固定 IP。
5. 安全组规则与 SSH 实际端口一致。
九、总结
防 SSH 暴力破解的核心思路是“让猜密码这件事不存在”:密钥登录 + 禁用密码解决根本问题,Fail2ban 负责清理噪音并封禁惯犯,安全组在网络层收窄入口。三层加起来,一台小云服务器也能安心地暴露在公网上。
- 点赞
- 收藏
- 关注作者
评论(0)