Last active
October 29, 2024 11:07
-
-
Save blockpane/600b988e3d0d79a1bf668a763d8379c9 to your computer and use it in GitHub Desktop.
Example of how to setup iptables to correctly filter Docker services
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
### This is an example script that sets up IP tables with Docker. Many admins are not aware that UFW | |
### and Docker do not play well together, and accidently expose RPC ports and vulnerable services to | |
### the network. This replaces UFW with Netfilter-persistent and adds rules to the DOCKER-USER chain | |
### which is the correct way to filter incoming traffic with docker. This specific example is for a | |
### Tendermint node running via docker, and wireguard / ssh running directly on the host. | |
# CHANGE THIS TO ACTUAL PUBLIC INTERFACE!!!!!!!!!! | |
EXT_IF=eth0 | |
ip a show ${EXT_IF} || exit 1 # Since we change input to drop at the end, be absolutely sure the interface exists! | |
[ $(id -u) -eq 0 ] || { echo "must be root"; kill 0; } | |
# Get rid of UFW | |
ufw disable | |
systemctl disable ufw | |
systemctl stop ufw | |
apt-get -y remove ufw | |
# Ensure rules are saved and restored on restart: | |
DEBIAN_FRONTEND=noninteractive | |
apt-get -y install iptables-persistent netfilter-persistent | |
systemctl enable netfilter-persistent | |
# Ensure we don't get dropped while loading new rules: | |
iptables -P INPUT ACCEPT | |
iptables -F INPUT | |
# Always allow VPN or loopback, and keep state: | |
iptables -A INPUT -i lo -j ACCEPT | |
iptables -A INPUT -i wg0 -j ACCEPT | |
iptables -A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT | |
### allow local communication with docker nets: | |
iptables -A INPUT -s 172.16.0.0/12 -j ACCEPT # very important! | |
# anything running directly on the host is added here: | |
# Wireguard | |
iptables -A INPUT -p udp -m udp --dport 51820 -j ACCEPT | |
# SSH, highly recommend adding '-s x.x.x.x/x' with trusted IP addresses, and/or installing fail2ban | |
iptables -A INPUT -p udp -m udp --dport 22 -j ACCEPT | |
### To filter docker ports we have to use the DOCKER-USER chain, RETURN allows the connection. | |
### we use the contrack modules original destination port attribute instead of tcp/udp module's dport | |
iptables -F DOCKER-USER | |
iptables -A DOCKER-USER -i ${EXT_IF} -m conntrack --ctstate RELATED,ESTABLISHED -j RETURN | |
### Public docker services, default is to DROP: | |
# Need to use the conntrack original destination port: | |
iptables -A DOCKER-USER -i ${EXT_IF} -p tcp -m tcp -m conntrack --ctorigdstport 26656 -j RETURN | |
# Block the rest | |
iptables -A DOCKER-USER -i ${EXT_IF} -j DROP # Drop all from external interface | |
iptables -A DOCKER-USER -j RETURN # allow all other interfaces | |
iptables -P INPUT DROP | |
# clean up any old ufw tables: | |
iptables-save| grep -v ufw |iptables-restore | |
### now ipv6 - docker containers accessed via ipv6 don't use the USER chain so INPUT chain handles it fine, not sure why :/ | |
ip6tables -P INPUT ACCEPT | |
ip6tables -F INPUT | |
ip6tables -A INPUT -i lo -j ACCEPT | |
# ipv6 needs ICMP or it will not work. | |
ip6tables -A INPUT -p icmpv6 -j ACCEPT | |
ip6tables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT | |
# public services | |
ip6tables -A INPUT -p tcp --dport 26656 -j ACCEPT | |
ip6tables -A INPUT -p udp --dport 51820 -j ACCEPT | |
ip6tables -A INPUT -p tcp --dport 22 -j ACCEPT | |
ip6tables -P INPUT DROP | |
ip6tables-save| grep -v ufw |ip6tables-restore | |
# save | |
netfilter-persistent save |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Thanks for the example! There seems to be a small error in line 48 for allowing SSH, because it uses TCP (instead of UDP) - maybe this is a copy & paste error from the line above ;)