[lxc-devel] [lxd/master] NIC Routed: Adds firewall based reverse path filtering for IPv4 and IPv6

tomponline on Github lxc-bot at linuxcontainers.org
Mon Mar 30 12:42:33 UTC 2020


A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 301 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20200330/39b1ecc7/attachment.bin>
-------------- next part --------------
From e1f77e74fbf6a636b7f78011073caf1a49826b69 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 30 Mar 2020 10:16:59 +0100
Subject: [PATCH 1/6] lxd/firewall/firewall/interface: Adds
 InstanceSetupRPFilter and InstanceClearRPFilter

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/firewall/firewall_interface.go | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/lxd/firewall/firewall_interface.go b/lxd/firewall/firewall_interface.go
index c89566e8b1..932c38ddb9 100644
--- a/lxd/firewall/firewall_interface.go
+++ b/lxd/firewall/firewall_interface.go
@@ -10,6 +10,7 @@ import (
 type Firewall interface {
 	String() string
 	Compat() (bool, bool)
+
 	NetworkSetupForwardingPolicy(networkName string, ipVersion uint, allow bool) error
 	NetworkSetupOutboundNAT(networkName string, subnet *net.IPNet, srcIP net.IP, append bool) error
 	NetworkSetupDHCPDNSAccess(networkName string, ipVersion uint) error
@@ -21,4 +22,7 @@ type Firewall interface {
 
 	InstanceSetupProxyNAT(projectName string, instanceName string, deviceName string, listen *deviceConfig.ProxyAddress, connect *deviceConfig.ProxyAddress) error
 	InstanceClearProxyNAT(projectName string, instanceName string, deviceName string) error
+
+	InstanceSetupRPFilter(projectName string, instanceName string, deviceName string, hostName string) error
+	InstanceClearRPFilter(projectName string, instanceName string, deviceName string) error
 }

From fbfdaa6923afd540b1e978e4e045d6a7c9794650 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 30 Mar 2020 10:17:41 +0100
Subject: [PATCH 2/6] lxd/firewall/drivers/drivers/xtables: Improves proxy NAT
 rule removal errors

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/firewall/drivers/drivers_xtables.go | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lxd/firewall/drivers/drivers_xtables.go b/lxd/firewall/drivers/drivers_xtables.go
index 3972e82673..00b7de99c2 100644
--- a/lxd/firewall/drivers/drivers_xtables.go
+++ b/lxd/firewall/drivers/drivers_xtables.go
@@ -418,7 +418,7 @@ func (d Xtables) InstanceClearProxyNAT(projectName string, instanceName string,
 	}
 
 	if len(errs) > 0 {
-		return err
+		return fmt.Errorf("Failed to remove proxy NAT rules for %q: %v", deviceName, errs)
 	}
 
 	return nil

From d52fee47c59bef1e27bb25a67b214baa3e0e9fdd Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 30 Mar 2020 10:18:08 +0100
Subject: [PATCH 3/6] lxd/firewall/drivers/drivers/xtables: Renames
 iptablesConfig to iptablesAdd

To match comment.

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/firewall/drivers/drivers_xtables.go | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/lxd/firewall/drivers/drivers_xtables.go b/lxd/firewall/drivers/drivers_xtables.go
index 00b7de99c2..4c3c67dab4 100644
--- a/lxd/firewall/drivers/drivers_xtables.go
+++ b/lxd/firewall/drivers/drivers_xtables.go
@@ -544,7 +544,7 @@ func (d Xtables) matchEbtablesRule(activeRule []string, matchRule []string, dele
 }
 
 // iptablesAdd adds an iptables rule.
-func (d Xtables) iptablesConfig(ipVersion uint, comment string, table string, method string, chain string, rule ...string) error {
+func (d Xtables) iptablesAdd(ipVersion uint, comment string, table string, method string, chain string, rule ...string) error {
 	var cmd string
 	if ipVersion == 4 {
 		cmd = "iptables"
@@ -584,12 +584,12 @@ func (d Xtables) iptablesConfig(ipVersion uint, comment string, table string, me
 
 // iptablesAppend appends an iptables rule.
 func (d Xtables) iptablesAppend(ipVersion uint, comment string, table string, chain string, rule ...string) error {
-	return d.iptablesConfig(ipVersion, comment, table, "-A", chain, rule...)
+	return d.iptablesAdd(ipVersion, comment, table, "-A", chain, rule...)
 }
 
 // iptablesPrepend prepends an iptables rule.
 func (d Xtables) iptablesPrepend(ipVersion uint, comment string, table string, chain string, rule ...string) error {
-	return d.iptablesConfig(ipVersion, comment, table, "-I", chain, rule...)
+	return d.iptablesAdd(ipVersion, comment, table, "-I", chain, rule...)
 }
 
 // iptablesClear clears iptables rules matching the supplied comment in the specified tables.

From 9771abdc899b2e01a3f5db87521a2f1605e54ef4 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 30 Mar 2020 10:18:32 +0100
Subject: [PATCH 4/6] lxd/firewall/drivers/drivers/xtables: Implements reverse
 path filters

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/firewall/drivers/drivers_xtables.go | 46 +++++++++++++++++++++++++
 1 file changed, 46 insertions(+)

diff --git a/lxd/firewall/drivers/drivers_xtables.go b/lxd/firewall/drivers/drivers_xtables.go
index 4c3c67dab4..8aea7b38da 100644
--- a/lxd/firewall/drivers/drivers_xtables.go
+++ b/lxd/firewall/drivers/drivers_xtables.go
@@ -665,3 +665,49 @@ func (d Xtables) iptablesClear(ipVersion uint, comment string, fromTables ...str
 
 	return nil
 }
+
+// InstanceSetupRPFilter activates reverse path filtering for the specified instance device on the host interface.
+func (d Xtables) InstanceSetupRPFilter(projectName string, instanceName string, deviceName string, hostName string) error {
+	comment := fmt.Sprintf("%s rpfilter", d.instanceDeviceIPTablesComment(projectName, instanceName, deviceName))
+	args := []string{
+		"-m", "rpfilter",
+		"--invert",
+		"-i", hostName,
+		"-j", "DROP",
+	}
+
+	// IPv4 filter.
+	err := d.iptablesPrepend(4, comment, "raw", "PREROUTING", args...)
+	if err != nil {
+		return err
+	}
+
+	// IPv6 filter.
+	err = d.iptablesPrepend(6, comment, "raw", "PREROUTING", args...)
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
+// InstanceClearRPFilter removes reverse path filtering for the specified instance device on the host interface.
+func (d Xtables) InstanceClearRPFilter(projectName string, instanceName string, deviceName string) error {
+	comment := fmt.Sprintf("%s rpfilter", d.instanceDeviceIPTablesComment(projectName, instanceName, deviceName))
+	errs := []error{}
+	err := d.iptablesClear(4, comment, "raw")
+	if err != nil {
+		errs = append(errs, err)
+	}
+
+	err = d.iptablesClear(6, comment, "raw")
+	if err != nil {
+		errs = append(errs, err)
+	}
+
+	if len(errs) > 0 {
+		return fmt.Errorf("Failed to remove reverse path filter rules for %q: %v", deviceName, errs)
+	}
+
+	return nil
+}

From 3e143adf08569d1b2dd80424f91a5bd6e5b8bc13 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 30 Mar 2020 11:18:26 +0100
Subject: [PATCH 5/6] lxd/device/nic/routed: Applies firewall based reverse
 path filter for IPv4 and IPv6

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/device/nic_routed.go | 22 +++++++++++++++++++++-
 1 file changed, 21 insertions(+), 1 deletion(-)

diff --git a/lxd/device/nic_routed.go b/lxd/device/nic_routed.go
index 0e1b3a7e6e..2319ba06a2 100644
--- a/lxd/device/nic_routed.go
+++ b/lxd/device/nic_routed.go
@@ -5,6 +5,8 @@ import (
 	"os"
 	"strings"
 
+	"github.com/pkg/errors"
+
 	deviceConfig "github.com/lxc/lxd/lxd/device/config"
 	"github.com/lxc/lxd/lxd/instance"
 	"github.com/lxc/lxd/lxd/instance/instancetype"
@@ -294,6 +296,12 @@ func (d *nicRouted) postStart() error {
 			return err
 		}
 
+		// Apply firewall rules for reverse path filtering of IPv4 and IPv6.
+		err = d.state.Firewall.InstanceSetupRPFilter(d.inst.Project(), d.inst.Name(), d.name, v["host_name"])
+		if err != nil {
+			return errors.Wrapf(err, "Error setting up reverse path filter")
+		}
+
 		if d.config["ipv4.address"] != "" {
 			_, err := shared.RunCommand("ip", "-4", "addr", "add", fmt.Sprintf("%s/32", d.ipv4HostAddress()), "dev", v["host_name"])
 			if err != nil {
@@ -330,15 +338,27 @@ func (d *nicRouted) postStop() error {
 
 	v := d.volatileGet()
 
+	errs := []error{}
+
 	// This will delete the parent interface if we created it for VLAN parent.
 	if shared.IsTrue(v["last_state.created"]) {
 		parentName := network.GetHostDevice(d.config["parent"], d.config["vlan"])
 		err := networkRemoveInterfaceIfNeeded(d.state, parentName, d.inst, d.config["parent"], d.config["vlan"])
 		if err != nil {
-			return err
+			errs = append(errs, err)
 		}
 	}
 
+	// Remove reverse path filters.
+	err := d.state.Firewall.InstanceClearRPFilter(d.inst.Project(), d.inst.Name(), d.name)
+	if err != nil {
+		errs = append(errs, err)
+	}
+
+	if len(errs) > 0 {
+		return fmt.Errorf("%v", errs)
+	}
+
 	return nil
 }
 

From aeab7539562a7626bfa0748c4cb217a903d97df8 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 30 Mar 2020 13:41:34 +0100
Subject: [PATCH 6/6] lxd/firewall/drivers/drivers/nftables: Implements reverse
 path filters

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/firewall/drivers/drivers_nftables.go      | 38 +++++++++++++++++++
 .../drivers/drivers_nftables_templates.go     |  8 ++++
 2 files changed, 46 insertions(+)

diff --git a/lxd/firewall/drivers/drivers_nftables.go b/lxd/firewall/drivers/drivers_nftables.go
index 8d070831af..be42a16751 100644
--- a/lxd/firewall/drivers/drivers_nftables.go
+++ b/lxd/firewall/drivers/drivers_nftables.go
@@ -471,3 +471,41 @@ func (d Nftables) removeChains(families []string, chainSuffix string, chains ...
 
 	return nil
 }
+
+// InstanceSetupRPFilter activates reverse path filtering for the specified instance device on the host interface.
+func (d Nftables) InstanceSetupRPFilter(projectName string, instanceName string, deviceName string, hostName string) error {
+	deviceLabel := d.instanceDeviceLabel(projectName, instanceName, deviceName)
+	tplFields := map[string]interface{}{
+		"namespace":      nftablesNamespace,
+		"chainSeparator": nftablesChainSeparator,
+		"deviceLabel":    deviceLabel,
+		"hostName":       hostName,
+	}
+
+	// IPv4 filter.
+	tplFields["family"] = "ip"
+	err := d.applyNftConfig(nftablesInstanceRPFilter, tplFields)
+	if err != nil {
+		return errors.Wrapf(err, "Failed adding reverse path filter rules for instance device %q (%s)", deviceLabel, tplFields["family"])
+	}
+
+	// IPv46filter.
+	tplFields["family"] = "ip6"
+	err = d.applyNftConfig(nftablesInstanceRPFilter, tplFields)
+	if err != nil {
+		return errors.Wrapf(err, "Failed adding reverse path filter rules for instance device %q (%s)", deviceLabel, tplFields["family"])
+	}
+
+	return nil
+}
+
+// InstanceClearRPFilter removes reverse path filtering for the specified instance device on the host interface.
+func (d Nftables) InstanceClearRPFilter(projectName string, instanceName string, deviceName string) error {
+	deviceLabel := d.instanceDeviceLabel(projectName, instanceName, deviceName)
+	err := d.removeChains([]string{"ip", "ip6"}, deviceLabel, "prert")
+	if err != nil {
+		return errors.Wrapf(err, "Failed clearing reverse path filter rules for instance device %q", deviceLabel)
+	}
+
+	return nil
+}
diff --git a/lxd/firewall/drivers/drivers_nftables_templates.go b/lxd/firewall/drivers/drivers_nftables_templates.go
index df60e83e58..56c7d5d5c4 100644
--- a/lxd/firewall/drivers/drivers_nftables_templates.go
+++ b/lxd/firewall/drivers/drivers_nftables_templates.go
@@ -126,3 +126,11 @@ chain fwd{{.chainSeparator}}{{.deviceLabel}} {
 	{{- end}}
 }
 `))
+
+// nftablesInstanceRPFilter defines the rules to perform reverse path filtering.
+var nftablesInstanceRPFilter = template.Must(template.New("nftablesInstanceRPFilter").Parse(`
+chain prert{{.chainSeparator}}{{.deviceLabel}} {
+	type filter hook prerouting priority -300; policy accept;
+	iif "{{.hostName}}" fib saddr . iif oif missing counter drop
+}
+`))


More information about the lxc-devel mailing list