Search code examples
dockernetwork-programmingdocker-composeiptables

Restrict docker container internet access


TLDR; - Added some iptable rules to a docker container to limit internet access. Working fine except that now I am unable to access container app from host machine but can do so from within container itself

I have a container running a webapp. This container uses mysql, redis etc. Every dependency are remote, accessible by an ip address, on a particular port.

So, for instance, mysql is accessible on ip 13.255.255.255

What I want is to allow the container only to be able to use mysql ip address and not any other. There are few curl requests originating from within the code which I do not want to go beyond my host machine's network.

I've added an entrypoint script in docker which adds some iptables rule in container.

ALLOWED_CIDR1=172.0.0.0/16
ALLOWED_CIDR2=13.255.255.255 #For mysql access

#iptables -P FORWARD DROP # we aren't a router
iptables -A INPUT -m state --state INVALID -j DROP
iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -A INPUT -i lo -j ACCEPT
iptables -A INPUT -s 127.0.0.1 -j ACCEPT
iptables -A OUTPUT -d 127.0.0.1 -j ACCEPT
iptables -P INPUT DROP # Drop everything we don't accept

iptables -A INPUT -s 0.0.0.0 -j ACCEPT
iptables -A INPUT -s ::1 -j ACCEPT
iptables -A OUTPUT -d ::1 -j ACCEPT
iptables -A INPUT -s $ALLOWED_CIDR1 -j ACCEPT
iptables -A INPUT -s $ALLOWED_CIDR1 -j ACCEPT
iptables -A OUTPUT -d $ALLOWED_CIDR2 -j ACCEPT
iptables -A OUTPUT -d $ALLOWED_CIDR2 -j ACCEPT
iptables -P INPUT DROP
iptables -P OUTPUT DROP

When I run container and do

docker-compose exec <container-name> curl http://google.com

I get following in response:

curl: (6) Could not resolve host: google.com

which is expected. Now, when I do

docker-compose exec <container-name> curl http://0.0.0.0

I get following response:

"Hello World!"

Which again is expected. However, when I do curl http://0.0.0.0 from my host machine, following is the output

* Trying 0.0.0.0...
* TCP_NODELAY set
* Connected to 0.0.0.0 (127.0.0.1) port 80 (#0)
> GET / HTTP/1.1
> Host: 0.0.0.0
> User-Agent: curl/7.62.0
> Accept: */*
> // Hangs here

So, I am unable to connect to http://0.0.0.0 from host machine, but can connect from inside the docker.


Solution

  • I am utterly stupid to overlook the iptables rules I posted myself.

    Wrong set of rules

    ALLOWED_CIDR1=172.0.0.0/16
    ALLOWED_CIDR2=13.255.255.255 #For mysql access
    
    #iptables -P FORWARD DROP # we aren't a router
    iptables -A INPUT -m state --state INVALID -j DROP
    iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
    iptables -A INPUT -i lo -j ACCEPT
    iptables -A INPUT -s 127.0.0.1 -j ACCEPT
    iptables -A OUTPUT -d 127.0.0.1 -j ACCEPT
    iptables -P INPUT DROP # Drop everything we don't accept
    
    iptables -A INPUT -s 0.0.0.0 -j ACCEPT
    iptables -A INPUT -s ::1 -j ACCEPT
    iptables -A OUTPUT -d ::1 -j ACCEPT
    iptables -A INPUT -s $ALLOWED_CIDR1 -j ACCEPT
    iptables -A INPUT -s $ALLOWED_CIDR1 -j ACCEPT
    iptables -A OUTPUT -d $ALLOWED_CIDR2 -j ACCEPT
    iptables -A OUTPUT -d $ALLOWED_CIDR2 -j ACCEPT
    iptables -P INPUT DROP
    iptables -P OUTPUT DROP
    
    

    You can see, in the above section:

    1. There is no OUTPUT ACCEPT for ip 0.0.0.0
    2. There is no OUTPUT ACCEPT rule for ip 192.168.x.x which is the ip address of my docker-0 network interface

    Both docker and host machine communicates using the docker0 network interface if network mode bridged is used while launching container (which happened to be my case).

    Another thing I noticed, I didn't required the 0.0.0.0 or 127.0.0.1 rules at all. Since the entrypoint script will add the iptable rules within docker container, we may never want to access webapp from within container itself. Hence, why bother with 127.0.0.1?

    All in all, here is what I did:

    1. Get my ip address for docker0 network. ip addr show docker0. It outputted 192.168.144.1/20
    2. I added 192.168.0.0/16 to ACCEPT rules in my entrypoint iptable rules, which covered my ipaddress in point 1
    3. Now I can access my container from outside

    My iptable rules looks like this now:

    ALLOWED_CIDR1=172.0.0.0/16
    ALLOWED_CIDR2=13.255.255.255
    ALLOWED_CIDR3=192.168.0.0/16
    
    iptables -A INPUT -m state --state INVALID -j DROP
    iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
    iptables -A INPUT -i lo -j ACCEPT
    iptables -P INPUT DROP # Drop everything we don't accept
    iptables -P OUTPUT DROP
    
    iptables -A INPUT -s $ALLOWED_CIDR1 -j ACCEPT
    iptables -A INPUT -s $ALLOWED_CIDR2 -j ACCEPT
    iptables -A INPUT -s $ALLOWED_CIDR3 -j ACCEPT
    iptables -A OUTPUT -d $ALLOWED_CIDR1 -j ACCEPT
    iptables -A OUTPUT -d $ALLOWED_CIDR2 -j ACCEPT
    iptables -A OUTPUT -d $ALLOWED_CIDR3 -j ACCEPT