CentOS 服务器在公网直接暴露 22 端口,几乎每分钟都在承受暴力破解。查看 /var/log/secure 日志,你会看到成千上万条来自不同 IP 的失败登录尝试。即便你配置了强密码,这种持续的攻击仍然消耗服务器资源,增加日志噪音,并带来潜在风险。把 SSH 登录方式从密码认证切换到证书认证,再结合端口敲门技术,可以从根本上解决这个问题。证书登录杜绝了密码被猜解的可能,端口敲门则让 SSH 服务对未授权扫描完全隐形。下面直接进入配置流程,不绕弯子。

生成并配置 SSH 密钥对

首先在本地电脑上生成密钥对。如果你用的是 Windows 的 PowerShell、Linux 或 macOS 终端,执行以下命令。建议使用 ed25519 算法,它在安全性和性能上都优于 RSA。

ssh-keygen -t ed25519 -C "your-comment-here"

系统会提示你输入保存路径和口令。口令可以为密钥增加一层保护,如果不想每次连接都输入,可以留空。命令执行完毕后,会在 ~/.ssh/ 目录下生成两个文件:id_ed25519 是私钥,必须严格保密,权限应为 600;id_ed25519.pub 是公钥,需要上传到服务器。

接下来把公钥部署到 CentOS 服务器。最简便的方法是使用 ssh-copy-id 命令。如果你的服务器目前还只能用密码登录,执行这条命令,按提示输入密码即可自动完成公钥追加。

ssh-copy-id -i ~/.ssh/id_ed25519.pub root@your-server-ip

如果没有 ssh-copy-id 工具,可以手动操作。先登录服务器,在用户家目录下创建 .ssh 目录并设置好权限,然后把本地公钥文件内容追加到服务器上的 authorized_keys 文件中。

mkdir -p ~/.ssh
chmod 700 ~/.ssh
echo "your-public-key-content" >> ~/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keys

公钥部署完成后,先不要急着关闭密码登录。新开一个终端窗口,测试证书登录是否成功。如果连接时提示输入私钥口令或直接登录成功,说明密钥配置无误。

加固 SSH 服务配置

确认证书登录可用后,开始修改 SSH 服务配置文件 /etc/ssh/sshd_config,彻底禁用密码认证并关闭 root 密码登录。用文本编辑器打开该文件,找到并修改以下几项。如果某项前面有 # 号,记得去掉注释并修改其值。

PermitRootLogin prohibit-password
PubkeyAuthentication yes
PasswordAuthentication no
ChallengeResponseAuthentication no
UsePAM yes

PermitRootLogin 设置为 prohibit-password 表示 root 用户只能通过密钥登录,这比直接设为 no 更安全,因为你仍然可以在紧急情况下使用密钥以 root 身份操作。PasswordAuthentication 设为 no 后,所有密码登录请求都会被拒绝。修改保存后,重启 SSH 服务使配置生效。

systemctl restart sshd

重启服务后,保持当前窗口不要断开。再开一个新终端测试连接,确认证书登录正常,而密码登录被拒绝。如果出现问题,你还能在已连接的窗口里回滚配置。这是操作服务器变更时的标准安全习惯。

理解端口敲门的设计思路

证书登录已经解决了密码被暴力破解的问题,但 SSH 服务本身仍然暴露在公网上。任何一个扫描器都能发现你的 22 端口在监听,并尝试利用可能存在的服务漏洞。端口敲门技术通过一种巧妙的思路解决这个问题:默认情况下,SSH 端口被防火墙完全关闭,对外部所有 IP 不可见。只有按照特定顺序访问几个指定端口之后,防火墙才会临时对来访 IP 开放 SSH 端口。这就像在门前敲出一段暗号,门才会打开。

端口敲门方案有很多实现方式,从简单的 iptables recent 模块,到专门的 knockd 守护进程,再到更现代的单包授权工具 fwknop。对于 CentOS 环境,knockd 是最均衡的选择,配置直观,资源占用极低,完全够用。

安装并配置 knockd

CentOS 7/8/9 的官方仓库通常不包含 knockd,需要先启用 EPEL 仓库再安装。

yum install epel-release -y
yum install knockd -y

安装完成后,编辑 knockd 的主配置文件 /etc/knockd.conf。默认内容是一个示例,需要根据实际情况修改。下面是一个典型配置,使用三个端口序列作为敲门暗号,敲门成功后临时开放 SSH 端口 30 秒。

[options]
        UseSyslog
        Interface = eth0

[openSSH]
        sequence    = 7000,8000,9000
        seq_timeout = 10
        command     = /sbin/iptables -I INPUT -s %IP% -p tcp --dport 22 -j ACCEPT
        tcpflags    = syn

[closeSSH]
        sequence    = 9000,8000,7000
        seq_timeout = 10
        command     = /sbin/iptables -D INPUT -s %IP% -p tcp --dport 22 -j ACCEPT
        tcpflags    = syn

Interface 需要填写你服务器的实际外网网卡名称,可以用 ip addr 命令查看,常见的是 eth0 或 ens33。sequence 定义了敲门端口序列,这里设置为 7000、8000、9000,你可以换成任意不常用的高端口。seq_timeout 规定必须在 10 秒内完成整个敲门序列,超时则需重来。command 中的 %IP% 是 knockd 自动替换为敲门来源 IP 的变量,无需手动修改。openSSH 段在敲门成功后执行 iptables 规则插入,放行来源 IP 访问 22 端口。closeSSH 段则用相反序列触发,删除之前插入的规则,关闭访问通道。

