105

This is my first time setting up an Ubuntu Server (14.04 LTS) and I am having trouble configuring the firewall (UFW).

I only need ssh and http, so I am doing this:

sudo ufw disable

sudo ufw reset
sudo ufw default deny incoming
sudo ufw default allow outgoing

sudo ufw allow 22/tcp
sudo ufw allow 80/tcp

sudo ufw enable
sudo reboot

But I can still connect to databases on other ports of this machine. Any idea about what am I doing wrong?

EDIT: these databases are on Docker containers. Could this be related? is it overriding my ufw config?

EDIT2: output of sudo ufw status verbose

Status: active
Logging: on (low)
Default: deny (incoming), allow (outgoing), deny (routed)
New profiles: skip

To                         Action      From
--                         ------      ----
22/tcp                     ALLOW IN    Anywhere
80/tcp                     ALLOW IN    Anywhere
22/tcp (v6)                ALLOW IN    Anywhere (v6)
80/tcp (v6)                ALLOW IN    Anywhere (v6)
ESala
  • 2,731
  • what database ? which port ? Are you sure this is the same machine ? what is the output of ufw status – solsTiCe Jul 25 '15 at 10:06
  • @solsTiCe yes, I am sure it is the same machine. The database is InfluxDB (on a docker container) with ports 8083 and 8086. I added the ufw status verbose output in the question. Thanks. – ESala Jul 25 '15 at 10:12

11 Answers11

158

The problem was using the -p flag on containers.

It turns out that Docker makes changes directly on your iptables, which are not shown with ufw status.

Possible solutions are:

  1. Stop using the -p flag. Use docker linking or docker networks instead.

  2. Bind containers locally so they are not exposed outside your machine:

    docker run -p 127.0.0.1:8080:8080 ...

  3. If you insist on using the -p flag, tell docker not to touch your iptables by disabling them in /etc/docker/daemon.json and restarting:

    { "iptables" : false }

I recommend option 1 or 2. Beware that option 3 has side-effects, like containers becoming unable to connect to the internet.

ESala
  • 2,731
  • 16
    This is so useful - I was going crazy with being able to access ports I had not allowed in ufw. Using iptables=false breaks a lot though, like containers becoming accessible on localhost, e. g. for reverse proxying. Better to make sure that container listen correct, don't use -p 80:80 but use -p 127.0.0.1:80:80 or different port like 127.0.0.1:3000:80 for reverse proxying with nginx or apache and no port clashes. – Andreas Reiff Apr 01 '16 at 19:50
  • 2
    @AndreasReiff you are right, but lately I have found it best to avoid -p as much as possible. For example, in the case of a reverse proxy I don't map any port for the worker containers, and then put nginx in it's own container with -p 80:80 and a --link to the others. This way there cannot be any port clashes and the only point of access is through nginx. – ESala Apr 03 '16 at 17:59
  • 2
    Also check this post for a third precaution you have to take! https://svenv.nl/unixandlinux/dockerufw – Henk Apr 14 '16 at 16:34
  • 2
    Important : /etc/default/docker is a configuration file used by the upstart config. If you have moved from upstart to systemd, this file will not be used. – orshachar Oct 05 '16 at 12:08
  • @ESala You should think about un-marking this answer as correct since it is dated. – ctbrown Nov 12 '17 at 16:38
  • Why does it work when using 127.0.0.1 but not with localhost or 0.0.0.0? – spencer.sm Jun 14 '19 at 05:05
  • 1
    Thank you so much! This answer still holds true as of 2021. It was driving me nuts I could access my database from anywhere, but with this answer I finally fixed the problem. – Duco Feb 15 '21 at 20:30
  • woah! that's some serious security breach... – Somebody Apr 05 '21 at 10:45
  • 1
    I wonder is there a way to access a port mapped as 127.0.0.1:3000:3000 via ssh tunnel.

    I.e I have a server with public ip where I do not want to expose port 3000 to the Internet, but still want to be able to access it (e.g from my laptop using ssh port forwarding)

    – Volodymyr Sorokin Dec 27 '22 at 22:20
  • This is a system-wide change that is likely to break functionality of things like reverse proxies where docker SHOULD have access to modify iptables. A better solution would address the specific port only, if possible. – Peter Kionga-Kamau Feb 09 '23 at 20:13
  • @ESala do you use NPM if yes, did you bind it to 127..1 and proxy via the actual Nginx server? I wanted to GEO block IPs of certain countries, and since NPM is a container exposed to 0.0.0.0:80 then blocking IP in UFW won't really prevent that IP from accessing port 80, it will block other ports, by my goal is to block everything – Pawel Cioch Jul 25 '23 at 15:45
  • After that I started to consider usage of ufw. – Krzysztof Mar 27 '24 at 19:27
