The following pull request was submitted through Github.
It can be accessed and reviewed at: https://github.com/lxc/lxd/pull/2429

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 option for bridged nic devices turns on MAC filtering on the host,
preventing the container from sending traffic for a MAC other than the
one known by LXD.

Signed-off-by: Stéphane Graber <stgra...@ubuntu.com>
From e24bf440a8c8b991300961bca7de94525ad4fbd7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgra...@ubuntu.com>
Date: Mon, 26 Sep 2016 17:41:25 -0400
Subject: [PATCH] Introduce security.mac_filtering
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

This option for bridged nic devices turns on MAC filtering on the host,
preventing the container from sending traffic for a MAC other than the
one known by LXD.

Signed-off-by: Stéphane Graber <stgra...@ubuntu.com>
---
 doc/api-extensions.md |   1 +
 doc/configuration.md  |  27 +++++-----
 lxd/container.go      |   2 +
 lxd/container_lxc.go  | 136 +++++++++++++++++++++++++++++++++++++++++++++++++-
 4 files changed, 152 insertions(+), 14 deletions(-)

diff --git a/doc/api-extensions.md b/doc/api-extensions.md
index 268bf44..83818a9 100644
--- a/doc/api-extensions.md
+++ b/doc/api-extensions.md
@@ -114,3 +114,4 @@ This includes:
  * DELETE /1.0/networks/<entry> (see rest-api.md for details)
  * ipv4.address property on "nic" type devices (when nictype is "bridged")
  * ipv6.address property on "nic" type devices (when nictype is "bridged")
+ * security.mac\_filtering property on "nic" type devices (when nictype is 
"bridged")
diff --git a/doc/configuration.md b/doc/configuration.md
index 1faa622..ce0036d 100644
--- a/doc/configuration.md
+++ b/doc/configuration.md
@@ -195,19 +195,20 @@ LXD supports different kind of network devices:
 
 Different network interface types have different additional properties, the 
current list is:
 
-Key             | Type      | Default           | Required  | Used by          
             | API extension | Description
-:--             | :--       | :--               | :--       | :--              
             | :--           | :--
-nictype         | string    | -                 | yes       | all              
             | -             | The device type, one of "physical", "bridged", 
"macvlan" or "p2p"
-limits.ingress  | string    | -                 | no        | bridged, p2p     
             | -             | I/O limit in bit/s (supports kbit, Mbit, Gbit 
suffixes)
-limits.egress   | string    | -                 | no        | bridged, p2p     
             | -             | I/O limit in bit/s (supports kbit, Mbit, Gbit 
suffixes)
-limits.max      | string    | -                 | no        | bridged, p2p     
             | -             | Same as modifying both limits.read and 
limits.write
-name            | string    | kernel assigned   | no        | all              
             | -             | The name of the interface inside the container
-host\_name      | string    | randomly assigned | no        | bridged, p2p, 
macvlan         | -             | The name of the interface inside the host
-hwaddr          | string    | randomly assigned | no        | all              
             | -             | The MAC address of the new interface
-mtu             | integer   | parent MTU        | no        | all              
             | -             | The MTU of the new interface
-parent          | string    | -                 | yes       | physical, 
bridged, macvlan    | -             | The name of the host device or bridge
-ipv4.address    | string    | -                 | no        | bridged          
             | network       | An IPv4 address to assign to the container 
through DHCP
-ipv6.address    | string    | -                 | no        | bridged          
             | network       | An IPv6 address to assign to the container 
through DHCP
+Key                     | Type      | Default           | Required  | Used by  
                     | API extension | Description
+:--                     | :--       | :--               | :--       | :--      
                     | :--           | :--
+nictype                 | string    | -                 | yes       | all      
                     | -             | The device type, one of "physical", 
"bridged", "macvlan" or "p2p"
+limits.ingress          | string    | -                 | no        | bridged, 
p2p                  | -             | I/O limit in bit/s (supports kbit, Mbit, 
Gbit suffixes)
+limits.egress           | string    | -                 | no        | bridged, 
p2p                  | -             | I/O limit in bit/s (supports kbit, Mbit, 
Gbit suffixes)
+limits.max              | string    | -                 | no        | bridged, 
p2p                  | -             | Same as modifying both limits.read and 
limits.write
+name                    | string    | kernel assigned   | no        | all      
                     | -             | The name of the interface inside the 