接下来配置 knockd 的启动参数。编辑 /etc/sysconfig/knockd 文件,或者直接修改 systemd 服务单元文件。如果该文件不存在,可以手动创建,内容如下。

OPTIONS="-i eth0"

同样,eth0 需要替换为你的实际网卡名称。然后启动 knockd 并设置开机自启。

systemctl start knockd
systemctl enable knockd

在启动 knockd 之前,还有关键一步:确保防火墙默认丢弃所有到 22 端口的流量。如果你使用 firewalld,执行以下命令移除 SSH 服务,并重载防火墙。

firewall-cmd --permanent --remove-service=ssh
firewall-cmd --reload

如果使用 iptables,需要添加一条 DROP 规则,并确保它在规则链中位于合适位置。注意,如果你的服务器没有其他管理方式,务必先确认 knockd 配置正确且已启动,再执行防火墙封堵操作,否则可能把自己锁在外面。

客户端敲门操作

配置完成后,SSH 端口 22 对外部是完全关闭的。你需要先完成敲门动作,才能进行 SSH 连接。在 Linux 或 macOS 终端,可以使用 knock 命令,这个工具通常随 knockd 一同安装。执行以下命令向服务器发送敲门序列。

knock your-server-ip 7000 8000 9000

如果本地没有 knock 命令,可以用 nmap 或者简单的 netcat 循环来敲门。用 nmap 的方式如下,每个端口单独发送一个 SYN 包。

for port in 7000 8000 9000; do nmap -Pn --max-retries=0 -p $port your-server-ip; done

用 netcat 的方式更轻量,但需要逐个端口执行。

nc -z your-server-ip 7000
nc -z your-server-ip 8000
nc -z your-server-ip 9000

敲门成功后,在 30 秒内使用 SSH 证书连接服务器即可。连接建立后,即使 iptables 规则被删除,已经建立的会话也不会中断。当你完成操作需要关闭访问通道时,可以执行关闭敲门序列,或者等待 knockd 自动清理。如果希望 openSSH 规则在一段时间后自动失效,可以在 command 中配合 at 命令或使用 iptables 的 recent 模块实现超时自动删除,但 knockd 默认不提供自动关闭功能,需要自行扩展。

对于 Windows 客户端,可以下载 knock 的 Windows 移植版本,或者使用图形化工具如 KiTTY(PuTTY 的衍生版本,内置端口敲门支持)。在 KiTTY 的连接设置中,找到 Connection > Data > Port Knocking 选项,填入敲门端口序列,保存后每次连接会自动完成敲门。

进阶:单包授权与 fwknop

端口敲门虽然隐蔽,但敲门包本身是明文传输的,网络上的监听者可以捕获到端口序列。单包授权是对端口敲门的进一步强化,它把敲门信息加密打包进单个数据包中,不仅隐藏了服务,还隐藏了敲门行为本身。fwknop 是实现单包授权的成熟方案,它通过 GPG 对称或非对称加密来保护敲门包,能有效防止重放攻击和序列嗅探。

在 CentOS 上部署 fwknop 同样需要 EPEL 仓库。安装 fwknop-server 包,然后根据官方文档配置访问策略和加密密钥。客户端使用 fwknop-client 发送加密的授权包。配置复杂度比 knockd 高出一个量级,但安全性也相应大幅提升。对于个人服务器或小团队使用,knockd 已经足够;如果你管理的是对安全要求极高的生产环境,值得投入时间部署 fwknop。

故障排查与安全建议

如果敲门后仍然无法连接,按顺序检查以下几点。首先确认 knockd 服务状态是否正常,用 systemctl status knockd 查看日志。其次检查防火墙规则是否正确插入,用 iptables -L -n 查看 INPUT 链中是否出现了来源 IP 的 ACCEPT 规则。如果规则没有出现,可能是网卡名称配置错误,或者 knockd 没有成功监听到敲门包。可以在服务器上运行 tcpdump 抓包,确认敲门包是否到达了服务器。

tcpdump -i eth0 'tcp port 7000 or tcp port 8000 or tcp port 9000'

关于密钥管理,私钥永远不要通过网络传输,也不要在多台设备间复制同一私钥。每台客户端设备生成独立的密钥对,把对应公钥都追加到服务器的 authorized_keys 文件中。如果某台设备丢失或不再使用,只需从服务器上删除对应的公钥行即可撤销访问权限。定期检查 authorized_keys 文件内容,清理不再使用的公钥。

对于端口敲门方案本身,不要把敲门端口设置成连续端口或常见端口,尽量选择 10000 以上的随机高端口。如果条件允许,配合 knockd 的自动关闭脚本,让开放窗口尽可能短。一个实用的做法是,在 openSSH 的 command 中加入一条 at 命令,在 60 秒后自动删除刚才插入的 ACCEPT 规则,这样即使忘记手动关闭,访问通道也会自动消失。

最后,始终保留一个本地控制台或带外管理通道作为应急手段。任何基于网络的安全策略都有失效的可能,防火墙规则配错、knockd 进程崩溃、网络波动导致敲门包丢失,这些情况都可能让你被挡在门外。有备用管理路径,你才能放心地在前沿安全配置上做各种尝试。