Nethence Newdoc Olddoc Lab Your IP BBDock  

Setting up Netfilter (iptables)

as root

Write down what services you want to keep available,

netstat -antupe --inet --inet6 | grep LISTEN

for example, say tcp/22, tcp/389, tcp/636 and a range from tcp/8000 to 8999. And ICMP.

Disable distributor’s firewall and define where your rule set will live depending on your distro. On Debian systems,

#Ubuntu
systemctl stop ufw
systemctl disable ufw

ruleset=/etc/iptables.rules
ruleset6=/etc/iptables.rules6

On Redhat systems,

systemctl stop firewalld
systemctl disable firewalld
yum install iptables-services iptables-utils

ruleset=/etc/sysconfig/iptables
ruleset6=/etc/sysconfig/ip6tables

Check what chains you got e.g. if there is DOCKER,

iptables -L | grep ^Chain
ps aux | grep dockerd
iptables -L
iptables -L -t nat

==> see the Docker situation chapter below

Then prepare the rule set restoration file,

cp -pi $ruleset $ruleset.`date +%s`
vi $ruleset

*filter

# policy
:INPUT DROP
:FORWARD DROP
:OUTPUT DROP

# allow ICMP
-A INPUT -p icmp -j ACCEPT
-A OUTPUT -p icmp -j ACCEPT

# allow all loopback traffic and return all traffic to 127/8 that do not use lo
-A INPUT -i lo -j ACCEPT
-A OUTPUT -o lo -j ACCEPT
-A INPUT ! -i lo -d 127.0.0.0/8 -j REJECT
-A OUTPUT ! -o lo -d 127.0.0.0/8 -j REJECT

# allow some inbound tcp ports
-A INPUT -p tcp -m state --state NEW --dport 22 -j ACCEPT
-A INPUT -p tcp -m state --state NEW --dport 389 -j ACCEPT
-A INPUT -p tcp -m state --state NEW --dport 636 -j ACCEPT
-A INPUT -p tcp -m state --state NEW --match multiport --dports 8000:8999 -j ACCEPT

# accept udp & tcp established inbound connections
-A INPUT -p udp -m state --state RELATED,ESTABLISHED -j ACCEPT
-A INPUT -p tcp -m state --state RELATED,ESTABLISHED -j ACCEPT

# allow udp & tcp outbound traffic
-A OUTPUT -p udp -m state --state NEW,ESTABLISHED,RELATED,INVALID  -j ACCEPT
-A OUTPUT -p tcp -m state --state NEW,ESTABLISHED,RELATED,INVALID  -j ACCEPT

# forcing the existing policy, using REJECT
-A INPUT -j REJECT
-A FORWARD -j REJECT
-A OUTPUT -j REJECT

COMMIT

Note. Dealing with all network interfaces at once here, otherwise add -i netif to the rules.

Ready to go

Now apply and enable the shit,

iptables-restore < $ruleset
#service iptables restore
iptables -L -v

And check that you can ping and reach your services from a remote host and that the rest is truly filtered accordingly (return, not drop) using ping, nmap, netcat, telnet and network clients.

The Docker situation

Ok, you have got Docker in da place. There are two course of action:

Solution 1) let Docker maintain its own rules in the DOCKER chain (opening the mapped ports to the world). For that, you need to tweak your rules so it does not interfere with the DOCKER, DOCKER-ISOLATION chains nor the nat table (and possibly mangle?) maintained by the Docker daemon, and keep a reference of those chains into the FORWARD chain.

Note. We have to avoid wiping out the entire table because the iptables-restore command will conflict with the Docker daemon: you would have to restart it after you apply your own firewall rules, with the unpleasant effect of stopping all the containers.

The main table maintained by Docker,

Chain INPUT (policy ACCEPT)
target     prot opt source               destination

Chain FORWARD (policy ACCEPT)
target     prot opt source               destination
DOCKER-ISOLATION  all  --  anywhere             anywhere
DOCKER     all  --  anywhere             anywhere
ACCEPT     all  --  anywhere             anywhere             ctstate RELATED,ESTABLISHED
ACCEPT     all  --  anywhere             anywhere
ACCEPT     all  --  anywhere             anywhere

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination

Chain DOCKER (1 references)
target     prot opt source               destination

Chain DOCKER-ISOLATION (1 references)
target     prot opt source               destination
RETURN     all  --  anywhere             anywhere

The nat table maintained by Docker,

Chain PREROUTING (policy ACCEPT)
target     prot opt source               destination
DOCKER     all  --  anywhere             anywhere             ADDRTYPE match dst-type LOCAL

Chain INPUT (policy ACCEPT)
target     prot opt source               destination

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination
DOCKER     all  --  anywhere            !127.0.0.0/8          ADDRTYPE match dst-type LOCAL

