[lxc-devel] [lxd/master] Allow NAT rule to be appended to POSTROUTING table

lxdicted on Github lxc-bot at linuxcontainers.org
Sun Jul 22 05:22:30 UTC 2018


A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 966 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20180722/6a05bae8/attachment.bin>
-------------- next part --------------
From 0c040ded97bd8389313a000f934bf7bfba3a1232 Mon Sep 17 00:00:00 2001
From: oms-kauz <admin at kauz.net>
Date: Sat, 21 Jul 2018 07:13:56 +0000
Subject: [PATCH] Allow NAT rule to be appended to POSTROUTING table

The current implementation of the NAT feature in LXD causes some
issues when it should collaborate with firewall/IPsec packages. This
patch adds therefor some flexibility within LXD on how firewall
rules are handled. This espacially comes into play during restarts
of lxd(8).

The ipv[46].nat configuration options are advanced to accept the
following options:
- true|insert: the firewall rule is insert at the beginning of the
  POSTROUTING table
- false: no rule is added
- append: the iptables rule is appended at the end of the POSTROUTING
  table

To keep the configuration options backwards compatible the old value
"true" and "false" are kept.
---
 doc/networks.md          |  4 ++--
 lxd/networks.go          | 16 ++++++++++++----
 lxd/networks_config.go   |  8 ++++++--
 lxd/networks_iptables.go | 13 ++++++++++---
 4 files changed, 30 insertions(+), 11 deletions(-)

diff --git a/doc/networks.md b/doc/networks.md
index 2b29b7a0c..767708371 100644
--- a/doc/networks.md
+++ b/doc/networks.md
@@ -36,7 +36,7 @@ ipv4.dhcp.expiry                | string    | ipv4 dhcp             | 1h
 ipv4.dhcp.gateway               | string    | ipv4 dhcp             | ipv4.address              | Address of the gateway for the subnet
 ipv4.dhcp.ranges                | string    | ipv4 dhcp             | all addresses             | Comma separated list of IP ranges to use for DHCP (FIRST-LAST format)
 ipv4.firewall                   | boolean   | ipv4 address          | true                      | Whether to generate filtering firewall rules for this network
-ipv4.nat                        | boolean   | ipv4 address          | false                     | Whether to NAT (will default to true if unset and a random ipv4.address is generated)
+ipv4.nat                        | string    | ipv4 address          | false                     | Whether and how to NAT ("true" / "insert" a NAT rule is inserted into the NAT table, "append" a NAT rule is appended into the NAT table, "false" no rule generated). It will default to "true"/"insert" if unset and a random ipv4.address is generated)
 ipv4.routes                     | string    | ipv4 address          | -                         | Comma separated list of additional IPv4 CIDR subnets to route to the bridge
 ipv4.routing                    | boolean   | ipv4 address          | true                      | Whether to route traffic in and out of the bridge
 ipv6.address                    | string    | standard mode         | random unused subnet      | IPv6 address for the bridge (CIDR notation). Use "none" to turn off IPv6 or "auto" to generate a new one
@@ -45,7 +45,7 @@ ipv6.dhcp.expiry                | string    | ipv6 dhcp             | 1h
 ipv6.dhcp.ranges                | string    | ipv6 stateful dhcp    | all addresses             | Comma separated list of IPv6 ranges to use for DHCP (FIRST-LAST format)
 ipv6.dhcp.stateful              | boolean   | ipv6 dhcp             | false                     | Whether to allocate addresses using DHCP
 ipv6.firewall                   | boolean   | ipv6 address          | true                      | Whether to generate filtering firewall rules for this network
-ipv6.nat                        | boolean   | ipv6 address          | false                     | Whether to NAT (will default to true if unset and a random ipv6.address is generated)
+ipv6.nat                        | string    | ipv6 address          | false                     | Whether and how to NAT ("true" / "insert" a NAT rule is inserted into the NAT table, "append" a NAT rule is appended into the NAT table, "false" no rule generated). It will default to "true"/"insert" if unset and a random ipv4.address is generated)
 ipv6.routes                     | string    | ipv6 address          | -                         | Comma separated list of additional IPv6 CIDR subnets to route to the bridge
 ipv6.routing                    | boolean   | ipv6 address          | true                      | Whether to route traffic in and out of the bridge
 raw.dnsmasq                     | string    | -                     | -                         | Additional dnsmasq configuration to append to the configuration
diff --git a/lxd/networks.go b/lxd/networks.go
index b81963be4..1f40703ea 100644
--- a/lxd/networks.go
+++ b/lxd/networks.go
@@ -1225,8 +1225,12 @@ func (n *network) Start() error {
 		}
 
 		// Configure NAT
-		if shared.IsTrue(n.config["ipv4.nat"]) {
-			err = networkIptablesPrepend("ipv4", n.name, "nat", "POSTROUTING", "-s", subnet.String(), "!", "-d", subnet.String(), "-j", "MASQUERADE")
+		if !shared.StringInSlice(n.config["ipv4.nat"], []string{"", "false"}) {
+			if shared.StringInSlice(n.config["ipv4.nat"], []string{"true", "insert"}) {
+				err = networkIptablesPrepend("ipv4", n.name, "nat", "POSTROUTING", "-s", subnet.String(), "!", "-d", subnet.String(), "-j", "MASQUERADE")
+			} else { // "append"
+				err = networkIptablesAppend("ipv4", n.name, "nat", "POSTROUTING", "-s", subnet.String(), "!", "-d", subnet.String(), "-j", "MASQUERADE")
+			}
 			if err != nil {
 				return err
 			}
@@ -1387,8 +1391,12 @@ func (n *network) Start() error {
 		}
 
 		// Configure NAT
-		if shared.IsTrue(n.config["ipv6.nat"]) {
-			err = networkIptablesPrepend("ipv6", n.name, "nat", "POSTROUTING", "-s", subnet.String(), "!", "-d", subnet.String(), "-j", "MASQUERADE")
+		if !shared.StringInSlice(n.config["ipv6.nat"], []string{"", "false"}) {
+			if shared.StringInSlice(n.config["ipv6.nat"], []string{"true", "insert"}) {
+				err = networkIptablesPrepend("ipv6", n.name, "nat", "POSTROUTING", "-s", subnet.String(), "!", "-d", subnet.String(), "-j", "MASQUERADE")
+			} else { // "append"
+				err = networkIptablesAppend("ipv6", n.name, "nat", "POSTROUTING", "-s", subnet.String(), "!", "-d", subnet.String(), "-j", "MASQUERADE")
+			}
 			if err != nil {
 				return err
 			}
diff --git a/lxd/networks_config.go b/lxd/networks_config.go
index 5c79a9d86..c6dfcf618 100644
--- a/lxd/networks_config.go
+++ b/lxd/networks_config.go
@@ -61,7 +61,9 @@ var networkConfigKeys = map[string]func(value string) error{
 		return networkValidAddressCIDRV4(value)
 	},
 	"ipv4.firewall":     shared.IsBool,
-	"ipv4.nat":          shared.IsBool,
+	"ipv4.nat": func(value string) error {
+		return shared.IsOneOf(value, []string{"true", "append", "insert", "false"})
+	},
 	"ipv4.dhcp":         shared.IsBool,
 	"ipv4.dhcp.gateway": networkValidAddressV4,
 	"ipv4.dhcp.expiry":  shared.IsAny,
@@ -77,7 +79,9 @@ var networkConfigKeys = map[string]func(value string) error{
 		return networkValidAddressCIDRV6(value)
 	},
 	"ipv6.firewall":      shared.IsBool,
-	"ipv6.nat":           shared.IsBool,
+	"ipv6.nat": func(value string) error {
+		return shared.IsOneOf(value, []string{"true", "append", "insert", "false"})
+	},
 	"ipv6.dhcp":          shared.IsBool,
 	"ipv6.dhcp.expiry":   shared.IsAny,
 	"ipv6.dhcp.stateful": shared.IsBool,
diff --git a/lxd/networks_iptables.go b/lxd/networks_iptables.go
index 1c0c2bc88..1dbaf65d3 100644
--- a/lxd/networks_iptables.go
+++ b/lxd/networks_iptables.go
@@ -8,7 +8,7 @@ import (
 	"github.com/lxc/lxd/shared"
 )
 
-func networkIptablesPrepend(protocol string, netName string, table string, chain string, rule ...string) error {
+func networkIptablesConfig(protocol string, netName string, table string, method string, chain string, rule ...string) error {
 	cmd := "iptables"
 	if protocol == "ipv6" {
 		cmd = "ip6tables"
@@ -34,8 +34,7 @@ func networkIptablesPrepend(protocol string, netName string, table string, chain
 		return nil
 	}
 
-	// Add the rule
-	args = append(baseArgs, []string{"-I", chain}...)
+	args = append(baseArgs, []string{method, chain}...)
 	args = append(args, rule...)
 	args = append(args, "-m", "comment", "--comment", fmt.Sprintf("generated for LXD network %s", netName))
 
@@ -47,6 +46,14 @@ func networkIptablesPrepend(protocol string, netName string, table string, chain
 	return nil
 }
 
+func networkIptablesAppend(protocol string, netName string, table string, chain string, rule ...string) error {
+	return networkIptablesConfig(protocol, netName, table, "-A", chain, rule...)
+}
+
+func networkIptablesPrepend(protocol string, netName string, table string, chain string, rule ...string) error {
+	return networkIptablesConfig(protocol, netName, table, "-I", chain, rule...)
+}
+
 func networkIptablesClear(protocol string, netName string, table string) error {
 	// Detect kernels that lack IPv6 support
 	if !shared.PathExists("/proc/sys/net/ipv6") && protocol == "ipv6" {


More information about the lxc-devel mailing list