How can I configure my firewall to work with Docker?
My computer is hosting an application on port 1234 that should never be accessible from anything but my machine. However, my Docker container (which is running Apache) should be able to access this. Similarly, my Docker container is hosting a number of resources on various ports that should similarly be only accessible from my computer. Now, this is further complicated by the fact that my system uses a bridge for that specific network:
NETWORK ID NAME DRIVER SCOPE
4d4b5e752963 bridge bridge local
c387eb42698a project_default bridge local
6818c0eb94bf host host local
f7e4ed6c05a2 none null local
This bridge (annoyingly) always has a randomly generated ID that changes each time I restart my Docker Compose setup:
br-c387eb42698a Link encap:Ethernet HWaddr 02:42:29:95:fd:2c
inet addr:172.18.0.1 Bcast:0.0.0.0 Mask:255.255.0.0
inet6 addr: fe80::42:29ff:fe95:fd2c/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:405753 errors:0 dropped:0 overruns:0 frame:0
TX packets:530699 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:85147217 (85.1 MB) TX bytes:75260996 (75.2 MB)
In short, I need the following:
-
Any host on
br-c387eb42698a
(or whatever the network name is) to access port 1234 on my local machine (conveniently 172.18.0.1) - My machine to be able to access any port on any host on that subnet. (default)
- The world to never be able to access any port from the subnet or port 1234.
Ideally, a solution here would work in an IP-agnostic way such that it does not depend on the bridge being on the 172.18.0.0/24
subnet, nor would it break or allow data leaks if I'm connected to a network that happens to contain the 172.18.0.0/24
subnet.
The easiest thing I can think of would be to use the interface name and set up UFW rules around that, but I can't really do that as my interface is constantly changing.
How can I do this? While I'd prefer to do this in pure ufw
(or iptables
if necessary), I'm open to solutions that entail configuring Docker to always assign a persistent ID (or even custom name?) to the bridge interface.
This is not possible in vanilla UFW because of the fact that the interface name is in a constant state of unguessability. In order to set something like this up, a manually defined network should be created. In short, one can use the driver options of Docker's bridge
in the compose config to create a custom network. This will look something like:
networks:
my_bridge:
driver: bridge
driver_opts:
com.docker.network.bridge.name: my-bridge
From here, the bridge config can be added to each service in the compose file:
myimage:
image: myimage:1.0
ports:
- "2580:2580"
networks:
- my_bridge
Then, upon next start of the Compose system, the new network will be created with the proper name:
my-bridge Link encap:Ethernet HWaddr 11:22:33:44:55:66
inet addr:172.18.0.1 Bcast:0.0.0.0 Mask:255.255.0.0
inet6 addr: fe80::42:1122:3344:5566/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:11 errors:0 dropped:0 overruns:0 frame:0
TX packets:6 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:788 (788.0 B) TX bytes:695 (695.0 B)
From there, adding UFW rules to allow connection to port 1234 are trivial:
ufw allow in on my-bridge from any to any port 1234
And suddenly, everything works perfectly!
To integrate the accepted answer, you can also use a docker command to create the network outside of docker-compose:
sudo docker network create -d bridge -o com.docker.network.bridge.name=my-bridge my_bridge
After that you can inspect the networks issuing
ip link show
Now, in your compose files you can just re-route the default network or define any network you like remapping it to the already existing one
version: "3"
services:
test:
image: nginx:alpine
ports:
- "80:80"
networks:
default:
external:
name: my_bridge