Docker inserting iptable Postrouting rules on top of admin added rules

Viewed 953

I had to add few iptable entries into NAT table, POSTROUTING chain, to allow docker containers to access internet through a different source-address/source-interface of Host Machine (to_source).

Things are working fine.

Ex:

target     prot opt source               destination
SNAT       all  --  100.100.8.0/22       10.1.2.3       to:100.64.0.5

But, when docker service is restarted, it inserts MASQUERADE rules on top of my entries and hence my fix as above is masked. Docker containers can't access internet now.

Ex:

target     prot opt source               destination
MASQUERADE  all  --  100.100.8.0/22       0.0.0.0/0
SNAT        all  --  100.100.8.0/22       10.1.2.3       to:100.64.0.5

Is there anyway to make my POSTROUTING rules to stay always on top of the chain even after docker restarts?

3 Answers

If Ubuntu is on your host, you can use the iptables-save utility to save the iptables rules to a file after you start the docker daemon. Then, once you flush the old rules, you can simply restore the original docker rules using iptables-restore & the saved rules file.

If you don't want to restore all the old iptables rules, you can alter the saved rules file to keep only the ones you need.

It is important to create the rule at the right time - after the docker containers are up, because that's when docker creates the MASQUERADE rules.

Using cron @reboot with a delay would not be reliable, because whenever you restart a container (or bring up a new one), the rules would be in the wrong order.

My solution is a script, executed by cron, which will check the position of my rule(s) and re-insert them if/when necessary.

# we attach a unique comment to our rule so that we can check its position later
unique_comment="docker SNAT source IP"

insert_rules() {
    # put your rules here.  Make sure the first rule is tagged with $unique_comment so it can be identified.
    sudo iptables -t nat -I POSTROUTING 1 -s 172.17.0.0/12 -j SNAT --to-source 1.2.3.4 -m comment --comment "$unique_comment"
    sudo iptables -t nat -I POSTROUTING 2 -s 192.168.0.0/16 -j SNAT --to-source 1.2.3.4
}

delete_rules() {
    pos=$1
    # delete the first two rules from POSTROUTING.  Adjust if necessary.
    sudo iptables -t nat -D POSTROUTING $pos
    sudo iptables -t nat -D POSTROUTING $pos
}


pos=$(sudo iptables -t nat -L POSTROUTING -v -n --line-numbers|grep "$unique_comment" | awk '{ print $1 }')
if [ "$pos" == "1" ]; then
    : # correct position; nothing to do
elif [ "$pos" == "" ]; then
    # rule does not exist; add it.
    insert_rules
else
    # rule is not first; re-insert it.
    delete_rules $pos
    insert_rules
fi

Now create a cron job to execute this script frequently as you need (e.g. every 1 or 2 minutes).

Related