如何利用 systemd 服务来修改 DOCKER-USER iptables 链?

如何利用 systemd 服务来修改 DOCKER-USER iptables 链?

我使用 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.serviceDOCKER-USER但服务运行时链并不存在。抛出了以下错误

enable-docker-vpn-killswitch.sh[2087]: iptables: No chain/target/match by that name.

我这里遗漏了什么?如果需要,我可以提供任何其他背景信息。

答案1

Docker 记录了该DOCKER-USER链(在 Docker 规则之前添加 iptables 策略):

Docker 安装了两个自定义的 iptables 链,分别名为DOCKER-USERDOCKER,并确保传入的数据包始终先经过这两个链的检查。这两个链是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

相关内容