The following pull request was submitted through Github. It can be accessed and reviewed at: https://github.com/lxc/lxd/pull/6397
This e-mail was sent by the LXC bot, direct replies will not reach the author unless they happen to be subscribed to this list. === Description (from pull-request) === This is our first attempt at defining the interface to be used in place of direct calls to ip/ip6/ebtables. We are hoping to get feedback on the direction we are headed in. We chose the level of abstraction based on hardcoded strings that we will be replacing in the end.
From 8757a123c676a158367b345f4dd65ba3168defc3 Mon Sep 17 00:00:00 2001 From: Oscar Ward <oscarwar...@gmail.com> Date: Thu, 31 Oct 2019 15:49:27 -0500 Subject: [PATCH 01/11] defined interface and created xtables ; does not include ebtables --- lxd/firewall/interfaces.go | 13 +++++++++++++ lxd/firewall/xtables.go | 40 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+) create mode 100644 lxd/firewall/interfaces.go create mode 100644 lxd/firewall/xtables.go diff --git a/lxd/firewall/interfaces.go b/lxd/firewall/interfaces.go new file mode 100644 index 0000000000..1ec9031095 --- /dev/null +++ b/lxd/firewall/interfaces.go @@ -0,0 +1,13 @@ +package firewall + +// Firewall represents an LXD firewall. +type Firewall interface { + // Network + NetworkAppend(protocol string, comment string, table string, chain string, rule ...string) error + NetworkPrepend(protocol string, comment string, table string, chain string, rule ...string) error + NetworkClear(protocol string, comment string, table string) error + + // Container + ContainerPrepend(protocol string, comment string, table string, chain string, rule ...string) error + ContainerClear(protocol string, comment string, table string, chain string, rule ...string) error +} diff --git a/lxd/firewall/xtables.go b/lxd/firewall/xtables.go new file mode 100644 index 0000000000..72b419d183 --- /dev/null +++ b/lxd/firewall/xtables.go @@ -0,0 +1,40 @@ +package firewall + +import ( + "fmt" + + "github.com/lxc/lxd/lxd/iptables" +) + +// NetworkAppend adds a network rule at end of ruleset. +func NetworkAppend(protocol string, comment string, table string, chain string, + rule ...string) error { + return iptables.NetworkAppend(protocol, fmt.Sprintf("LXD network %s", comment), + table, chain, rule...) +} + +// NetworkPrepend adds a network rule at start of ruleset. +func NetworkPrepend(protocol string, comment string, table string, chain string, + rule ...string) error { + return iptables.NetworkPrepend(protocol, fmt.Sprintf("LXD network %s", comment), + table, chain, rule...) +} + +// NetworkClear removes network rules. +func NetworkClear(protocol string, comment string, table string) error { + return iptables.NetworkClear(protocol, fmt.Sprintf("LXD network %s", comment), + table) +} + +// ContainerPrepend adds container rule at start of ruleset. +func ContainerPrepend(protocol string, comment string, table string, + chain string, rule ...string) error { + return iptables.ContainerPrepend(protocol, fmt.Sprintf("LXD container %s", comment), + table, chain, rule...) +} + +// ContainerClear removes container rules. +func ContainerClear(protocol string, comment string, table string) error { + return iptables.ContainerClear(protocol, fmt.Sprintf("LXD container %s", comment), + table) +} From d993619a5cdb55cdd69a07a734e465c3b213747f Mon Sep 17 00:00:00 2001 From: Oscar Ward <oscarwar...@gmail.com> Date: Mon, 4 Nov 2019 13:05:36 -0600 Subject: [PATCH 02/11] made notes for the interface --- lxd/firewall/interfaces.go | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/lxd/firewall/interfaces.go b/lxd/firewall/interfaces.go index 1ec9031095..e11792e157 100644 --- a/lxd/firewall/interfaces.go +++ b/lxd/firewall/interfaces.go @@ -1,7 +1,36 @@ package firewall +// proxy.go: +// Stop() - clear both ipv4 and ipv6 instance nat +// setupNAT() - set ipv4 and ipv6 prerouting and output nat +// nic_bridged.go: +// removeFilters() - clear ipv6 filters and set ebtables to default +// generateFilterEbtablesRules() +// matchEbtablesRule() +// setFilters() - set ebtables defaults and iptables defaults +// generateFilterEbtablesRules() +// generateFilterIptablesRules() +// networks.go +// Setup() +// Stop() + // Firewall represents an LXD firewall. type Firewall interface { + // Filter functions + // FOLLOWS: functions which utilize iptables/ebtables + // removeFilters() error // FIXME args (nic_bridged) + // setFilters() error // FIXME args (nic_bridged) + // Stop() error // (proxy) + // setupNAT() error // (proxy) + // Setup() error // (networks) + // Stop() error // (networks) + // needs <shared>, (m deviceConfig.Device) <deviceConfig>, (d *nicBridged) + + // NOTE: requires generateFilterEbtablesRules() + // NOTE: requires matchEbtablesRule() + // NOTE: xtables will need to include shared + // NOTE: nicBridged may need generate/filter functions for nft + // Network NetworkAppend(protocol string, comment string, table string, chain string, rule ...string) error NetworkPrepend(protocol string, comment string, table string, chain string, rule ...string) error From b0df22e49aadb3d953a0b673ef8a2b73bd8e5183 Mon Sep 17 00:00:00 2001 From: Louise Montalvo <louanmonta...@gmail.com> Date: Mon, 4 Nov 2019 13:57:34 -0600 Subject: [PATCH 03/11] Change Firewall interface to a more generic format ; add more notes --- lxd/firewall/interfaces.go | 34 ++++++++++++++++++++++++++------- lxd/firewall/xtables.go | 39 +++++++++++++++----------------------- 2 files changed, 42 insertions(+), 31 deletions(-) diff --git a/lxd/firewall/interfaces.go b/lxd/firewall/interfaces.go index e11792e157..c9ddda43fd 100644 --- a/lxd/firewall/interfaces.go +++ b/lxd/firewall/interfaces.go @@ -1,5 +1,10 @@ package firewall +import ( + "github.com/lxc/lxd/lxd/device" + deviceConfig "github.com/lxc/lxd/lxd/device/config" +) + // proxy.go: // Stop() - clear both ipv4 and ipv6 instance nat // setupNAT() - set ipv4 and ipv6 prerouting and output nat @@ -12,7 +17,19 @@ package firewall // generateFilterIptablesRules() // networks.go // Setup() +// - Remove existing IPv4 iptables rules: 1208 +// - Clear iptables NAT config: 1221 +// - Configure IPv4 firewall for DHCP/DNS: 1246 +// - Allow IPv4 forwarding: 1271 +// - Configure IPv4 NAT: 1371 +// - Remove existing IPv6 iptables rules: 1411 +// - Update IPv6 iptables DHCP/DNS overrides in the dnsmasq config: 1461 +// - Allow IPv6 forwarding: 1505 +// - Configure IPv6 NAT: 1565 +// - Configure tunnel (?) NAT: 1735 // Stop() +// - Cleanup IPv4 iptables: 1985 +// - Cleanup IPv6 iptables: 2005 // Firewall represents an LXD firewall. type Firewall interface { @@ -31,12 +48,15 @@ type Firewall interface { // NOTE: xtables will need to include shared // NOTE: nicBridged may need generate/filter functions for nft - // Network - NetworkAppend(protocol string, comment string, table string, chain string, rule ...string) error - NetworkPrepend(protocol string, comment string, table string, chain string, rule ...string) error - NetworkClear(protocol string, comment string, table string) error + // Proxy + ProxyStop() (*device.RunConfig, error) + ProxySetupNAT() - // Container - ContainerPrepend(protocol string, comment string, table string, chain string, rule ...string) error - ContainerClear(protocol string, comment string, table string, chain string, rule ...string) error + // NIC bridged + BridgeRemoveFilters(deviceConfig.Device) error + BridgeSetFilters(deviceConfig.Device) error + + // Network + NetworkSetup(map[string]string) error + NetworkStop() error } diff --git a/lxd/firewall/xtables.go b/lxd/firewall/xtables.go index 72b419d183..d97eb5ecec 100644 --- a/lxd/firewall/xtables.go +++ b/lxd/firewall/xtables.go @@ -6,35 +6,26 @@ import ( "github.com/lxc/lxd/lxd/iptables" ) -// NetworkAppend adds a network rule at end of ruleset. -func NetworkAppend(protocol string, comment string, table string, chain string, - rule ...string) error { - return iptables.NetworkAppend(protocol, fmt.Sprintf("LXD network %s", comment), - table, chain, rule...) +// Proxy +func ProxyStop() (*device.RunConfig, error) { + return nil, nil } +func ProxySetupNAT() { -// NetworkPrepend adds a network rule at start of ruleset. -func NetworkPrepend(protocol string, comment string, table string, chain string, - rule ...string) error { - return iptables.NetworkPrepend(protocol, fmt.Sprintf("LXD network %s", comment), - table, chain, rule...) } -// NetworkClear removes network rules. -func NetworkClear(protocol string, comment string, table string) error { - return iptables.NetworkClear(protocol, fmt.Sprintf("LXD network %s", comment), - table) +// NIC bridged +func BridgeRemoveFilters(deviceConfig.Device) error { + return nil } - -// ContainerPrepend adds container rule at start of ruleset. -func ContainerPrepend(protocol string, comment string, table string, - chain string, rule ...string) error { - return iptables.ContainerPrepend(protocol, fmt.Sprintf("LXD container %s", comment), - table, chain, rule...) +func BridgeSetFilters(deviceConfig.Device) error { + return nil } -// ContainerClear removes container rules. -func ContainerClear(protocol string, comment string, table string) error { - return iptables.ContainerClear(protocol, fmt.Sprintf("LXD container %s", comment), - table) +// Network +func NetworkSetup(map[string]string) error { + return nil +} +func NetworkStop() error { + return nil } From 27c780d52a0d74061a93e47798f631aa00793405 Mon Sep 17 00:00:00 2001 From: Danny Tran <dannyt...@utexas.edu> Date: Mon, 4 Nov 2019 14:15:01 -0600 Subject: [PATCH 04/11] Added XTables struct ; changed function casing --- lxd/firewall/interfaces.go | 12 ++++++------ lxd/firewall/xtables.go | 20 +++++++++++--------- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/lxd/firewall/interfaces.go b/lxd/firewall/interfaces.go index c9ddda43fd..4c45c3d19c 100644 --- a/lxd/firewall/interfaces.go +++ b/lxd/firewall/interfaces.go @@ -49,14 +49,14 @@ type Firewall interface { // NOTE: nicBridged may need generate/filter functions for nft // Proxy - ProxyStop() (*device.RunConfig, error) - ProxySetupNAT() + proxyStop() (*device.RunConfig, error) + proxySetupNAT() // NIC bridged - BridgeRemoveFilters(deviceConfig.Device) error - BridgeSetFilters(deviceConfig.Device) error + bridgeRemoveFilters(deviceConfig.Device) error + bridgeSetFilters(deviceConfig.Device) error // Network - NetworkSetup(map[string]string) error - NetworkStop() error + networkSetup(map[string]string) error + networkStop() error } diff --git a/lxd/firewall/xtables.go b/lxd/firewall/xtables.go index d97eb5ecec..095e58af48 100644 --- a/lxd/firewall/xtables.go +++ b/lxd/firewall/xtables.go @@ -1,31 +1,33 @@ package firewall import ( - "fmt" - - "github.com/lxc/lxd/lxd/iptables" + "github.com/lxc/lxd/lxd/device" + deviceConfig "github.com/lxc/lxd/lxd/device/config" ) +// XTables is an implmentation of LXD firewall using {ip, ip6, eb}tables +type XTables struct {} + // Proxy -func ProxyStop() (*device.RunConfig, error) { +func (xt *XTables) proxyStop() (*device.RunConfig, error) { return nil, nil } -func ProxySetupNAT() { +func (xt *XTables) proxySetupNAT() { } // NIC bridged -func BridgeRemoveFilters(deviceConfig.Device) error { +func (xt *XTables) bridgeRemoveFilters(deviceConfig.Device) error { return nil } -func BridgeSetFilters(deviceConfig.Device) error { +func (xt *XTables) bridgeSetFilters(deviceConfig.Device) error { return nil } // Network -func NetworkSetup(map[string]string) error { +func (xt *XTables) networkSetup(map[string]string) error { return nil } -func NetworkStop() error { +func (xt *XTables) networkStop() error { return nil } From aec3f290f22f8cefafb40446004bc6fc0e0007e6 Mon Sep 17 00:00:00 2001 From: Louise Montalvo <louanmonta...@gmail.com> Date: Mon, 4 Nov 2019 15:07:38 -0600 Subject: [PATCH 05/11] Update Firewall interface with lower-level clear functions ; change casing in Firewall interface --- lxd/firewall/interfaces.go | 15 ++++++++------- lxd/firewall/xtables.go | 21 +++++++++++++-------- 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/lxd/firewall/interfaces.go b/lxd/firewall/interfaces.go index 4c45c3d19c..3a373815b0 100644 --- a/lxd/firewall/interfaces.go +++ b/lxd/firewall/interfaces.go @@ -1,7 +1,6 @@ package firewall import ( - "github.com/lxc/lxd/lxd/device" deviceConfig "github.com/lxc/lxd/lxd/device/config" ) @@ -48,15 +47,17 @@ type Firewall interface { // NOTE: xtables will need to include shared // NOTE: nicBridged may need generate/filter functions for nft + // Lower-level clear functions + NetworkClear(string, string, string) error + ContainerClear(string, string, string) error + // Proxy - proxyStop() (*device.RunConfig, error) - proxySetupNAT() + ProxySetupNAT() // NIC bridged - bridgeRemoveFilters(deviceConfig.Device) error - bridgeSetFilters(deviceConfig.Device) error + BridgeRemoveFilters(deviceConfig.Device) error + BridgeSetFilters(deviceConfig.Device) error // Network - networkSetup(map[string]string) error - networkStop() error + NetworkSetup(map[string]string) error } diff --git a/lxd/firewall/xtables.go b/lxd/firewall/xtables.go index 095e58af48..742e192628 100644 --- a/lxd/firewall/xtables.go +++ b/lxd/firewall/xtables.go @@ -8,26 +8,31 @@ import ( // XTables is an implmentation of LXD firewall using {ip, ip6, eb}tables type XTables struct {} +// Lower-level clear functions +func (xt *XTables) NetworkClear(protocol string, comment string, table string) error { + return nil +} +func (xt *XTables) ContainerClear(protocol string, comment string, table string) error { + return nil +} + // Proxy -func (xt *XTables) proxyStop() (*device.RunConfig, error) { +func (xt *XTables) ProxyStop() (*device.RunConfig, error) { return nil, nil } -func (xt *XTables) proxySetupNAT() { +func (xt *XTables) ProxySetupNAT() { } // NIC bridged -func (xt *XTables) bridgeRemoveFilters(deviceConfig.Device) error { +func (xt *XTables) BridgeRemoveFilters(deviceConfig.Device) error { return nil } -func (xt *XTables) bridgeSetFilters(deviceConfig.Device) error { +func (xt *XTables) BridgeSetFilters(deviceConfig.Device) error { return nil } // Network -func (xt *XTables) networkSetup(map[string]string) error { - return nil -} -func (xt *XTables) networkStop() error { +func (xt *XTables) NetworkSetup(map[string]string) error { return nil } From 9525e4cffde50f866577e501f58b3eba0107e4d9 Mon Sep 17 00:00:00 2001 From: Danny Tran <dannyt...@utexas.edu> Date: Mon, 4 Nov 2019 15:19:18 -0600 Subject: [PATCH 06/11] Added implementation of xtables clear functions --- lxd/firewall/xtables.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lxd/firewall/xtables.go b/lxd/firewall/xtables.go index 742e192628..51616690a3 100644 --- a/lxd/firewall/xtables.go +++ b/lxd/firewall/xtables.go @@ -1,6 +1,7 @@ package firewall import ( + "github.com/lxc/lxd/lxd/iptables" "github.com/lxc/lxd/lxd/device" deviceConfig "github.com/lxc/lxd/lxd/device/config" ) @@ -9,11 +10,15 @@ import ( type XTables struct {} // Lower-level clear functions + +// NetworkClear removes network rules. func (xt *XTables) NetworkClear(protocol string, comment string, table string) error { - return nil + return iptables.NetworkClear(protocol, comment, table) } + +// ContainerClear removes container rules. func (xt *XTables) ContainerClear(protocol string, comment string, table string) error { - return nil + return iptables.ContainerClear(protocol, comment, table) } // Proxy From 391a6316aaa093c4030f79a9e61c65f67e02bcd6 Mon Sep 17 00:00:00 2001 From: Louise Montalvo <louanmonta...@gmail.com> Date: Mon, 4 Nov 2019 15:21:40 -0600 Subject: [PATCH 07/11] Remove old ProxyStop() function from the Firewall interface --- lxd/firewall/xtables.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/lxd/firewall/xtables.go b/lxd/firewall/xtables.go index 51616690a3..26f17f1f53 100644 --- a/lxd/firewall/xtables.go +++ b/lxd/firewall/xtables.go @@ -22,9 +22,6 @@ func (xt *XTables) ContainerClear(protocol string, comment string, table string) } // Proxy -func (xt *XTables) ProxyStop() (*device.RunConfig, error) { - return nil, nil -} func (xt *XTables) ProxySetupNAT() { } From 3a3a3a9298f91662ccb50774240df7d57c430443 Mon Sep 17 00:00:00 2001 From: Oscar Ward <oscarwar...@gmail.com> Date: Mon, 4 Nov 2019 15:39:57 -0600 Subject: [PATCH 08/11] ProxySetupNAT implemented --- lxd/firewall/xtables.go | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/lxd/firewall/xtables.go b/lxd/firewall/xtables.go index 26f17f1f53..72c4f2c471 100644 --- a/lxd/firewall/xtables.go +++ b/lxd/firewall/xtables.go @@ -1,8 +1,9 @@ package firewall import ( + "fmt" "github.com/lxc/lxd/lxd/iptables" - "github.com/lxc/lxd/lxd/device" + //"github.com/lxc/lxd/lxd/device" deviceConfig "github.com/lxc/lxd/lxd/device/config" ) @@ -22,8 +23,20 @@ func (xt *XTables) ContainerClear(protocol string, comment string, table string) } // Proxy -func (xt *XTables) ProxySetupNAT() { +func (xt *XTables) ProxySetupNAT(ipv string, IPAddr string, comment string, connType, address, port string, cPort string) error { + if IPAddr != "" { + err := iptables.ContainerPrepend(ipv, comment, "nat", "PREROUTING", "-p", connType, "--destination", address, "--dport", port, "-j", "DNAT", "--to-destination", fmt.Sprintf("%s:%s", IPAddr, cPort)) + if err != nil { + return err + } + + err = iptables.ContainerPrepend(ipv, comment, "nat", "OUTPUT", "-p", connType, "--destination", address, "--dport", port, "-j", "DNAT", "--to-destination", fmt.Sprintf("%s:%s", IPAddr, cPort)) + if err != nil { + return err + } + } + return nil } // NIC bridged From 2808a8a6400e3a8aad8a6d397feb1c9d9740fa8a Mon Sep 17 00:00:00 2001 From: Louise Montalvo <louanmonta...@gmail.com> Date: Mon, 4 Nov 2019 15:57:43 -0600 Subject: [PATCH 09/11] Implement BridgeRemoveFilters() for XTables --- lxd/firewall/xtables.go | 109 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 107 insertions(+), 2 deletions(-) diff --git a/lxd/firewall/xtables.go b/lxd/firewall/xtables.go index 72c4f2c471..db5fb9f786 100644 --- a/lxd/firewall/xtables.go +++ b/lxd/firewall/xtables.go @@ -2,9 +2,12 @@ package firewall import ( "fmt" + "net" + "strings" + "github.com/lxc/lxd/lxd/iptables" - //"github.com/lxc/lxd/lxd/device" deviceConfig "github.com/lxc/lxd/lxd/device/config" + "github.com/lxc/lxd/shared" ) // XTables is an implmentation of LXD firewall using {ip, ip6, eb}tables @@ -40,7 +43,47 @@ func (xt *XTables) ProxySetupNAT(ipv string, IPAddr string, comment string, conn } // NIC bridged -func (xt *XTables) BridgeRemoveFilters(deviceConfig.Device) error { +func (xt *XTables) BridgeRemoveFilters(m deviceConfig.Device, IPv4 net.IP, IPv6 net.IP) error { + // Get a current list of rules active on the host. + out, err := shared.RunCommand("ebtables", "--concurrent", "-L", "--Lmac2", "--Lx") + if err != nil { + return fmt.Errorf("Failed to remove network filters for %s: %v", m["name"], err) + } + + // Get a list of rules that we would have applied on instance start. + rules := generateFilterEbtablesRules(m, IPv4, IPv6) + + errs := []error{} + // Iterate through each active rule on the host and try and match it to one the LXD rules. + for _, line := range strings.Split(out, "\n") { + line = strings.TrimSpace(line) + fields := strings.Fields(line) + fieldsLen := len(fields) + + for _, rule := range rules { + // Rule doesn't match if the field lenths aren't the same, move on. + if len(rule) != fieldsLen { + continue + } + + // Check whether active rule matches one of our rules to delete. + if !matchEbtablesRule(fields, rule, true) { + continue + } + + // If we get this far, then the current host rule matches one of our LXD + // rules, so we should run the modified command to delete it. + _, err = shared.RunCommand(fields[0], append([]string{"--concurrent"}, fields[1:]...)...) + if err != nil { + errs = append(errs, err) + } + } + } + + if len(errs) > 0 { + return fmt.Errorf("Failed to remove network filters rule for %s: %v", m["name"], errs) + } + return nil } func (xt *XTables) BridgeSetFilters(deviceConfig.Device) error { @@ -51,3 +94,65 @@ func (xt *XTables) BridgeSetFilters(deviceConfig.Device) error { func (xt *XTables) NetworkSetup(map[string]string) error { return nil } + +// generateFilterEbtablesRules returns a customised set of ebtables filter rules based on the device. +func generateFilterEbtablesRules(m deviceConfig.Device, IPv4 net.IP, IPv6 net.IP) [][]string { + // MAC source filtering rules. Blocks any packet coming from instance with an incorrect Ethernet source MAC. + // This is required for IP filtering too. + rules := [][]string{ + {"ebtables", "-t", "filter", "-A", "INPUT", "-s", "!", m["hwaddr"], "-i", m["host_name"], "-j", "DROP"}, + {"ebtables", "-t", "filter", "-A", "FORWARD", "-s", "!", m["hwaddr"], "-i", m["host_name"], "-j", "DROP"}, + } + + if shared.IsTrue(m["security.ipv4_filtering"]) && IPv4 != nil { + rules = append(rules, + // Prevent ARP MAC spoofing (prevents the instance poisoning the ARP cache of its neighbours with a MAC address that isn't its own). + []string{"ebtables", "-t", "filter", "-A", "INPUT", "-p", "ARP", "-i", m["host_name"], "--arp-mac-src", "!", m["hwaddr"], "-j", "DROP"}, + []string{"ebtables", "-t", "filter", "-A", "FORWARD", "-p", "ARP", "-i", m["host_name"], "--arp-mac-src", "!", m["hwaddr"], "-j", "DROP"}, + // Prevent ARP IP spoofing (prevents the instance redirecting traffic for IPs that are not its own). + []string{"ebtables", "-t", "filter", "-A", "INPUT", "-p", "ARP", "-i", m["host_name"], "--arp-ip-src", "!", IPv4.String(), "-j", "DROP"}, + []string{"ebtables", "-t", "filter", "-A", "FORWARD", "-p", "ARP", "-i", m["host_name"], "--arp-ip-src", "!", IPv4.String(), "-j", "DROP"}, + // Allow DHCPv4 to the host only. This must come before the IP source filtering rules below. + []string{"ebtables", "-t", "filter", "-A", "INPUT", "-p", "IPv4", "-s", m["hwaddr"], "-i", m["host_name"], "--ip-src", "0.0.0.0", "--ip-dst", "255.255.255.255", "--ip-proto", "udp", "--ip-dport", "67", "-j", "ACCEPT"}, + // IP source filtering rules. Blocks any packet coming from instance with an incorrect IP source address. + []string{"ebtables", "-t", "filter", "-A", "INPUT", "-p", "IPv4", "-i", m["host_name"], "--ip-src", "!", IPv4.String(), "-j", "DROP"}, + []string{"ebtables", "-t", "filter", "-A", "FORWARD", "-p", "IPv4", "-i", m["host_name"], "--ip-src", "!", IPv4.String(), "-j", "DROP"}, + ) + } + + if shared.IsTrue(m["security.ipv6_filtering"]) && IPv6 != nil { + rules = append(rules, + // Allow DHCPv6 and Router Solicitation to the host only. This must come before the IP source filtering rules below. + []string{"ebtables", "-t", "filter", "-A", "INPUT", "-p", "IPv6", "-s", m["hwaddr"], "-i", m["host_name"], "--ip6-src", "fe80::/ffc0::", "--ip6-dst", "ff02::1:2/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", "--ip6-proto", "udp", "--ip6-dport", "547", "-j", "ACCEPT"}, + []string{"ebtables", "-t", "filter", "-A", "INPUT", "-p", "IPv6", "-s", m["hwaddr"], "-i", m["host_name"], "--ip6-src", "fe80::/ffc0::", "--ip6-dst", "ff02::2/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", "--ip6-proto", "ipv6-icmp", "--ip6-icmp-type", "router-solicitation", "-j", "ACCEPT"}, + // IP source filtering rules. Blocks any packet coming from instance with an incorrect IP source address. + []string{"ebtables", "-t", "filter", "-A", "INPUT", "-p", "IPv6", "-i", m["host_name"], "--ip6-src", "!", fmt.Sprintf("%s/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", IPv6.String()), "-j", "DROP"}, + []string{"ebtables", "-t", "filter", "-A", "FORWARD", "-p", "IPv6", "-i", m["host_name"], "--ip6-src", "!", fmt.Sprintf("%s/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", IPv6.String()), "-j", "DROP"}, + ) + } + + return rules +} + +// matchEbtablesRule compares an active rule to a supplied match rule to see if they match. +// If deleteMode is true then the "-A" flag in the active rule will be modified to "-D" and will +// not be part of the equality match. This allows delete commands to be generated from dumped add commands. +func matchEbtablesRule(activeRule []string, matchRule []string, deleteMode bool) bool { + for i := range matchRule { + // Active rules will be dumped in "add" format, we need to detect + // this and switch it to "delete" mode if requested. If this has already been + // done then move on, as we don't want to break the comparison below. + if deleteMode && (activeRule[i] == "-A" || activeRule[i] == "-D") { + activeRule[i] = "-D" + continue + } + + // Check the match rule field matches the active rule field. + // If they don't match, then this isn't one of our rules. + if activeRule[i] != matchRule[i] { + return false + } + } + + return true +} From 103771600cf75e9f875fdb8d237c9c13b7a6fb47 Mon Sep 17 00:00:00 2001 From: Louise Montalvo <louanmonta...@gmail.com> Date: Mon, 4 Nov 2019 16:02:38 -0600 Subject: [PATCH 10/11] Add parameter names to the Firewall interface --- lxd/firewall/interfaces.go | 14 ++++++++------ lxd/firewall/xtables.go | 4 ++-- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/lxd/firewall/interfaces.go b/lxd/firewall/interfaces.go index 3a373815b0..7d95f8ccec 100644 --- a/lxd/firewall/interfaces.go +++ b/lxd/firewall/interfaces.go @@ -1,6 +1,8 @@ package firewall import ( + "net" + deviceConfig "github.com/lxc/lxd/lxd/device/config" ) @@ -48,16 +50,16 @@ type Firewall interface { // NOTE: nicBridged may need generate/filter functions for nft // Lower-level clear functions - NetworkClear(string, string, string) error - ContainerClear(string, string, string) error + NetworkClear(protocol string, comment string, table string) error + ContainerClear(protocol string, comment string, table string) error // Proxy - ProxySetupNAT() + ProxySetupNAT(ipv string, IPAddr string, comment string, connType, address, port string, cPort string) error // NIC bridged - BridgeRemoveFilters(deviceConfig.Device) error - BridgeSetFilters(deviceConfig.Device) error + BridgeRemoveFilters(m deviceConfig.Device, IPv4 net.IP, IPv6 net.IP) error + BridgeSetFilters(m deviceConfig.Device) error // Network - NetworkSetup(map[string]string) error + NetworkSetup(oldConfig map[string]string) error } diff --git a/lxd/firewall/xtables.go b/lxd/firewall/xtables.go index db5fb9f786..b19cf3eeba 100644 --- a/lxd/firewall/xtables.go +++ b/lxd/firewall/xtables.go @@ -86,12 +86,12 @@ func (xt *XTables) BridgeRemoveFilters(m deviceConfig.Device, IPv4 net.IP, IPv6 return nil } -func (xt *XTables) BridgeSetFilters(deviceConfig.Device) error { +func (xt *XTables) BridgeSetFilters(m deviceConfig.Device) error { return nil } // Network -func (xt *XTables) NetworkSetup(map[string]string) error { +func (xt *XTables) NetworkSetup(oldConfig map[string]string) error { return nil } From 27d1349307b1d6bbd02095343f2f089ecef4a955 Mon Sep 17 00:00:00 2001 From: Louise Montalvo <louanmonta...@gmail.com> Date: Mon, 4 Nov 2019 16:34:43 -0600 Subject: [PATCH 11/11] Implement BridgeSetFilters for XTables --- lxd/firewall/interfaces.go | 5 +-- lxd/firewall/xtables.go | 70 +++++++++++++++++++++++++++++++++++++- 2 files changed, 72 insertions(+), 3 deletions(-) diff --git a/lxd/firewall/interfaces.go b/lxd/firewall/interfaces.go index 7d95f8ccec..222cf0213a 100644 --- a/lxd/firewall/interfaces.go +++ b/lxd/firewall/interfaces.go @@ -49,16 +49,17 @@ type Firewall interface { // NOTE: xtables will need to include shared // NOTE: nicBridged may need generate/filter functions for nft - // Lower-level clear functions + // Lower-level functions NetworkClear(protocol string, comment string, table string) error ContainerClear(protocol string, comment string, table string) error + VerifyIPv6Module() error // Proxy ProxySetupNAT(ipv string, IPAddr string, comment string, connType, address, port string, cPort string) error // NIC bridged BridgeRemoveFilters(m deviceConfig.Device, IPv4 net.IP, IPv6 net.IP) error - BridgeSetFilters(m deviceConfig.Device) error + BridgeSetFilters(m deviceConfig.Device, config map[string]string, IPv4 net.IP, IPv6 net.IP, name string) error // Network NetworkSetup(oldConfig map[string]string) error diff --git a/lxd/firewall/xtables.go b/lxd/firewall/xtables.go index b19cf3eeba..aa03827c74 100644 --- a/lxd/firewall/xtables.go +++ b/lxd/firewall/xtables.go @@ -4,8 +4,10 @@ import ( "fmt" "net" "strings" + "encoding/hex" "github.com/lxc/lxd/lxd/iptables" + "github.com/lxc/lxd/lxd/device" deviceConfig "github.com/lxc/lxd/lxd/device/config" "github.com/lxc/lxd/shared" ) @@ -24,6 +26,20 @@ func (xt *XTables) NetworkClear(protocol string, comment string, table string) e func (xt *XTables) ContainerClear(protocol string, comment string, table string) error { return iptables.ContainerClear(protocol, comment, table) } +func (xt *XTables) VerifyIPv6Module() error { + // Check br_netfilter is loaded and enabled for IPv6. + sysctlPath := "bridge/bridge-nf-call-ip6tables" + sysctlVal, err := device.NetworkSysctlGet(sysctlPath) + if err != nil { + return fmt.Errorf("Error reading net sysctl %s: %v", sysctlPath, err) + } + + if sysctlVal != "1\n" { + return fmt.Errorf("security.ipv6_filtering requires br_netfilter and sysctl net.bridge.bridge-nf-call-ip6tables=1") + } + + return nil +} // Proxy func (xt *XTables) ProxySetupNAT(ipv string, IPAddr string, comment string, connType, address, port string, cPort string) error { @@ -86,7 +102,27 @@ func (xt *XTables) BridgeRemoveFilters(m deviceConfig.Device, IPv4 net.IP, IPv6 return nil } -func (xt *XTables) BridgeSetFilters(m deviceConfig.Device) error { +func (xt *XTables) BridgeSetFilters(m deviceConfig.Device, config map[string]string, IPv4 net.IP, IPv6 net.IP, name string) error { + rules := generateFilterEbtablesRules(config, IPv4, IPv6) + for _, rule := range rules { + _, err := shared.RunCommand(rule[0], append([]string{"--concurrent"}, rule[1:]...)...) + if err != nil { + return err + } + } + + rules, err := generateFilterIptablesRules(config, IPv6) + if err != nil { + return err + } + + for _, rule := range rules { + err = iptables.ContainerPrepend(rule[0], fmt.Sprintf("%s - %s_filtering", name, rule[0]), "filter", rule[1], rule[2:]...) + if err != nil { + return err + } + } + return nil } @@ -133,6 +169,38 @@ func generateFilterEbtablesRules(m deviceConfig.Device, IPv4 net.IP, IPv6 net.IP return rules } +// generateFilterIptablesRules returns a customised set of iptables filter rules based on the device. +func generateFilterIptablesRules(m deviceConfig.Device, IPv6 net.IP) (rules [][]string, err error) { + mac, err := net.ParseMAC(m["hwaddr"]) + if err != nil { + return + } + + macHex := hex.EncodeToString(mac) + + // These rules below are implemented using ip6tables because the functionality to inspect + // the contents of an ICMPv6 packet does not exist in ebtables (unlike for IPv4 ARP). + // Additionally, ip6tables doesn't really provide a nice way to do what we need here, so we + // have resorted to doing a raw hex comparison of the packet contents at fixed positions. + // If these rules are not added then it is possible to hijack traffic for another IP that is + // not assigned to the instance by sending a specially crafted gratuitous NDP packet with + // correct source address and MAC at the IP & ethernet layers, but a fraudulent IP or MAC + // inside the ICMPv6 NDP packet. + if shared.IsTrue(m["security.ipv6_filtering"]) && IPv6 != nil { + ipv6Hex := hex.EncodeToString(IPv6) + + rules = append(rules, + // Prevent Neighbor Advertisement IP spoofing (prevents the instance redirecting traffic for IPs that are not its own). + []string{"ipv6", "INPUT", "-i", m["parent"], "-p", "ipv6-icmp", "-m", "physdev", "--physdev-in", m["host_name"], "-m", "icmp6", "--icmpv6-type", "136", "-m", "string", "!", "--hex-string", fmt.Sprintf("|%s|", ipv6Hex), "--algo", "bm", "--from", "48", "--to", "64", "-j", "DROP"}, + []string{"ipv6", "FORWARD", "-i", m["parent"], "-p", "ipv6-icmp", "-m", "physdev", "--physdev-in", m["host_name"], "-m", "icmp6", "--icmpv6-type", "136", "-m", "string", "!", "--hex-string", fmt.Sprintf("|%s|", ipv6Hex), "--algo", "bm", "--from", "48", "--to", "64", "-j", "DROP"}, + // Prevent Neighbor Advertisement MAC spoofing (prevents the instance poisoning the NDP cache of its neighbours with a MAC address that isn't its own). + []string{"ipv6", "INPUT", "-i", m["parent"], "-p", "ipv6-icmp", "-m", "physdev", "--physdev-in", m["host_name"], "-m", "icmp6", "--icmpv6-type", "136", "-m", "string", "!", "--hex-string", fmt.Sprintf("|%s|", macHex), "--algo", "bm", "--from", "66", "--to", "72", "-j", "DROP"}, + []string{"ipv6", "FORWARD", "-i", m["parent"], "-p", "ipv6-icmp", "-m", "physdev", "--physdev-in", m["host_name"], "-m", "icmp6", "--icmpv6-type", "136", "-m", "string", "!", "--hex-string", fmt.Sprintf("|%s|", macHex), "--algo", "bm", "--from", "66", "--to", "72", "-j", "DROP"}, + ) + } + + return +} // matchEbtablesRule compares an active rule to a supplied match rule to see if they match. // If deleteMode is true then the "-A" flag in the active rule will be modified to "-D" and will
_______________________________________________ lxc-devel mailing list lxc-devel@lists.linuxcontainers.org http://lists.linuxcontainers.org/listinfo/lxc-devel