From 071f2a9bd5c0043b3319d891ab23e0bf8017a74a Mon Sep 17 00:00:00 2001 From: Patrick Pacher Date: Tue, 4 Aug 2020 09:55:49 +0200 Subject: [PATCH] Silience "not found" errors in recover-iptables --- cmds/portmaster-start/recover_linux.go | 61 +++++++++++++++++++++++++- firewall/interception/nfqueue_linux.go | 38 +++++++++------- network/packet/parse.go | 2 + 3 files changed, 84 insertions(+), 17 deletions(-) diff --git a/cmds/portmaster-start/recover_linux.go b/cmds/portmaster-start/recover_linux.go index bd344cbf..ec6e05f8 100644 --- a/cmds/portmaster-start/recover_linux.go +++ b/cmds/portmaster-start/recover_linux.go @@ -1,6 +1,11 @@ package main import ( + "fmt" + "os" + "strings" + + "github.com/hashicorp/go-multierror" "github.com/safing/portmaster/firewall/interception" "github.com/spf13/cobra" ) @@ -9,7 +14,44 @@ var recoverIPTablesCmd = &cobra.Command{ Use: "recover-iptables", Short: "Removes obsolete IP tables rules in case of an unclean shutdown", RunE: func(*cobra.Command, []string) error { - return interception.DeactivateNfqueueFirewall() + // interception.DeactiveNfqueueFirewall uses coreos/go-iptables + // which shells out to the /sbin/iptables binary. As a result, + // we don't get the errno of the actual error and need to parse the + // output instead. Make sure it's always english by setting LC_ALL=C + currentLocale := os.Getenv("LC_ALL") + os.Setenv("LC_ALL", "C") // nolint:errcheck - we tried at least ... + defer os.Setenv("LC_ALL", currentLocale) // nolint:errcheck + + err := interception.DeactivateNfqueueFirewall() + if err == nil { + return nil + } + + // we don't want to show ErrNotExists to the user + // as that only means portmaster did the cleanup itself. + mr, ok := err.(*multierror.Error) + if !ok { + return err + } + + var filteredErrors *multierror.Error + for _, err := range mr.Errors { + // if we have a permission denied error, all errors will be the same + if strings.Contains(err.Error(), "Permission denied") { + return fmt.Errorf("failed to cleanup iptables: %w", os.ErrPermission) + } + + if !strings.Contains(err.Error(), "No such file or directory") { + filteredErrors = multierror.Append(filteredErrors, err) + } + } + + if filteredErrors != nil { + filteredErrors.ErrorFormat = formatNfqErrors + return filteredErrors + } + + return nil }, SilenceUsage: true, } @@ -17,3 +59,20 @@ var recoverIPTablesCmd = &cobra.Command{ func init() { rootCmd.AddCommand(recoverIPTablesCmd) } + +func formatNfqErrors(es []error) string { + if len(es) == 1 { + return fmt.Sprintf("1 error occurred:\n\t* %s\n\n", es[0]) + } + + points := make([]string, len(es)) + for i, err := range es { + // only display the very first line of each error + first := strings.Split(err.Error(), "\n")[0] + points[i] = fmt.Sprintf("* %s", first) + } + + return fmt.Sprintf( + "%d errors occurred:\n\t%s\n\n", + len(es), strings.Join(points, "\n\t")) +} diff --git a/firewall/interception/nfqueue_linux.go b/firewall/interception/nfqueue_linux.go index cc771115..1b6595c6 100644 --- a/firewall/interception/nfqueue_linux.go +++ b/firewall/interception/nfqueue_linux.go @@ -7,6 +7,7 @@ import ( "strings" "github.com/coreos/go-iptables/iptables" + "github.com/hashicorp/go-multierror" "github.com/safing/portbase/log" "github.com/safing/portmaster/firewall/interception/nfqexp" @@ -139,16 +140,20 @@ func activateNfqueueFirewall() error { } // DeactivateNfqueueFirewall drops portmaster related IP tables rules. +// Any errors encountered accumulated into a *multierror.Error. func DeactivateNfqueueFirewall() error { // IPv4 - errV4 := deactivateIPTables(iptables.ProtocolIPv4, v4once, v4chains) - - // IPv6 - if errV6 := deactivateIPTables(iptables.ProtocolIPv6, v6once, v6chains); errV6 != nil && errV4 == nil { - return errV6 + var result *multierror.Error + if err := deactivateIPTables(iptables.ProtocolIPv4, v4once, v4chains); err != nil { + result = multierror.Append(result, err) } - return errV4 + // IPv6 + if err := deactivateIPTables(iptables.ProtocolIPv6, v6once, v6chains); err != nil { + result = multierror.Append(result, err) + } + + return result } func activateIPTables(protocol iptables.Protocol, rules, once, chains []string) error { @@ -193,31 +198,32 @@ func deactivateIPTables(protocol iptables.Protocol, rules, chains []string) erro return err } - var firstErr error + var multierr *multierror.Error + for _, rule := range rules { splittedRule := strings.Split(rule, " ") ok, err := tbls.Exists(splittedRule[0], splittedRule[1], splittedRule[2:]...) - if err != nil && firstErr == nil { - firstErr = err + if err != nil { + multierr = multierror.Append(multierr, err) } if ok { - if err = tbls.Delete(splittedRule[0], splittedRule[1], splittedRule[2:]...); err != nil && firstErr == nil { - firstErr = err + if err = tbls.Delete(splittedRule[0], splittedRule[1], splittedRule[2:]...); err != nil { + multierr = multierror.Append(multierr, err) } } } for _, chain := range chains { splittedRule := strings.Split(chain, " ") - if err = tbls.ClearChain(splittedRule[0], splittedRule[1]); err != nil && firstErr == nil { - firstErr = err + if err = tbls.ClearChain(splittedRule[0], splittedRule[1]); err != nil { + multierr = multierror.Append(multierr, err) } - if err = tbls.DeleteChain(splittedRule[0], splittedRule[1]); err != nil && firstErr == nil { - firstErr = err + if err = tbls.DeleteChain(splittedRule[0], splittedRule[1]); err != nil { + multierr = multierror.Append(multierr, err) } } - return firstErr + return multierr } // StartNfqueueInterception starts the nfqueue interception. diff --git a/network/packet/parse.go b/network/packet/parse.go index 1ceb8613..d78b1321 100644 --- a/network/packet/parse.go +++ b/network/packet/parse.go @@ -58,6 +58,7 @@ func parseUDP(packet gopacket.Packet, info *Info) error { return nil } +/* func parseUDPLite(packet gopacket.Packet, info *Info) error { if udpLite, ok := packet.TransportLayer().(*layers.UDPLite); ok { info.Protocol = UDPLite @@ -66,6 +67,7 @@ func parseUDPLite(packet gopacket.Packet, info *Info) error { } return nil } +*/ func parseICMPv4(packet gopacket.Packet, info *Info) error { if icmp, ok := packet.Layer(layers.LayerTypeICMPv4).(*layers.ICMPv4); ok {