# Restricting External Container Access with Iptables

**URL:** <https://forums.docker.com/t/restricting-external-container-access-with-iptables/2225>\
**Category:** General\
**Created:** [July 5, 2015, 3:39am UTC](https://forums.docker.com/t/restricting-external-container-access-with-iptables/2225 "2015-07-05T03:39:48Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![rudijs](https://sea3.discourse-cdn.com/docker/user_avatar/forums.docker.com/rudijs/32/658_2.png) [@rudijs](https://forums.docker.com/u/rudijs)\
**Post date:** [July 5, 2015, 3:39am UTC](https://forums.docker.com/t/restricting-external-container-access-with-iptables/2225/1 "2015-07-05T03:39:48Z")

</div>

Hi,

The Docker networking documentation show’s how to easily restrict external container access to a single IP using Iptables.

```auto
Docker’s forward rules permit all external source IPs by default. 
To allow only a specific IP or network to access the containers, 
insert a negated rule at the top of the DOCKER filter chain. 
For example, to restrict external access such that only source IP 8.8.8.8 can access the containers, 
the following rule could be added:

$ iptables -I DOCKER -i ext_if ! -s 8.8.8.8 -j DROP

```

What’s the best approach for allowing, say, two external IP addresses?

Adding another rule negating another IP won’t work as the 1st negation would have already matched and returned from the table.

My initial thoughts are a PRE\_DOCKER table with a final return of DROP before the DOCKER table.

Any thoughts or suggestions would be greatly appreciated.

Cheers.

---

<div class="post-metadata">

**Author:** ![rudijs](https://sea3.discourse-cdn.com/docker/user_avatar/forums.docker.com/rudijs/32/658_2.png) [@rudijs](https://forums.docker.com/u/rudijs)\
**Post date:** [July 5, 2015, 8:33am UTC](https://forums.docker.com/t/restricting-external-container-access-with-iptables/2225/2 "2015-07-05T08:33:12Z")

</div>

Hi,

Here’s my 1st iteration on a solution for this.

So far it seems to work just as required, but feedback would be appreciated.

Goal: Restrict access by IP to docker container listening on the host port 5000 (Docker Private Resgistry).

Senario: Currently, by default, all traffic is allowed access port 5000

```auto
Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target prot opt in out source destination         
    0 0 DOCKER all -- * docker0 0.0.0.0/0 0.0.0.0/0           
    0 0 ACCEPT all -- * docker0 0.0.0.0/0 0.0.0.0/0 ctstate RELATED,ESTABLISHED
    0 0 ACCEPT all -- docker0 !docker0 0.0.0.0/0 0.0.0.0/0           
    0 0 ACCEPT all -- docker0 docker0 0.0.0.0/0 0.0.0.0/0           

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

```

Solution:

a) Create a PRE\_DOCKER table with a default rule of REJECT, insert this as the 1st table on the FORWARD chain.

```auto
sudo iptables -N PRE_DOCKER
sudo iptables -I FORWARD -o docker0 -j PRE_DOCKER
sudo iptables -A PRE_DOCKER -i docker0 ! -o docker0 -j ACCEPT
sudo iptables -A PRE_DOCKER -i docker0 -o docker0 -j ACCEPT
sudo iptables -A PRE_DOCKER -j REJECT

```

b) Insert rules before the default REJECT to allow IP addresses.

One IP from the USA and one IP from Asia:

```auto
sudo iptables -I PRE_DOCKER -s 192.184.41.144 -j ACCEPT
sudo iptables -I PRE_DOCKER -s 120.29.76.14 -j ACCEPT

```

c) End result:

```auto
Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target prot opt in out source destination         
    0 0 PRE_DOCKER all -- * docker0 0.0.0.0/0 0.0.0.0/0           
    0 0 DOCKER all -- * docker0 0.0.0.0/0 0.0.0.0/0           
    0 0 ACCEPT all -- * docker0 0.0.0.0/0 0.0.0.0/0 ctstate RELATED,ESTABLISHED
    0 0 ACCEPT all -- docker0 !docker0 0.0.0.0/0 0.0.0.0/0           
    0 0 ACCEPT all -- docker0 docker0 0.0.0.0/0 0.0.0.0/0           

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

Chain PRE_DOCKER (1 references)
 pkts bytes target prot opt in out source destination         
    0 0 ACCEPT all -- * * 120.29.76.14 0.0.0.0/0           
    0 0 ACCEPT all -- * * 192.184.41.144 0.0.0.0/0           
    0 0 ACCEPT all -- docker0 !docker0 0.0.0.0/0 0.0.0.0/0           
    0 0 ACCEPT all -- docker0 docker0 0.0.0.0/0 0.0.0.0/0           
    0 0 REJECT all -- * * 0.0.0.0/0 0.0.0.0/0 reject-with icmp-port-unreachable

```

It’s a Sunday afternoon here so I’m just kind of tinkering, but my tests so far work OK.

Seems reasonable to you too?

I could be totally wrong, iptables can be tricky and I’m no guru with it.

Cheers.

---

<div class="post-metadata">

**Author:** ![rudijs](https://sea3.discourse-cdn.com/docker/user_avatar/forums.docker.com/rudijs/32/658_2.png) [@rudijs](https://forums.docker.com/u/rudijs)\
**Post date:** [July 11, 2015, 4:55am UTC](https://forums.docker.com/t/restricting-external-container-access-with-iptables/2225/3 "2015-07-11T04:55:13Z")

</div>

Hi,

2nd iteration… posting here for posterity, feedback welcome.

This seems to work as I require now.

Access to Docker containers via Public and Private interfaces are filtered.

```auto
# Create a PRE_DOCKER table
iptables -N PRE_DOCKER

# Insert this as the first table on the FORWARD chain.
iptables -I FORWARD -o docker0 -j PRE_DOCKER

# Access is specifically ordered

# Docker internal use
iptables -A PRE_DOCKER -o docker0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
iptables -A PRE_DOCKER -i docker0 ! -o docker0 -j ACCEPT
iptables -A PRE_DOCKER -m state --state RELATED -j ACCEPT
iptables -A PRE_DOCKER -i docker0 -o docker0 -j ACCEPT

# Docker Containers Restricted LAN Access
iptables -A PRE_DOCKER -i eth1 -s 192.168.33.0/24 -j ACCEPT

# Docker Containers Public Access (http is open for all)
iptables -A PRE_DOCKER -i eth0 -p tcp --dport 80 -j ACCEPT
iptables -A PRE_DOCKER -i eth0 -p tcp --dport 443 -j ACCEPT

# All other Docker Containers Public Admin access (my IP)
sudo iptables -I PRE_DOCKER -s 192.184.41.144 -j ACCEPT

# Set the default action to REJECT
iptables -A PRE_DOCKER -j REJECT

```

---

<div class="post-metadata">

**Author:** ![rudijs](https://sea3.discourse-cdn.com/docker/user_avatar/forums.docker.com/rudijs/32/658_2.png) [@rudijs](https://forums.docker.com/u/rudijs)\
**Post date:** [July 17, 2015, 2:39am UTC](https://forums.docker.com/t/restricting-external-container-access-with-iptables/2225/4 "2015-07-17T02:39:56Z")

</div>

3rd iteration on my solution for this, posting here for posterity and to complete this thread.

I’ve made this into a full bash script and will provide a link to a blog post which has a more complete intro and description.

```auto
#!/usr/bin/env bash

# Usage:
# timeout 10 docker_iptables.sh
#
# Use the builtin shell timeout utility to prevent infinite loop (see below)

if [! -x /usr/bin/docker]; then
    exit
fi

# Create a PRE_DOCKER table
iptables -N PRE_DOCKER

# Default action
iptables -I PRE_DOCKER -j DROP

# Docker Containers Public Admin access (insert your IPs here)
iptables -I PRE_DOCKER -i eth0 -s 192.184.41.144 -j ACCEPT
iptables -I PRE_DOCKER -i eth0 -s 120.29.76.14 -j ACCEPT

# Docker Containers Restricted LAN Access (insert your LAN IP range or multiple IPs here)
iptables -I PRE_DOCKER -i eth1 -s 192.168.1.101 -j ACCEPT
iptables -I PRE_DOCKER -i eth1 -s 192.168.1.102 -j ACCEPT

# Docker internal use
iptables -I PRE_DOCKER -o docker0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
iptables -I PRE_DOCKER -i docker0 ! -o docker0 -j ACCEPT
iptables -I PRE_DOCKER -m state --state RELATED -j ACCEPT
iptables -I PRE_DOCKER -i docker0 -o docker0 -j ACCEPT

# Docker container named www-nginx public access policy
WWW_IP_CMD="/usr/bin/docker inspect --format='{{.NetworkSettings.IPAddress}}' www-nginx"
WWW_IP=$($WWW_IP_CMD)

# Double check, wait for docker socket (upstart docker.conf already does this)
while [! -e "/var/run/docker.sock"]; do echo "Waiting for /var/run/docker.sock..."; sleep 1; done

# Wait for docker web server container IP
while [-z "$WWW_IP"]; do echo "Waiting for www-nginx IP..."; WWW_IP=$($WWW_IP_CMD); done

# Insert web server container filter rules
iptables -I PRE_DOCKER -i eth0 -p tcp -d $WWW_IP --dport 80 -j ACCEPT
iptables -I PRE_DOCKER -i eth0 -p tcp -d $WWW_IP --dport 443 -j ACCEPT

# Finally insert the PRE_DOCKER table before the DOCKER table in the FORWARD chain.
iptables -I FORWARD -o docker0 -j PRE_DOCKER

```

Blog post is at [http://rudijs.github.io/2015-07/docker-restricting-container-access-with-iptables/](http://rudijs.github.io/2015-07/docker-restricting-container-access-with-iptables/)

---

<div class="post-metadata">

**Author:** ![pmendes](https://avatars.discourse-cdn.com/v4/letter/p/45deac/32.png) [@pmendes](https://forums.docker.com/u/pmendes)\
**Post date:** [June 30, 2016, 5:36pm UTC](https://forums.docker.com/t/restricting-external-container-access-with-iptables/2225/5 "2016-06-30T17:36:39Z")

</div>

Hi Rudi and thanks for this excellent script. I seem to have all outgoing traffic blocked (apart from the admin IPs) when I apply these rules. I need my containers to send outgoing traffic, ideally without any restrictions.

I’ve tried different additional rules without success, do you have any idea how this could be achieved?

Thanks,  
Pedro.

---

<div class="post-metadata">

**Author:** ![90bigfoot](https://sea3.discourse-cdn.com/docker/user_avatar/forums.docker.com/90bigfoot/32/21689_2.png) [@90bigfoot](https://forums.docker.com/u/90bigfoot)\
**Post date:** [November 14, 2018, 5:27pm UTC](https://forums.docker.com/t/restricting-external-container-access-with-iptables/2225/7 "2018-11-14T17:27:47Z")

</div>

Hi, @rudijs, I’ve followed your instructions and put my rules into UFW configuration in `before.rules`, and all works like a charm!  
Just replaced `PRE_DOCKER` with `DOCKER-USER`.
