From d42007865aae52a828ef842878b820afaa601c1c Mon Sep 17 00:00:00 2001 From: Nathan Gieseker Date: Wed, 26 Jun 2019 02:14:56 -0700 Subject: [PATCH] update iptables --- go.mod | 2 +- go.sum | 2 + .../containernetworking/cni/libcni/api.go | 1 - .../cni/pkg/invoke/args.go | 68 ++------ .../cni/pkg/invoke/delegate.go | 29 ++-- .../coreos/go-iptables/iptables/iptables.go | 161 +++++++++++++----- vendor/modules.txt | 2 +- 7 files changed, 149 insertions(+), 116 deletions(-) diff --git a/go.mod b/go.mod index a77083bf..ad04c767 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ require ( github.com/alexflint/go-filemutex v0.0.0-20171022225611-72bdc8eae2ae github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44 github.com/containernetworking/cni v0.7.0 - github.com/coreos/go-iptables v0.3.0 + github.com/coreos/go-iptables v0.4.1 github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7 github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c github.com/d2g/dhcp4client v1.0.0 diff --git a/go.sum b/go.sum index 37ab0f4b..d25d249b 100644 --- a/go.sum +++ b/go.sum @@ -10,6 +10,8 @@ github.com/containernetworking/cni v0.7.0 h1:1Qy7EwdC08mx5wUB0DpjCuBrk6e/uXg9yI9 github.com/containernetworking/cni v0.7.0/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= github.com/coreos/go-iptables v0.3.0 h1:UTQkjHl9rPwwtXZhXbY3T932cV9aUnKlSsZ7YGfJVXM= github.com/coreos/go-iptables v0.3.0/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU= +github.com/coreos/go-iptables v0.4.1 h1:TyEMaK2xD/EcB0385QcvX/OvI2XI7s4SJEI2EhZFfEU= +github.com/coreos/go-iptables v0.4.1/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU= github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7 h1:u9SHYsPQNyt5tgDm3YN7+9dYrpK96E5wFilTFWIDZOM= github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/d2g/dhcp4 v0.0.0-20150413103026-f0e4d29ff023 h1:V8CudRUx05/Y7gMvaz8QM5i3nj1aNHTvgEX2bklfFN8= diff --git a/vendor/github.com/containernetworking/cni/libcni/api.go b/vendor/github.com/containernetworking/cni/libcni/api.go index 0f14d342..360733e7 100644 --- a/vendor/github.com/containernetworking/cni/libcni/api.go +++ b/vendor/github.com/containernetworking/cni/libcni/api.go @@ -69,7 +69,6 @@ type CNI interface { AddNetworkList(ctx context.Context, net *NetworkConfigList, rt *RuntimeConf) (types.Result, error) CheckNetworkList(ctx context.Context, net *NetworkConfigList, rt *RuntimeConf) error DelNetworkList(ctx context.Context, net *NetworkConfigList, rt *RuntimeConf) error - GetNetworkListCachedResult(net *NetworkConfigList, rt *RuntimeConf) (types.Result, error) AddNetwork(ctx context.Context, net *NetworkConfig, rt *RuntimeConf) (types.Result, error) CheckNetwork(ctx context.Context, net *NetworkConfig, rt *RuntimeConf) error diff --git a/vendor/github.com/containernetworking/cni/pkg/invoke/args.go b/vendor/github.com/containernetworking/cni/pkg/invoke/args.go index 913528c1..39b63972 100644 --- a/vendor/github.com/containernetworking/cni/pkg/invoke/args.go +++ b/vendor/github.com/containernetworking/cni/pkg/invoke/args.go @@ -15,7 +15,6 @@ package invoke import ( - "fmt" "os" "strings" ) @@ -23,8 +22,6 @@ import ( type CNIArgs interface { // For use with os/exec; i.e., return nil to inherit the // environment from this process - // For use in delegation; inherit the environment from this - // process and allow overrides AsEnv() []string } @@ -60,17 +57,17 @@ func (args *Args) AsEnv() []string { pluginArgsStr = stringify(args.PluginArgs) } - // Duplicated values which come first will be overrided, so we must put the - // custom values in the end to avoid being overrided by the process environments. - env = append(env, - "CNI_COMMAND="+args.Command, - "CNI_CONTAINERID="+args.ContainerID, - "CNI_NETNS="+args.NetNS, - "CNI_ARGS="+pluginArgsStr, - "CNI_IFNAME="+args.IfName, - "CNI_PATH="+args.Path, - ) - return dedupEnv(env) + // Ensure that the custom values are first, so any value present in + // the process environment won't override them. + env = append([]string{ + "CNI_COMMAND=" + args.Command, + "CNI_CONTAINERID=" + args.ContainerID, + "CNI_NETNS=" + args.NetNS, + "CNI_ARGS=" + pluginArgsStr, + "CNI_IFNAME=" + args.IfName, + "CNI_PATH=" + args.Path, + }, env...) + return env } // taken from rkt/networking/net_plugin.go @@ -83,46 +80,3 @@ func stringify(pluginArgs [][2]string) string { return strings.Join(entries, ";") } - -// DelegateArgs implements the CNIArgs interface -// used for delegation to inherit from environments -// and allow some overrides like CNI_COMMAND -var _ CNIArgs = &DelegateArgs{} - -type DelegateArgs struct { - Command string -} - -func (d *DelegateArgs) AsEnv() []string { - env := os.Environ() - - // The custom values should come in the end to override the existing - // process environment of the same key. - env = append(env, - "CNI_COMMAND="+d.Command, - ) - return dedupEnv(env) -} - -// dedupEnv returns a copy of env with any duplicates removed, in favor of later values. -// Items not of the normal environment "key=value" form are preserved unchanged. -func dedupEnv(env []string) []string { - out := make([]string, 0, len(env)) - envMap := map[string]string{} - - for _, kv := range env { - // find the first "=" in environment, if not, just keep it - eq := strings.Index(kv, "=") - if eq < 0 { - out = append(out, kv) - continue - } - envMap[kv[:eq]] = kv[eq+1:] - } - - for k, v := range envMap { - out = append(out, fmt.Sprintf("%s=%s", k, v)) - } - - return out -} diff --git a/vendor/github.com/containernetworking/cni/pkg/invoke/delegate.go b/vendor/github.com/containernetworking/cni/pkg/invoke/delegate.go index 8defe4dd..30b4672f 100644 --- a/vendor/github.com/containernetworking/cni/pkg/invoke/delegate.go +++ b/vendor/github.com/containernetworking/cni/pkg/invoke/delegate.go @@ -16,17 +16,22 @@ package invoke import ( "context" + "fmt" "os" "path/filepath" "github.com/containernetworking/cni/pkg/types" ) -func delegateCommon(delegatePlugin string, exec Exec) (string, Exec, error) { +func delegateCommon(expectedCommand, delegatePlugin string, exec Exec) (string, Exec, error) { if exec == nil { exec = defaultExec } + if os.Getenv("CNI_COMMAND") != expectedCommand { + return "", nil, fmt.Errorf("CNI_COMMAND is not " + expectedCommand) + } + paths := filepath.SplitList(os.Getenv("CNI_PATH")) pluginPath, err := exec.FindInPath(delegatePlugin, paths) if err != nil { @@ -39,42 +44,32 @@ func delegateCommon(delegatePlugin string, exec Exec) (string, Exec, error) { // DelegateAdd calls the given delegate plugin with the CNI ADD action and // JSON configuration func DelegateAdd(ctx context.Context, delegatePlugin string, netconf []byte, exec Exec) (types.Result, error) { - pluginPath, realExec, err := delegateCommon(delegatePlugin, exec) + pluginPath, realExec, err := delegateCommon("ADD", delegatePlugin, exec) if err != nil { return nil, err } - // DelegateAdd will override the original "CNI_COMMAND" env from process with ADD - return ExecPluginWithResult(ctx, pluginPath, netconf, delegateArgs("ADD"), realExec) + return ExecPluginWithResult(ctx, pluginPath, netconf, ArgsFromEnv(), realExec) } // DelegateCheck calls the given delegate plugin with the CNI CHECK action and // JSON configuration func DelegateCheck(ctx context.Context, delegatePlugin string, netconf []byte, exec Exec) error { - pluginPath, realExec, err := delegateCommon(delegatePlugin, exec) + pluginPath, realExec, err := delegateCommon("CHECK", delegatePlugin, exec) if err != nil { return err } - // DelegateCheck will override the original CNI_COMMAND env from process with CHECK - return ExecPluginWithoutResult(ctx, pluginPath, netconf, delegateArgs("CHECK"), realExec) + return ExecPluginWithoutResult(ctx, pluginPath, netconf, ArgsFromEnv(), realExec) } // DelegateDel calls the given delegate plugin with the CNI DEL action and // JSON configuration func DelegateDel(ctx context.Context, delegatePlugin string, netconf []byte, exec Exec) error { - pluginPath, realExec, err := delegateCommon(delegatePlugin, exec) + pluginPath, realExec, err := delegateCommon("DEL", delegatePlugin, exec) if err != nil { return err } - // DelegateDel will override the original CNI_COMMAND env from process with DEL - return ExecPluginWithoutResult(ctx, pluginPath, netconf, delegateArgs("DEL"), realExec) -} - -// return CNIArgs used by delegation -func delegateArgs(action string) *DelegateArgs { - return &DelegateArgs{ - Command: action, - } + return ExecPluginWithoutResult(ctx, pluginPath, netconf, ArgsFromEnv(), realExec) } diff --git a/vendor/github.com/coreos/go-iptables/iptables/iptables.go b/vendor/github.com/coreos/go-iptables/iptables/iptables.go index 3d523fc8..9601bc78 100644 --- a/vendor/github.com/coreos/go-iptables/iptables/iptables.go +++ b/vendor/github.com/coreos/go-iptables/iptables/iptables.go @@ -29,11 +29,16 @@ import ( // Adds the output of stderr to exec.ExitError type Error struct { exec.ExitError - cmd exec.Cmd - msg string + cmd exec.Cmd + msg string + proto Protocol + exitStatus *int //for overriding } func (e *Error) ExitStatus() int { + if e.exitStatus != nil { + return *e.exitStatus + } return e.Sys().(syscall.WaitStatus).ExitStatus() } @@ -44,8 +49,8 @@ func (e *Error) Error() string { // IsNotExist returns true if the error is due to the chain or rule not existing func (e *Error) IsNotExist() bool { return e.ExitStatus() == 1 && - (e.msg == "iptables: Bad rule (does a matching rule exist in that chain?).\n" || - e.msg == "iptables: No chain/target/match by that name.\n") + (e.msg == fmt.Sprintf("%s: Bad rule (does a matching rule exist in that chain?).\n", getIptablesCommand(e.proto)) || + e.msg == fmt.Sprintf("%s: No chain/target/match by that name.\n", getIptablesCommand(e.proto))) } // Protocol to differentiate between IPv4 and IPv6 @@ -57,10 +62,15 @@ const ( ) type IPTables struct { - path string - proto Protocol - hasCheck bool - hasWait bool + path string + proto Protocol + hasCheck bool + hasWait bool + hasRandomFully bool + v1 int + v2 int + v3 int + mode string // the underlying iptables operating mode, e.g. nf_tables } // New creates a new IPTables. @@ -76,15 +86,21 @@ func NewWithProtocol(proto Protocol) (*IPTables, error) { if err != nil { return nil, err } - checkPresent, waitPresent, err := getIptablesCommandSupport(path) - if err != nil { - return nil, fmt.Errorf("error checking iptables version: %v", err) - } + vstring, err := getIptablesVersionString(path) + v1, v2, v3, mode, err := extractIptablesVersion(vstring) + + checkPresent, waitPresent, randomFullyPresent := getIptablesCommandSupport(v1, v2, v3) + ipt := IPTables{ - path: path, - proto: proto, - hasCheck: checkPresent, - hasWait: waitPresent, + path: path, + proto: proto, + hasCheck: checkPresent, + hasWait: waitPresent, + hasRandomFully: randomFullyPresent, + v1: v1, + v2: v2, + v3: v3, + mode: mode, } return &ipt, nil } @@ -255,10 +271,28 @@ func (ipt *IPTables) executeList(args []string) ([]string, error) { } rules := strings.Split(stdout.String(), "\n") + + // strip trailing newline if len(rules) > 0 && rules[len(rules)-1] == "" { rules = rules[:len(rules)-1] } + // nftables mode doesn't return an error code when listing a non-existent + // chain. Patch that up. + if len(rules) == 0 && ipt.mode == "nf_tables" { + v := 1 + return nil, &Error{ + cmd: exec.Cmd{Args: args}, + msg: fmt.Sprintf("%s: No chain/target/match by that name.\n", getIptablesCommand(ipt.proto)), + proto: ipt.proto, + exitStatus: &v, + } + } + + for i, rule := range rules { + rules[i] = filterRuleOutput(rule) + } + return rules, nil } @@ -273,11 +307,18 @@ func (ipt *IPTables) NewChain(table, chain string) error { func (ipt *IPTables) ClearChain(table, chain string) error { err := ipt.NewChain(table, chain) + // the exit code for "this table already exists" is different for + // different iptables modes + existsErr := 1 + if ipt.mode == "nf_tables" { + existsErr = 4 + } + eerr, eok := err.(*Error) switch { case err == nil: return nil - case eok && eerr.ExitStatus() == 1: + case eok && eerr.ExitStatus() == existsErr: // chain already exists. Flush (clear) it. return ipt.run("-t", table, "-F", chain) default: @@ -301,6 +342,16 @@ func (ipt *IPTables) ChangePolicy(table, chain, target string) error { return ipt.run("-t", table, "-P", chain, target) } +// Check if the underlying iptables command supports the --random-fully flag +func (ipt *IPTables) HasRandomFully() bool { + return ipt.hasRandomFully +} + +// Return version components of the underlying iptables command +func (ipt *IPTables) GetIptablesVersion() (int, int, int) { + return ipt.v1, ipt.v2, ipt.v3 +} + // run runs an iptables command with the given arguments, ignoring // any stdout output func (ipt *IPTables) run(args ...string) error { @@ -336,7 +387,7 @@ func (ipt *IPTables) runWithOutput(args []string, stdout io.Writer) error { if err := cmd.Run(); err != nil { switch e := err.(type) { case *exec.ExitError: - return &Error{*e, cmd, stderr.String()} + return &Error{*e, cmd, stderr.String(), ipt.proto, nil} default: return err } @@ -355,45 +406,40 @@ func getIptablesCommand(proto Protocol) string { } // Checks if iptables has the "-C" and "--wait" flag -func getIptablesCommandSupport(path string) (bool, bool, error) { - vstring, err := getIptablesVersionString(path) - if err != nil { - return false, false, err - } - - v1, v2, v3, err := extractIptablesVersion(vstring) - if err != nil { - return false, false, err - } - - return iptablesHasCheckCommand(v1, v2, v3), iptablesHasWaitCommand(v1, v2, v3), nil +func getIptablesCommandSupport(v1 int, v2 int, v3 int) (bool, bool, bool) { + return iptablesHasCheckCommand(v1, v2, v3), iptablesHasWaitCommand(v1, v2, v3), iptablesHasRandomFully(v1, v2, v3) } -// getIptablesVersion returns the first three components of the iptables version. -// e.g. "iptables v1.3.66" would return (1, 3, 66, nil) -func extractIptablesVersion(str string) (int, int, int, error) { - versionMatcher := regexp.MustCompile("v([0-9]+)\\.([0-9]+)\\.([0-9]+)") +// getIptablesVersion returns the first three components of the iptables version +// and the operating mode (e.g. nf_tables or legacy) +// e.g. "iptables v1.3.66" would return (1, 3, 66, legacy, nil) +func extractIptablesVersion(str string) (int, int, int, string, error) { + versionMatcher := regexp.MustCompile(`v([0-9]+)\.([0-9]+)\.([0-9]+)(?:\s+\((\w+))?`) result := versionMatcher.FindStringSubmatch(str) if result == nil { - return 0, 0, 0, fmt.Errorf("no iptables version found in string: %s", str) + return 0, 0, 0, "", fmt.Errorf("no iptables version found in string: %s", str) } v1, err := strconv.Atoi(result[1]) if err != nil { - return 0, 0, 0, err + return 0, 0, 0, "", err } v2, err := strconv.Atoi(result[2]) if err != nil { - return 0, 0, 0, err + return 0, 0, 0, "", err } v3, err := strconv.Atoi(result[3]) if err != nil { - return 0, 0, 0, err + return 0, 0, 0, "", err } - return v1, v2, v3, nil + mode := "legacy" + if result[4] != "" { + mode = result[4] + } + return v1, v2, v3, mode, nil } // Runs "iptables --version" to get the version string @@ -436,6 +482,20 @@ func iptablesHasWaitCommand(v1 int, v2 int, v3 int) bool { return false } +// Checks if an iptables version is after 1.6.2, when --random-fully was added +func iptablesHasRandomFully(v1 int, v2 int, v3 int) bool { + if v1 > 1 { + return true + } + if v1 == 1 && v2 > 6 { + return true + } + if v1 == 1 && v2 == 6 && v3 >= 2 { + return true + } + return false +} + // Checks if a rule specification exists for a table func (ipt *IPTables) existsForOldIptables(table, chain string, rulespec []string) (bool, error) { rs := strings.Join(append([]string{"-A", chain}, rulespec...), " ") @@ -447,3 +507,26 @@ func (ipt *IPTables) existsForOldIptables(table, chain string, rulespec []string } return strings.Contains(stdout.String(), rs), nil } + +// counterRegex is the regex used to detect nftables counter format +var counterRegex = regexp.MustCompile(`^\[([0-9]+):([0-9]+)\] `) + +// filterRuleOutput works around some inconsistencies in output. +// For example, when iptables is in legacy vs. nftables mode, it produces +// different results. +func filterRuleOutput(rule string) string { + out := rule + + // work around an output difference in nftables mode where counters + // are output in iptables-save format, rather than iptables -S format + // The string begins with "[0:0]" + // + // Fixes #49 + if groups := counterRegex.FindStringSubmatch(out); groups != nil { + // drop the brackets + out = out[len(groups[0]):] + out = fmt.Sprintf("%s -c %s %s", out, groups[1], groups[2]) + } + + return out +} diff --git a/vendor/modules.txt b/vendor/modules.txt index edde941b..e917e330 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -32,7 +32,7 @@ github.com/containernetworking/cni/pkg/skel github.com/containernetworking/cni/pkg/version github.com/containernetworking/cni/pkg/types/020 github.com/containernetworking/cni/libcni -# github.com/coreos/go-iptables v0.3.0 +# github.com/coreos/go-iptables v0.4.1 github.com/coreos/go-iptables/iptables # github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7 github.com/coreos/go-systemd/activation