12

16.04 presents new challenges. I did all the steps as shown Running Docker behind the ufw firewall BUT I could NOT get docker plus UFW to work on 16.04. In other words no matter what I did all docker ports became globally exposed to the internet. Until I found this: How to set Docker 1.12+ to NOT interfere with IPTABLES/FirewallD

I had to create the file /etc/docker/daemon.json and put the following in:

{
    "iptables": false
}

I then issued sudo service docker stop then sudo service docker start FINALLY docker is simply following the appropriate rules in UFW.

Additional data: Docker overrules UFW!

Eliah Kagan
  • 117,780
  • 2
    With Ubuntu 16.04 and Docker version 17.09, this solution breaks outbound connection from the containers. See response https://askubuntu.com/a/833363/262702 and https://askubuntu.com/a/954041/262702 – ctbrown Nov 12 '17 at 16:41
7

If you're using the init system of systemd (Ubuntu 15.10 and later) edit the /etc/docker/daemon.json (might need to create it if it does not exist), make sure it has iptables key configured:

{   "iptables" : false }

EDIT: this might cause you to lose connection to the internet from inside containers

If you have UFW enabled, verify that you can access the internet from inside containers. if not - you must define DEFAULT_FORWARD_POLICY as ACCEPT on /etc/default/ufw and apply the trick described here: https://stackoverflow.com/a/17498195/507564

orshachar
  • 179
  • 1
  • 4
5

In my case I've ended up modifying iptables to allow access to Docker only from specific IPs.

As per ESala's answer:

If you use -p flag on containers Docker makes changes directly to iptables, ignoring the ufw.

Example of records added to iptables by Docker

Routing to 'DOCKER' chain:

-A PREROUTING -m addrtype --dst-type LOCAL -j DOCKER
-A OUTPUT ! -d 127.0.0.0/8 -m addrtype --dst-type LOCAL -j DOCKER

Forwarding packets from 'DOCKER' chain to container:

-A DOCKER -i docker0 -j RETURN
-A DOCKER ! -i docker0 -p tcp -m tcp --dport 6379 -j DNAT --to-destination 172.17.0.3:6379

You can modify iptables to allow access to DOCKER chain only from specified source IP (e.g. 1.1.1.1):

-A PREROUTING -s 1.1.1.1 -m addrtype --dst-type LOCAL -j DOCKER
-A OUTPUT -s 1.1.1.1 ! -d 127.0.0.0/8 -m addrtype --dst-type LOCAL -j DOCKER

You may want to use iptables-save > /tmp/iptables.conf and iptables-restore < /tmp/iptables.conf to dump, edit, and restore iptables rules.

4

UPDATE 10.2021

When we use only one port-number then docker will use an ephemeral port see docker-compose ports

Specify just the container port (an ephemeral host port is chosen for the host port).

I thought that my original answer worked, because I could not connect to the expected port 1234.
So please don't do this - follow the advice in the other answers.
I will not delete this answer, because it might still be useful to someone to understand why not to do this.

original answer

I used docker-compose to start several containers and also had the problem that one port was exposed to the world ignoring ufw rules.

