I just tried out Docker. It is awesome but seems not work nicely with ufw. By default, docker will manipulate the iptables a little bit. The outcome is not a bug but not what I expected. For more details you can read The dangers of UFW + Docker
My goal is to set up a system like
Host (running ufw) -> docker container 1 - nginx (as a reverse proxy)
-> docker container 2 - node web 1
-> docker container 3 - node web 2
-> .......
I want to manage the incoming traffic (e.g. restrict access) through ufw therefore I don't want docker to touch my iptables. Here is my test
Environment:
- a newly installed Ubuntu 14.04 (kernel: 3.13.0-53 )
- Docker 1.6.2
- ufw forwarding is enabled.( Enable UFW forwarding )
--iptables=false
was added to the Docker daemon.
First Attempt
docker run --name ghost -v /home/xxxx/ghost_content:/var/lib/ghost -d ghost
docker run --name nginx -p 80:80 -v /home/xxxx/nginx_site_enable:/etc/nginx/conf.d:ro --link ghost:ghost -d nginx
No luck. The first command is fine but the second command will throw an error
Error response from daemon: Cannot start container
Second Attempt
Then I found this: unable to link containers with --iptables=false #12701
After running the following command, everything looks OK.
sudo iptables -N DOCKER
However, I noticed that I can not establish any outbound connections inside containers. For example:
xxxxg@ubuntu:~$ sudo docker exec -t -i nginx /bin/bash
root@b0d33f22d3f4:/# ping 74.125.21.147
PING 74.125.21.147 (74.125.21.147): 56 data bytes
^C--- 74.125.21.147 ping statistics ---
35 packets transmitted, 0 packets received, 100% packet loss
root@b0d33f22d3f4:/#
If I remove --iptables=false
from the Docker daemon, then the internet connection of containers will be back to normal but the ufw will not work 'properly' (well...by my definition).
So, what is the best practice of docker + ufw? Can anyone provide some help?
Thanks.
Bart.
For what it's worth here's an addendum to @mkubaczyk's answer for the case where there are more bridge networks involved in the whole setup. These may be provided by Docker-Compose projects and here's how the proper rules can be generated, given that these projects are controlled by
systemd
./etc/systemd/system/compose-project@.service
/usr/local/bin/update-iptables-for-docker-bridges
Obviously, this won't scale that well.
It's also noteworthy that the whole basic concept will disguise the source of any connection for the applications running in a container.
I've had such problem like months ago and lately decided to describe the issue along with the solution on my blog. Here's the shortcut.
Using
--iptables=false
won't help you much with the case you described. It's simply not enough here. By default, none of your containers can do any outgoing connection.There's a small step you're omitting on your way to have containers behind UFW here. You can use
--iptables=false
or create/etc/docker/daemon.json
file with content as followsthe result will be the same, but the latter option requires you to restart whole docker service with
service docker restart
or even do a reboot if docker had a chance to add iptables rules before you disabled this function.When it's done, just do two more things:
so you set up default forward policy in UFW for accept, and use:
That way what you're achieving is disabling docker messy behavior in your iptables rules and at the same time docker is provided with necessary routing so containers will do outgoing connections just fine. UFW rules will be still restricted from this point on, though.
Hope this resolves the issue for you and any that gets here in search of an answer.
I described the problem and solution more comprehensively at https://www.mkubaczyk.com/2017/09/05/force-docker-not-bypass-ufw-rules-ubuntu-16-04/
Not quite sure what your asking but from what I can gather you would like better control over who can access your apps running inside Docker? I have answered a similar question here to control traffic via a front end proxy rather than with IP tables Block external access to docker containers
Hope this helps
Dylan
Edit
With the above approach you can then use UFW to only allow incoming connections to port 80 (ie the proxy). This keeps any port exposure to a minimum with the added bonus that you can control traffic through a proxy configuration & DNS
I spent two hours trying out the proposals above and from other posts. The only solution that worked was from Tsuna's post in this Github thread:
Problem
This problem has been around for a long time.
Disable iptables in Docker will take other problems.
Rollback changes first
If you have modified your server according to the current solution that we find on the internet, please rollback these changes first, including:
--iptables=false
, including configuration file/etc/docker/daemon.json
.DROP
instead ofACCEPT
./etc/ufw/after.rules
.Solving UFW and Docker issues
This solution needs to modify only one UFW configuration file, all Docker configurations and options remain the default. Doesn't need to disable the docker iptables function.
Modify the UFW configuration file
/etc/ufw/after.rules
and add the following rules at the end of the file:Using the command
sudo systemctl restart ufw
to restart UFW after changing the file. Now the public network can't access any published docker ports, the container and the private network can visit each other regularly, and the containers can also access the external network from inside.If you want to allow public networks to access the services provided by the Docker container, for example, the service port of a container is
80
. Run the following command to allow the public networks to access this service:This command allows the public network to access all published ports whose container port is 80.
Note: If we publish a port by using option
-p 8080:80
, we should use the container port80
, not the host port8080
.If there are multiple containers with a service port of 80, but we only want the external network to access a particular container. For example, if the private address of the container is 172.17.0.2, use the following command:
If the network protocol of service is UDP, for example, a DNS service, you can use the following command to allow the external network to access all published DNS services:
Similarly, if only for a specific container, such as IP address 172.17.0.2:
How it works?
The following rules allow the private networks to be able to visit each other. Typically, private networks are more trusted than public networks.
The following rules allow UFW to manage whether the public networks are allowed to visit the services provided by the Docker container. So that we can manage all firewall rules in one place.
The following rules block connection requests initiated by all public networks, but allow internal networks to access external networks. For TCP protocol, it prevents from actively establishing a TCP connection from public networks. For UDP protocol, all accesses to ports which is less then 32767 are blocked. Why is this port? Since the UDP protocol is stateless, it is not possible to block the handshake signal that initiates the connection request as TCP does. For GNU/Linux we can find the local port range in the file
/proc/sys/net/ipv4/ip_local_port_range
. The default range is32768 60999
. When accessing a UDP protocol service from a running container, the local port will be randomly selected one from the port range, and the server will return the data to this random port. Therefore, we can assume that the listening port of the UDP protocol inside all containers are less then 32768. This is the reason that we don't want public networks to access the UDP ports that less then 32768.More
https://github.com/chaifeng/ufw-docker
Usage
Update: 2018-09-10
The reason for choosing
ufw-user-forward
, notufw-user-input
using
ufw-user-input
Pro:
Easy to use and understand, supports older versions of Ubuntu.
For example, to allow the public to visit a published port whose container port is
8080
, use the command:Con:
It not only exposes ports of containers but also exposes ports of the host.
For example, if a service is running on the host, and the port is
8080
. The commandufw allow 8080
allows the public network to visit the service and all published ports whose containers' port is8080
. But we just want to expose the service running on the host, or just the service running inside containers, not the both.To avoid this problem, we may need to use a command similar to the following for all containers:
using
ufw-user-forward
Pro:
Cannot expose services running on hosts and containers at the same time by the same command.
For example, if we want to publish the port
8080
of containers, use the following command:The public network can access all published ports whose container ports are
8080
.But the port
8080
of the host is still not be accessed by the public network. If we want to do so, execute the following command to allow the public access the port on the host separately:Con:
Doesn't support older versions of Ubuntu, and the command is a bit more complicated. But you can use my script https://github.com/chaifeng/ufw-docker.
Conclusion
If we are using an older version of Ubuntu, we can use
ufw-user-input
chain. But be careful to avoid exposing services that should not be exposed.If we are using a newer version of Ubuntu which is support
ufw route
sub-command, we'd better useufw-user-forward
chain, and useufw route
command to manage firewall rules for containers.Update: Oct 6, 2018
The script ufw-docker supports Docker Swarm now. Please see the latest code for more, https://github.com/chaifeng/ufw-docker
Install for Docker Swarm modeWe can only use this script on manager nodes to manage firewall rules when using in Swarm mode.
after.rules
files on all nodes, including managers and workersRunning in Docker Swarm mode, this script will add a global service
ufw-docker-agent
. The image chaifeng/ufw-docker-agent is also automatically built from this project.