2

we have a system which has been migrated from a traditional service to docker. We only want to have a firewall in production, therefore we want to have the iptables config only in the server, not inside the config of docker.

We had the following requierments:

  • The servers can communicate with each other.
  • You can ping the server
  • You can SSH
  • You can access the server to some web service thorugh some ports.

After reading for some hours/days I came to the following config:

# Delete old entries if any
iptables -F INPUT
iptables -F DOCKER-USER
iptables -F OUTPUT

# Set firewall 
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT # Allow established connections
iptables -A INPUT -i lo -j ACCEPT # Allow localhost communication
iptables -A OUTPUT -o lo -j ACCEPT # Allow output to the internet from localhost
iptables -A INPUT -p icmp --icmp-type 8 -j ACCEPT # Allow ICMP
iptables -A INPUT -p tcp --dport 22 -j ACCEPT # Allow SSH
iptables -A INPUT -j DROP # Drop eveything else

# Docker specific, allow connections to the ports of the web services:
iptables -A DOCKER-USER -i ens192 -s 192.168.69.0/24 -p tcp -m conntrack --ctorigdstport 8080 --ctdir ORIGINAL -j ACCEPT
iptables -A DOCKER-USER -i ens192 -s 192.168.69.0/24 -p tcp -m conntrack --ctorigdstport 9000 --ctdir ORIGINAL -j ACCEPT
iptables -A DOCKER-USER -i ens192 -s 192.168.69.0/24 -p tcp -m conntrack --ctorigdstport 19900 --ctdir ORIGINAL -j ACCEPT
iptables -A DOCKER-USER -i docker0 -j ACCEPT # Allow input from other containers
iptables -A DOCKER-USER -i ens192 -j DROP # Drop all access to the containers through default interface

At this point:

  • I can not access the web services if I'm not in that subnet.
  • I can ping and I can connect thorugh ssh.
  • The containers can comunicate with each other. So most of the requierments where fullfilled.

But now when we try to access the internet from a container it has not access. (e.g. ping to google, or local network). But from localhost it works.

I have tried many options like multiple combinations of the followings:

iptables -A DOCKER-USER -o docker0 -j ACCEPT
iptables -A OUTPUT -o docker0 -j ACCEPT
iptables -A DOCKER-USER -o ens192 -j ACCEPT

But none of them seem to allow me to ping from a docker container.

3 Answers 3

1

In my opinion you're overthinking this a bit since docker comes with some sane default configuration which it also manages automatically. E.g. in the default setup containers are not accessible via external network but they can connect to the internet themselves.

Therefore you should just EXPOSE the relevant ports of your containers on the host's external IP or bind to the host's loopback interface and then simply think of the containers as ordinary services running on the host!

I.e. if you are running a web service with docker bind it to the respective host ports:

docker run -p 80:80 -p 443:443 ...

Then configure your iptables to whitelist the required ports and DROP everything else:

iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT # Allow established connections
iptables -A INPUT -p icmp --icmp-type 8 -j ACCEPT # Allow ICMP
iptables -A INPUT -p tcp --dport 22 -j ACCEPT # Allow SSH
iptables -A INPUT -p tcp --dport 80 -j ACCEPT # Allow HTTP
iptables -A INPUT -p tcp --dport 443 -j ACCEPT # Allow HTTPS
iptables -A INPUT -j DROP # Drop eveything else

Everything else regarding the setup of the internal networking, allowing/blocking connections from/to the containers and forwarding the exposed ports docker is already taking care of for you.

This especially means what you should definitely NOT do is to mess with the DOCKER chains in iptables because these are automatically managed by the docker daemon!

As said above the defaults are in most cases already what you want anyways. If you need more control for a custom configuration you would need to disable the iptables management in docker and manage the network configuration manually - generally this is not what you want to do.

You can see a bit more in the docker networking documentation.

4
  • Hello, my problem with this aproach is that I want the same docker container for production and development. And in development we want to be able to access databases running in diferent ports, swagger APIs.... The same as we dd without docker. And then in production have the layer of security on top.
    – nck
    Jul 27, 2020 at 9:55
  • 1
    @nck I don't see why this wouldn't work with this approach at all... since the iptables rules have to be implemented on the host this doesn't make any difference for the containers. You can use the same images for development and production. For injecting environment-specific configuration use environment variables and/or secrets. If you are using docker-compose also use a .env file
    – acran
    Jul 27, 2020 at 14:15
  • 1
    Thank you. However, isn't it more appropriate to utilize DOCKER-USER chain instead of the general INPUT? Related: serverfault.com/a/933803/345785 (Steps for limiting outside connections to docker container with iptables?...)
    – Artfaith
    Mar 23, 2023 at 7:29
  • It depends on your requirements. I used INPUT in this answer to highlight the approach of thinking about the containers as ordinary services running on the host. For most people this approach would be enough. For more sophisticated setups, yes, DOCKER-USER or even a completely manual configuration may be more appropriate.
    – acran
    Mar 23, 2023 at 16:52
0

I think I found a working solution, but I'm not sure if there is a better way.

With the things I tried I had to also declare the established connections for the DOCKER-USER set adding this two lines in comparison with my question:

iptables -A DOCKER-USER -i docker0 -j ACCEPT
> iptables -A DOCKER-USER -o docker0 -j ACCEPT
> iptables -A DOCKER-USER -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
iptables -A DOCKER-USER -i ens192 -j DROP
0

From your question, I assume that these iptables-rules are supposed to be installed on the docker-host.

You a missing a central point here: the firewall-rules are only valid for the host itself, not the containers!

Let's assume, your docker0 interface has the ip range 172.16.0.0/16. The host interface address is 172.16.0.1, and your first container might have the address 172.16.0.2.

But any outgoing traffic from the container to the internet needs to be send out from your host's main network interface, which is ens192. So, any firewall rule meant for a container must reside inside the FORWARD chain! (and don't forget leave ip_forwarding enabled for the host)

4
  • From the documentation I understand that the forward chain comes after the docker-user chain. As stated in docs.docker.com/network/iptables
    – nck
    Jul 20, 2020 at 11:19
  • From the linux-kernel Point of View, there exist three chains natively inside the filter table: INPUT, OUTPUT, and FORWARD. Every other chain must be linked inside one of those three, or it will have no effect. So, If the first rule of the FORWARD chain is to Pass everything inside the DOCKER_USER chain, you are correct. But your post didn't include auch a rule...
    – Martin
    Jul 21, 2020 at 12:37
  • is it not done automatically by docker?
    – nck
    Jul 21, 2020 at 13:40
  • it should be done automatically, yes - I think i missunderstood your question quite a bit. I think what you are trying to accomplish is to be able to access (for example) an apache running inside docker from outside the docker host. Correct ? The right way to do this is by exposing & publishing a specific port of the container... Read here for details... (or here )
    – Martin
    Jul 21, 2020 at 14:02

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .