Skip to content

Instantly share code, notes, and snippets.

@supermarsx
Created August 26, 2025 15:33
Show Gist options
  • Save supermarsx/598cf66685a5941f70e282d9ff3e3440 to your computer and use it in GitHub Desktop.
Save supermarsx/598cf66685a5941f70e282d9ff3e3440 to your computer and use it in GitHub Desktop.
Docker NAT enabler, solve nat, iptables errors when using DinD (Docker in Docker)
#!/usr/bin/env bash
# docker-nat-modules.sh — Detect, load & persist required iptables/NAT kernel modules
# Safe to run multiple times; will only add missing modules to persistence.
# ────────────────────────────────────────────────────────────────────────────────
# Config
CANDIDATE_MODULES=(
ip_tables
iptable_nat
nf_nat
nf_conntrack
xt_MASQUERADE
)
PERSIST_FILE="/etc/modules-load.d/docker.conf"
# ────────────────────────────────────────────────────────────────────────────────
# Pretty printing helpers
hr() { printf '+-%-20s-+-%-14s-+-%-10s-+-%-10s-+-%-25s-+-%-10s-+\n' "--------------------" "--------------" "----------" "----------" "-------------------------" "----------"; }
row() { printf '| %-20s | %-14s | %-10s | %-10s | %-25s | %-10s |\n' "$@"; }
banner() {
cat <<'BANNER'
┌─────────────────────────────────────────────────────────────────────────────┐
│ Docker NAT Enabler • iptables module auto-detector & persister │
│ Runs safely, idempotent, ASCII-chic │
└─────────────────────────────────────────────────────────────────────────────┘
BANNER
}
have_cmd() { command -v "$1" >/dev/null 2>&1; }
need_root() {
if [[ $EUID -ne 0 ]]; then
echo "[i] Not running as root, re-executing with sudo..."
exec sudo "$0" "$@"
fi
}
# Detect iptables backend (nft vs legacy)
detect_backend() {
if have_cmd iptables; then
local v
v=$(iptables -V 2>/dev/null || true)
if [[ $v == *"nf_tables"* ]]; then echo "nft"; else echo "legacy"; fi
else
echo "unknown"
fi
}
# Check if module is already loaded
is_loaded() { awk -v m="$1" '$1==m {found=1} END{exit !found}' /proc/modules 2>/dev/null; }
# Check if module exists on disk (built as module). If built-in, modprobe -n exits 0 but may say builtin.
exists_mod() { modprobe -n "$1" >/dev/null 2>&1; }
# Append to persistence file if not present
persist_module() {
local m="$1"
mkdir -p "$(dirname "$PERSIST_FILE")"
touch "$PERSIST_FILE"
if ! grep -qE "^\\s*${m}\\s*$" "$PERSIST_FILE"; then
echo "$m" >> "$PERSIST_FILE"
return 0
fi
return 1
}
# Check NAT table availability
check_nat_table() {
if iptables -t nat -L -n >/dev/null 2>&1; then
echo "ok"
else
echo "fail"
fi
}
# ────────────────────────────────────────────────────────────────────────────────
main() {
banner
need_root "$@"
local backend; backend=$(detect_backend)
echo "[i] iptables backend: $backend"
hr; row "Module" "LoadedBefore" "Action" "Persisted" "Notes" "NatTable"; hr
local enabled_list=() disabled_list=()
for m in "${CANDIDATE_MODULES[@]}"; do
local loaded_before action persisted note nat_status
if is_loaded "$m"; then
loaded_before="yes"
else
loaded_before="no"
fi
action="skipped"
persisted="no"
note=""
if [[ "$loaded_before" == "no" ]]; then
if exists_mod "$m"; then
if modprobe "$m" 2>/dev/null; then
action="loaded"
else
action="fail"
note="modprobe error"
fi
else
action="n/a"
note="not available"
fi
else
action="present"
fi
if [[ "$action" == "loaded" || "$action" == "present" ]]; then
if persist_module "$m"; then
persisted="yes"
else
persisted="kept"
fi
fi
# If module is built into the kernel, modprobe -n will succeed but lsmod won't show it; handle hint
if [[ "$action" == "n/a" && $(modprobe -n -v "$m" 2>/dev/null | grep -ci builtin) -gt 0 ]]; then
note="built-in"
persisted="kept"
action="present"
fi
nat_status=$(check_nat_table)
row "$m" "$loaded_before" "$action" "$persisted" "$note" "$nat_status"
if [[ "$action" == "present" || "$action" == "loaded" ]]; then
enabled_list+=("$m")
else
disabled_list+=("$m")
fi
done
hr
echo "[✓] Review or manage persistence in: $PERSIST_FILE"
echo
echo "Enabled modules : ${enabled_list[*]:-(none)}"
echo "Disabled modules: ${disabled_list[*]:-(none)}"
echo
# Extra guidance if nat still missing
if [[ $(check_nat_table) == "fail" ]]; then
cat <<'TIPS'
[!] The iptables NAT table still seems unavailable.
• If running inside a container, ensure the container is privileged and has access to the host modules (mount /lib/modules).
• On Debian/Ubuntu with nftables, if dockerd expects legacy:
sudo update-alternatives --set iptables /usr/sbin/iptables-legacy
• Then re-run this script.
TIPS
else
echo "[✓] NAT table is available. You're good to go."
fi
}
main "$@"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment