Score:1

How can I utilize a systemd service to modify the DOCKER-USER iptables chain?

nr flag

I run an ubuntu-lts host with docker. I'd like to modify the DOCKER-USER iptables chain on start-up to add a custom reject rule. The issue I've encountered is that the DOCKER-USER chain does not exist when my service runs.

Here is my 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

The systemd service in question:

# /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

which executes the following script

#!/bin/sh
iptables -I DOCKER-USER -s 172.30.0.0/16 -j REJECT

This rule is removed when my wireguard config spins up to allow access for the vpn subnet.

docker-compose-app.service is another custom service that simply ups my containers on boot.

Despite the dependency on docker-compose-app.service, the DOCKER-USER chain is not present when the service runs. The following error is thrown

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

What am I missing here? I can provide any additional context if needed.

asktyagi avatar
in flag
Please add your `docker-compose-app` unit file content to the question. Also check if [this](https://serverfault.com/questions/1053369/systemd-how-to-start-service-after-another-service-started) can help.
kharderr avatar
nr flag
@asktyagi Apologies for the delay response - haven't had time to work on this. I edited the post with more context. Let me know if there's anything else that would be helpful. I'll read through that link as well and try to get the `After` bit of my service working. Thanks.
A.B avatar
cl flag
A.B
you can create (-N) DOCKER-USER if it doesn't exist. Docker copes with both cases later.
kharderr avatar
nr flag
@A.B Great- that'd do the trick. Thanks! How do I mark this as resolved since there aren't any official answers?
A.B avatar
cl flag
A.B
There: added a proper answer. I Hope you don't mind the answer is more than a single line.
kharderr avatar
nr flag
Great write-up, thank you
Score:1
cl flag
A.B

Docker documents the DOCKER-USER chain (Add iptables policies before Docker’s rules):

Docker installs two custom iptables chains named DOCKER-USER and DOCKER, and it ensures that incoming packets are always checked by these two chains first. These chains are part of the FORWARD chain.

While it's not documented, Docker won't complain if it already finds the DOCKER-USER chain. That means that attempting to create this chain before adding a rule to it, and also ignoring an error if it already existed allows to add such rule before or after Docker was started during boot, without having to care about services order during boot.

Usually it even makes sense to have its own firewall rules added before network and services start, including Docker services and thus Docker, so usually one configures it before (probably with a Before=network-pre.target or maybe just Before=network.target etc. with systemd) and this requires creating the chain oneself.

Just insert in the script the command to create the DOCKER-USER chain (the || true isn't really needed, that's just to remind us to also not complain if the chain already existed).

#!/bin/sh

iptables -N DOCKER-USER || true
iptables -I DOCKER-USER -s 172.30.0.0/16 -j REJECT

Also indeed as OP already did, it's best to insert rather than append rules to this chain, because Docker usually adds a final -j RETURN to it: if Docker created it first, appending rules after this would have no effect. To keep natural order that would have been done with -A one can also number the insertions. For example to add an exception to OP's exception the rules would be:

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

To make the script idempotent (but yet not atomic), one can choose to flush the chain after its (attempted) creation, so in case it's restarted later, rules aren't duplicated. For the same reason (idempotence and identical result) add -j RETURN at the end (Docker won't add it again if found if it was itself restarted again), even if this entry doesn't really matter. In this case, insertions won't matter anymore since the chain is guaranteed empty. With above example this would become:

#!/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

To be thorough on the subject, to also achieve ruleset atomicity when restarting this script later, so a packet doesn't get allowed right while the chain was just flushed, one can use iptables-restore, which guarantees atomicity, in a different way than usual: single chain restoration rather than complete table restoration. Here restore only the DOCKER-USER chain:

#!/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
I sit in a Tesla and translated this thread with Ai:

mangohost

Post an answer

Most people don’t grasp that asking a lot of questions unlocks learning and improves interpersonal bonding. In Alison’s studies, for example, though people could accurately recall how many questions had been asked in their conversations, they didn’t intuit the link between questions and liking. Across four studies, in which participants were engaged in conversations themselves or read transcripts of others’ conversations, people tended not to realize that question asking would influence—or had influenced—the level of amity between the conversationalists.