服务器暴露在公网上,SSH 端口每天被扫几千次是常态。密钥登录、非标端口、fail2ban 封 IP、二次验证……这些手段我都用过。它们各有各的效果,但也各有各的短板。

这篇文章讲的是另一种思路:端口敲门 (Port Knocking)——让 SSH 端口在扫描器眼里根本不存在,只有知道正确敲门序列的人才能打开它。

同时我会把到目前为止接触过的三种 SSH 防护方案放在一起做个横向对比,聊聊各自适合什么场景。任何方案都有适用边界,没有银弹。


端口敲门是什么

简单说就是一个机制:SSH 端口默认在防火墙上完全关闭DROP),外部看这个端口就像服务根本没跑。你需要按照预设的顺序访问几个特定端口(比如先敲 7000,再敲 8000,再敲 9000),然后 knockd 守护进程检测到正确的序列后,自动在防火墙上临时开放 SSH 端口几秒钟,让你连进来。连完之后端口再次关闭。

整个过程对用户来说就是多一个敲门动作:

# 敲门
knock 你的服务器 7000 8000 9000

# 然后正常 SSH(端口此时已开放)
ssh user@你的服务器 -p 51888

而对扫描器来说,这个服务器上根本没有 SSH 端口。它扫到的所有端口都是关闭的。


Ubuntu 24.04 安装与配置

1. 安装

sudo apt update
sudo apt install -y knockd

2. 配置敲门序列

编辑配置文件 /etc/knockd.conf

[options]
    logfile = /var/log/knockd.log
    interface = eth0

[openSSH]
    sequence    = 7000:udp,8000:tcp,9000:udp
    seq_timeout = 10
    command     = sudo /usr/sbin/ufw allow from %IP% to any port 51888 proto tcp
    tcpflags    = syn

[closeSSH]
    sequence    = 9000:udp,8000:tcp,7000:udp
    seq_timeout = 10
    command     = sudo /usr/sbin/ufw delete allow from %IP% to any port 51888 proto tcp
    tcpflags    = syn

关键参数说明:

参数说明
sequence敲门序列,支持 TCP/UDP 混合,端口号可自定义
seq_timeout完成整个序列的时间窗口(秒),超时重置
command序列正确后执行的命令——这里是针对敲门者的 IP 临时开放 SSH 端口
tcpflags匹配 TCP SYN 包,仅在实际建立连接时生效

注意command 中的 %IP% 是 knockd 自动替换为敲门者 IP 的变量。from %IP% 的意思是只给敲门的那个 IP 开端口,而不是全开。

3. 配置 knockd 监听接口

如果上面用的是 eth0,需要确认你的网卡名称:

ip addr show

找到你的公网网卡接口名(可能是 eth0ens3enp1s0 等),替换配置文件中的 interface

4. 设置 UFW 放行敲门端口

敲门端口不能也被 UFW 挡住。打开 /etc/default/knockd,确保:

START_KNOCKD=1
KNOCKD_OPTS="-i eth0"

然后配置 UFW 允许敲门端口:

sudo ufw allow 7000/udp
sudo ufw allow 8000/tcp
sudo ufw allow 9000/udp

注意:这些端口只开放敲门功能,不会暴露服务。敲门包到达后,系统什么也不返回(静默丢弃或回应 RST),看起来就像端口未开放。

5. 启动服务

sudo systemctl enable knockd
sudo systemctl start knockd

检查状态:

sudo systemctl status knockd
tail -f /var/log/knockd.log

6. 客户端工具安装

Linux / macOS:

sudo apt install knockd    # 客户端敲 knock 命令即可
# 或直接使用
knock 服务器IP 7000 8000 9000

Windows:可以用 PuTTY 连接工具配合 knock.exe(knockd 项目提供的 Windows 版客户端),或用 PowerShell 脚本替代。

# PowerShell 版本的敲门脚本
$server="你的服务器IP"
$ports=@(7000,8000,9000)
$ports | ForEach-Object {
    $udp = New-Object System.Net.Sockets.UdpClient
    $udp.Connect($server, $_)
    $udp.Send([byte[]]@(), 0)
    $udp.Close()
}

7. 完整登录流程

# 第一步:敲门
knock my-server.com 7000 8000 9000

# 第二步:立即 SSH(10 秒窗口内)
ssh -p 51888 user@my-server.com

# SSH 连接建立后,端口自动关闭
# 其他人扫描 51888 端口依然是关闭状态

三种 SSH 防护方案对比

为了说清楚各自的定位,我把它们放在一张表里:

维度UFW + Fail2banKnockd 端口敲门Google Authenticator 2FA
防御思路事后封禁事前隐藏额外认证
是否改变攻击面否,端口仍暴露是,端口彻底隐形否,端口仍暴露
对扫描器的效果封 IP,但扫到端口会暴露压根扫不到端口封 IP,端口暴露
0day 防护❌ 无法防护(绕过认证直接打 sshd)✅ 端口不开放,0day 没机会触发❌ 无法防护
暴力破解防护✅ 封禁后失效✅ 端口不开放,无人可破解✅ 多一层因子
日常使用便利性⭐⭐⭐⭐⭐ 无感⭐⭐⭐ 多一步敲门⭐⭐⭐⭐ 多输 6 位码
自动化脚本/工具兼容性⭐⭐⭐⭐⭐ 完全兼容⭐⭐ 需要先敲门⭐⭐⭐ 需处理交互输入
配置复杂度⭐⭐⭐(简单)⭐⭐⭐⭐(中等)⭐⭐⭐(简单到中等)
风险点IP 可伪造绕过封禁(大规模代理)敲门序列可能被嗅探手机丢失、密钥被窃
多人共用场景✅ 适用⚠️ 不太方便(每人要会敲门)✅ 适用
生产环境推荐度⭐⭐⭐(够用但有风险)⭐⭐⭐⭐⭐(最佳)⭐⭐⭐⭐(很好但有局限性)

