Skip to content

Commit d0b6ee6

Browse files
committed
fix(bootstrap): fix nftables healthcheck and warn on missing flannel modules
Flannel's embedded traffic manager in k3s v1.35.x is compiled without the nft backend — it only has iptables-legacy support, which requires kernel modules (ip_tables, iptable_nat, iptable_filter, iptable_mangle) that modern distributions (Fedora 43+, RHEL 10+) no longer load by default. Changes: - cluster-entrypoint.sh: When running under Podman, check whether the iptable_nat module is loaded and emit an actionable warning if not. The modules are expected to be loaded at boot via modules-load.d (installed by the RPM spec); the warning covers the case where the host hasn't rebooted since installation. - cluster-healthcheck.sh: Replace the hardcoded 127.0.0.1 NodePort check with the node's actual InternalIP. When kube-proxy runs in nftables mode, NodePort DNAT rules only match the node's real IP addresses — loopback is not in the nftables nodeport-ips set, so the old check always failed. Tested on Fedora 43 (kernel 6.19, Podman 5.8.1) with the full lifecycle: gateway start, provider create/list/delete, sandbox create/exec/delete.
1 parent 4b67305 commit d0b6ee6

2 files changed

Lines changed: 38 additions & 6 deletions

File tree

deploy/docker/cluster-entrypoint.sh

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -675,12 +675,34 @@ fi
675675
# Select kube-proxy mode
676676
# ---------------------------------------------------------------------------
677677
# Under Podman, use native nftables kube-proxy mode so no legacy iptables
678-
# kernel modules (ip_tables, iptable_nat, etc.) are required on the host.
679-
# Docker retains the default iptables mode for maximum compatibility.
678+
# kernel modules are needed for kube-proxy service routing.
679+
#
680+
# Flannel's embedded traffic manager in k3s v1.35.x still uses the iptables
681+
# binary (no nft backend compiled in). The iptables binary inside the
682+
# container is iptables-legacy, which requires the iptable_nat, iptable_filter,
683+
# and ip_tables kernel modules. Modern distributions (Fedora 43+, RHEL 10+)
684+
# no longer load these modules by default. The RPM spec installs a
685+
# modules-load.d config to load them at boot; if the host has not rebooted
686+
# since installation the modules may still be absent.
687+
#
688+
# Docker retains the default iptables kube-proxy mode for maximum compatibility.
680689
EXTRA_KUBE_PROXY_ARGS=""
681690
if [ "${CONTAINER_RUNTIME:-}" = "podman" ]; then
682691
echo "Podman detected — using nftables kube-proxy mode"
683692
EXTRA_KUBE_PROXY_ARGS="--kube-proxy-arg=proxy-mode=nftables"
693+
694+
# Verify legacy iptables kernel modules are loaded on the host.
695+
# Flannel's traffic manager calls iptables-legacy for masquerade rules,
696+
# which requires iptable_nat and related modules. These are loaded at
697+
# boot via modules-load.d (installed by the RPM), but may be missing if
698+
# the host hasn't rebooted since installation.
699+
if ! cat /proc/modules 2>/dev/null | grep -q '^iptable_nat '; then
700+
echo "Warning: iptable_nat kernel module is not loaded on the host." >&2
701+
echo " Flannel masquerade rules will fail without it." >&2
702+
echo " Load it now with: sudo modprobe iptable_nat" >&2
703+
echo " To persist across reboots:" >&2
704+
echo " echo iptable_nat | sudo tee /etc/modules-load.d/openshell-flannel.conf" >&2
705+
fi
684706
fi
685707

686708
# Execute k3s with explicit resolv-conf passed as a kubelet arg.

deploy/docker/cluster-healthcheck.sh

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,18 @@ kubectl -n openshell get secret openshell-ssh-handshake >/dev/null 2>&1 || exit
7575
# ---------------------------------------------------------------------------
7676
# Verify the gateway NodePort (30051) is actually accepting TCP connections.
7777
# After a container restart, kube-proxy may need extra time to re-program
78-
# iptables rules for NodePort routing. Without this check the health check
79-
# can pass before the port is routable, causing "Connection refused" on the
80-
# host-mapped port.
78+
# iptables/nftables rules for NodePort routing. Without this check the
79+
# health check can pass before the port is routable, causing "Connection
80+
# refused" on the host-mapped port.
81+
#
82+
# When kube-proxy runs in nftables mode (Podman), NodePort DNAT rules only
83+
# match traffic destined to the node's real IP addresses — loopback
84+
# (127.0.0.1) is not in the nodeport-ips set. Use the node's InternalIP
85+
# so the check works with both iptables and nftables kube-proxy modes.
8186
# ---------------------------------------------------------------------------
82-
timeout 2 bash -c 'echo >/dev/tcp/127.0.0.1/30051' 2>/dev/null || exit 1
87+
NODEPORT_CHECK_IP="127.0.0.1"
88+
NODE_IP=$(kubectl get nodes -o jsonpath='{.items[0].status.addresses[?(@.type=="InternalIP")].address}' 2>/dev/null || true)
89+
if [ -n "$NODE_IP" ]; then
90+
NODEPORT_CHECK_IP="$NODE_IP"
91+
fi
92+
timeout 2 bash -c "echo >/dev/tcp/${NODEPORT_CHECK_IP}/30051" 2>/dev/null || exit 1

0 commit comments

Comments
 (0)