The Problem

I am primarily using firewalld for managing the systems with fine-grained access controls. But I recently installed docker on my Raspberry Pi Zero 2W, and started a Forgejo container in port 3000. Additionally, I have setup Pihole on Rasberry Pi. Which used port 53, 80, 443 & 67.

iamyaash@pi-network:~ $ sudo ss -tulnp | grep pihole
udp   UNCONN 0      0       0.0.0.0:53         0.0.0.0:*    users:(("pihole-FTL",pid=642,fd=22))
udp   UNCONN 0      0       0.0.0.0:67         0.0.0.0:*    users:(("pihole-FTL",pid=642,fd=20))
udp   UNCONN 0      0       0.0.0.0:123        0.0.0.0:*    users:(("pihole-FTL",pid=642,fd=33))
udp   UNCONN 0      0             *:53               *:*    users:(("pihole-FTL",pid=642,fd=24))
udp   UNCONN 0      0             *:123              *:*    users:(("pihole-FTL",pid=642,fd=43))
tcp   LISTEN 0      200     0.0.0.0:80         0.0.0.0:*    users:(("pihole-FTL",pid=642,fd=37))
tcp   LISTEN 0      32      0.0.0.0:53         0.0.0.0:*    users:(("pihole-FTL",pid=642,fd=23))
tcp   LISTEN 0      200     0.0.0.0:443        0.0.0.0:*    users:(("pihole-FTL",pid=642,fd=38))
tcp   LISTEN 0      200        [::]:80            [::]:*    users:(("pihole-FTL",pid=642,fd=39))
tcp   LISTEN 0      32         [::]:53            [::]:*    users:(("pihole-FTL",pid=642,fd=25))
tcp   LISTEN 0      200        [::]:443           [::]:*    users:(("pihole-FTL",pid=642,fd=40))

Here’s the allowed ports in firewalld:

iamyaash@pi-network:~ $ sudo firewall-cmd --zone=piholezone --list-ports 
53/tcp 80/tcp 443/tcp 2424/tcp 53/udp 67/udp

I noticed that I can still access forgejo container in port 3000. Which is weird and not making sense, because I have clearly mentioned that I allow access to only 53, 80, 443, 2424, & 67. How come I can access port 3000 without any restrictions.

Here you can see the port 3000 running:

iamyaash@pi-network:~ $ sudo ss -tulnp | grep 3000
tcp   LISTEN 0  4096    0.0.0.0:3000    0.0.0.0:*    users:(("docker-proxy",pid=1358,fd=7))
tcp   LISTEN 0  4096       [::]:3000       [::]:*    users:(("docker-proxy",pid=1366,fd=7))

Let’s recap,

  • Using firewalld primarily for managing port access
  • Only allowed port 53, 80, 443, 67 & 2424
  • Running Forgejo container on port 3000, which should not supposed be accessible but I can

What really happened?

Even though I have set access to specific ports, it somehow allows access to other ports. It’s because Docker creates it’s own iptables rules for container network traffic, which effectively take precedence over firewalld’s zone-based rules for relevant Docker network interfaces.

This means, Docker’s iptables rules allow access to port 3000 as configured, bypassing firewalld’s zone restrictions on your interface.

You can check the actual iptables rules that Docker created, which allowed port 3000 regardless of firewalld zones:

sudo iptable -L -n -v | grep 3000

You can check nftables rules as well:

sudo nft list ruleset | grep 3000

Output:

iamyaash@pi-network:~ $ sudo iptables -L -n -v | grep 3000
6   360 ACCEPT  6   --  !br-3ea8f4e2b7c7 br-3ea8f4e2b7c7    0.0.0.0/0 172.18.0.2   tcp dpt:3000
iamyaash@pi-network:~ $ sudo nft list ruleset | grep 3000
# Warning: table ip nat is managed by iptables-nft, do not touch!
iifname != "br-3ea8f4e2b7c7" tcp dport 3000 counter packets 6 bytes 360 dnat to 172.18.0.2:3000
# Warning: table ip filter is managed by iptables-nft, do not touch!
iifname != "br-3ea8f4e2b7c7" oifname "br-3ea8f4e2b7c7" ip daddr 172.18.0.2 tcp dport 3000 counter packets 6 bytes 360 accept
# Warning: table ip6 nat is managed by iptables-nft, do not touch!

Summary Report

  • Docker uses Linux’s netfilter framework (iptables) to manage container network isolation and port forwarding,
  • When Docker is installed with its default settings, it directly manipulates iptables rules to create network bridges, forward container traffic, and allow access to published container ports.
  • Firewalld is a front-end manager for iptables zones and rules to simplify firewall configurations based on network zones/interfaces.
  • Firewalld zone rules apply based on interface assignment.
  • Docker’s iptables rules allow published ports from containers to be accessed even if those ports are not open in firewalld’s zone configuration for the host interface.
  • This can cause confusion, where port access appears possible despite firewall rules seemingly blocking them.