How to Restrict External Access to Docker Containers Using IPTables Rules (Fix Ignored INPUT Rules)


2 views

When you run a Docker container with port mapping like this:

docker run --name some_container_1 -p 8080:80 -d some_image

You might notice that the exposed port (8080 in this case) becomes accessible from external networks, even when your iptables INPUT chain has restrictive rules. This behavior occurs because Docker manipulates iptables rules in a special way.

Docker creates its own networking rules in these chains:

  • FORWARD chain for container-to-container communication
  • DOCKER chain for port forwarding
  • NAT table for address translation

The key issue is that Docker bypasses the INPUT chain completely when handling incoming connections to published ports.

Instead of modifying the INPUT chain, we need to add restrictions to the DOCKER chain. Here's how to limit access to only IP 123.456.789.0:

# First, delete the existing rule in DOCKER chain
sudo iptables -D DOCKER -d 172.17.0.2/32 ! -i docker0 -o docker0 -p tcp -m tcp --dport 80 -j ACCEPT

# Add new restricted rule
sudo iptables -I DOCKER -d 172.17.0.2/32 ! -i docker0 -o docker0 -p tcp -m tcp --dport 80 -s 123.456.789.0 -j ACCEPT

To make these changes permanent:

# Save current rules
sudo iptables-save > /etc/iptables/rules.v4

# On Debian/Ubuntu, install persistent package
sudo apt-get install iptables-persistent

For newer Docker versions, you can use the --iptables=false flag when starting Docker, then manage all rules manually:

# Stop Docker
sudo systemctl stop docker

# Edit Docker service file
sudo nano /etc/systemd/system/docker.service.d/override.conf

# Add:
[Service]
ExecStart=
ExecStart=/usr/bin/dockerd --iptables=false

# Reload and restart
sudo systemctl daemon-reload
sudo systemctl start docker

To confirm your restrictions are working:

# Check active rules
sudo iptables -L DOCKER -n -v

# Test connectivity
curl -I http://your-server:8080  # From allowed IP
curl -I http://your-server:8080  # From blocked IP

If you encounter problems:

  1. Check Docker logs: journalctl -u docker.service
  2. Verify rule order: sudo iptables -L DOCKER -n --line-numbers
  3. Test with temporary rule: sudo iptables -I DOCKER 1 -j LOG to see traffic

When running a Docker container with port mapping like:

docker run --name some_container_1 -p 8080:80 -d some_image

The container's port 80 becomes accessible on the host's port 8080. By default, Docker bypasses the host's iptables INPUT chain rules, making the port accessible from external networks when you might want to restrict access.

From the provided iptables output, we can see Docker creates its own chains and rules:

Chain DOCKER (1 references)
num   pkts bytes target     prot opt in     out     source               destination         
1       24  1524 ACCEPT     tcp  --  !docker0 docker0  0.0.0.0/0            172.17.0.2           tcp dpt:80

This rule allows all traffic to reach the container's port 80, regardless of the INPUT chain restrictions.

To restrict access to only specific IPs (like 123.456.789.0), we need to modify Docker's forwarding rules:

# First, flush existing Docker rules
sudo iptables -t filter -F DOCKER

# Then add the restricted rule
sudo iptables -t filter -A DOCKER -d 172.17.0.2/32 ! -i docker0 -o docker0 -p tcp -m tcp --dport 80 -s 123.456.789.0/24 -j ACCEPT

# Add a DROP rule for other sources
sudo iptables -t filter -A DOCKER -d 172.17.0.2/32 ! -i docker0 -o docker0 -p tcp -m tcp --dport 80 -j DROP

To make these changes permanent:

sudo apt-get install iptables-persistent
sudo netfilter-persistent save

For newer Docker versions, you can use the --iptables=false flag when starting the daemon and manage all rules manually:

dockerd --iptables=false

Then create custom rules in the INPUT chain:

sudo iptables -A INPUT -p tcp --dport 8080 -s 123.456.789.0/24 -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 8080 -j DROP

After applying these rules, verify with:

sudo iptables -t filter -L DOCKER -n -v

You should see your restricted rules in place.