Port Knocking 技术介绍
Port Knocking 是一种网络安全技术,它利用了一个简单但强大的概念:在实际建立网络连接之前,先通过一系列特定的端口“敲击”来验证访问请求。这种方法的核心优势在于它能够在不显露服务存在的前提下,实现对服务的安全访问控制,从而有效地隐藏了网络服务,减少了潜在的攻击面。
原理
Port Knocking 工作原理涉及到客户端按照预定义的顺序向服务器的非监听端口发送一系列数据包。这些“敲门”尝试本身不会建立真正的网络连接,而是通过特定的模式来传达访问请求。服务器端运行的敲门守护进程(如knockd
)在网络层监听这些尝试,而不依赖于任何特定的应用层协议。当守护进程识别出正确的敲门序列时,它将执行预配置的动作,如修改防火墙规则来暂时允许请求者的 IP 地址访问特定服务。
历史
Port Knocking 是在 21 世纪初提出的,作为一种增强网络服务安全的方法。Martin Krzywinski 是 2003 年引入 Port Knocking 概念的人之一,他在网上发表了一篇关于这一主题的文章,引起了人们的广泛兴趣。自那以后,Port Knocking 已经发展成为一种被广泛讨论和实施的安全策略,尽管它并不是所有场景的最佳选择。
技术实现
Port Knocking 可以通过多种方式实现,最常见的方法是使用专门的守护进程(如knockd
)在服务器上运行,监听网络上的敲门尝试。knockd
可以配置特定的端口序列和相应的动作,例如,当检测到正确的敲门序列时,它可以运行脚本或命令来修改防火墙规则,从而临时允许来自特定 IP 的访问。
拓展技术 SPA
Port Knocking 应被视为多层防御策略的一部分,而不是一个全面的安全解决方案。在设计和实施敲门策略时,应该考虑如何将其与其他安全措施(如强密码策略、两因素认证、加密通信等)结合起来,以增强整体安全性。
除了基本的 Port Knocking 外,还有一些增强的变体,如 Single Packet Authorization(SPA)。SPA 是 Port Knocking 的一个改进版本,它只需要单个加密的认证数据包来完成“敲门”过程,相比于传统的 Port Knocking,SPA 可以提供更高的安全性和更低的网络噪音,认证数据包可以包含更多的验证信息,并且可以更好地抵御重放攻击。
与传统的 Port Knocking 相比,它通过单个数据包来完成认证过程,而不是一系列端口敲击。这个数据包通常是加密的,并包含认证信息,如用户凭证、请求时间戳以及请求的动作(比如打开特定端口的请求)。
SPA 的实现原理
SPA 的核心是利用加密技术来保护认证数据包,确保只有拥有正确密钥的服务器能够解密并理解这个数据包的内容。这种方法减少了网络上的噪音,提高了安全性,并减少了被未授权用户发现的风险。
数据包构成:SPA 数据包通常包括认证凭证(如加密的用户名和密码)、请求的动作(比如打开 SSH 端口),以及时间戳以防重放攻击。
数据包发送:客户端构造一个包含上述信息的数据包,使用预先共享的密钥进行加密,然后发送给服务器。
数据包接收和验证:服务器端的 SPA 软件(如
fwknop
)监听特定的端口等待 SPA 数据包。一旦收到,它尝试使用共享密钥解密数据包,并验证其中的信息。如果验证成功,且请求的动作被授权,服务器将执行该动作(比如临时开放防火墙端口)。 由于使用 SPA 进行安全认证和授权需要在客户端和服务器端都进行配置,使用起来更麻烦点。fwknop
是一个广泛使用的 SPA 实现,提供了客户端和服务器端工具。
Knockd 配置
sudo apt install knockd
配置 knockd
- 编辑
knockd
的配置文件/etc/knockd.conf
,为你的 SSH 服务端口(30131)设置一个敲门序列。这里的序列只是一个示例,你应该选择一个难以猜测的唯一序列。
[options]
UseSyslog # 使用系统日志记录knockd的活动,方便审计和故障排查。
[openSSH]
sequence = 7000,8000,9000 # 定义用于“敲门”的端口序列。应选择难以预测的端口号。
seq_timeout = 5 # 敲击序列中每个敲击之间的最大时间间隔(秒)。应根据网络条件调整。
command = /sbin/iptables -A INPUT -s %IP% -p tcp --dport 30131 -j ACCEPT # 当检测到正确的敲门序列时执行的命令。
tcpflags = syn # 仅监听SYN包,以减少误报。
[closeSSH]
sequence = 9000,8000,7000
seq_timeout = 5
command = /sbin/iptables -D INPUT -s %IP% -p tcp --dport 30131 -j ACCEPT
tcpflags = syn
[openHTTPS]
sequence = 12345,54321,24680,13579
seq_timeout = 5
command = /usr/local/sbin/knock_add -i -c INPUT -p tcp -d 19 -f %IP%
tcpflags = syn
编辑 /etc/default/knockd
文件,启用 knockd
服务:
注意配置监听正确的网卡
ip link show
更改 KNOCKD_OPTS="-i enp3s0"
再重启服务 systemctl restart knockd.service
高级配置
使用正则表达式定义复杂序列
knockd
支持使用正则表达式来定义敲门序列,这为定义复杂且难以预测的敲门序列提供了可能。这要求 knockd
的版本支持正则表达式。
[openCustomService]
sequence = 7000,8000+|9000,10000-11000
seq_timeout = 10
command = /sbin/iptables -A INPUT -s %IP% -p tcp --dport 22022 -j ACCEPT
tcpflags = syn
在这个示例中,敲门序列包括固定的 7000 端口,然后是 8000 端口一次或多次,接着是 9000 端口一次,最后是 10000 到 11000 之间的任意端口一次。这种方式大大增加了序列的复杂性,当然还可以定义为 UDP 格式的请求包。
结合防火墙限制尝试频率
结合防火墙(如 iptables)配置来实施频率限制。对于 iptables,你可以使用 recent
模块来跟踪和限制敏感应用。
iptables -A INPUT -p tcp --dport 30131 -m state --state NEW -m recent --set --name SSH
-A INPUT
: 将规则添加到INPUT
链。INPUT
链处理进入本机的数据包。-p tcp
: 指定规则仅适用于 TCP 协议的数据包。--dport 30131
: 指定目标端口为 30131,即此规则仅适用于尝试连接到端口 30131 的数据包。-m state
: 载入state
模块,该模块允许规则基于连接的状态(例如 NEW, ESTABLISHED)来匹配数据包。--state NEW
: 仅匹配尝试建立新连接的数据包。这意味着已经建立的连接(例如,已经通过认证的 SSH 会话)不受此规则的影响。-m recent
: 载入recent
模块,该模块用于跟踪和匹配最近访问过指定端口的 IP 地址。--set
: 将符合条件的数据包的源 IP 地址添加到recent
模块跟踪的列表中。--name SSH
: 为recent
模块跟踪的列表命名为"SSH"。这个名字在第二条规则中被引用,以应用频率限制。
iptables -A INPUT -p tcp --dport 30131 -m state --state NEW -m recent --update --seconds 300 --hitcount 10 --name SSH -j DROP
--update
: 更新列表中 IP 地址的计数器和时间戳。如果一个 IP 地址的新连接尝试匹配此规则,其计数器增加。--seconds 300
: 设置时间窗口为 300 秒。仅考虑在过去 300 秒内的连接尝试。--hitcount 10
: 设置在指定的时间窗口内允许的最大连接尝试次数为 10 次。超过这个限制的连接尝试将被下一参数指定的动作处理。-j DROP
: 对于超过频率限制的数据包,采取的动作是丢弃。这意味着如果一个 IP 地址在 300 秒内对端口 30131 的连接尝试超过 10 次,它的后续尝试将被防火墙拦截并丢弃,不会达到应用层。 手动保存防火墙规则
sudo iptables-save > /etc/iptables/rules.v4
sudo ip6tables-save > /etc/iptables/rules.v6
检查 knockd
的日志
首先,检查knockd
的日志以确认它是否正在运行并且能够检测到敲门尝试。如果你在knockd
配置中启用了UseSyslog
,那么日志应该会被发送到系统日志中。
你可以使用以下命令查看系统日志,并筛选出与 knockd
相关的条目:
sudo journalctl -u knockd
或者,如果你的系统使用的是其他日志系统,你可能需要查看 /var/log/syslog
或 /var/log/messages
:
sudo grep knockd /var/log/syslog
配置自动关闭脚本
为了防止平时使用时遗忘手动关闭端口,可以在 knockd
的配置中设置一个“关闭”序列,或者使用一种方法是设置一个自动运行的脚本或命令,该脚本在一定时间后自动删除防火墙规则。这种自动关闭机制足够灵活,不会中断正在进行的会话,但又能确保端口不会无限期地保持开放状态。
#!/bin/bash
# IP地址和端口作为参数传递
IP=$1
PORT=$2
DELAY=$3 # 延迟时间,单位为秒
# 添加UFW规则允许来自指定IP的访问
ufw allow from $IP to any port $PORT
# 等待指定的延迟时间
sleep $DELAY
# 删除UFW规则以关闭端口
ufw delete allow from $IP to any port $PORT
然后,你可以在 knockd
的配置中将打开端口的命令更改为调用这个脚本,并传递 IP 地址、端口和延迟时间作为参数。例如:
command = /usr/local/bin/auto_close_port.sh %IP% 30131 300
进行端口敲门
直接 netcat 模拟端口请求
nc -zv <服务器IP> 7000 && nc -zv <服务器IP> 8000 && nc -zv <服务器IP> 9000 && nc -zvu <服务器IP> 21116
使用 nmap
nmap -Pn --host-timeout 201 --max-retries 0 -p 7000 8000 9000 <服务器IP>
Knock 客户端
brew install knock
knock 192.168.1.100 7000 8000 9000