From 97ec8618d3ec8cff1baf7acaa0cf55a2220d6992 Mon Sep 17 00:00:00 2001 From: Dan Winship Date: Sat, 7 Jun 2025 10:17:44 -0400 Subject: [PATCH] Fix kubelet iptables startup, clarify semantics of utiliptables calls. --- pkg/kubelet/kubelet_network_linux.go | 13 +++++++------ pkg/util/iptables/iptables.go | 16 ++++++++++++++-- pkg/util/iptables/iptables_test.go | 8 +++++++- 3 files changed, 28 insertions(+), 9 deletions(-) diff --git a/pkg/kubelet/kubelet_network_linux.go b/pkg/kubelet/kubelet_network_linux.go index 430e3105da2..05cad40e860 100644 --- a/pkg/kubelet/kubelet_network_linux.go +++ b/pkg/kubelet/kubelet_network_linux.go @@ -37,12 +37,13 @@ const ( ) func (kl *Kubelet) initNetworkUtil() { - iptClients, err := utiliptables.NewDualStack() - if err != nil { - klog.ErrorS(err, "Failed to initialize iptables") - } - - if err != nil || len(iptClients) == 0 { + iptClients := utiliptables.NewBestEffort() + if len(iptClients) == 0 { + // We don't log this as an error because kubelet itself doesn't need any + // of this (it sets up these rules for the benefit of *other* components), + // and because we *expect* this to fail on hosts where only nftables is + // supported (in which case there can't be any other components using + // iptables that would need these rules anyway). klog.InfoS("No iptables support on this system; not creating the KUBE-IPTABLES-HINT chain") return } diff --git a/pkg/util/iptables/iptables.go b/pkg/util/iptables/iptables.go index 54b0e2a9fad..27433ca5a5c 100644 --- a/pkg/util/iptables/iptables.go +++ b/pkg/util/iptables/iptables.go @@ -275,12 +275,24 @@ func newDualStackInternal(exec utilexec.Interface) (map[v1.IPFamily]Interface, e } // NewDualStack returns a map containing an IPv4 Interface (if IPv4 iptables is supported) -// and an IPv6 Interface (if IPv6 iptables is supported). If either family is not -// supported, no Interface will be returned for that family. +// and an IPv6 Interface (if IPv6 iptables is supported). If only one family is supported, +// it will return a map with one Interface *and* an error (indicating the problem with the +// other family). If neither family is supported, it will return an empty map and an +// error. func NewDualStack() (map[v1.IPFamily]Interface, error) { return newDualStackInternal(utilexec.New()) } +// NewBestEffort returns a map containing an IPv4 Interface (if IPv4 iptables is +// supported) and an IPv6 Interface (if IPv6 iptables is supported). If iptables is not +// supported, then it just returns an empty map. This function is intended to make things +// simple for callers that just want "best-effort" iptables support, where neither partial +// nor complete lack of iptables support is considered an error. +func NewBestEffort() map[v1.IPFamily]Interface { + ipts, _ := newDualStackInternal(utilexec.New()) + return ipts +} + // EnsureChain is part of Interface. func (runner *runner) EnsureChain(table Table, chain Chain) (bool, error) { fullArgs := makeFullArgs(table, chain) diff --git a/pkg/util/iptables/iptables_test.go b/pkg/util/iptables/iptables_test.go index e06b6a3e5c1..18475526213 100644 --- a/pkg/util/iptables/iptables_test.go +++ b/pkg/util/iptables/iptables_test.go @@ -329,7 +329,7 @@ func TestNewDualStack(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { fexec := fakeExecForCommands(tc.commands) - runners, _ := newDualStackInternal(fexec) + runners, err := newDualStackInternal(fexec) if tc.ipv4 && runners[v1.IPv4Protocol] == nil { t.Errorf("Expected ipv4 runner, got nil") @@ -341,6 +341,12 @@ func TestNewDualStack(t *testing.T) { } else if !tc.ipv6 && runners[v1.IPv6Protocol] != nil { t.Errorf("Expected no ipv6 runner, got one") } + + if len(runners) == 2 && err != nil { + t.Errorf("Got 2 runners but also an error (%v)", err) + } else if len(runners) != 2 && err == nil { + t.Errorf("Got %d runners but no error", len(runners)) + } }) } }