端口敲门(Port Knocking)脚本

什么是端口敲门?

端口敲门是一种网络安全技术,通过按特定顺序”敲击”一系列端口,才能打开目标端口的访问权限。这就像是一种密码锁,只有知道正确的”敲门”顺序,才能获得访问权限。

使用方法

脚本接受两个主要参数:

  • -allow:要保护的端口(想要最终访问的端口)
  • -knowk:敲门端口序列(按顺序敲击的端口)

使用示例

1
bash knockers.sh -allow 22 -knowk 88,99,33,44

这个命令表示:

  • 保护SSH端口(22)
  • 需要按顺序敲击 88→99→33→44 这四个端口才能获得SSH访问权限

脚本源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
#!/usr/bin/env bash
##author:wangsendi
##url:https://www.yuque.com/wangsendi
###端口敲门 __help中使用方法 allow 是要访问的端口 knowk 是敲门端口 依次敲门就行

# 默认值
_allow=false
_knowk=false
_allow_values=""
_knowk_values=""

# 解析参数
while [[ $# -gt 0 ]]; do
key="$1"
case $key in
-allow)
_allow=true
shift
_allow_values="$1"
shift
;;
-knowk)
_knowk=true
shift
_knowk_values="$1"
shift
;;
*)
# 对于未知参数,你可以选择忽略或者给出错误信息
echo "Unknown option: $1"
shift
;;
esac
done
__main() {
# 转换逗号分隔的字符串为数组
mapfile -d , -t _allow_ports < <(echo -n "$_allow_values" | tr -d '\n')
mapfile -d , -t _knowk_ports < <(echo -n "$_knowk_values" | tr -d '\n')

# 打印参数值
echo "allow_ports: ${_allow_ports[*]}"
echo "knowk_ports: ${_knowk_ports[*]}"

{

#开始执行的通用rule
nft delete table inet portknock
nft add table inet portknock
# 创建链
nft add chain inet portknock input '{ type filter hook input priority -10 ; policy accept ; }'

nft add set inet portknock guarded_ports '{ type inet_service; }'
nft add element inet portknock guarded_ports \{ "${_allow_values}" \}

# 定义客户端 IPv4 地址集合
nft add set inet portknock clients_ipv4 '{ type ipv4_addr; flags timeout; }'

# 定义客户端 IPv6 地址集合
nft add set inet portknock clients_ipv6 '{ type ipv6_addr; flags timeout; }'

# 定义 IPv4 候选者集合
nft add set inet portknock candidates_ipv4 '{ type ipv4_addr . inet_service; flags timeout; }'

# 定义 IPv6 候选者集合
nft add set inet portknock candidates_ipv6 '{ type ipv6_addr . inet_service; flags timeout; }'

# 定义 input 链
nft add chain inet portknock input '{ type filter hook input priority -10; policy accept; }'

# 如果是本地回环接口,则返回
nft add rule inet portknock input iifname "lo" return
}
if [ "${#_knowk_ports[@]}" -eq 1 ]; then
nft add rule inet portknock input tcp dport "${_knowk_ports[0]}" add @clients_ipv4 '{ ip saddr timeout 10s }' log prefix '"Successful portknock: "'
nft add rule inet portknock input tcp dport "${_knowk_ports[0]}" add @clients_ipv6 '{ ip6 saddr timeout 10s }' log prefix '"Successful portknock: "'
else
for item in $(seq 0 $((${#_knowk_ports[@]} - 1))); do
case "${item}" in
0)
nft add rule inet portknock input tcp dport "${_knowk_ports[$item]}" add @candidates_ipv4 \{ ip saddr . "${_knowk_ports[item + 1]}" timeout 10s \}
nft add rule inet portknock input tcp dport "${_knowk_ports[$item]}" add @candidates_ipv6 \{ ip6 saddr . "${_knowk_ports[item + 1]}" timeout 10s \}
;;
"$((${#_knowk_ports[@]} - 1))")
nft add rule inet portknock input tcp dport "${_knowk_ports[$item]}" ip saddr . tcp dport @candidates_ipv4 add @clients_ipv4 '{ ip saddr timeout 10s }' log prefix '"Successful portknock: "'
nft add rule inet portknock input tcp dport "${_knowk_ports[$item]}" ip6 saddr . tcp dport @candidates_ipv6 add @clients_ipv6 '{ ip6 saddr timeout 10s }' log prefix '"Successful portknock: "'
;;
*)
nft add rule inet portknock input tcp dport "${_knowk_ports[$item]}" ip saddr . tcp dport @candidates_ipv4 add @candidates_ipv4 \{ ip saddr . "${_knowk_ports[item + 1]}" timeout 10s \}
nft add rule inet portknock input tcp dport "${_knowk_ports[$item]}" ip6 saddr . tcp dport @candidates_ipv6 add @candidates_ipv6 \{ ip6 saddr . "${_knowk_ports[item + 1]}" timeout 10s \}
;;
esac
done
fi

{
# 保护规则
nft add rule inet portknock input tcp dport @guarded_ports ip saddr @clients_ipv4 counter accept
nft add rule inet portknock input tcp dport @guarded_ports ip6 saddr @clients_ipv6 counter accept
nft add rule inet portknock input tcp dport @guarded_ports ct state established,related counter accept
nft add rule inet portknock input tcp dport @guarded_ports counter reject with tcp reset
}
}
__main

__help() {
bash /apps/data/workspace/default/shell/knockers/knockers.sh -allow 22 -knowk 88,99,33,44
}

工作原理简介

  1. 脚本创建一个nftables防火墙表和规则集
  2. 设置受保护的端口(如SSH 22端口)
  3. 定义”敲门”顺序(如 88→99→33→44)
  4. 当客户端按正确顺序敲击这些端口时,其IP地址会被临时加入白名单(10秒有效期)
  5. 白名单内的IP可以访问受保护的端口,其他IP则被拒绝

实际应用场景

此脚本适合用于保护服务器的敏感端口(如SSH)不被随意扫描和暴力破解。即使攻击者知道你的SSH端口,没有正确的”敲门”序列也无法连接。

优点

  • 同时支持IPv4和IPv6
  • 可设置单个或多个敲门端口
  • 白名单有超时机制,增强安全性
  • 成功敲门会记录日志