The fix to only make the port available to my docker containers was this change in my docker-compose.yml file:

ports:
- "1234:1234"

to this:

ports:
- "1234"

Now the other docker-containers still can use the port, but I cannot access it from outside.

TmTron
  • 436
3

A fast workaround is when running Docker and doing the port mapping. You can always do

docker run ...-p 127.0.0.1:<ext pot>:<internal port> ...

to prevent your Docker from being accessed from outside.

kimy82
  • 131
2

Use of /etc/docker/daemon.json with content

{
  "iptables": false
}

might sound like a solution but it only works until the next reboot. After that you may notice that none of your containers has access to Internet so you can't for example ping any website. It may be undesired behavior.

Same applies to binding a container to specific IP. You may not want to do that. The ultimate option is to create a container and have it behind UFW not matter what happens and how you create this container, so there's a solution:

After you create /etc/docker/daemon.json file, invoke:

sed -i -e 's/DEFAULT_FORWARD_POLICY="DROP"/DEFAULT_FORWARD_POLICY="ACCEPT"/g' /etc/default/ufw
ufw reload

so you set up default forward policy in UFW for accept, and use:

iptables -t nat -A POSTROUTING ! -o docker0 -s 172.17.0.0/16 -j MASQUERADE

If you're about to use docker-compose, then the IP from command above should be replaced with IP of network docker-compose creates when running it with docker-compose up.

I described the problem and solution more comprehensively in this article

Hope it helps!

Zanna
  • 70,465
mkubaczyk
  • 129
1

Use --network=host when you start container so docker will map port to isolated host-only network instead of default bridge network. I see no legal ways to block bridged network. Alternatively you can use custom user-defined network with isolation.

0
  1. Login to your docker console:

    sudo docker exec -i -t docker_image_name /bin/bash

  2. And then inside your docker console:

    sudo apt-get update
    sudo apt-get install ufw
    sudo ufw allow 22
    
  3. Add your ufw rules and enable the ufw

    sudo ufw enable

    • Your Docker image need to be started with --cap-add=NET_ADMIN

To enable "NET_ADMIN" Docker option:

1.Stop Container:

docker stop yourcontainer; 2.Get container id:

docker inspect yourcontainer; 3.Modify hostconfig.json(default docker path:/var/lib/docker, you can change yours)

vim /var/lib/docker/containers/containerid/hostconfig.json

4.Search "CapAdd", and modify null to ["NET_ADMIN"];

....,"VolumesFrom":null,"CapAdd":["NET_ADMIN"],"CapDrop":null,.... 5.Restart docker in host machine;

service docker restart; 6.Start yourconatiner;

docker start yourcontainer;

Stefan
  • 1
0

An addition to the accepted answer

If you map a port like 127.0.0.1:8080:8080 to keep it closed from the Internet, but still want to be able to access it from your working environment (e.g for monitoring/administration/debugging purposes, with IP whitelist or ssh tunnel) there is a "well-known" solution for that:

https://github.com/chaifeng/ufw-docker

0

The following solution worked perfectly for me, and it also doesn't suffer from many of the downsides of the other methods, namely { "iptables" : false } and it disabling internet access from within the containers.

The credit for the solution goes to tsuna on GitHub. It's dead simple, and involves three easy steps:

Open /etc/ufw/after.rules in an editor:

sudo nano /etc/ufw/after.rules

Append the following lines to the end of the file (you might need to replace eth0 with whatever your external facing interface is):

# Put Docker behind UFW
*filter
:DOCKER-USER - [0:0]
:ufw-user-input - [0:0]

-A DOCKER-USER -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT -A DOCKER-USER -m conntrack --ctstate INVALID -j DROP -A DOCKER-USER -i eth0 -j ufw-user-input -A DOCKER-USER -i eth0 -j DROP COMMIT

Then finally, for the changes to take effect:

sudo systemctl restart ufw # or `sudo reboot` if this wasn't effective
Arad Alvand
  • 101
  • 2