diff --git a/calico_node/startup/startup.go b/calico_node/startup/startup.go index ce9cf7623a6..ac2f73127e9 100644 --- a/calico_node/startup/startup.go +++ b/calico_node/startup/startup.go @@ -341,6 +341,29 @@ func validateIP(ipn *net.IPNet) { warning("Unable to confirm IPv%d address %s is assigned to this host", ip.Version(), ip) } +// evaluateENVBool evaluates a passed environment variable +// Returns True if the envVar is defined and set to true. +// Returns False if the envVar is defined and set to false. +// Returns defaultValue in the envVar is not defined. +// An log entry will always be written +func evaluateENVBool(envVar string, defaultValue bool) bool { + envValue, isSet := os.LookupEnv(envVar) + + if isSet { + + switch strings.ToLower(envValue) { + + case "false", "0", "no", "n", "f": + log.Infof("%s is %t through environment variable", envVar, false) + return false + } + log.Infof("%s is %t through environment variable", envVar, true) + return true + } + log.Infof("%s is %t (defaulted) through environment variable", envVar, defaultValue) + return defaultValue +} + // autoDetectCIDR auto-detects the IP and Network using the requested // detection method. func autoDetectCIDR(method string, version int) *net.IPNet { @@ -497,11 +520,14 @@ func configureIPPools(client *client.Client) { // Ensure there are pools created for each IP version. if !ipv4Present { log.Debug("Create default IPv4 IP pool") - createIPPool(client, ipv4Cidr, ipv4IpipModeEnvVar) + outgoingNATEnabled := evaluateENVBool("CALICO_IPV4POOL_NAT_OUTGOING", true) + createIPPool(client, ipv4Cidr, ipv4IpipModeEnvVar, outgoingNATEnabled) } if !ipv6Present && ipv6Supported() { log.Debug("Create default IPv6 IP pool") - createIPPool(client, ipv6Cidr, string(ipip.Undefined)) + outgoingNATEnabled := evaluateENVBool("CALICO_IPV6POOL_NAT_OUTGOING", false) + + createIPPool(client, ipv6Cidr, string(ipip.Undefined), outgoingNATEnabled) } } @@ -510,10 +536,9 @@ func configureIPPools(client *client.Client) { // simplistic check of /proc/sys/net/ipv6 (since platforms that do not have IPv6 // compiled in will not have this entry). func ipv6Supported() bool { - // First check the Felix parm. - switch strings.ToLower(os.Getenv("FELIX_IPV6SUPPORT")) { - case "false", "0", "no", "n", "f": - log.Info("IPv6 support disabled through environment") + // First check if Felix param is false + IPv6isSupported := evaluateENVBool("FELIX_IPV6SUPPORT", true) + if !IPv6isSupported { return false } @@ -526,7 +551,7 @@ func ipv6Supported() bool { // createIPPool creates an IP pool using the specified CIDR. This // method is a no-op if the pool already exists. -func createIPPool(client *client.Client, cidr *net.IPNet, ipipModeName string) { +func createIPPool(client *client.Client, cidr *net.IPNet, ipipModeName string, isNATOutgoingEnabled bool) { version := cidr.Version() ipipMode := ipip.Mode(ipipModeName) @@ -540,7 +565,7 @@ func createIPPool(client *client.Client, cidr *net.IPNet, ipipModeName string) { CIDR: *cidr, }, Spec: api.IPPoolSpec{ - NATOutgoing: true, + NATOutgoing: isNATOutgoingEnabled, IPIP: &api.IPIPConfiguration{ Enabled: ipipMode != ipip.Undefined, Mode: ipipMode, @@ -563,8 +588,8 @@ func createIPPool(client *client.Client, cidr *net.IPNet, ipipModeName string) { terminate() } } else { - message("Created default IPv%d pool (%s) with NAT outgoing enabled. IPIP mode: %s", - version, cidr, ipipModeName) + message("Created default IPv%d pool (%s) with NAT outgoing %t. IPIP mode: %s", + version, cidr, isNATOutgoingEnabled, ipipModeName) } } diff --git a/calico_node/startup/startup_test.go b/calico_node/startup/startup_test.go index 88194abdeb6..9c7d639fa1b 100644 --- a/calico_node/startup/startup_test.go +++ b/calico_node/startup/startup_test.go @@ -69,7 +69,7 @@ type EnvItem struct { } var _ = Describe("FV tests against a real etcd", func() { - changedEnvVars := []string{"CALICO_IPV4POOL_CIDR", "CALICO_IPV6POOL_CIDR", "NO_DEFAULT_POOLS", "CALICO_IPV4POOL_IPIP"} + changedEnvVars := []string{"CALICO_IPV4POOL_CIDR", "CALICO_IPV6POOL_CIDR", "NO_DEFAULT_POOLS", "CALICO_IPV4POOL_IPIP", "CALICO_IPV6POOL_NAT_OUTGOING", "CALICO_IPV4POOL_NAT_OUTGOING"} BeforeEach(func() { for _, envName := range changedEnvVars { @@ -83,7 +83,7 @@ var _ = Describe("FV tests against a real etcd", func() { }) DescribeTable("Test IP pool env variables", - func(envList []EnvItem, expectedIPv4 string, expectedIPv6 string, expectIpv4IpipMode string) { + func(envList []EnvItem, expectedIPv4 string, expectedIPv6 string, expectIpv4IpipMode string, expectedIPV4NATOutgoing bool, expectedIPV6NATOutgoing bool) { // Create a new client. cfg, _ := client.LoadClientConfigFromEnvironment() c := testutils.CreateCleanClient(*cfg) @@ -106,6 +106,7 @@ var _ = Describe("FV tests against a real etcd", func() { // Look through the pool for the expected data. foundv4Expected := false foundv6Expected := false + for _, pool := range poolList.Items { if pool.Metadata.CIDR.String() == expectedIPv4 { foundv4Expected = true @@ -118,6 +119,9 @@ var _ = Describe("FV tests against a real etcd", func() { if pool.Spec.IPIP != nil { Expect(pool.Spec.IPIP.Enabled).To(BeFalse()) } + + Expect(pool.Spec.NATOutgoing).To(Equal(expectedIPV6NATOutgoing), "Expected IPv6 to be %t but was %t", expectedIPV6NATOutgoing, pool.Spec.NATOutgoing) + } else { // off is not a real mode value but use it instead of empty string if expectIpv4IpipMode == "off" { @@ -128,6 +132,9 @@ var _ = Describe("FV tests against a real etcd", func() { Expect(pool.Spec.IPIP.Enabled).To(BeTrue()) Expect(pool.Spec.IPIP.Mode).To(Equal(ipip.Mode(expectIpv4IpipMode))) } + + Expect(pool.Spec.NATOutgoing).To(Equal(expectedIPV4NATOutgoing), "Expected IPv4 to be %t but was %t", expectedIPV4NATOutgoing, pool.Spec.NATOutgoing) + } } Expect(foundv4Expected).To(BeTrue(), @@ -137,31 +144,49 @@ var _ = Describe("FV tests against a real etcd", func() { }, Entry("No env variables set", []EnvItem{}, - "192.168.0.0/16", "fd80:24e2:f998:72d6::/64", "off"), + "192.168.0.0/16", "fd80:24e2:f998:72d6::/64", "off", true, false), Entry("IPv4 Pool env var set", []EnvItem{{"CALICO_IPV4POOL_CIDR", "172.16.0.0/24"}}, - "172.16.0.0/24", "fd80:24e2:f998:72d6::/64", "off"), + "172.16.0.0/24", "fd80:24e2:f998:72d6::/64", "off", true, false), Entry("IPv6 Pool env var set", []EnvItem{{"CALICO_IPV6POOL_CIDR", "fdff:ffff:ffff:ffff:ffff::/80"}}, - "192.168.0.0/16", "fdff:ffff:ffff:ffff:ffff::/80", "off"), + "192.168.0.0/16", "fdff:ffff:ffff:ffff:ffff::/80", "off", true, false), Entry("Both IPv4 and IPv6 Pool env var set", []EnvItem{ {"CALICO_IPV4POOL_CIDR", "172.16.0.0/24"}, {"CALICO_IPV6POOL_CIDR", "fdff:ffff:ffff:ffff:ffff::/80"}, }, - "172.16.0.0/24", "fdff:ffff:ffff:ffff:ffff::/80", "off"), + "172.16.0.0/24", "fdff:ffff:ffff:ffff:ffff::/80", "off", true, false), Entry("CALICO_IPV4POOL_IPIP set off", []EnvItem{{"CALICO_IPV4POOL_IPIP", "off"}}, - "192.168.0.0/16", "fd80:24e2:f998:72d6::/64", "off"), + "192.168.0.0/16", "fd80:24e2:f998:72d6::/64", "off", true, false), Entry("CALICO_IPV4POOL_IPIP set always", []EnvItem{{"CALICO_IPV4POOL_IPIP", "always"}}, - "192.168.0.0/16", "fd80:24e2:f998:72d6::/64", "always"), + "192.168.0.0/16", "fd80:24e2:f998:72d6::/64", "always", true, false), Entry("CALICO_IPV4POOL_IPIP set cross-subnet", []EnvItem{{"CALICO_IPV4POOL_IPIP", "cross-subnet"}}, - "192.168.0.0/16", "fd80:24e2:f998:72d6::/64", "cross-subnet"), + "192.168.0.0/16", "fd80:24e2:f998:72d6::/64", "cross-subnet", true, false), Entry("IPv6 Pool and IPIP set", []EnvItem{ {"CALICO_IPV6POOL_CIDR", "fdff:ffff:ffff:ffff:ffff::/80"}, {"CALICO_IPV4POOL_IPIP", "always"}, }, - "192.168.0.0/16", "fdff:ffff:ffff:ffff:ffff::/80", "always"), + "192.168.0.0/16", "fdff:ffff:ffff:ffff:ffff::/80", "always", true, false), + Entry("IPv6 NATOutgoing Set Enabled", + []EnvItem{ + {"CALICO_IPV6POOL_NAT_OUTGOING", "true"}}, + "192.168.0.0/16", "fd80:24e2:f998:72d6::/64", "off", true, true), + Entry("IPv6 NATOutgoing Set Disabled", + []EnvItem{ + {"CALICO_IPV6POOL_NAT_OUTGOING", "false"}}, + "192.168.0.0/16", "fd80:24e2:f998:72d6::/64", "off", true, false), + Entry("IPv4 NATOutgoing Set Disabled", + []EnvItem{ + {"CALICO_IPV4POOL_NAT_OUTGOING", "false"}}, + "192.168.0.0/16", "fd80:24e2:f998:72d6::/64", "off", false, false), + Entry("IPv6 NAT OUTGOING and IPV4 NAT OUTGOING SET", + []EnvItem{ + {"CALICO_IPV4POOL_NAT_OUTGOING", "false"}, + {"CALICO_IPV6POOL_NAT_OUTGOING", "true"}, + }, + "192.168.0.0/16", "fd80:24e2:f998:72d6::/64", "off", false, true), ) Describe("Test NO_DEFAULT_POOLS env variable", func() { diff --git a/calico_node/tests/st/calicoctl/test_default_pools.py b/calico_node/tests/st/calicoctl/test_default_pools.py index 3ec7e0f1cef..2a8246810f0 100644 --- a/calico_node/tests/st/calicoctl/test_default_pools.py +++ b/calico_node/tests/st/calicoctl/test_default_pools.py @@ -49,33 +49,33 @@ def tearDownClass(cls): cls.host.cleanup() @parameterized.expand([ - (False, "CALICO_IPV4POOL_CIDR", "10.0.0.0/27", 0, None, "Too small"), - (False, "CALICO_IPV4POOL_CIDR", "10.0.0.0/32", 0, None, "Too small, but legal CIDR"), - (False, "CALICO_IPV4POOL_CIDR", "10.0.0.0/33", 0, None, "Impossible CIDR"), - (False, "CALICO_IPV4POOL_CIDR", "256.0.0.0/24", 0, None, "Invalid IP"), - (True, "CALICO_IPV4POOL_CIDR", "10.0.0.0/24", 2, None, "Typical non-default pool"), - (True, "CALICO_IPV4POOL_CIDR", "10.0.0.0/26", 2, None, "Smallest legal pool"), - (True, "CALICO_IPV6POOL_CIDR", "fd00::/122", 2, None, "Smallest legal pool"), - (False, "CALICO_IPV6POOL_CIDR", "fd00::/123", 0, None, "Too small"), - (False, "CALICO_IPV6POOL_CIDR", "fd00::/128", 0, None, "Too small, but legal CIDR"), - (False, "CALICO_IPV6POOL_CIDR", "fd00::/129", 0, None, "Impossible CIDR"), - (True, "CALICO_IPV4POOL_CIDR", "10.0.0.0/24", 2, "cross-subnet", "Typ. non-def pool, IPIP"), - (True, "CALICO_IPV4POOL_CIDR", "10.0.0.0/24", 2, "always", "Typ. non-default pool, IPIP"), - (True, "CALICO_IPV4POOL_CIDR", "10.0.0.0/24", 2, "off", "Typical pool, explicitly no IPIP"), - (True, "CALICO_IPV6POOL_CIDR", "fd00::/122", 2, "always", "IPv6 - IPIP not permitted"), - (True, "CALICO_IPV6POOL_CIDR", "fd00::/122", 2, "cross-subnet", "IPv6 - IPIP not allowed"), - (True, "CALICO_IPV6POOL_CIDR", "fd00::/122", 2, "off", "IPv6, IPIP explicitly off"), - (False, "CALICO_IPV6POOL_CIDR", "fd00::/122", 0, "junk", "Invalid IPIP value"), - (False, "CALICO_IPV4POOL_CIDR", "10.0.0.0/24", 0, "reboot", "Invalid IPIP value"), - (False, "CALICO_IPV4POOL_CIDR", "0.0.0.0/0", 0, None, "Invalid, link local address"), - (False, "CALICO_IPV6POOL_CIDR", "::/0", 0, None, "Invalid, link local address"), - (True, "CALICO_IPV6POOL_CIDR", "fd80::0:0/120", 2, None, "Valid, but non-canonical form"), - (False, "CALICO_IPV6POOL_CIDR", "1.2.3.4/24", 0, None, "Wrong type"), - (False, "CALICO_IPV4POOL_CIDR", "fd00::/24", 0, None, "Wrong type"), - (True, "CALICO_IPV6POOL_CIDR", "::0:a:b:c:d:e:0/120", 2, None, "Valid, non-canonical form"), - (False, "CALICO_IPV4POOL_CIDR", "1.2/16", 0, None, "Valid, unusual form"), + (False, "CALICO_IPV4POOL_CIDR", "10.0.0.0/27", 0, None, True, "Too small"), + (False, "CALICO_IPV4POOL_CIDR", "10.0.0.0/32", 0, None, True, "Too small, but legal CIDR"), + (False, "CALICO_IPV4POOL_CIDR", "10.0.0.0/33", 0, None, True, "Impossible CIDR"), + (False, "CALICO_IPV4POOL_CIDR", "256.0.0.0/24", 0, None, True, "Invalid IP"), + (True, "CALICO_IPV4POOL_CIDR", "10.0.0.0/24", 2, None, True, "Typical non-default pool"), + (True, "CALICO_IPV4POOL_CIDR", "10.0.0.0/26", 2, None, True, "Smallest legal pool"), + (True, "CALICO_IPV6POOL_CIDR", "fd00::/122", 2, None, False, "Smallest legal pool"), + (False, "CALICO_IPV6POOL_CIDR", "fd00::/123", 0, None, False, "Too small"), + (False, "CALICO_IPV6POOL_CIDR", "fd00::/128", 0, None, False, "Too small, but legal CIDR"), + (False, "CALICO_IPV6POOL_CIDR", "fd00::/129", 0, None, False, "Impossible CIDR"), + (True, "CALICO_IPV4POOL_CIDR", "10.0.0.0/24", 2, "cross-subnet", True,"Typ. non-def pool, IPIP"), + (True, "CALICO_IPV4POOL_CIDR", "10.0.0.0/24", 2, "always", True,"Typ. non-default pool, IPIP"), + (True, "CALICO_IPV4POOL_CIDR", "10.0.0.0/24", 2, "off", True, "Typical pool, explicitly no IPIP"), + (True, "CALICO_IPV6POOL_CIDR", "fd00::/122", 2, "always", False, "IPv6 - IPIP not permitted"), + (True, "CALICO_IPV6POOL_CIDR", "fd00::/122", 2, "cross-subnet", False, "IPv6 - IPIP not allowed"), + (True, "CALICO_IPV6POOL_CIDR", "fd00::/122", 2, "off", False, "IPv6, IPIP explicitly off"), + (False, "CALICO_IPV6POOL_CIDR", "fd00::/122", 0, "junk", False, "Invalid IPIP value"), + (False, "CALICO_IPV4POOL_CIDR", "10.0.0.0/24", 0, "reboot", True, "Invalid IPIP value"), + (False, "CALICO_IPV4POOL_CIDR", "0.0.0.0/0", 0, None, True, "Invalid, link local address"), + (False, "CALICO_IPV6POOL_CIDR", "::/0", 0, None, False, "Invalid, link local address"), + (True, "CALICO_IPV6POOL_CIDR", "fd80::0:0/120", 2, None, False, "Valid, but non-canonical form"), + (False, "CALICO_IPV6POOL_CIDR", "1.2.3.4/24", 0, None, False, "Wrong type"), + (False, "CALICO_IPV4POOL_CIDR", "fd00::/24", 0, None, True, "Wrong type"), + (True, "CALICO_IPV6POOL_CIDR", "::0:a:b:c:d:e:0/120", 2, None, False, "Valid, non-canonical form"), + (False, "CALICO_IPV4POOL_CIDR", "1.2/16", 0, None, True, "Valid, unusual form"), ]) - def test_default_pools(self, success_expected, param, value, exp_num_pools, ipip, description): + def test_default_pools(self, success_expected, param, value, exp_num_pools, ipip, nat_outgoing, description): """ Test that the various options for default pools work correctly """ @@ -138,7 +138,12 @@ def test_default_pools(self, success_expected, param, value, exp_num_pools, ipip "Didn't find ipip mode in pool %s" % pool # Check NAT setting - assert pool['spec']['nat-outgoing'] is True, "Didn't find nat enabled in pool %s" % pool + if 'nat-outgoing' in pool['spec']: + assert pool['spec']['nat-outgoing'] is nat_outgoing, \ + "Wrong NAT default in pool %s, expected nat-outgoing to be %s" % (pool, nat_outgoing) + else: + assert nat_outgoing is False, \ + "Wrong NAT default in pool %s, expecting nat-outgoing to be disabled" % pool def test_no_default_pools(self): """