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

allows iptables to recognize docker's chain name "DOCKER-USER" if you haven't already started a container since rebooting.

Another example:

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

This is normal iptables, it's just that Docker prefers the chain name of DOCKER-USER. So it can be iptables -A DOCKER-USER instead of -I, ect. It's just a chain name.

However, and this is a big one, connection limits especially incoming packet drops are probably best done at the PREROUTING level if possible with Docker. This is because packets are passed through more Docker chains than just DOCKER-USER. Such as docker0 and CONTAINER-ISOLATION 1 and 2. Let alone any other complexities of running multiple containers. Meaning just because you drop ping packets for docker-user it doesn't mean ping isn't getting bounced around in there. It just means ping won't fully reach the service running in the container.

So: iptables -t mangle -A PREROUTING -p icmp --icmp-type any -j DROP
should technically be better than dropping ping via DOCKER-USER you see?

It's actually somewhat of a significant security risk. You can use iptables' INPUT, OUTPUT, FORWARD rules to restrict network access to the host os, but default docker settings sort of open the container up anyways. I'm having trouble stopping DDOS attacks to my game server for this very reason. At least maybe until I narrow down the PREROUTING.

After starting/running a container do iptables -S or iptables -L > ipoutput.txt and cat ipoutput.txt to take a glance at what the docker daemon does to iptables itself.

Edit I've narrowed down the DDOS settings for my UDP game server. Requires netfilter, ipset, and sysctl kernel settings. This is meant as just an example, but good luck doing all of this past PREROUTING with Docker installed lmao. Changing sysctl will certainly break connections you already have open! So could any of these iptables rules. About the only thing that won't is ipset.

apt install iptables-persistent netfilter-persistent
copy these rules into the bottom of /etc/sysctl.conf

kernel.printk = 4 4 1 7
kernel.sysrq = 0
kernel.shmmax = 4294967296
kernel.shmall = 4194304
kernel.core_uses_pid = 1
kernel.msgmnb = 65536
kernel.msgmax = 65536
vm.swappiness = 20
vm.dirty_ratio = 80
vm.dirty_background_ratio = 5
fs.file-max = 2097152
net.core.netdev_max_backlog = 262144
net.core.rmem_default = 31457280
net.core.rmem_max = 67108864
net.core.wmem_default = 31457280
net.core.wmem_max = 67108864
net.core.somaxconn = 65535
net.core.optmem_max = 25165824
net.ipv4.neigh.default.gc_thresh1 = 4096
net.ipv4.neigh.default.gc_thresh2 = 8192
net.ipv4.neigh.default.gc_thresh3 = 16384
net.ipv4.neigh.default.gc_interval = 5
net.ipv4.neigh.default.gc_stale_time = 120
net.netfilter.nf_conntrack_max = 120000000
net.ipv4.ip_local_port_range = 1024 65000
net.ipv4.ip_no_pmtu_disc = 1
net.ipv4.route.flush = 1
net.ipv4.icmp_echo_ignore_all = 1
net.ipv4.route.max_size = 8048576
net.ipv4.icmp_echo_ignore_broadcasts = 1
net.ipv4.icmp_ignore_bogus_error_responses = 1
net.ipv4.udp_mem = 65536 131072 262144
net.ipv4.udp_rmem_min = 16384
net.ipv4.udp_wmem_min = 16384
net.ipv4.ip_forward = 0
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.all.accept_source_route = 0
net.ipv4.conf.all.rp_filter = 1
net.ipv6.conf.all.disable_ipv6 = 1
net.ipv6.conf.default.disable_ipv6 = 1
net.ipv6.conf.lo.disable_ipv6 = 1
net.ipv6.conf.eth0.disable_ipv6 = 1
net.ipv4.conf.all.mc_forwarding=0
net.ipv6.conf.all.mc_forwarding=0
net.ipv6.conf.all.accept_redirects=0
net.ipv4.conf.all.secure_redirects=0
net.ipv4.conf.all.send_redirects=0
net.ipv4.conf.all.accept_source_route=0

Before doing sysctl -p be sure to do: modprobe ip_conntrack
next we need ipset:
apt-get install ipset
then do the command:
ipset create DDOS hash:ip timeout 86400

Now the iptables DDOS rules. You'll see "--match-set DDOS" a few times, this is checking the IPs stored in the ipset table we created and named DDOS. These IPs get added if they match either the iptables connlimit OR the iptables hitcount rules you'll see below :) Meaning if a source IP triggers either rule, it's added to the ipset table and DROPPED for a full day (86400 seconds).

The container uses host port 2302

iptables -t raw -A PREROUTING ! -p udp -j DROP
iptables -t mangle -A PREROUTING -m state --state INVALID -j DROP
iptables -t raw -A PREROUTING -s 169.254.0.0/16 -j DROP
iptables -t raw -A PREROUTING -s 192.0.2.0/24 -j DROP
iptables -t raw -A PREROUTING -s 192.168.0.0/16 -j DROP
iptables -t raw -A PREROUTING -s 10.0.0.0/8 -j DROP
iptables -t raw -A PREROUTING -s 0.0.0.0/8 -j DROP
iptables -t raw -A PREROUTING -s 240.0.0.0/5 -j DROP
iptables -t raw -A PREROUTING -s 127.0.0.0/8 ! -i lo -j DROP
iptables -t raw -A PREROUTING -s 224.0.0.0/3 -j DROP
iptables -t mangle -A PREROUTING -i eth0 -p udp --dport 2302 -m length ! --length 32:900 -j DROP
iptables -t mangle -A PREROUTING -i eth0 -p udp --dport 2302 -m connlimit --connlimit-above 3 --connlimit-mask 32 -j SET --add-set DDOS src
iptables -t mangle -A PREROUTING -i eth0 -p udp --dport 2302 -m set --match-set DDOS src -j DROP
iptables -t raw -A PREROUTING -i eth0 -p udp --dport 2302 -m string --string "DNS" --algo kmp -j DROP
iptables -t raw -A PREROUTING -i eth0 -p udp --dport 2302 -m string --string "ffffffff676574737461747573" --algo kmp -j DROP
iptables -t raw -A PREROUTING -i eth0 -p udp --dport 2302 -m u32 --u32 "22&0xFFFF=0x0008" -j DROP
iptables -t raw -A PREROUTING -i eth0 -p udp --dport 2302 --source-port 123 -j DROP
iptables -t raw -A PREROUTING -i eth0 -p udp --dport 2302 --source-port 80 -j DROP
iptables -t raw -A PREROUTING -i eth0 -p udp --dport 2302 --source-port 443 -j DROP
iptables -t raw -A PREROUTING -i eth0 -p udp --dport 2302 --source-port 427 -j DROP
iptables -t raw -A PREROUTING -i eth0 -p udp --dport 2302 --source-port 500 -j DROP
iptables -t raw -A PREROUTING -i eth0 -p udp --dport 2302 --source-port 2869 -j DROP
iptables -t raw -A PREROUTING -i eth0 -p udp --dport 2302 -m string --string "SNMP" --algo kmp -j DROP
iptables -t raw -A PREROUTING -i eth0 -p udp --dport 2302 -m string --string "NTP" --algo kmp -j DROP
iptables -t raw -A PREROUTING -i eth0 -p udp --dport 2302 -m string --string "Source Engine Query" --algo kmp --to 65535 -j DROP
iptables -t raw -A PREROUTING -i eth0 -p udp --dport 2302 -m string --string "getstatus" --algo kmp -j DROP
iptables -t raw -A PREROUTING -i eth0 -p udp --dport 2302 -m string --string "QUIC" --algo kmp -j DROP
iptables -t raw -A PREROUTING -i eth0 -p udp --dport 2302 -m string --string "SNMP" --algo kmp -j DROP
iptables -t raw -A PREROUTING -i eth0 -p udp --dport 2302 -m string --string "MDNS" --algo kmp -j DROP
iptables -t raw -A PREROUTING -i eth0 -p udp --dport 2302 -m string --string "SRVLOC" --algo kmp -j DROP
iptables -t raw -A PREROUTING -i eth0 -p udp --dport 2302 -m string --string "ISAKMP" --algo kmp -j DROP
iptables -t raw -A PREROUTING -i eth0 -p udp --dport 2302 -m string --string "NBDS" --algo kmp -j DROP
iptables -t raw -A PREROUTING -i eth0 -p udp --dport 2302 -m string --string "XDCMP" --algo kmp -j DROP
iptables -t raw -A PREROUTING -i eth0 -p udp --dport 2302 -m string --string "SSDP" --algo kmp -j DROP
iptables -t raw -A PREROUTING -i eth0 -p udp --dport 2302 -m string --string "RTCP" --algo kmp -j DROP
iptables -t raw -A PREROUTING -i eth0 -p udp --dport 2302 -m string --string "H.248" --algo kmp -j DROP
iptables -t raw -A PREROUTING -i eth0 -p udp --dport 2302 -m string --string "H.323" --algo kmp -j DROP
iptables -t mangle -A PREROUTING -i eth0 -p udp --dport 2302 -m state --state NEW,RELATED,ESTABLISHED -m recent --set
iptables -t mangle -A PREROUTING -i eth0 -p udp --dport 2302 -m state --state NEW,RELATED,ESTABLISHED -m recent --update --seconds 1 --hitcount 255 -j SET --add-set DDOS src
iptables -t mangle -A POSTROUTING ! -p udp -j DROP
iptables -t mangle -A POSTROUTING -m string --string "QUIC" --algo kmp -j DROP
iptables -t mangle -A POSTROUTING -m string --string "RTCP" --algo kmp -j DROP
iptables -t mangle -A POSTROUTING -m string --string "CLDAP" --algo kmp -j DROP
iptables -t mangle -A POSTROUTING -m string --string "DCP-" --algo kmp -j DROP
iptables -t mangle -A POSTROUTING -p udp -m length --length 120 -m u32 --u32 "0 >> 22 & 0x3c @ 8 = 0x2000000" -j DROP

To check stored IPs that iptables banned, do:
ipset list

I've found that deleting or even flushing all the IPs in the ipset DDOS table does not close connections to legit clients in the game :)

Side note:
The rule actually dropping packets from IPs in the ipset table is:
iptables -t mangle -A PREROUTING -i eth0 -p udp --dport 2302 -m set --match-set DDOS src -j DROP

And you'll notice that particular rule is very close to the top of the list. Since IPs only get added to ipset if they match either of the 2 trigger rules we might as well as drop them asap from then on since it's just a source IP check in the packets :)

Another note: if -t raw -A PREROUTING doesn't work try -t mangle -A PREROUTING

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