9

I have Ubuntu server with Docker to serve MySQL and SSH/SFTP and I need all ports except 3306 and 22 to be firewalled, pretty standard and trivial requirement, right?

Now, I managed to find a sort of solution but it doesn't work fully for me as after applying it, either:

  1. I can't access any of the mentioned services (by default)
  2. I can't access the internet from within containers (if I put iptables: false in Docker's daemon.json config file)

I tried a number of other search results but they're mostly so complex that I don't understand what they're doing or they're heavily scripted making me, an iptables layman, an impossible task to take something from it.

Proposed solution looks rather simple and easy to comprehend but entire Docker networking complexity makes it much harder to debug.

Can someone please share their working iptables rules for Docker hosts or at least guide me in right direction?

I use docker-compose to launch services and this is my yaml:

version: '3.7'

services:
  mysql:
    container_name: 'mysql'
    image: mysql:8.0.13
    command: --default-authentication-plugin=mysql_native_password
    user: 1000:1000
    ports:
      - "3306:3306"
    volumes:
      - ./data:/var/lib/mysql
      - ./config/custom.cnf:/etc/mysql/conf.d/custom.cnf
    networks:
      - database
    restart: always

networks:
  database:
    driver: bridge

Edit: What I found is that allowing Docker to manage iptables rules is recommended and less demanding, at least in a long term and its okay to let Docker open required ports even though I didn't do that in a way I prefer, its still valid. What I want at this point is to find out is it possible to use iptables to block the ports opened by Docker and how (via mangle prerouting perhaps?). Any suggestions? Thanks a ton!

dzhi
  • 770
  • 3
  • 10
  • 23
  • Do you restart the docker engine after changing iptables? – BMitch Dec 20 '18 at 14:05
  • @BMitch Absolutely. Thing is that rules from the article above are made so that Docker workloads docnot need tocbe restarted after making changes to the firewall. Got any rules to share? – dzhi Dec 20 '18 at 14:07
  • I typically configure a host with some default restrictive rules, and then start docker. Biggest thing for me is to add ports for swarm mode. For me, it just works from there. – BMitch Dec 20 '18 at 14:12
  • Make sure you don't have anything else mucking with the iptables entries, like firewalld. – BMitch Dec 20 '18 at 14:13
  • No, I'm running Ubuntu and iptables without any front-end. – dzhi Dec 20 '18 at 21:22

2 Answers2

7

I can't access the internet from within containers (if I put iptables: false in Docker's daemon.json config file)

Docker relies on iptables to configure its networking. This includes NAT rules to handle access to and from the external network, and lots of other rules to configure containers access to each other on docker networks. By default, this access is open, but there are options when creating networks to restrict outside access and inter container communication. Therefore I do not recommend setting the iptables option to false in docker since that will break all of that functionality as you've seen.

Publishing a port on the host implicitly allows access from outside. So the easiest option to avoid outside access is to not publish the port. You could publish the port onto a specific interface, e.g. 127.0.0.1:8080:80 which would publish the port 8080 on the host's loopback interface (127.0.0.1) to connect to a container's port 80, and that loopback interface is not externally accessible. However, if leaving the port unpublished is not an option, it is possible to do this with iptables.

What I want at this point is to find out is it possible to use iptables to block the ports opened by Docker and how

This can be done by modifying the DOCKER-USER filter chain. You can find examples of this, like:

$ iptables -I DOCKER-USER -i ext_if ! -s 192.168.1.0/24 -j DROP

from the following docs: https://docs.docker.com/network/iptables/

Note that the port is changed by some mangling rules that run before the filter rules, so if you want to filter by port, you'll need to use conntrack to get the original destination port:

$ iptables -I DOCKER-USER -i eth0 -p tcp \
    -m conntrack --ctorigdstport 8080 -j DROP
$ iptables -I DOCKER-USER -i eth0 -s 10.0.0.0/24 -p tcp \
    -m conntrack --ctorigdstport 8080 -j ACCEPT

Note that there is a default rule in DOCKER-USER to accept all, so you'll want to insert rules at the top of the chain (-I) rather than appending to the end (-A).

See: Steps for limiting outside connections to docker container with iptables?

BMitch
  • 5,189
  • 1
  • 21
  • 30
3

What I want at this point is to find out is it possible to use iptables to block the ports opened by Docker and how (via mangle prerouting perhaps?).

After a lot of research and tests, I found out that the best (and easiest) way to expose only the ports you want and don't expose docker ports is to set the IP in docker-compose.yml to 127.0.0.1 (expose only to the host)

ports:
  - "127.0.0.1:3306:3306"

Then you can use nginx or any other reverse proxy to expose to the outside.

This way:

  • You don't need to add DOCKER-USER iptables rules everytime you restart docker
  • Port 3306 is exposed only inside the host
  • Docker can manage iptables rules as it wants.
paaacman
  • 133
  • 4
  • Hi, thanks for the tips! btw, why do we still need a reverse proxy? – zukijuki Jan 26 '22 at 06:13
  • 1
    We still need a reverse proxy because we want to expose the service through the reverse proxy, to a specific domain only or with allow / deny IP rules, for exemple. – paaacman Jan 26 '22 at 15:57