Linux 进阶 L19:SSH 加固——把黑客挡在门外
你的服务器每时每刻都在被陌生人敲门。这不是危言耸听,下面这段日志就截自我自己的这台博客服务器(今天刚抓的):
$ journalctl -u ssh | grep "Failed password" | tail -3
Oct 10 11:58:23 ip-172-26-2-80 sshd-session[1006426]: Failed password for root from 195.178.110.227 port 56700 ssh2
Oct 10 11:59:48 ip-172-26-2-80 sshd-session[1006649]: Failed password for root from 195.178.110.227 port 59918 ssh2
看不懂没关系,关键信息就三个:有人(195.178.110.227)、在试 root 账号的密码、失败了。这种扫描全网的机器 24 小时不停,你的服务器上线第一天就会被光顾。密码再复杂,也架不住日夜不停的字典爆破。
好消息是:SSH 加固做完,爆破就彻底失效——因为服务器根本不再接受密码登录。这篇把 SSH 加固从头到尾做一遍,全是真机实操。
先看清现状:你的 SSH 现在什么配置
打开服务器上的 SSH 配置文件看看(Debian 13,OpenSSH 10.0):
$ sshd -V
OpenSSH_10.0p2 Debian-7+deb13u4, OpenSSL 3.5.7 9 Jun 2026
$ grep -vE "^\s*#|^\s*$" /etc/ssh/sshd_config
PermitRootLogin yes
PubkeyAuthentication yes
PasswordAuthentication yes
KbdInteractiveAuthentication no
解读一下这三行 yes:
PermitRootLogin yes—— 允许 root 直接登录。黑客最爱,因为用户名都不用猜,全世界的 Linux 都有 root。PubkeyAuthentication yes—— 允许密钥登录。好东西,留着。PasswordAuthentication yes—— 允许密码登录。这就是黑客正在利用的那扇门。
默认配置等于"大门敞开,欢迎试密码"。加固的目标就是:关掉密码这扇门,只留密钥这条小路。
密码登录和密钥登录,到底差在哪
打个比方:
密码登录 = 你家门上有个密码键盘,任何人都能走过去试。试错了也没代价,换个密码继续试。
密钥登录 = 你家门只能用一把特制的钥匙开。钥匙分两半:一半(公钥)放在门上,一半(私钥)揣你兜里。没钥匙的人连"试"的机会都没有——服务器直接拒绝,连密码输入框都不给你。
密钥有多难破解?我们用的是 ed25519 算法,256 位。暴力破解它需要的算力,比把全宇宙的原子都用上还不够。密码是"难猜",密钥是"猜不动",这是质的区别。
第一步:生成你的密钥对
在你自己的电脑(不是服务器)上运行:
$ ssh-keygen -t ed25519 -C "my-laptop"
Generating public/private ed25519 key pair.
Your identification has been saved in /tmp/demo_key
Your public key has been saved in /tmp/demo_key.pub
The key fingerprint is:
SHA256:7QHleGlij0LPrOhc6fK6r/sIBWruu24Zzbe9OwgYFJ8 demo@local
The key's randomart image is:
+--[ED25519 256]--+
| .. . |
| .. . + . |
|. .E . = = |
| o . . = O |
|..= . . S + |
|oo = o + . . |
| .+ + B . |
|.o +.* o |
|o=o OX=o+ |
+----[SHA256]-----+
(上面是在服务器上演示生成的,用完就删了,你在自己电脑上跑,路径会是 ~/.ssh/id_ed25519。)
逐句解读:
-t ed25519—— 指定算法。ed25519 是目前最推荐的:又短又快又安全,比老式的 rsa-2048 强。-C "my-laptop"—— 注释,随便写,方便以后认出"这是哪台电脑的钥匙"。运行后会问你存哪、要不要设密码(passphrase):直接回车用默认路径;passphrase 建议设一个,等于给钥匙再加一把锁,私钥丢了也不怕。
生成完得到两个文件,千万分清:
公钥长这样(一行):
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEJ1WNUPwAJHWi24DZWWBhFjBB0VrVtw4BiYFqdYI/u+ demo@local
第二步:把公钥送上服务器
最省事的方法,一条命令搞定(在你电脑上运行,把 your-server-ip 换成服务器 IP):
$ ssh-copy-id root@your-server-ip
它会让你输一次密码(最后一次输密码的机会,珍惜),然后自动把公钥追加到服务器的 ~/.ssh/authorized_keys 文件里。
手动党也可以自己来,分两步:
# 1. 在你电脑上:显示公钥,复制整行
$ cat ~/.ssh/id_ed25519.pub
# 2. 登录服务器:粘贴进去
$ mkdir -p ~/.ssh && chmod 700 ~/.ssh
$ nano ~/.ssh/authorized_keys # 粘贴公钥,保存
$ chmod 600 ~/.ssh/authorized_keys
注意两个权限数字:~/.ssh 目录必须是 700,authorized_keys 必须是 600。权限不对,SSH 会直接无视你的公钥——这是很多人"密钥配好了却登不上"的头号原因。
第三步:验证密钥登录(重要,别跳过)
先别急着关密码! 新开一个终端窗口,用密钥试登录:
$ ssh -i ~/.ssh/id_ed25519 root@your-server-ip
如果直接进去了、没问你要密码,说明密钥通道通了。再确认一遍服务器认出了你的钥匙:
$ ssh root@your-server-ip "cat ~/.ssh/authorized_keys" | cut -c1-60
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEJ1WNUPwAJHWi24DZWWBhFjBB0VrVtw4...
两个窗口都保持登录状态——这是你的"安全绳"。万一后面改错了配置,还有个活着的会话能进去修。老手翻车,十有八九是把自己锁在门外还断了所有连接。
第四步:关闭密码登录(核心动作)
确认密钥能用之后,编辑服务器上的 SSH 配置:
$ sudo nano /etc/ssh/sshd_config
找到这一行,改成 no:
PasswordAuthentication no
保存后重启 SSH 服务让配置生效:
$ sudo systemctl restart ssh
$ sudo systemctl status ssh --no-pager | head -3
● ssh.service - OpenBSD Secure Shell server
Loaded: loaded (/usr/lib/systemd/system/ssh.service; enabled; ...)
Active: active (running) since ...
验证:从你电脑上故意用密码试一次(加 -o PreferredAuthentications=password 强制走密码):
$ ssh -o PreferredAuthentications=password root@your-server-ip
root@your-server-ip: Permission denied (publickey).
Permission denied (publickey) —— 服务器连密码输入框都不给了,只认密钥。爆破脚本从此对你这台机器彻底失业:它手里没有你的私钥,试一亿个密码也没用。
第五步:禁止 root 直接登录
root 是全网都知道的用户名,等于告诉黑客"用户名我已经告诉你了,你只管猜密码"。配合密钥登录,正确姿势是:用普通用户登录,再 sudo 提权。
# 在服务器上:先确保你有个带 sudo 权限的普通用户,并且它的 authorized_keys 里也有你的公钥
$ sudo useradd -m -s /bin/bash deploy
$ sudo usermod -aG sudo deploy
$ sudo mkdir -p /home/deploy/.ssh && sudo cp ~/.ssh/authorized_keys /home/deploy/.ssh/
$ sudo chown -R deploy:deploy /home/deploy/.ssh && sudo chmod 700 /home/deploy/.ssh && sudo chmod 600 /home/deploy/.ssh/authorized_keys
# 确认 deploy 能用密钥登录后,再改配置
$ sudo nano /etc/ssh/sshd_config
改成:
PermitRootLogin no
重启 ssh 后,ssh root@your-server-ip 会直接被拒绝,只能 ssh deploy@your-server-ip 再 sudo -i。攻击面又小一圈。
第六步:换个端口?(可选,聊聊真相)
很多人加固教程第一条就是"把 22 端口改掉"。说实话:换端口防不住定向攻击,只能挡住扫全网的脚本小子——但挡掉 99% 的噪音日志也是实实在在的好处,日志干净了,真有事你才看得见。
$ sudo nano /etc/ssh/sshd_config
# 把 #Port 22 改成(比如)
Port 2222
$ sudo systemctl restart ssh
# 记得防火墙放行新端口!不然自己都进不来
$ sudo ufw allow 2222/tcp
登录时加上 -p:
$ ssh -p 2222 deploy@your-server-ip
⚠️ 血泪提醒:改端口前,务必确认防火墙放行了新端口,云服务商的安全组也要放行。先开新端口测试能连,再考虑关 22。我见过太多人改完端口、防火墙没开,直接失联。
第七步:Fail2Ban 兜底(纵深防御)
关了密码登录之后,爆破已经不可能成功。但扫描器还是会不停敲门,写满你的日志。Fail2Ban 就是"看门大爷":谁短时间内失败太多次,直接拉黑 IP。
我这台服务器上它正在工作,看看它的"战绩":
$ sudo fail2ban-client status sshd
Status for the jail: sshd
|- Filter
| |- Currently failed: 0
| |- Total failed: 7
| `- Journal matches: _SYSTEMD_UNIT=ssh.service + _COMM=sshd
`- Actions
|- Currently banned: 0
|- Total banned: 1
`- Banned IP list:
解读:累计拦截了 7 次失败登录,封过 1 个 IP。配合"只认密钥"的配置,现在这台服务器的状态是:密码试不进来(配置层面拒绝),乱试的 IP 还会被封(Fail2Ban 层面拉黑)。双保险。
Fail2Ban 的安装配置我之前专门写过一篇(《Fail2Ban 防护:自动封禁 SSH 爆破 IP》),这里不重复,记住它的定位就行:它是"减噪音"的,不是"防入侵"的。真正的防线是密钥登录,Fail2Ban 只是让日志清净、顺手挡掉低级扫描。
第八步(进阶):密钥 + 密码双因素
偏执狂专用:要求"既有钥匙,又要对上暗号"才放行。适合放了重要数据的机器。
$ sudo nano /etc/ssh/sshd_config
# 取消注释或添加:
AuthenticationMethods publickey,password
PasswordAuthentication yes # 注意:这里要重新打开,但只在"已有密钥"之后才问密码
重启后登录流程变成:先验证密钥 → 再问密码。两道都过才进门。缺点是自动化脚本(比如 rsync 定时备份)会变麻烦,按需使用。
急救包:万一把自己锁门外了怎么办
先别慌,按这个顺序自救:
检查是不是还有活着的 SSH 会话——没断开的那个窗口就是救命稻草,直接进去改配置。
云服务商的网页控制台:Lightsail / 各大云都有浏览器里的紧急 SSH(走的是独立通道,不走你改坏的那个 sshd 配置,大多数情况下还能进)。我这台 Lightsail 就留着这条后路。
VNC / 救援模式:终极手段,挂载磁盘改文件,慢但是稳。
预防胜于治疗:每次改
sshd_config之前,先cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak备个份;改完用sshd -t检查语法,通过了再重启:
$ sudo sshd -t && echo "语法 OK" && sudo systemctl restart ssh
sshd -t 不输出就是没毛病,有毛病它会直接告诉你第几行写错了。
小结
SSH 加固的本质就一句话:把"人人都能试"变成"没钥匙连试的资格都没有"。做完前两行(密钥登录、禁 root),你的服务器就已经比 95% 的公网机器安全了。剩下的,是让日志更干净、让偏执狂更安心。
下一篇预告:防火墙进阶——ufw 只是开胃菜,iptables/nftables 才是真正的包过滤,下期带你看透 Linux 防火墙的五条链。