用 docker 搭建了一个frp,但最近发现有很多不明 IP 的访问,虽然并未造成安全问题,总归是不胜其扰,遂打算编个程序 block 这些 IP。
思路
起初考虑使用frptables这个项目,但由于我采用的是 docker 部署,需要将这个工具放在 docker 内部,这样至少有两个不便:
- 一是要考虑 docker 权限,要么开启特权模式(
--privileged),要么设置主机网络模式(--host),反而增加了风险;
- 二是不方便后续升级更新,一旦 docker 更新,所有操作都需要重新安装一遍。
于是排除了这一方案,不过这个项目给了我一些启发,我用 shell 脚本自己实现了一个。
实现
写一个 shell 脚本block-ip.sh,从 docker 日志中获取 IP 地址,如果是中国的地址则不拦截,不是中国的地址且不在 iptables 中的则实施拦截。为了监控 log 变更,还需要使用 inotify-tools 中的inotifywait。
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
| #!/bin/bash
# 白名单 IP 地址列表
WHITELIST=("0.0.0.0" "172.*" "192.168.*")
# 启动 docker logs -f 命令,并将日志输出重定向到临时文件
TMPFILE=$(mktemp -t block.unwanted.ip.XXXXXX) || exit 1
docker logs -f frps > $TMPFILE 2>/dev/null &
# 检查 docker logs 命令是否成功启动
if [ $? -ne 0 ]; then
echo "无法启动 docker logs 命令。"
exit 1
fi
# 获取 docker logs 命令的进程 ID
DOCKER_PID=$!
# 函数:清理环境
cleanup() {
echo "运行结束,正在清理..."
kill -9 $DOCKER_PID 2>/dev/null
rm -f $TMPFILE 2>/dev/null
exit 1
}
# 捕获 Ctrl+C 信号
trap cleanup INT
# 日志函数,输出到终端和文件
LOG_FILE="/var/log/block_ip.log"
log() {
local msg=$1
echo "$(date '+%F %T.%3N') $msg" | tee -a $LOG_FILE
}
log "开始运行..."
# 使用 inotifywait 监视临时文件的变化
while inotifywait -qq -e modify $TMPFILE; do
# 在文件发生变化时执行操作
# 获取最新的 Docker 日志中的 IP 地址
ip=$(tail -n 1 $TMPFILE | grep -oE '((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3})')
# 检查 IP 地址是否在白名单中
in_whitelist=false
for white_ip in ${WHITELIST[@]}; do
if [[ $ip == $white_ip ]]; then
in_whitelist=true
log "IP地址 $ip 在白名单列表,不进行拦截。"
break
fi
done
if [[ -n $ip ]] && ! $in_whitelist; then
# 检查 IP 地址是否已经存在于 iptables 规则中
is_existing=$(iptables -C DOCKER-USER -s $ip -j DROP 2>&1 | grep -o $ip)
if [[ -n $is_existing ]]; then
log "IP地址 $ip 已做拦截,无需重复添加。"
else
# 获取 IP 地址归属国家
ip_country=$(curl -s https://ipinfo.io/${ip}/country)
if [[ $ip_country == "CN" ]]; then
log "IP地址 $ip 属于中国,不进行拦截。"
else
iptables -I DOCKER-USER -s $ip -j DROP
log "IP地址 $ip 来自 $ip_country,添加到拦截规则。"
fi
fi
fi
done
|
运行
直接启动
用./block-ip.sh执行,或者用 nohup 后台执行。
1
2
3
| nohup ./block-ip.sh > /dev/null 2>&1 &
# 可简写成
nohup ./block-ip.sh &> /dev/null &
|
以服务方式启动
新建/etc/systemd/system/block-ip.service
1
2
3
4
5
6
7
8
9
10
11
12
13
| [Unit]
Description=block daemon
After=syslog.target network.target
Wants=network.target
[Service]
Type=simple
ExecStart=/usr/local/block.sh
Restart= always
RestartSec=1min
[Install]
WantedBy=multi-user.target
|
其他
- 这个思路可以扩展到其他用途,只要能匹配到正则式,就可以实施拦截。
- 对于在
/var/log/block-ip.log日志文件,考虑用 logrotate 处理。
- 也考虑使用 fail2ban 的自定义规则实现。
补充
随着被拦截的 IP 越来越多,iptables 防火墙的效率会降低,有必要隔一段时间一波,脚本如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| #!/bin/bash
# 使用 awk 计数并删除规则
count=$(iptables-save | grep 'DOCKER-USER -s' | awk '
{
# 打印删除命令
gsub(/^-A/, "iptables -D")
print
# 执行删除命令
system($0)
# 增加计数器
count++
}
END {
# 输出计数器
print count
}')
# 输出删除的规则数量
echo -e "已删除:\n$count 条规则"
|
Author
winsphinx
LastMod
2024-05-11
(a3a53c39)
[原创] block-ip
License
