端口敲门 (knockd):让 SSH 端口对扫描器隐形——兼论三种 SSH 防护方案的适用边界
服务器暴露在公网上,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
找到你的公网网卡接口名(可能是 eth0、ens3、enp1s0 等),替换配置文件中的 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 + Fail2ban | Knockd 端口敲门 | 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 是最好的方案。每人各自绑定自己的手机,权限独立,一个人手机丢了不影响其他人。配合 AllowUsers 和 pam_succeed_if 可以对不同用户做精细控制。
适用条件:
- 3 个以上用户
- 用户技术水平参差不齐
- 需要分权限管理
组合建议
| 场景 | 推荐方案 | 理由 |
|---|---|---|
| 个人博客/VPS | UFW + 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,没有版权自由转载。标明出处非常感谢,删了也没事。