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