container
+host\_name              | string    | randomly assigned | no        | bridged, 
p2p, macvlan         | -             | The name of the interface inside the host
+hwaddr                  | string    | randomly assigned | no        | all      
                     | -             | The MAC address of the new interface
+mtu                     | integer   | parent MTU        | no        | all      
                     | -             | The MTU of the new interface
+parent                  | string    | -                 | yes       | 
physical, bridged, macvlan    | -             | The name of the host device or 
bridge
+ipv4.address            | string    | -                 | no        | bridged  
                     | network       | An IPv4 address to assign to the 
container through DHCP
+ipv6.address            | string    | -                 | no        | bridged  
                     | network       | An IPv6 address to assign to the 
container through DHCP
+security.mac\_filtering | boolean   | false             | no        | bridged  
                     | network       | Prevent the container from spoofing 
another's MAC address
 
 ### Type: disk
 Disk entries are essentially mountpoints inside the container. They can
diff --git a/lxd/container.go b/lxd/container.go
index 2d61b51..cd6060f 100644
--- a/lxd/container.go
+++ b/lxd/container.go
@@ -106,6 +106,8 @@ func containerValidDeviceConfigKey(t, k string) bool {
                        return true
                case "ipv6.address":
                        return true
+               case "security.mac_filtering":
+                       return true
                default:
                        return false
                }
diff --git a/lxd/container_lxc.go b/lxd/container_lxc.go
index 99282dc..c95fa0a 100644
--- a/lxd/container_lxc.go
+++ b/lxd/container_lxc.go
@@ -873,8 +873,16 @@ func (c *containerLXC) initLXC() error {
                        }
 
                        // Host Virtual NIC name
+                       vethName := ""
                        if m["host_name"] != "" {
-                               err = lxcSetConfigItem(cc, 
"lxc.network.veth.pair", m["host_name"])
+                               vethName = m["host_name"]
+                       } else if shared.IsTrue(m["security.mac_filtering"]) {
+                               // We need a known device name for MAC filtering
+                               vethName = deviceNextVeth()
+                       }
+
+                       if vethName != "" {
+                               err = lxcSetConfigItem(cc, 
"lxc.network.veth.pair", vethName)
                                if err != nil {
                                        return err
                                }
@@ -1177,6 +1185,7 @@ func (c *containerLXC) startCommon() (string, error) {
        // Cleanup any existing leftover devices
        c.removeUnixDevices()
        c.removeDiskDevices()
+       c.removeNetworkFilters()
 
        var usbs []usbDevice
 
@@ -1264,6 +1273,44 @@ func (c *containerLXC) startCommon() (string, error) {
                                        return "", err
                                }
                        }
+               } else if m["type"] == "nic" {
+                       if m["nictype"] == "bridged" && 
shared.IsTrue(m["security.mac_filtering"]) {
+                               m, err = c.fillNetworkDevice(k, m)
+                               if err != nil {
+                                       return "", err
+                               }
+
+                               // Read device name from config
+                               vethName := ""
+                               for i := 0; i < 
len(c.c.ConfigItem("lxc.network")); i++ {
+                                       val := 
c.c.ConfigItem(fmt.Sprintf("lxc.network.%d.hwaddr", i))
+                                       if len(val) == 0 || val[0] != 
m["hwaddr"] {
+                                               continue
+                                       }
+
+                                       val = 
c.c.ConfigItem(fmt.Sprintf("lxc.network.%d.link", i))
+                                       if len(val) == 0 || val[0] != 
m["parent"] {
+                                               continue
+                                       }
+
+                                       val = 
c.c.ConfigItem(fmt.Sprintf("lxc.network.%d.veth.pair", i))
+                                       if len(val) == 0 {
+                                               continue
+                                       }
+
+                                       vethName = val[0]
+                                       break
+                               }
+
+                               if vethName == "" {
+                                       return "", fmt.Errorf("Failed to find 
device name for mac_filtering")
+                               }
+
+                               err = c.createNetworkFilter(vethName, 
m["parent"], m["hwaddr"])
+                               if err != nil {
+                                       return "", err
+                               }
+                       }
                }
        }
 
@@ -1746,6 +1793,12 @@ func (c *containerLXC) OnStop(target string) error {
                        shared.LogError("Unable to remove disk devices", 
log.Ctx{"err": err})
                }
 
