How do I add iptable rules to my Linode while running Docker containers?
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:
- Block all incoming traffic.
- Allow access from one IP address on 2 ports
- 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:
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