如果你有一台公网服务器,打开登录日志看一下,会发现几乎每时每刻都有人在尝试登录——来自世界各地的 IP,用常见用户名组合着密码一轮一轮地爆破。SSH 是管理服务器的命门,把它加固好,是每一台公网服务器上线前的必修课。
这篇文章记录我自己给服务器做 SSH 加固的完整过程:从密钥登录、关闭密码认证,到 fail2ban 与防火墙,最后附上一份可以直接参考的 sshd_config 配置。
第一步:先看看谁在爆破
加固之前,先了解一下威胁有多大。在 Debian/Ubuntu 上,SSH 登录日志在 /var/log/auth.log;CentOS/Rocky 上在 /var/log/secure。也可以直接用 journalctl 看:
# Debian / Ubuntu
journalctl -u ssh --since "24 hours ago" | grep "Failed password" | wc -l
# 看一眼最近是谁在尝试
journalctl -u ssh --since "24 hours ago" | grep "Failed password" | tail -20
如果数字是几十上百甚至更多,说明你的 22 端口已经暴露在扫描器之下。接下来做的每一件事,都是在把这些尝试挡在门外。
第二步:用密钥登录代替密码
密码是可以被猜的,而密钥对几乎不可能被暴力破解。SSH 密钥由「私钥 + 公钥」组成:公钥放在服务器上,私钥保存在你自己的电脑里,登录时用私钥签名,服务器用公钥验证。
在自己电脑上生成密钥对(推荐 ed25519,比 RSA 更短更快更安全):
ssh-keygen -t ed25519 -C "my-server-key"
# 一路回车即可,也可以设置一个私钥口令(passphrase)作为第二道保险
然后把公钥装到服务器上:
ssh-copy-id -i ~/.ssh/id_ed25519.pub user@服务器IP
# 如果服务器 SSH 端口不是 22,加 -p 参数:
# ssh-copy-id -p 2222 -i ~/.ssh/id_ed25519.pub user@服务器IP
装好后先别急着关密码登录,先在另一个终端用密钥登录验证一次:
ssh -i ~/.ssh/id_ed25519 user@服务器IP
# 能直接登录(不需要输密码,只输私钥口令),说明密钥配置成功
密钥登录验证通过之前,千万不要关闭密码认证,否则可能把自己锁在门外。
第三步:关闭密码认证,禁止 root 直登
密钥验证通过后,就可以收紧 /etc/ssh/sshd_config 了。用 sudo nano /etc/ssh/sshd_config(或 vim)编辑,找到并修改以下几项:
# 1. 关闭密码登录(只允许密钥)
PasswordAuthentication no
# 2. 禁止 root 直接用密码/密钥登录
PermitRootLogin no
# 3. 允许公钥认证(确保这一项是 yes)
PubkeyAuthentication yes
# 4. 不允许空密码账户登录
PermitEmptyPasswords no
关于 PermitRootLogin:日常管理用普通用户 + sudo 就足够了。如果确实需要 root 远程登录(不太推荐),至少改成 prohibit-password,即只允许 root 用密钥登录。
改完先校验语法,再平滑重载(reload 不会断开现有连接):
sudo sshd -t # 语法检查,有错误会提示
sudo systemctl reload sshd
第四步:修改默认端口(可选但有效)
把 22 换成高位端口,能直接过滤掉绝大多数按默认端口扫描的脚本。比如改成 2222:
Port 2222
改端口的三个注意点:
- 先测试再切换:改完后用
ssh -p 2222 user@服务器IP在新端口登录一次,确认能进再考虑关掉旧端口; - 记得放行防火墙:新端口要在 ufw / firewalld / 云安全组里放行,否则你会发现自己连不上了;
- 端口只是"障眼法":它挡得住脚本,挡不住有心人,必须和密钥认证、fail2ban 配合使用。
第五步:限制谁能登录
如果你的服务器只给固定几个人用,可以直接限定可登录的用户或用户组,其他用户一律拒绝:
# 只允许 user1、user2 登录
AllowUsers user1 user2
# 或者只允许某个组登录(推荐:把管理员都放进 sudo 组)
AllowGroups sudo
注意:AllowUsers / AllowGroups 二者只需配一个,如果都配,则用户必须同时满足两条规则。
第六步:登录与超时策略
进一步收紧登录环节,减少被拖住的窗口:
# 登录提示超时:60 秒内没输完密码/密钥口令就断开
LoginGraceTime 60
# 单次连接最多尝试 3 次认证
MaxAuthTries 3
# 空闲连接 5 分钟无操作后开始探测,连续 2 次无响应则断开
ClientAliveInterval 300
ClientAliveCountMax 2
其中 ClientAliveInterval / ClientAliveCountMax 是为了回收挂死的连接,避免一堆幽灵会话占着资源。修改这些配置同样记得 sudo sshd -t 后 sudo systemctl reload sshd。
第七步:用 fail2ban 防暴力破解
即使关闭了密码认证,fail2ban 仍然值得装——它是「事后防御」的最后一道闸:检测到某个 IP 多次认证失败,就自动把它拉黑一段时间。
# Debian / Ubuntu
sudo apt install fail2ban
# CentOS / Rocky
sudo dnf install fail2ban
配置 /etc/fail2ban/jail.local(这个文件优先级高于 jail.conf,升级不会覆盖):
[sshd]
enabled = true
port = ssh # 如果改了端口,改成实际端口号,如 2222
maxretry = 5 # 5 次失败
findtime = 10m # 10 分钟内
bantime = 1h # 封禁 1 小时(可加大,如 24h)
重启并查看状态:
sudo systemctl enable --now fail2ban
sudo fail2ban-client status sshd # 能看到当前封禁的 IP 列表
注意:fail2ban 只是缓解手段,日志里那些爆破尝试依然会打到 SSH 上。真正把大门焊死的是「密钥登录 + 关闭密码认证」。
第八步:用防火墙收窄暴露面
最后用防火墙把服务器对外端口收窄:除了 SSH 和必须的服务端口,其余一律拒绝。以 ufw 为例:
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 2222/tcp # SSH 新端口
sudo ufw allow 80/tcp # HTTP
sudo ufw allow 443/tcp # HTTPS
sudo ufw enable
如果服务器在云上(阿里云、腾讯云、AWS 等),云平台的安全组同样要同步收窄——安全组是流量进服务器的第一道关卡,防火墙是第二道。
进阶:双因素认证(可选)
想要更强的保障,可以给 SSH 加 Google Authenticator 双因素认证(需要 PAM 模块支持):
sudo apt install libpam-google-authenticator
google-authenticator # 按提示绑定手机 App,生成 6 位动态码
然后配置 PAM 与 sshd 启用验证码校验。加上之后,即使私钥泄露,攻击者没有你的手机也登录不了。不过要注意:双因素会稍微增加自己登录的麻烦,是否启用看你的安全需求。
附:一份完整的加固参考配置
下面是我整理的 sshd_config 关键项汇总,可直接对照调整:
# /etc/ssh/sshd_config(关键项)
Port 2222
# 认证方式
PubkeyAuthentication yes
PasswordAuthentication no
PermitEmptyPasswords no
KbdInteractiveAuthentication no
# 账户
PermitRootLogin no
AllowGroups sudo
# 登录策略
LoginGraceTime 60
MaxAuthTries 3
ClientAliveInterval 300
ClientAliveCountMax 2
# 会话与转发(按需开启)
X11Forwarding no
AllowTcpForwarding yes
MaxSessions 4
# 协议版本
Protocol 2
改完执行:
sudo sshd -t && sudo systemctl reload sshd
最重要的一步:别把自己锁在门外
所有加固操作里,最大的风险不是被攻击,而是配置出错把自己关在服务器外面。我踩过一次,所以把这几条经验写在这里:
- 开一个 tmux / screen 会话再改配置:即使连接断了,会话还活着,重新连上就能恢复;
- 保持一个已登录的终端不动:作为"逃生窗口",万一新配置有问题还能从这个会话里改回来;
- 每次修改都先
sshd -t校验:语法错误会导致 sshd 拒绝启动; - 用 reload 而不是 restart:reload 平滑重载,不会中断现有连接;
- 改端口、关密码这类"高风险"操作,先开新终端测试通过再切换,不要在同一个会话里改完就退出。
收尾:养成看日志的习惯
加固不是一次性的事。隔一段时间看一眼登录日志,确认没有异常:
journalctl -u ssh --since "7 days ago" | grep "Accepted"
只看到你自己的登录记录,说明防线是稳的。折腾服务器这件事,越往后越发现:安全不是某一个神操作,而是一堆小习惯叠在一起的结果。