+               // Clean all network filters
+               err = c.removeNetworkFilters()
+               if err != nil {
+                       shared.LogError("Unable to remove network filters", 
log.Ctx{"err": err})
+               }
+
                // Reboot the container
                if target == "reboot" {
 
@@ -2055,6 +2108,7 @@ func (c *containerLXC) cleanup() {
        // Unmount any leftovers
        c.removeUnixDevices()
        c.removeDiskDevices()
+       c.removeNetworkFilters()
 
        // Remove the security profiles
        AADeleteProfile(c)
@@ -4303,6 +4357,14 @@ func (c *containerLXC) createNetworkDevice(name string, 
m shared.Device) (string
                return "", fmt.Errorf("Failed to bring up the interface: %s", 
err)
        }
 
+       // Set the filter
+       if m["nictype"] == "bridged" && 
shared.IsTrue(m["security.mac_filtering"]) {
+               err = c.createNetworkFilter(dev, m["parent"], m["hwaddr"])
+               if err != nil {
+                       return "", err
+               }
+       }
+
        return dev, nil
 }
 
@@ -4441,6 +4503,70 @@ func (c *containerLXC) fillNetworkDevice(name string, m 
shared.Device) (shared.D
        return newDevice, nil
 }
 
+func (c *containerLXC) createNetworkFilter(name string, bridge string, hwaddr 
string) error {
+       err := shared.RunCommand("ebtables", "-A", "FORWARD", "-s", "!", 
hwaddr, "-i", name, "-o", bridge, "-j", "DROP")
+       if err != nil {
+               return err
+       }
+
+       err = shared.RunCommand("ebtables", "-A", "INPUT", "-s", "!", hwaddr, 
"-i", name, "-j", "DROP")
+       if err != nil {
+               return err
+       }
+
+       return nil
+}
+
+func (c *containerLXC) removeNetworkFilter(hwaddr string, bridge string) error 
{
+       out, err := exec.Command("ebtables", "-L", "--Lmac2", "--Lx").Output()
+       for _, line := range strings.Split(string(out), "\n") {
+               line = strings.TrimSpace(line)
+               fields := strings.Fields(line)
+
+               if len(fields) == 12 {
+                       match := []string{"ebtables", "-t", "filter", "-A", 
"INPUT", "-s", "!", hwaddr, "-i", fields[9], "-j", "DROP"}
+                       if reflect.DeepEqual(fields, match) {
+                               fields[3] = "-D"
+                               err = shared.RunCommand(fields[0], 
fields[1:]...)
+                               if err != nil {
+                                       return err
+                               }
+                       }
+               } else if len(fields) == 14 {
+                       match := []string{"ebtables", "-t", "filter", "-A", 
"FORWARD", "-s", "!", hwaddr, "-i", fields[9], "-o", bridge, "-j", "DROP"}
+                       if reflect.DeepEqual(fields, match) {
+                               fields[3] = "-D"
+                               err = shared.RunCommand(fields[0], 
fields[1:]...)
+                               if err != nil {
+                                       return err
+                               }
+                       }
+               }
+       }
+
+       return nil
+}
+
+func (c *containerLXC) removeNetworkFilters() error {
+       for k, m := range c.expandedDevices {
+               m, err := c.fillNetworkDevice(k, m)
+               if err != nil {
+                       return err
+               }
+
+               if m["type"] != "nic" || m["nictype"] != "bridged" {
+                       continue
+               }
+
+               err = c.removeNetworkFilter(m["hwaddr"], m["parent"])
+               if err != nil {
+                       return err
+               }
+       }
+
+       return nil
+}
+
 func (c *containerLXC) insertNetworkDevice(name string, m shared.Device) error 
{
        // Load the go-lxc struct
        err := c.initLXC()
@@ -4521,6 +4647,14 @@ func (c *containerLXC) removeNetworkDevice(name string, 
m shared.Device) error {
                deviceRemoveInterface(hostName)
        }
 
+       // Remove any filter
+       if m["nictype"] == "bridged" {
+               err = c.removeNetworkFilter(m["hwaddr"], m["parent"])
+               if err != nil {
+                       return err
+               }
+       }
+
        return nil
 }
 
_______________________________________________
lxc-devel mailing list
lxc-devel@lists.linuxcontainers.org
http://lists.linuxcontainers.org/listinfo/lxc-devel

Reply via email to