Docker Firewall settings make NFTables Corrupt? Can you help?

Hello and thanks for reading!

I can not reach a created portainer container. While troubleshooting I came across some weird things that I want to share here.

My question: does anyone have an idea or even know how I can get to the container without opening my firewall completely? Thanks in advance. i din’t understand why a standard Docker Ruleset kills the connection itself. Thx for help.

—| What happened |—

I created a volume and a Portainer container with the following command, reachable on Port 9443 and Port 8000:

sudo docker volume create portainer_data

sudo docker run -d -p 8000:8000 -p 9443:9443 --name portainer -v /var/run/docker.sock:/var/run/docker.sock -v portainer_data:/data cr .portainer.io/portainer.io/portainer/portainer-ce:latest

Unfortunately, I did not reach the container from another machine (https://192.168.1.129:9443). The 129 address is the static IP of the host where Docker is running.

I then looked more closely at firewall rules (nftables) and the firewall rules that Docker sets itself. I noticed that if I delete the Docker firewall settings, the container is reachable on Port 9443. I did this by restarting the firewall (sudo sytsemctl restart nftables.service) , which causes flush ruleset from /etc/nftables.conf to be executed as the first statement.

My firewall ruleset is small, stateful and simple (The logic from the Docker host’s point of view):

  • accept SSH/22 requests from outside.
  • receive already existing sessions
  • you are allowed to make all requests to the outside
  • answers/requests from existing sessions are accepted

—| Ressources (Screenshots, Configs, Commands) |—

Environment: Raspberry Pi 3B+

OS: Raspberry Pi OS (64Bit) lite
OS Version (lsb_release -a):

No LSB modules are available.
Distributor ID: Debian
Description:    Debian GNU/Linux 11 (bullseye)
Release:        11
Codename:       bullseye

Docker Version (sudo docker version):

Client: Docker Engine - Community
 Version:           23.0.1
 API version:       1.42
 Go version:        go1.19.5
 Git commit:        a5ee5b1
 Built:             Thu Feb  9 19:46:41 2023
 OS/Arch:           linux/arm64
 Context:           default

Server: Docker Engine - Community
 Engine:
  Version:          23.0.1
  API version:      1.42 (minimum version 1.12)
  Go version:       go1.19.5
  Git commit:       bc3805a
  Built:            Thu Feb  9 19:46:41 2023
  OS/Arch:          linux/arm64
  Experimental:     false
 containerd:
  Version:          1.6.18
  GitCommit:        2456e983eb9e37e47538f59ea18f2043c9a73640
 runc:
  Version:          1.1.4
  GitCommit:        v1.1.4-0-g5fd4c4d
 docker-init:
  Version:          0.19.0
  GitCommit:        de40ad0

Docker installation over sudo curl -fsSL http s://get.docker.com -o get-docker.sh | sudo chmod+x | sudo get-docker.sh

— — — —

Command Strings to create the Container

sudo docker volume create portainer_data

sudo docker run -d -p 8000:8000 -p 9443:9443 --name portainer -v /var/run/docker.sock:/var/run/docker.sock -v portainer_data:/data cr .portainer.io/portainer/portainer-ce:latest

— — — —

sudo nano /etc/nftables.conf

#!/usr/sbin/nft -f

flush ruleset

table inet filter {
        chain input {
                type filter hook input priority filter; policy drop;
                ct state {established, related} accept
                tcp dport 22 accept
        }

        chain forward {
                type filter hook forward priority filter; policy drop;
        }

        chain output {
                type filter hook output priority filter; policy accept;
        }
}

— — — —

sudo nft list ruleset (before install Container Portainer)

table inet filter {
        chain input {
                type filter hook input priority filter; policy drop;
                ct state { established, related } accept
                tcp dport 22 accept
        }

        chain forward {
                type filter hook forward priority filter; policy drop;
        }

        chain output {
                type filter hook output priority filter; policy accept;
        }
}
table ip nat {
        chain DOCKER {
                iifname "docker0" counter packets 0 bytes 0 return
        }
        chain POSTROUTING {
                type nat hook postrouting priority srcnat; policy accept;
                oifname != "docker0" ip saddr 172.17.0.0/16 counter packets 0 bytes 0 masquerade
        }

        chain PREROUTING {
                type nat hook prerouting priority dstnat; policy accept;
                fib daddr type local counter packets 1 bytes 52 jump DOCKER
        }

        chain OUTPUT {
                type nat hook output priority -100; policy accept;
                ip daddr != 127.0.0.0/8 fib daddr type local counter packets 0 bytes 0 jump DOCKER
        }
}

table ip filter {
        chain DOCKER {
        }

        chain DOCKER-ISOLATION-STAGE-1 {
                iifname "docker0" oifname != "docker0" counter packets 0 bytes 0 jump DOCKER-ISOLATION-STAGE-2
                counter packets 0 bytes 0 return
        }

        chain DOCKER-ISOLATION-STAGE-2 {
                oifname "docker0" counter packets 0 bytes 0 drop
                counter packets 0 bytes 0 return
        }

        chain FORWARD {
                type filter hook forward priority filter; policy drop;
                counter packets 0 bytes 0 jump DOCKER-USER
                counter packets 0 bytes 0 jump DOCKER-ISOLATION-STAGE-1
                oifname "docker0" ct state related,established counter packets 0 bytes 0 accept
                oifname "docker0" counter packets 0 bytes 0 jump DOCKER
                iifname "docker0" oifname != "docker0" counter packets 0 bytes 0 accept
                iifname "docker0" oifname "docker0" counter packets 0 bytes 0 accept
        }

        chain DOCKER-USER {
                counter packets 0 bytes 0 return
        }
}

— — — —

sudo nft list ruleset (after install Container Portainer)

table inet filter {
        chain input {
                type filter hook input priority filter; policy drop;
                ct state { established, related } accept
                tcp dport 22 accept
        }

        chain forward {
                type filter hook forward priority filter; policy drop;
        }

        chain output {
                type filter hook output priority filter; policy accept;
        }
}
table ip nat {
        chain DOCKER {
                iifname "docker0" counter packets 0 bytes 0 return
                iifname != "docker0" meta l4proto tcp tcp dport 9443 counter packets 20 bytes 1040 dnat to 172.17.0.2:9443
                iifname != "docker0" meta l4proto tcp tcp dport 8000 counter packets 0 bytes 0 dnat to 172.17.0.2:8000
        }

        chain POSTROUTING {
                type nat hook postrouting priority srcnat; policy accept;
                oifname != "docker0" ip saddr 172.17.0.0/16 counter packets 0 bytes 0 masquerade
                meta l4proto tcp ip saddr 172.17.0.2 ip daddr 172.17.0.2 tcp dport 9443 counter packets 0 bytes 0 masquerade
                meta l4proto tcp ip saddr 172.17.0.2 ip daddr 172.17.0.2 tcp dport 8000 counter packets 0 bytes 0 masquerade
        }

        chain PREROUTING {
                type nat hook prerouting priority dstnat; policy accept;
                fib daddr type local counter packets 24 bytes 1248 jump DOCKER
        }

        chain OUTPUT {
                type nat hook output priority -100; policy accept;
                ip daddr != 127.0.0.0/8 fib daddr type local counter packets 0 bytes 0 jump DOCKER
        }
}
table ip filter {
        chain DOCKER {
                iifname != "docker0" oifname "docker0" meta l4proto tcp ip daddr 172.17.0.2 tcp dport 9443 counter packets 20 bytes 1040 accept
                iifname != "docker0" oifname "docker0" meta l4proto tcp ip daddr 172.17.0.2 tcp dport 8000 counter packets 0 bytes 0 accept
        }

        chain DOCKER-ISOLATION-STAGE-1 {
                iifname "docker0" oifname != "docker0" counter packets 0 bytes 0 jump DOCKER-ISOLATION-STAGE-2
                counter packets 20 bytes 1040 return
        }

        chain DOCKER-ISOLATION-STAGE-2 {
                oifname "docker0" counter packets 0 bytes 0 drop
                counter packets 0 bytes 0 return
        }

        chain FORWARD {
                type filter hook forward priority filter; policy drop;
                counter packets 20 bytes 1040 jump DOCKER-USER
                counter packets 20 bytes 1040 jump DOCKER-ISOLATION-STAGE-1
                oifname "docker0" ct state related,established counter packets 0 bytes 0 accept
                oifname "docker0" counter packets 20 bytes 1040 jump DOCKER
                iifname "docker0" oifname != "docker0" counter packets 0 bytes 0 accept
                iifname "docker0" oifname "docker0" counter packets 0 bytes 0 accept
        }

        chain DOCKER-USER {
                counter packets 20 bytes 1040 return
        }
}

Since I can see that you really did your research on the forum searching for nftables related questions and you also tried to answer some questions in the meantime, I would really like to help you, but I don’t have experience with nftables yet.

I don’t know what is installed on Rasberry Pi, but wouldn’t it be easier to configure nftables through firewalld-cmd or UFW?

1 Like