From 75ba9b484476cf75478a9bcfe8892dbf35c21d1d Mon Sep 17 00:00:00 2001 From: GuyTempleton Date: Tue, 10 Aug 2021 22:55:10 +0100 Subject: [PATCH] Allow negation of interfaces for iptables rules Based on matching functionality in Kiam Introduced by https://github.com/uswitch/kiam/pull/54 --- README.md | 1 + iptables/iptables.go | 14 ++++++++++++-- iptables/iptables_test.go | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 47 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4b70567e..b2646144 100644 --- a/README.md +++ b/README.md @@ -146,6 +146,7 @@ different than `docker0` depending on which virtual network you use e.g. * for kops (on kubenet), use `cbr0` * for CNI, use `cni0` * for [EKS](https://docs.aws.amazon.com/eks/latest/userguide/what-is-eks.html)/[amazon-vpc-cni-k8s](https://github.com/aws/amazon-vpc-cni-k8s), even with calico installed uses `eni+`. (Each pod gets an interface like `eni4c0e15dfb05`) + * If using security groups per pod however, you will need to instead use `!eth0` as pods making use of security groups per pod [will use](https://aws.amazon.com/blogs/containers/introducing-security-groups-for-pods/) `vlan` interfaces as well as the `eni+` interfaces used for other pods. * for weave use `weave` * for flannel use `cni0` * for [kube-router](https://github.com/cloudnativelabs/kube-router) use `kube-bridge` diff --git a/iptables/iptables.go b/iptables/iptables.go index 1ed838c1..5ffee430 100644 --- a/iptables/iptables.go +++ b/iptables/iptables.go @@ -24,9 +24,15 @@ func AddRule(appPort, metadataAddress, hostInterface, hostIP string) error { return err } + ruleSpec := []string{"-p", "tcp", "-d", metadataAddress, "--dport", "80", + "-j", "DNAT", "--to-destination", hostIP + ":" + appPort, + } + if strings.HasPrefix(hostInterface, "!") { + ruleSpec = append(ruleSpec, "!") + } + ruleSpec = append(ruleSpec, "-i", strings.TrimPrefix(hostInterface, "!")) return ipt.AppendUnique( - "nat", "PREROUTING", "-p", "tcp", "-d", metadataAddress, "--dport", "80", - "-j", "DNAT", "--to-destination", hostIP+":"+appPort, "-i", hostInterface, + "nat", "PREROUTING", ruleSpec..., ) } @@ -39,6 +45,10 @@ func checkInterfaceExists(hostInterface string) error { return nil } + if strings.HasPrefix(hostInterface, "!") { + hostInterface = strings.TrimPrefix(hostInterface, "!") + } + _, err := net.InterfaceByName(hostInterface) return err } diff --git a/iptables/iptables_test.go b/iptables/iptables_test.go index 8eff5f31..b71adab6 100644 --- a/iptables/iptables_test.go +++ b/iptables/iptables_test.go @@ -36,6 +36,40 @@ func TestCheckInterfaceExistsPassesWithPlus(t *testing.T) { } } +func TestCheckInterfaceExistsPassesWithNegated(t *testing.T) { + var ifc string + switch os := runtime.GOOS; os { + case "darwin": + ifc = "!lo0" + case "linux": + ifc = "!lo" + default: + // everything else that we don't know or care about...fail + ifc = "!unknown" + t.Fatalf("%s OS '%s'\n", ifc, os) + } + if err := checkInterfaceExists(ifc); err != nil { + t.Error("Should pass with negated interface(s). Interface received:", ifc) + } +} + +func TestCheckInterfaceExistsFailsWithDoubleNegated(t *testing.T) { + var ifc string + switch os := runtime.GOOS; os { + case "darwin": + ifc = "!!lo0" + case "linux": + ifc = "!!lo" + default: + // everything else that we don't know or care about...fail + ifc = "!!unknown" + t.Fatalf("%s OS '%s'\n", ifc, os) + } + if err := checkInterfaceExists(ifc); err == nil { + t.Error("Should fail with invalid interface. Interface received:", ifc) + } +} + func TestAddRule(t *testing.T) { t.Skip() }