Skip to content

Instantly share code, notes, and snippets.

@yorickdowne
Last active August 9, 2021 07:45
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save yorickdowne/7eb9357a7b9daa02b8b0b709905fdb2e to your computer and use it in GitHub Desktop.
Save yorickdowne/7eb9357a7b9daa02b8b0b709905fdb2e to your computer and use it in GitHub Desktop.
UFW and docker iptables via DOCKER-USER

Use ufw for some, but not all, Docker traffic

This is a slight tweak to https://github.com/chaifeng/ufw-docker by way of https://p1ngouin.com/posts/how-to-manage-iptables-rules-with-ufw-and-docker

For an automated way of handling ufw rules via docker-compose, see https://github.com/shinebayar-g/ufw-docker-automated

Use case

I wanted Docker to keep managing its own iptables rules, including automatically opening port forwards, with the option of stepping in and restricting a port that I did not want open to "world". Container-to-container traffic should flow unimpeded, no matter the ufw rules.

The ufw-user-input chain is preferred because it is easier for most users to understand.

Implementation

1) Edit after.rules:

sudo nano /etc/ufw/after.rules and add to the end of the file:

*filter
:ufw-user-input - [0:0]
:DOCKER-USER - [0:0]

# ufw in front of docker while allowing all inter-container traffic

-A DOCKER-USER -j RETURN -s 10.0.0.0/8
-A DOCKER-USER -j RETURN -s 172.16.0.0/12
-A DOCKER-USER -j RETURN -s 192.168.0.0/16

-A DOCKER-USER -j ufw-user-input
-A DOCKER-USER -j RETURN

COMMIT

Note this deliberately keeps ufw rules from influencing any traffic sourced from RFC1918, which includes the docker containers. This may not be what you need, in which case just remove those three lines, and be sure to allow needed container traffic through explicit ufw rules, if you are blocking a port.

2) Edit before.init

sudo nano /etc/ufw/before.init and change stop) to read:

stop)
    # typically required
    iptables -F DOCKER-USER || true
    iptables -A DOCKER-USER -j RETURN || true
    iptables -X ufw-user-input || true

Then, make it executable: sudo chmod 750 /etc/ufw/before.init

Dropping ufw-user-input through before.init is a required step. Without it, ufw cannot be reloaded, it would display an error message stating "ERROR: Could not load logging rules".

3) Reload ufw

sudo ufw reload

Example

Reference common ufw rules and commands to help in creating ufw rules.

Say I have Grafana enabled on port 3000 and no reverse proxy. I'd like to keep it reachable via SSH tunnel while dropping all other connections.

First, verify that Grafana is running and port 3000 is open to world using something like https://www.yougetsignal.com/tools/open-ports/

Next, create ufw rules to allow access from localhost and drop access from anywhere else:

  • sudo ufw allow from 127.0.0.1 to any port 3000
  • sudo ufw deny 3000

Check again that port 3000 is now closed.

Connect to your host with ssh tunneling, e.g. ssh -L3000:host:3000 user@host and browse to http://127.0.0.1:3000 on the client you started the SSH session from. You expect to be able to reach the Grafana dashboard.

@ksurl
Copy link

ksurl commented Mar 31, 2021

is DOCKER-USER literal or insert the docker user you use to run docker commands or the docker daemon?

@yorickdowne
Copy link
Author

DOCKER-USER is literal, that is the name of an iptables chain meant for user supplied policies. Docker creates this chain, among many others, but it’s up to the user to add policies to it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment