我使用 docker 运行 ubuntu-lts 主机。我想在启动时修改 DOCKER-USER iptables 链以添加自定义拒绝规则。我遇到的问题是,当我的服务运行时 DOCKER-USER 链不存在。
这是我的docker-compose:
version: "3"
services:
service-on-vpn:
...
networks:
vpn:
ipv4_address: 172.30.0.3
networks:
direct:
name: direct
ipam:
config:
- subnet: 172.20.0.0/16
vpn:
name: vpn
ipam:
config:
- subnet: 172.30.0.0/16
有问题的 systemd 服务:
# /etc/systemd/system/docker-vpn-killswitch.service
[Unit]
Description=Docker VPN Killswitch
Requires=docker-compose-app.service
After=docker-compose-app.service
Before=wg-quick@wg0
[Service]
Type=oneshot
Restart=on-failure
ExecStart=/home/.../enable-docker-vpn-killswitch.sh
[Install]
WantedBy=multi-user.target
执行以下脚本
#!/bin/sh
iptables -I DOCKER-USER -s 172.30.0.0/16 -j REJECT
当我的 wireguard 配置启动以允许访问 vpn 子网时,此规则被删除。
docker-compose-app.service
是另一项自定义服务,它在启动时简单地启动我的容器。
尽管依赖于docker-compose-app.service
,DOCKER-USER
但服务运行时链并不存在。抛出了以下错误
enable-docker-vpn-killswitch.sh[2087]: iptables: No chain/target/match by that name.
我这里遗漏了什么?如果需要,我可以提供任何其他背景信息。
答案1
Docker 记录了该DOCKER-USER
链(在 Docker 规则之前添加 iptables 策略):
Docker 安装了两个自定义的 iptables 链,分别名为
DOCKER-USER
和DOCKER
,并确保传入的数据包始终先经过这两个链的检查。这两个链是FORWARD
链的一部分。
虽然没有记录,但如果 Docker 已经找到了该DOCKER-USER
链,它不会抱怨。这意味着在向其添加规则之前尝试创建此链,并且如果该链已经存在则忽略错误,这允许在启动期间在 Docker 启动之前或之后添加此类规则,而无需关心启动期间的服务顺序。
通常,在网络和服务启动之前添加自己的防火墙规则是有意义的,包括 Docker 服务和 Docker,因此通常人们会提前对其进行配置(可能使用Before=network-pre.target
或者可能只是Before=network.target
使用 systemd 等),这需要自己创建链。
只需在脚本中插入创建链的命令DOCKER-USER
(实际上|| true
不需要,只是为了提醒我们如果链已经存在也不要抱怨)。
#!/bin/sh
iptables -N DOCKER-USER || true
iptables -I DOCKER-USER -s 172.30.0.0/16 -j REJECT
而且正如 OP 已经做的那样,最好是将规则插入而不是附加到此链中,因为 Docker 通常会-j RETURN
在其中添加最终规则:如果 Docker 首先创建它,则在此之后附加规则将不起作用。为了保持自然顺序,-A
也可以对插入进行编号。例如,要向 OP 的例外添加例外,规则将是:
iptables -I DOCKER-USER 1 -s 172.30.42.0/24 -j ACCEPT
iptables -I DOCKER-USER 2 -s 172.30.0.0/16 -j REJECT
制作脚本幂等(但不是原子性的),可以选择在创建(尝试)链后刷新链,这样如果稍后重新启动,规则就不会重复。出于同样的原因(幂等性和相同的结果),-j RETURN
在末尾添加(如果 Docker 本身再次重新启动,如果找到它,Docker 将不会再次添加它),即使这个条目并不重要。在这种情况下,插入不再重要,因为链保证为空。使用上面的例子,这将变成:
#!/bin/sh
iptables -N DOCKER-USER || true
iptables -F DOCKER-USER
iptables -A DOCKER-USER -s 172.30.42.0/24 -j ACCEPT
iptables -A DOCKER-USER -s 172.30.0.0/16 -j REJECT
iptables -A DOCKER-USER -j RETURN
要彻底了解主题,也要制定规则原子性稍后重新启动此脚本时,如果链条刚刚被刷新,数据包就不会被允许,可以使用iptables-restore
,它以不同于通常的方式保证原子性:单链恢复而不是完整的表恢复。这里只恢复链DOCKER-USER
:
#!/bin/sh
iptables-restore --noflush << 'EOF'
*filter
:DOCKER-USER - [0:0]
-A DOCKER-USER -s 172.30.42.0/24 -j ACCEPT
-A DOCKER-USER -s 172.30.0.0/16 -j REJECT
-A DOCKER-USER -j RETURN
COMMIT
EOF