Skip to content

Instantly share code, notes, and snippets.

@antwal
Last active November 22, 2025 20:33
Show Gist options
  • Select an option

  • Save antwal/d78ec555a431c93681a035b13b5e06e9 to your computer and use it in GitHub Desktop.

Select an option

Save antwal/d78ec555a431c93681a035b13b5e06e9 to your computer and use it in GitHub Desktop.
Allow forward to Docker Network Bridge (Debian 11/12)

⚠️ Important Note on Firewall Compatibility ⚠️

The manual iptables configuration and isolation rules (DOCKER-ISOLATION) described in this document are valid up to Docker CE version 27.

From version 28 and later (29, etc.), Docker has migrated to the NFTables backend by default.

Debian 12 - Downgrade Docker CE

apt install docker-ce=5:27.5.1-1~debian.12~bookworm 
apt install docker-ce-cli=5:27.5.1-1~debian.12~bookworm 
apt install docker-ce-rootless-extras=5:27.5.1-1~debian.12~bookworm

Create Docker Network

docker network create --driver bridge container-net
docker network create --driver bridge --subnet "192.168.33.0/24" --gateway "192.168.33.10" internal
docker network create --driver bridge --subnet "172.20.46.0/24" --gateway "172.20.46.254" external

Network Example

/etc/network/interfaces

# This file describes the network interfaces available on your system
# and how to activate them. For more information, see interfaces(5).

source /etc/network/interfaces.d/*

# The loopback network interface
auto lo
iface lo inet loopback

# The primary network interface
allow-hotplug eno1
iface eno1 inet dhcp
post-up /etc/iptables-single-bridge.sh
#post-up /etc/iptables-multi-bridge.sh

File permissions

chmod a+x /etc/iptables-single-bridge.sh
chmod a+x /etc/iptables-multi-bridge.sh

Check rules

iptables -L FORWARD -v -n

Default Docker Rules:

Chain FORWARD (policy DROP 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination         
43060   26M DOCKER-USER  0    --  *      *       0.0.0.0/0            0.0.0.0/0           
43060   26M DOCKER-ISOLATION-STAGE-1  0    --  *      *       0.0.0.0/0            0.0.0.0/0           
    0     0 ACCEPT     0    --  *      docker0  0.0.0.0/0            0.0.0.0/0            ctstate RELATED,ESTABLISHED
    0     0 DOCKER     0    --  *      docker0  0.0.0.0/0            0.0.0.0/0           
    0     0 ACCEPT     0    --  docker0 !docker0  0.0.0.0/0            0.0.0.0/0           
    0     0 ACCEPT     0    --  docker0 docker0  0.0.0.0/0            0.0.0.0/0           
26303   23M ACCEPT     0    --  *      br-caed2ff56793  0.0.0.0/0            0.0.0.0/0            ctstate RELATED,ESTABLISHED
  130 49312 DOCKER     0    --  *      br-caed2ff56793  0.0.0.0/0            0.0.0.0/0           
16627 2975K ACCEPT     0    --  br-caed2ff56793 !br-caed2ff56793  0.0.0.0/0            0.0.0.0/0           
  129 49264 ACCEPT     0    --  br-caed2ff56793 br-caed2ff56793  0.0.0.0/0            0.0.0.0/0 

Added Docker Rules:

    0     0 ACCEPT     0    --  br-caed2ff56793 eno1    0.0.0.0/0            0.0.0.0/0           
  109  7000 ACCEPT     0    --  eno1   br-caed2ff56793  0.0.0.0/0            0.0.0.0/0           
    0     0 ACCEPT     0    --  eno1   br-caed2ff56793  0.0.0.0/0            0.0.0.0/0            state RELATED,ESTABLISHED

Add on your Router all Static Routing (example)

Network 192.168.33.0/24
Distance: 1
Value: 172.20.1.66 (IP Address of eno1)

Allow routing between Docker Container from another network

Bad way (not recommended for me, but most used)

docker network create myNetwork
docker network connect myNetwork internal
docker network connect myNetwork external

Fast & Easy way (security problems)

iptables --flush DOCKER-ISOLATION

Secure way (my method, but more complicated)

Check bridge name of network (example):

iptables -I DOCKER-ISOLATION-STAGE-2 -o internal -i external -j ACCEPT
iptables -I DOCKER-ISOLATION-STAGE-2 -o external -i internal -j ACCEPT
#!/bin/bash
NETWORKS=(
'exclude=host'
'exclude=none'
'exclude=bridge'
'include=internal'
'include=external'
)
echo "Procesing network: $IFACE"
[ "$IFACE" = "eno1" ] || exit 0 # we only want interface "eno1"
echo "Checking if docker is installed..."
command -v docker >/dev/null 2>&1 || { echo >&2 "docker is not installed."; exit 1; }
echo "Checking if iptables is installed..."
command -v iptables >/dev/null 2>&1 || { echo >&2 "iptables is not installed."; exit 1; }
for network in "${NETWORKS[@]}"; do
keyNetwork="${network%%=*}"
nameNetwork="${network#*=}"
if [[ "${keyNetwork}" == "include" ]]; then
echo "Get docker bridge ID..."
NETWORK_ID=$(docker network inspect -f {{.Id}} $nameNetwork)
if [[ ! -z "$NETWORK_ID" ]]; then
NETWORK_BRIDGE="br-${NETWORK_ID:0:12}"
echo "Adding new rules for network: ${NETWORK_BRIDGE}"
iptables -A FORWARD -i $NETWORK_BRIDGE -o $IFACE -j ACCEPT
iptables -A FORWARD -i $IFACE -o $NETWORK_BRIDGE -j ACCEPT
iptables -A FORWARD -i $IFACE -o $NETWORK_BRIDGE -m state --state "RELATED,ESTABLISHED" -j ACCEPT
fi
fi
done
echo "Docker bridge rules loaded."
#!/bin/bash
IPT="/sbin/iptables"
DOCKER="/usr/bin/docker"
NETWORK_NAME="container-net"
[ "$IFACE" = "eno1" ] || exit 0 # we only want interface "eno1"
echo -n "Checking docker..."
! [ -f $DOCKER ] && echo "Docker is not installed." && exit 0
echo -n "Check docker bridge..."
NETWORK_ID=$(docker network inspect -f {{.Id}} $NETWORK_NAME)
[ -z "$NETWORK_ID" ] && echo -n "Docker Container Bridge not is valid" && exit 0
NETWORK_BRIDGE="br-${NETWORK_ID:0:12}"
echo -n "Loading docker bridge iptables rules..."
$IPT -A FORWARD -i $NETWORK_BRIDGE -o $IFACE -j ACCEPT
$IPT -A FORWARD -i $IFACE -o $NETWORK_BRIDGE -j ACCEPT
$IPT -A FORWARD -i $IFACE -o $NETWORK_BRIDGE -m state --state "RELATED,ESTABLISHED" -j ACCEPT
echo -n "Docker bridge rules loaded."
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment