Created
August 26, 2025 15:33
-
-
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)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/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