Chain POSTROUTING (policy ACCEPT)
target     prot opt source               destination
MASQUERADE  all  --  172.17.0.0/16        anywhere

Chain DOCKER (2 references)
target     prot opt source               destination
RETURN     all  --  anywhere             anywhere

The workaround,

mkdir -p ~/bin/
vi ~/bin/iptables.bash

#!/bin/bash

    # flush the built-in chains
    iptables -F INPUT
    iptables -F FORWARD
    iptables -F OUTPUT

    # policy
    iptables -P INPUT DROP
    iptables -P FORWARD DROP
    iptables -P OUTPUT DROP

    # allow icmp
    iptables -A INPUT -p icmp -j ACCEPT
    iptables -A OUTPUT -p icmp -j ACCEPT

    # allow all loopback traffic and return all traffic to 127/8 that do not use lo
    iptables -A INPUT -i lo -j ACCEPT
    iptables -A OUTPUT -o lo -j ACCEPT
    iptables -A INPUT ! -i lo -d 127.0.0.0/8 -j REJECT
    iptables -A OUTPUT ! -o lo -d 127.0.0.0/8 -j REJECT

    # let Docker do its job
    iptables -A FORWARD -j DOCKER-ISOLATION
    iptables -A FORWARD -o docker0 -j DOCKER
    iptables -A FORWARD -o docker0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT

    iptables -A FORWARD -i docker0 -j ACCEPT
    #iptables -A FORWARD -i docker0 ! -o docker0 -j ACCEPT
    #iptables -A FORWARD -i docker0 -o docker0 -j ACCEPT

    # allow some inbound tcp ports
    #iptables -A INPUT -p tcp -m state --state NEW --dport 22 -j ACCEPT
    #iptables -A INPUT -p tcp -m state --state NEW --dport 389 -j ACCEPT
    #iptables -A INPUT -p tcp -m state --state NEW --dport 636 -j ACCEPT
    #iptables -A INPUT -p tcp -m state --state NEW --match multiport --dports 8000:8999 -j ACCEPT

    # accept udp & tcp established inbound connections
    iptables -A INPUT -p udp -m state --state RELATED,ESTABLISHED -j ACCEPT
    iptables -A INPUT -p tcp -m state --state RELATED,ESTABLISHED -j ACCEPT

    # allow udp & tcp outbound traffic
    iptables -A OUTPUT -p udp -m state --state NEW,ESTABLISHED,RELATED,INVALID -j ACCEPT
    iptables -A OUTPUT -p tcp -m state --state NEW,ESTABLISHED,RELATED,INVALID -j ACCEPT

    # forcing the existing policy, using REJECT
    iptables -A INPUT -j REJECT
    iptables -A FORWARD -j REJECT
    iptables -A OUTPUT -j REJECT

    chmod +x iptables.bash

Solution 2 I read about) prevent it from managing its rules and take over the shit,

vi /etc/default/docker

DOCKER_OPTS=--iptables=false

ps aux | grep dockerd
service restart docker
ps aux | grep dockerd

so you can now play with your own rules but you need to take care of the Docker needs yourself e.g.,

ifconfig
vi $ruleset

-A FORWARD -i docker0 -o INTERFACE -j ACCEPT
-A FORWARD -i INTERFACE -o docker0 -j ACCEPT

-A FORWARD -p tcp -m state --state NEW --dport 80 -j ACCEPT
-A FORWARD -p tcp -m state --state NEW --dport 443 -j ACCEPT
#-A FORWARD -p tcp -m state --state NEW --dport 4567 -j ACCEPT
#-A FORWARD -p tcp -m state --state NEW --dport 8080 -j ACCEPT

preferably open only the ports you’ve reverse-proxied.

draft

Also allow the containers to access internet using NAT,

sysctl net.ipv4.conf.all.forwarding=1
sysctl net.ipv4.conf.all.forwarding
vi $ruleset

*nat

__TODO__

COMMIT

Ref. Understanding Docker Networking Drivers and their use cases – https://blog.docker.com/2016/12/understanding-docker-networking-drivers-use-cases/

Enable at boot time

On Debian systems,

vi /etc/network/if-pre-up.d/iptables

#!/bin/sh
/sbin/iptables-restore < /etc/iptables.rules

chmod +x /etc/network/if-pre-up.d/iptables

On Redhat systems,

cd /etc/sysconfig/
cp -pi iptables-config iptables-config.`date +%s`
cp -pi system-config-securitylevel system-config-securitylevel.`date +%s`
#system-config-securitylevel-tui
chkconfig iptables on
chkconfig ip6tables on

Old school

You can also run a script to then generate the ruleset with iptables-save,

iptables -F
iptables -F -t nat
iptables -F -t mangle
iptables -X
iptables -P INPUT DROP
iptables -P FORWARD DROP
iptables -P OUTPUT ACCEPT
iptables -A INPUT -p icmp -j ACCEPT
...

Fail-safe

To be safe while accessing a remote server and testing new rules, flush those every 5 minutes,

crontab -e

like,

SHELL=/bin/ksh
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin
MAILTO=root
HOME=/root
#
*/5 * * * * iptables -F

NAT

To enable NAT,

echo 1 > /proc/sys/net/ipv4/ip_forward

iptables -P FORWARD ACCEPT
iptables -t nat -A POSTROUTING -s 10.1.1.0/24 -o eth0 -j MASQUERADE
iptables -A INPUT -i dummy0 -s 10.1.1.0/24 -j ACCEPT
iptables -A OUTPUT -o dummy0 -d 10.1.1.0/24 -j ACCEPT

and to redirect port 80 to 10.1.1.1,

iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 80 -j DNAT --to-destination 10.1.1.1:80

Note. to enable port redirect, e.g. redirect TCP port 80 to 8080 on eth1,

iptables -A PREROUTING -i eth1 -p tcp --dport 80 -j REDIRECT --to-port 8080

Misc

Wipe out the rules

Just in case, to totally disable firewalling rules,

iptables -F
iptables -F -t nat
iptables -F -t mangle
iptables -X
iptables -P INPUT ACCEPT
iptables -P OUTPUT ACCEPT
iptables -P FORWARD ACCEPT

check,

iptables -L -v

Additional notes

Using REJECT not DROP](http://www.chiark.greenend.org.uk/~peterb/network/drop-vs-reject)

To clean up your rules (esp. in the DOCKER chain that we do not want to flush),

iptables -L DOCKER -n | grep ^REJECT | awk '{print $4}' | uniq
for ip in `iptables -L DOCKER -n | grep ^REJECT | awk '{print $4}' | uniq`; do echo -n $ip...; iptables -D DOCKER -s $ip -j REJECT && echo done; done; unset ip
iptables -L DOCKER -n | grep ^REJECT | awk '{print $4}' | uniq

iptables -L DOCKER -n | grep ^RETURN | awk '{print $4}' | uniq
for ip in `iptables -L DOCKER -n | grep ^RETURN | awk '{print $4}' | uniq`; do echo -n $ip...; iptables -D DOCKER -s $ip -j RETURN && echo done; done; unset ip
iptables -L DOCKER -n | grep ^RETURN | awk '{print $4}' | uniq

iptables -L DOCKER -n | grep ^DROP | awk '{print $4}' | uniq
for ip in `iptables -L DOCKER -n | grep ^DROP | awk '{print $4}' | uniq`; do echo -n $ip...; iptables -D DOCKER -s $ip -j DROP && echo done; done
iptables -L DOCKER -n | grep ^DROP | awk '{print $4}' | uniq

So in the end if you got only one container running with e.g. ports 25 143 597 up,

Chain DOCKER (1 references)
target     prot opt source               destination         
ACCEPT     tcp  --  0.0.0.0/0            172.17.0.X           tcp dpt:587
ACCEPT     tcp  --  0.0.0.0/0            172.17.0.X           tcp dpt:143
ACCEPT     tcp  --  0.0.0.0/0            172.17.0.X           tcp dpt:25

Trash

# log iptables denied calls (access via 'dmesg' command)
-A INPUT -m limit --limit 5/min -j LOG --log-prefix "iptables denied: " --log-level 7

If you really want to restrict ICMP to ping (bad idea),

#-A INPUT -p icmp -m icmp --icmp-type 8 -j ACCEPT
-A INPUT -p icmp -m icmp --icmp-type echo-request -j ACCEPT

you can also add other restrictions to ICMP e.g.,

-A INPUT -p icmp -m state --state NEW,ESTABLISHED -m limit --limit 10/s -j ACCEPT

If you’re experiencing some issues accessing some services, maybe -m state --state NEW is in cause and try without it.

You can define some restrictions for tcp ports e.g.,

#-A INPUT -p tcp -m tcp --dport 22 --tcp-flags SYN,RST,ACK SYN -m state --state NEW -m limit --limit 1/s -j ACCEPT
#-A INPUT -p tcp -m tcp --dport 80 --tcp-flags SYN,RST,ACK SYN -m state --state NEW -j ACCEPT

Note. the short and simplest form would be,

#iptables -A INPUT -m state --state NEW -j ACCEPT
#iptables -A OUTPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
#iptables -A INPUT -p tcp --dport 22 -j ACCEPT

Note. also worth interesting,

#... -j REJECT --reject-with tcp-reset

References


Home | GitHub | Docker Hub | Donate | Contact