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