UFW + Fail2ban 的短板

这套组合是目前个人服务器最常用的方案。效果也确实有——非标端口过滤掉大部分脚本扫描,fail2ban 封掉暴力破解的 IP。

但它的核心问题是:你仍然暴露了攻击面。 SSH 端口对外是开放的,sshd 进程直接暴露在公网上。如果哪天 openssh 爆出一个远程代码执行漏洞(历史上出现过),你的服务器就在危险范围内。fail2ban 只能事后封禁,不能预防 0day。

另外,攻击者可以用大规模的 IP 代理池绕过 IP 封禁——每个 IP 尝试一两次,不会触发 maxretry。

Knockd 的短板

端口敲门最大的优势是让端口在外部完全不可见,0day 漏洞对它无效——因为攻击者连端口都扫不到,就谈不上利用漏洞。

但它有显著的实用性问题:

  • 每次 SSH 前多一步敲门,不符合"即开即用"的习惯
  • 敲门序列可能被嗅探。如果攻击者能监控你的网络流量(比如你在不安全的 WiFi 上),就能看到你敲了哪些端口。解决方案是使用 HMAC 加密的敲门序列(knockd 支持 --hmac 选项)
  • 自动化脚本、rsync、Ansible 等工具需要额外处理敲门步骤
  • 如果 knockd 服务挂了,你可能连敲门都没人响应

2FA 的短板

TOTP 二次验证提供了"你知道的 + 你拥有的"双重保障,安全性很高。但它同样不解决端口暴露的问题。2FA 保护的是"密码泄露"的场景,对 sshd 本身的 0day 漏洞无能为力。

另外,多人共用场景下 2FA 虽然可行,但每台服务器都要各自绑定——有 10 台服务器就要绑 10 次。密钥分发和管理也有成本。


我的看法:三层防护,各管一摊

说得直白一点,三个方案的定位完全不同:

UFW + Fail2ban  → 守大门的保安
Knockd          → 隐形防弹门
2FA             → 第二把锁

它们解决的是不同层面的问题,不应该互相替代,而是按场景选择组合。

个人开发机 / 非生产环境 → UFW + Fail2ban

理由很简单:够用且方便。你一个人用服务器,非标端口 + 密钥登录 + fail2ban 已经把风险降到很低了。再加 knocd 或 2FA 的必要性不大,反而增加日常操作成本。

适用条件

  • 一到两个人使用
  • 不涉及敏感数据或核心业务
  • 能接受偶尔的 IP 封禁日志

生产环境 → 端口敲门 + 仅内网操作(最推荐)

生产服务器应该遵循一个原则:尽可能缩小攻击面。 端口敲门让 SSH 端口在公网完全隐形,结合以下做法更稳妥:

  • SSH 仅监听内网 IP,通过 VPN(WireGuard/OpenVPN)跳板进入内网再 SSH
  • 公网完全不暴露 SSH 端口
  • 如果必须在公网暴露,用 knockd 隐藏端口

这套组合的攻击面最小。漏洞利用的前提是"能连上服务"——连服务都找不到,什么漏洞都没用。

适用条件

  • 生产环境、核心数据
  • 团队操作,有统一的运维流程
  • 自动化工具可通过 VPN 通道免敲门直连

多人共用服务器 → 2FA

如果服务器上有多个用户(比如团队开发机、教学环境),2FA 是最好的方案。每人各自绑定自己的手机,权限独立,一个人手机丢了不影响其他人。配合 AllowUserspam_succeed_if 可以对不同用户做精细控制。

适用条件

  • 3 个以上用户
  • 用户技术水平参差不齐
  • 需要分权限管理

组合建议

场景推荐方案理由
个人博客/VPSUFW + fail2ban够用,省事,一个人不需要复杂方案
公司生产服务器UFW + knockd + VPN最小攻击面
团队开发机(多人共用)UFW + fail2ban + 2FA权限分离,每人独立绑定
高安全要求(金融/合规)UFW + knockd + VPN + 2FA三层叠加,防御纵深
非技术人员维护的服务器UFW + fail2ban + 非标端口简单到不会配错

最后几句话

安全方案的选择不是"越强越好",而是要匹配你的风险和成本承受力

对个人博客来说,花半天配置 knocd + 步骤链 + HMAC 加密,结果一个月 SSH 连不了几次——这个投入产出比不值得。UFW + 非标端口 + fail2ban 已经够用了。

对生产环境来说,端口暴露的风险不可接受——一个 sshd 0day 就能让你全部沦陷。knockd + VPN 的方案值得花时间搭。

至于 2FA,它是个好东西,但它防的是密码泄露密钥被盗,不是端口暴露。分清它在你的安全模型里要承担什么角色,别指望一劳永逸。

以上是我的观点,不一定对。安全方案没有标准答案,只有适合你的和暂时还行的。


原文发表于 cn-res.vip,没有版权自由转载。标明出处非常感谢,删了也没事。