How do I add iptable rules to my Linode while running Docker containers?

Linode Staff

We have a Linode that has Docker containers running on it and we are planning to apply some network rules for the incoming traffic.

I have applied network rules via iptables but they are not getting applied.

Could someone help us get these network rules in place? Basically we need rules to be in place to do the following:

  1. Block all incoming traffic.
  2. Allow access from one IP address on 2 ports
  3. And also allow ssh access from 2 other IP addresses.

Any insight may help here.

4 Replies

It looks like you are running into a relatively common issue based on how Docker interacts with iptables. Docker will actually manipulate iptable rules. More specifically it installs custom iptable chains DOCKER-USER and DOCKER and they supersede other rules. You will want to add your rules to the chain DOCKER-USER because the rules in this chain are applied before any rules that Docker creates automatically.

It's important to note that you will want to edit the iptable rules directly in these situations as programs like UFW are iptable wrappers and the changes made with such programs are superseded by the changes made by Docker.

Docker has some great official documentation on this. You can read in more detail here:

The above documentation contains one additional option for resolving this issue. You can elect to prevent Docker from manipulating iptables. You will do this by editing /etc/docker/daemon.json. It's worth noting that this is likely to break container networking though as noted above.

I hope this helps. If anyone else has more experience with setting iptable rules, please chime in.

I am also facing the same issue.

And I have below rule applied,

iptables -I DOCKER -i ext_if ! -s < ipaddress > -j DROP

and

iptables -I DOCKER-USER -i ext_if ! -s < ipaddress > -j DROP

But it is still accepting the connections from ipaddress other than mentioned in the above rules.

I'm not conversant with Docker at all.

However, if these:

iptables -I DOCKER -i ext_if ! -s <ipaddress> -j DROP
iptables -I DOCKER-USER -i ext_if ! -s <ipaddress> -j DROP

are your actual rules, they are incorrect. In particular ext_if needs to be changed to the name of the interface. On a Linode, this is typically eth0 (but may be eth1, eth2, etc. depending on how many you have).

The link cited by @jecochard above:

Add iptables Policies Before Docker Rules.

states this quite plainly:

Please note that you will need to change ext_if to correspond with your host’s actual external interface.

You can get a list of the available interfaces (and their configurations) using

ip link show

-- sw

iptables -N DOCKER-USER

is the recommend iptables chain to use according to official Docker documentation.

Another example:

iptables -I DOCKER-USER -p icmp --icmp-type any -j DROP

Here's what's happening. For incoming packets iptables goes in this order:

iptables -t raw -A PREROUTING
iptables -t mangle -A PREROUTING
iptables -t nat -A PREROUTING <----this is where Docker by default inserts it's first rule! This is why inseting rules at say, iptables -A INPUT will not drop packets to the container.

So if you don't want to use DOCKER-USER (which is a custom chain all the way down in INPUT by the way) you can use either raw or mangle.

Here's the difference between raw and mangle. raw is more efficient, but does not have all kernel filtering functionalities such as conntrack (for state, TCP, and other connections). You can attempt to get around this by installing something such as ipset which is still more efficient than conntrack but still may not have everything you need.

Or just use mangle if you need conntrack. Either way raw and mangle are both before that sneaky docker nat PREROUTING rule and therefore won't get bypassed by it.

3: Assuming public interface name eth0 and wanting to allow a made up ip of 45.56.78.98 and 99 for ssh port 22:

iptables -t raw -A PREROUTING -i eth0 -p tcp --dport 22 -s 45.56.78.98 -j ACCEPT
iptables -t raw -A PREROUTING -i eth0 -p tcp --dport 22 -s 45.56.78.99 -j ACCEPT
iptables -t raw -A PREROUTING -i eth0 -p tcp --dport 22 -j DROP

would drop all ipv4 tcp to port 22 except from those two ip addresses.

2: And for one ip to two ports for both tcp and udp:

iptables -t raw -A PREROUTING -i eth0 -p udp --dport 5667 -s 201.67.78.99 -j ACCEPT
iptables -t raw -A PREROUTING -i eth0 -p tcp --dport 5667 -s 201.67.78.99 -j ACCEPT
iptables -t raw -A PREROUTING -i eth0 -p udp --dport 5668 -s 201.67.78.99 -j ACCEPT
iptables -t raw -A PREROUTING -i eth0 -p tcp --dport 5668 -s 201.67.78.99 -j ACCEPT

1: And then drop all ipv4 incoming traffic:

iptables -t raw -A PREROUTING -i eth0 -j DROP

Here's the whole thing, all the ipv4 drops can actually just be done at the end this way rather than separate:

iptables -t raw -A PREROUTING -i eth0 -p tcp --dport 22 -s 45.56.78.98 -j ACCEPT
iptables -t raw -A PREROUTING -i eth0 -p tcp --dport 22 -s 45.56.78.99 -j ACCEPT
iptables -t raw -A PREROUTING -i eth0 -p udp --dport 5667 -s 201.67.78.99 -j ACCEPT
iptables -t raw -A PREROUTING -i eth0 -p tcp --dport 5667 -s 201.67.78.99 -j ACCEPT
iptables -t raw -A PREROUTING -i eth0 -p udp --dport 5668 -s 201.67.78.99 -j ACCEPT
iptables -t raw -A PREROUTING -i eth0 -p tcp --dport 5668 -s 201.67.78.99 -j ACCEPT
iptables -t raw -A PREROUTING -i eth0 -j DROP

Reply

Please enter an answer
Tips:

You can mention users to notify them: @username

You can use Markdown to format your question. For more examples see the Markdown Cheatsheet.

> I’m a blockquote.

I’m a blockquote.

[I'm a link] (https://www.google.com)

I'm a link

**I am bold** I am bold

*I am italicized* I am italicized

Community Code of Conduct