端口敲门
与 fwknop 兼容的单包授权(SPA,Single Packet Authorization):受保护的端口平时对外不可连接,客户端先发送一个加密、带 HMAC 认证且防重放的敲门包,Gately 认证通过后,只为这个来源地址在限定时间内放行。常用于保护 SSH 直连:Web 终端用不了的场景(scp、IDE 远程开发、端口转发)也能安全地从外网连接,扫描器与暴力破解看不到端口。
能力
- 兼容 fwknop 客户端:直接使用 fwknop 官方客户端(Linux、macOS、WSL),密钥格式与
fwknop -k生成的KEY_BASE64、HMAC_KEY_BASE64相同,规则页可一键生成客户端命令与~/.fwknoprc配置段; - 安全:报文用 AES-256-CBC 加密,先校验 HMAC(SHA-256 / 384 / 512)再解密,密钥不对的报文不会进入解密流程;每个报文只能使用一次(防重放),时间戳超出有效期(默认 120 秒)即拒绝;可限定用户名、可要求报文写明放行地址;
- 防火墙拦截:受保护端口写入系统防火墙(nftables,内核没有 nf_tables 时用 iptables + ipset),只放行敲门成功的地址,保护本机 sshd 等不经过 Gately 的服务;
- Gately 入口按敲门放行:端口转发、协议分流、STUN 内置转发的规则,以及协议分流中的单条路由,可以选择“需要敲门”,由 Gately 自己判断,不需要防火墙权限,Windows、macOS、没有
NET_ADMIN的容器也能用; - 只开一个端口:协议分流可以识别经 TCP 发来的敲门包(
fwknop -P tcp),同一个端口既接收敲门,又按敲门结果放行 SSH 等路由; - 与入侵防护联动:被拒绝的敲门会发布“端口敲门被拒绝”事件,入侵防护内置“端口敲门失败”模板可按次数封禁来源;已被封禁的地址发来的敲门直接丢弃;
- 当前授权一览(剩余时间、可撤销),敲门成功与被拒绝都记录在运行日志,可在通知中心订阅。
快速开始:保护本机 SSH
- 打开「端口敲门」,添加规则:端口填
tcp/22,开启「防火墙拦截」,密钥留空会自动生成; - 在规则卡片上点「客户端」,填写服务器的域名或公网 IP,复制命令行或
~/.fwknoprc配置段; - 在客户端安装 fwknop:macOS
brew install fwknop,Debian / Ubuntuapt install fwknop-client,Windows 在 WSL 中安装; - 敲门后 30 秒内连接 SSH:
fwknop -n gately-ssh && ssh user@example.com敲门包默认发到 UDP 62201 端口。Gately 部署在路由器后面时,需要把 UDP 62201 与受保护端口一起转发到 Gately 所在的主机;开启「自动放行防火墙」后,本机防火墙(OpenWrt fw4、firewalld、ufw 等)会自动放行敲门端口,拦截生效后也放行受保护端口,能否连接只由敲门决定。
防止把自己锁在外面
- 默认开启「内网免敲门」:私有地址、链路本地地址与本机网卡所在的公网 IPv6 网段(网卡前缀为 /64 或更短时按该前缀,云主机常见的 /124、/128 只包含本机自身)无需敲门,可以在设置中追加免敲门白名单。运营商级 NAT 地址 100.64.0.0/10 不在其中:同一运营商的其他用户也使用这段地址;Tailscale 用户可以把 100.64.0.0/10 加到免敲门白名单;
- 开启防火墙拦截时已建立的连接不受影响,授权到期后已建立的连接也照常保持;
- Gately 正常停止时会删除拦截规则;被强制结束(
kill -9、docker rm -f)时规则会保留,直到 Gately 重新启动。请确认能从内网或其他途径登录这台机器。
规则
| 字段 | 说明 |
|---|---|
| 端口 | 客户端可以请求放行的端口,如 tcp/22、udp/51820,最多 16 个。客户端 -A 参数中的端口必须全部在此列表中,否则整个敲门被拒绝 |
| 防火墙拦截 | 开启后在系统防火墙中拦截这些端口,只放行敲门成功的地址;关闭时不改动防火墙,规则只供“需要敲门”的入口引用 |
| 加密密钥 / HMAC 密钥 | base64 编码,留空自动生成(32 / 64 字节,与 fwknop -k 相同),也可以填写已有 fwknop 服务端的密钥。复制规则会生成新密钥;修改、停用或删除规则会撤销它产生的全部授权 |
| HMAC 算法 | SHA-256(fwknop 默认)、SHA-384、SHA-512,需与客户端的 --hmac-digest-type 一致 |
| 放行时长 | 敲门成功后可以建立新连接的时间,默认 30 秒 |
| 客户端最长时长 | 客户端用 -f 请求更长时长时的上限(默认 300 秒),0 表示忽略客户端请求的时长;请求比放行时长短时总是采用 |
| 限定用户名 | 只接受报文中用户名为此值的敲门(fwknop 默认发送本机登录名,可用 -U 指定) |
| 要求写明地址 | 拒绝不带地址的报文(fwknop -s),客户端必须用 -R 或 -a 写明放行地址 |
被端口转发、协议分流或 STUN 引用的规则不能删除,卡片上会列出引用方。
放行哪个地址
-R(推荐):客户端先查询自己的公网 IPv4 写入报文,报文即使在途中被截获,也无法用于其他地址;-s:报文不带地址,放行报文的来源地址。截获报文的人有机会抢先从自己的地址重放,规则可开启「要求写明地址」拒绝这种报文;-a 地址:放行指定的地址,例如替另一台设备敲门。
fwknop 2.6 客户端只能经 IPv4 发送敲门包、放行 IPv4 地址,服务器需要能经 IPv4 访问;Gately 本身也接受经 IPv6 发来的敲门包,放行其来源地址。
全局设置
- UDP 敲门端口:默认 62201(fwknop 客户端的默认端口);填 0 不监听 UDP,只接收经协议分流发来的敲门。没有启用的规则时不监听;
- 自动放行防火墙:见上文;
- 内网免敲门 / 免敲门白名单:只影响防火墙拦截;“需要敲门”的入口由各自的 IP 名单控制;
- 报文有效期:报文时间戳与本机时间的最大偏差,默认 120 秒,客户端与服务器的时钟都需要准确。
防火墙拦截
| 环境 | 防火墙拦截 | Gately 入口按敲门放行 |
|---|---|---|
Linux(root 或 CAP_NET_ADMIN,有 nftables) | 可用 | 可用 |
Docker(host 网络 + cap_add: NET_ADMIN) | 可用,规则写入宿主机 | 可用 |
| 群晖 DSM 等没有 nf_tables 的 Linux | 使用 iptables(legacy)+ ipset,官方 Docker 镜像已内置 | 可用 |
| Windows、macOS、FreeBSD、没有权限的 Linux | 不可用,页面顶部给出原因 | 可用 |
Gately 在 nftables 中创建独立的 inet gately_knock 表,不改动系统已有的规则:prerouting 链(优先级 -160,连接跟踪之后、目的地址转换之前)把发往受保护端口的包交给 gate 链,依次放行目标不是本机的流量(经本机转发给其他设备的连接不受影响)、回环接口、已建立的连接、TCP 中不是发起连接的包、免敲门网段与敲门放行集合中的 地址 + 协议 + 端口,其余丢弃。在 prerouting 判断,因此映射到 Docker 容器的端口按映射前的端口拦截。放行写入带超时的集合元素,到期由内核自动删除。
没有 nf_tables 时改用 iptables 的 mangle 表:PREROUTING 跳到 GATELY_KNOCK,每个受保护端口一个 ipset 集合(gkn4_tcp_22 等,带超时)。表或跳转被防火墙重载、nft flush ruleset 等清除后 10 秒内自动重写,并带上仍然有效的放行。页面上的「查看防火墙规则」显示当前写入的规则。
端口转发进来的 SSH
经 Gately 端口转发到本机 127.0.0.1:22 的连接来自回环接口,不受防火墙拦截。这种情况请在端口转发规则上选择“需要敲门”,或在防火墙拦截中保护对外的转发端口。
注意
- Docker 端口映射改写来源地址:Gately 以桥接网络运行、敲门端口经
-p 62201:62201/udp映射时,若 Docker 的 userland-proxy 处理了这个映射(例如经 IPv6、回环地址访问或关闭了 iptables 集成),Gately 看到的报文来源是 Docker 网关地址,-s报文放行的就是网关地址,等于没有放行真实客户端,甚至可能放行经同一网关转发的所有连接。请让 Gately 使用 host 网络(防火墙拦截本来也需要),或让客户端用-R/-a写明放行地址,并开启「要求写明地址」; - 只拦截 SYN 的取舍:为了不切断启用前已建立、但还没有被连接跟踪记录的连接,TCP 只丢弃发起连接的 SYN 包,不带 SYN 的包直接放行。理论上,开启了 SYN cookie(
net.ipv4.tcp_syncookies)的系统在 SYN 队列溢出时,会接受一个能猜中 cookie 的裸 ACK 而建立连接;cookie 是用内核密钥与时间计算出的 32 位初始序列号,实际几乎不可能猜中;对安全要求极高的场景可以关闭 SYN cookie(sysctl net.ipv4.tcp_syncookies=0)。
Gately 入口按敲门放行
端口转发、协议分流与 STUN(内置转发)的「IP 访问控制」中可以选择一条敲门规则作为“需要敲门”:来源地址必须持有该规则未到期的授权才能建立新连接(UDP 在新建会话时判断:授权到期后 WireGuard 等已有会话照常转发,与防火墙拦截保持已建立连接一致;白名单 / 黑名单仍逐包判断),其余连接直接断开;与白名单 / 黑名单同时设置时两者都要满足。敲门规则可以关闭「防火墙拦截」,只供这些入口使用。
协议分流还可以在单条路由上选择“需要敲门”,例如 443 端口上 HTTPS 路由对所有人开放,SSH 路由只对敲门成功的地址开放。
只开放一个端口
在协议分流规则中添加一条「端口敲门」路由(协议 spa),客户端用 TCP 把敲门包发到同一个端口:
fwknop -A tcp/443 -P tcp -p 443 -R -D example.com --use-hmac \
--key-base64-rijndael <加密密钥> --key-base64-hmac <HMAC 密钥>
ssh -p 443 user@example.com- 敲门路由把报文交给端口敲门模块,不转发、不回复;
-A中的端口需要在敲门规则的端口列表中(例如tcp/443); - 整条规则要求敲门时,没敲门的来源只能使用敲门路由,敲门后才能使用其他路由与默认目标;
- 只有规则中有敲门路由时才识别敲门报文,不影响其他规则的协议识别;
- 敲门规则的 UDP 端口可以设为 0,对外只开放协议分流的端口;
- 带敲门路由的协议分流端口与 UDP 敲门端口不能出现在开启防火墙拦截的规则中(保存、启用时检查;拦截后敲门报文本身无法送达),生成防火墙规则时也会自动排除这些端口。
与入侵防护联动
被拒绝的敲门会发布「端口敲门被拒绝」事件(字段 via 为 udp 或 tcp),同一来源每 5 秒最多一次。UDP 包的来源地址可以伪造,为了不让攻击者借入侵防护封禁任意 IP,经 UDP 发来、HMAC 认证失败的包只计数、不发布事件;通过 HMAC 认证(出自持有密钥的客户端)但被规则拒绝的(解密失败、重放、过期、请求未授权端口、用户名不符等)才发布。经协议分流 TCP 端口发来的报文来源经过握手,无法伪造,HMAC 不匹配也会发布。在入侵防护中用「端口敲门失败」模板创建规则(默认 10 分钟内 5 次即封禁 1 天),持续发送伪造报文的来源会被封禁,被封禁地址的敲门直接丢弃。不是敲门格式的数据(端口扫描、其他协议)只计数、不发事件。
认证前先按来源网段限速(IPv4 每个 /24、IPv6 每个 /64 每秒最多 10 个报文),再受全局每秒 200 个的上限约束,超出的直接丢弃:伪造报文洪水不会占满 CPU,单个来源的洪水也不会挤掉其他来源的正常敲门。HMAC 认证失败的报文不逐条记录日志,每分钟汇总为一条“N 个来源的 M 个无效敲门报文”。
限制
- 只支持 fwknop 的 Rijndael(AES)CBC 模式加 HMAC,不支持 GnuPG、旧版 IV 模式与不带 HMAC 的报文;
- 不执行命令消息(
fwknop -C),不支持 NAT 访问请求(-N); - 敲门授权保存在内存中,Gately 重启后需要重新敲门(已建立的连接不受影响)。防重放记录同样只在内存中,因此时间戳早于本次启动的报文在重启后一律拒绝;客户端时钟比服务器慢时,重启后头几秒的敲门可能被拒绝,再敲一次即可。