From a436529bbc2416668ea04b769f734834c3cf723f Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Sat, 21 Feb 2026 15:18:21 +0100 Subject: [PATCH] libnetwork: nftabler.Cleanup: pass context and make logs more useful The Cleanup function oportunistically tries to cleanup old rule (if any). In cases where the rules didn't exist, it would log the error returned by the command, but this would always be `exit status 1`, which doesn't provide details about the actual failure (i.e., if it's the expected "does not exist"). We could improve the code by first calling `nft list table` to check if rules were found, but that would expose a similar problem (error could be due to the rules not present, or "other cause". This patch just changes the logs to be more informative, and passes the context to allow cancelling the command if the context is cancelled. Before this patch: INFO[2026-02-21T13:30:10.428457589Z] Deleting nftables IPv4 rules error="exit status 1" INFO[2026-02-21T13:30:10.453012214Z] Deleting nftables IPv6 rules error="exit status 1" With this patch: INFO[2026-02-21T14:10:06.183933878Z] Deleting nftables IPv4 rules error="exit status 1" output="Error: Could not process rule: No such file or directory\ndelete table ip docker-bridges" INFO[2026-02-21T14:10:06.212198878Z] Deleting nftables IPv6 rules error="exit status 1" output="Error: Could not process rule: No such file or directory\ndelete table ip6 docker-bridges" Signed-off-by: Sebastiaan van Stijn --- .../bridge/internal/nftabler/cleaner.go | 28 ++++++++++++------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/daemon/libnetwork/drivers/bridge/internal/nftabler/cleaner.go b/daemon/libnetwork/drivers/bridge/internal/nftabler/cleaner.go index c57c36d654..94aa9cefe4 100644 --- a/daemon/libnetwork/drivers/bridge/internal/nftabler/cleaner.go +++ b/daemon/libnetwork/drivers/bridge/internal/nftabler/cleaner.go @@ -3,6 +3,7 @@ package nftabler import ( + "bytes" "context" "os/exec" @@ -16,21 +17,28 @@ import ( // after switching to a different Firewaller implementation. func Cleanup(ctx context.Context, config firewaller.Config) { if config.IPv4 { - if err := exec.Command("nft", "delete", "table", string(nftables.IPv4), dockerTable).Run(); err != nil { - log.G(ctx).WithError(err).Info("Deleting nftables IPv4 rules") - } else { - log.G(ctx).Info("Deleted nftables IPv4 rules") - } + tryCleanup(ctx, nftables.IPv4, "IPv4") } if config.IPv6 { - if err := exec.Command("nft", "delete", "table", string(nftables.IPv6), dockerTable).Run(); err != nil { - log.G(ctx).WithError(err).Info("Deleting nftables IPv6 rules") - } else { - log.G(ctx).Info("Deleted nftables IPv6 rules") - } + tryCleanup(ctx, nftables.IPv6, "IPv6") } } +func tryCleanup(ctx context.Context, family nftables.Family, label string) { + cmd := exec.CommandContext(ctx, "nft", "delete", "table", string(family), dockerTable) + out, err := cmd.CombinedOutput() + if err != nil { + // May not exist ("Error: Could not process rule: No such file or directory") + log.G(ctx).WithFields(log.Fields{ + "error": err, + "output": string(bytes.TrimRight(out, "\n ^")), // remove "^^^^^" added in nft's error message. + }).Info("Deleting nftables " + label + " rules") + return + } + + log.G(ctx).Info("Deleted nftables " + label + " rules") +} + func (nft *Nftabler) SetFirewallCleaner(fc firewaller.FirewallCleaner) { nft.cleaner = fc }