[lxc-devel] [lxd/master] Network: Adds project restricted.networks.uplinks setting and auto sets OVN network when only 1 uplink available

tomponline on Github lxc-bot at linuxcontainers.org
Wed Sep 23 10:20:53 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/20200923/f5075502/attachment.bin>
-------------- next part --------------
From 8ca190120f9461048bb38537a7ba1f1883e11a41 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Wed, 23 Sep 2020 09:20:04 +0100
Subject: [PATCH 01/10] lxd/network: Removes client side default network type
 when creating network

Let server decide the appropriate network type to use when not specified by user.

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

diff --git a/lxc/network.go b/lxc/network.go
index 17980f40c2..41c2f6e549 100644
--- a/lxc/network.go
+++ b/lxc/network.go
@@ -255,7 +255,7 @@ func (c *cmdNetworkCreate) Command() *cobra.Command {
 	cmd.Long = cli.FormatSection(i18n.G("Description"), i18n.G(`Create new networks`))
 
 	cmd.Flags().StringVar(&c.network.flagTarget, "target", "", i18n.G("Cluster member name")+"``")
-	cmd.Flags().StringVarP(&c.network.flagType, "type", "t", "bridge", i18n.G("Network type"))
+	cmd.Flags().StringVarP(&c.network.flagType, "type", "t", "", i18n.G("Network type"))
 
 	cmd.RunE = c.Run
 

From c896c22ae8c97cfb93ebf4112b6e6ecaa291bd48 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Wed, 23 Sep 2020 09:20:43 +0100
Subject: [PATCH 02/10] lxd/networks: Default to ovn network type when creating
 non-default network project

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

diff --git a/lxd/networks.go b/lxd/networks.go
index 32a58f7043..3e399350c0 100644
--- a/lxd/networks.go
+++ b/lxd/networks.go
@@ -144,7 +144,11 @@ func networksPost(d *Daemon, r *http.Request) response.Response {
 	}
 
 	if req.Type == "" {
-		req.Type = "bridge"
+		if projectName != project.Default {
+			req.Type = "ovn" // Only OVN networks are allowed inside network enabled projects.
+		} else {
+			req.Type = "bridge" // Default to bridge for non-network enabled projects.
+		}
 	}
 
 	if req.Config == nil {

From e115d907ebd313a280b9fd80f1688a950d679d41 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Tue, 22 Sep 2020 14:42:36 +0100
Subject: [PATCH 03/10] api: Adds projects_networks_restricted_uplinks
 extension

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 doc/api-extensions.md | 4 ++++
 shared/version/api.go | 1 +
 2 files changed, 5 insertions(+)

diff --git a/doc/api-extensions.md b/doc/api-extensions.md
index 615a8c047f..f5377108b8 100644
--- a/doc/api-extensions.md
+++ b/doc/api-extensions.md
@@ -1154,3 +1154,7 @@ Also introduces two new global config keys that apply to all `ovn` networks and
 
 ## projects\_networks
 Adds the `features.networks` config key to projects and the ability for a project to hold networks.
+
+## projects\_networks\_restricted\_uplinks
+Adds the `restricted.networks.uplinks` project config key to indicate (as a comma delimited list) which networks
+the networks created inside the project can use as their uplink parent network.
diff --git a/shared/version/api.go b/shared/version/api.go
index 85da3b0a87..6e2603d3c1 100644
--- a/shared/version/api.go
+++ b/shared/version/api.go
@@ -225,6 +225,7 @@ var APIExtensions = []string{
 	"container_syscall_intercept_bpf_devices",
 	"network_type_ovn",
 	"projects_networks",
+	"projects_networks_restricted_uplinks",
 }
 
 // APIExtensionsCount returns the number of available API extensions.

From 6b195c1fbfc7ca448dd0666128212aaae88c3a55 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Tue, 22 Sep 2020 14:55:22 +0100
Subject: [PATCH 04/10] doc/projects: Adds restricted.networks.uplinks

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 doc/projects.md | 1 +
 1 file changed, 1 insertion(+)

diff --git a/doc/projects.md b/doc/projects.md
index e8c1142e8d..b7fa5d8961 100644
--- a/doc/projects.md
+++ b/doc/projects.md
@@ -40,6 +40,7 @@ restricted.devices.unix-block        | string    | -                     | block
 restricted.devices.unix-char         | string    | -                     | block                     | Prevents use of devices of type "unix-char"
 restricted.devices.unix-hotplug      | string    | -                     | block                     | Prevents use of devices of type "unix-hotplug"
 restricted.devices.usb               | string    | -                     | block                     | Prevents use of devices of type "usb"
+restricted.networks.uplinks          | string    | -                     | block                     | Comma delimited list of network names that can be used as uplinks for networks in this project.
 restricted.virtual-machines.lowlevel | string    | -                     | block                     | Prevents use of low-level virtual-machine options like raw.qemu, volatile, etc.
 
 Those keys can be set using the lxc tool with:

From 7b6f82e7355b3da0f02a73c75698636c0ee7ec73 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Tue, 22 Sep 2020 15:08:19 +0100
Subject: [PATCH 05/10] lxd/networks: Updates doNetworkUpdate to use n.Validate
 so that project is available to validator

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

diff --git a/lxd/networks.go b/lxd/networks.go
index 3e399350c0..ae67d059a8 100644
--- a/lxd/networks.go
+++ b/lxd/networks.go
@@ -816,7 +816,7 @@ func doNetworkUpdate(d *Daemon, projectName string, name string, req api.Network
 	}
 
 	// Validate the merged configuration.
-	err = network.Validate(name, n.Type(), req.Config)
+	err = n.Validate(req.Config)
 	if err != nil {
 		return response.BadRequest(err)
 	}

From 172478a726796e33efea75c89ae32dd7cbde70fa Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Tue, 22 Sep 2020 15:07:33 +0100
Subject: [PATCH 06/10] lxd/network/network/load: Removes unused Validate

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/network/network_load.go | 21 ---------------------
 1 file changed, 21 deletions(-)

diff --git a/lxd/network/network_load.go b/lxd/network/network_load.go
index 862ba2831b..ed098ef3d9 100644
--- a/lxd/network/network_load.go
+++ b/lxd/network/network_load.go
@@ -1,9 +1,6 @@
 package network
 
 import (
-	"github.com/pkg/errors"
-
-	"github.com/lxc/lxd/lxd/project"
 	"github.com/lxc/lxd/lxd/state"
 )
 
@@ -43,21 +40,3 @@ func LoadByName(s *state.State, project string, name string) (Network, error) {
 
 	return n, nil
 }
-
-// Validate validates the supplied network name and configuration for the specified network type.
-func Validate(name string, netType string, config map[string]string) error {
-	driverFunc, ok := drivers[netType]
-	if !ok {
-		return ErrUnknownDriver
-	}
-
-	n := driverFunc()
-	n.init(nil, 0, project.Default, name, netType, "", config, "Unknown")
-
-	err := n.ValidateName(name)
-	if err != nil {
-		return errors.Wrapf(err, "Network name invalid")
-	}
-
-	return n.Validate(config)
-}

From df9d197962f5d09b540d36966c51fb745e2f1152 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Tue, 22 Sep 2020 15:07:46 +0100
Subject: [PATCH 07/10] lxd/network/network/load: Renames project arg to
 projectName for clarity

Improves comments.

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

diff --git a/lxd/network/network_load.go b/lxd/network/network_load.go
index ed098ef3d9..cf59074663 100644
--- a/lxd/network/network_load.go
+++ b/lxd/network/network_load.go
@@ -23,9 +23,9 @@ func LoadByType(driverType string) (Type, error) {
 	return n, nil
 }
 
-// LoadByName loads an instantiated network from the database by name.
-func LoadByName(s *state.State, project string, name string) (Network, error) {
-	id, netInfo, err := s.Cluster.GetNetworkInAnyState(project, name)
+// LoadByName loads an instantiated network from the database by project and name.
+func LoadByName(s *state.State, projectName string, name string) (Network, error) {
+	id, netInfo, err := s.Cluster.GetNetworkInAnyState(projectName, name)
 	if err != nil {
 		return nil, err
 	}
@@ -36,7 +36,7 @@ func LoadByName(s *state.State, project string, name string) (Network, error) {
 	}
 
 	n := driverFunc()
-	n.init(s, id, project, name, netInfo.Type, netInfo.Description, netInfo.Config, netInfo.Status)
+	n.init(s, id, projectName, name, netInfo.Type, netInfo.Description, netInfo.Config, netInfo.Status)
 
 	return n, nil
 }

From f0b69275081b4dd8384bdc38cece224c32327f6f Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Tue, 22 Sep 2020 15:08:45 +0100
Subject: [PATCH 08/10] lxd/api/project: Adds restricted.networks.uplinks to
 validation

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

diff --git a/lxd/api_project.go b/lxd/api_project.go
index cb9d79ca58..770e9abf65 100644
--- a/lxd/api_project.go
+++ b/lxd/api_project.go
@@ -555,6 +555,7 @@ var projectConfigKeys = map[string]func(value string) error{
 	"restricted.devices.usb":               isEitherAllowOrBlock,
 	"restricted.devices.nic":               isEitherAllowOrBlockOrManaged,
 	"restricted.devices.disk":              isEitherAllowOrBlockOrManaged,
+	"restricted.networks.uplinks":          validate.IsAny,
 }
 
 func projectValidateConfig(config map[string]string) error {

From 78b66cd9e3e336797afca1479459c352811b081f Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Wed, 23 Sep 2020 11:17:48 +0100
Subject: [PATCH 09/10] lxd/network/driver/ovn: Adds allowedUplinkNetworks
 function

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

diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go
index 5d8f142c9b..fd43146ca1 100644
--- a/lxd/network/driver_ovn.go
+++ b/lxd/network/driver_ovn.go
@@ -825,6 +825,67 @@ func (n *ovn) Create(clientType cluster.ClientType) error {
 	return nil
 }
 
+// allowedUplinkNetworks returns a list of allowed networks to use as uplinks based on project restrictions.
+func (n *ovn) allowedUplinkNetworks() ([]string, error) {
+	// Uplink networks are always from the default project.
+	networks, err := n.state.Cluster.GetNetworks(project.Default)
+	if err != nil {
+		return nil, errors.Wrapf(err, "Failed getting uplink networks")
+	}
+
+	// Remove ourselves from the networks list if we are in the default project.
+	if n.project == project.Default {
+		allNets := networks
+		networks = make([]string, 0, len(allNets)-1)
+		for _, network := range allNets {
+			if network == n.name {
+				continue
+			}
+
+			networks = append(networks, network)
+		}
+	}
+
+	// Load the project to get uplink network restrictions.
+	var project *api.Project
+	err = n.state.Cluster.Transaction(func(tx *db.ClusterTx) error {
+		project, err = tx.GetProject(n.project)
+		if err != nil {
+			return err
+		}
+
+		return nil
+	})
+	if err != nil {
+		return nil, errors.Wrapf(err, "Failed to load restrictions for project %q", n.project)
+	}
+
+	// If project is not restricted, return full network list.
+	if !shared.IsTrue(project.Config["restricted"]) {
+		return networks, nil
+	}
+
+	allowedNetworks := []string{}
+
+	// There are no allowed networks if restricted.networks.uplinks is not set.
+	if project.Config["restricted.networks.uplinks"] == "" {
+		return allowedNetworks, nil
+	}
+
+	// Parse the allowed uplinks and return any that are present in the actual defined networks.
+	allowedRestrictedUplinks := strings.Split(project.Config["restricted.networks.uplinks"], ",")
+
+	for _, allowedRestrictedUplink := range allowedRestrictedUplinks {
+		allowedRestrictedUplink = strings.TrimSpace(allowedRestrictedUplink)
+
+		if shared.StringInSlice(allowedRestrictedUplink, networks) {
+			allowedNetworks = append(allowedNetworks, allowedRestrictedUplink)
+		}
+	}
+
+	return allowedNetworks, nil
+}
+
 func (n *ovn) setup(update bool) error {
 	// If we are in mock mode, just no-op.
 	if n.state.OS.MockMode {

From 99bc3f4f5a31e201c884235a46a7525d5e23cc9b Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Wed, 23 Sep 2020 11:18:08 +0100
Subject: [PATCH 10/10] lxd/network/driver/ovn: Enforce project
 restricted.networks.uplinks setting

And auto set the `network` property when not specified and when only one possible uplink network available.

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/network/driver_ovn.go | 37 +++++++++++++++++++++++++++++++++++--
 1 file changed, 35 insertions(+), 2 deletions(-)

diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go
index fd43146ca1..40a3cdb524 100644
--- a/lxd/network/driver_ovn.go
+++ b/lxd/network/driver_ovn.go
@@ -81,7 +81,7 @@ func (n *ovn) Info() Info {
 // Validate network config.
 func (n *ovn) Validate(config map[string]string) error {
 	rules := map[string]func(value string) error{
-		"network":       validate.IsNotEmpty,
+		"network":       validate.IsAny, // Is validated during setup.
 		"bridge.hwaddr": validate.Optional(validate.IsNetworkMAC),
 		"bridge.mtu":    validate.Optional(validate.IsNetworkMTU),
 		"ipv4.address": func(value string) error {
@@ -905,6 +905,31 @@ func (n *ovn) setup(update bool) error {
 	var routerExtPortIPv4, routerIntPortIPv4, routerExtPortIPv6, routerIntPortIPv6 net.IP
 	var routerExtPortIPv4Net, routerIntPortIPv4Net, routerExtPortIPv6Net, routerIntPortIPv6Net *net.IPNet
 
+	// Record updated config so we can store back into DB and n.config variable.
+	updatedConfig := make(map[string]string)
+
+	// Check project restrictions.
+	allowedUplinkNetworks, err := n.allowedUplinkNetworks()
+	if err != nil {
+		return err
+	}
+
+	if n.config["network"] != "" {
+		if !shared.StringInSlice(n.config["network"], allowedUplinkNetworks) {
+			return fmt.Errorf(`Option "network" value %q is not one of the allowed uplink networks in project`, n.config["network"])
+		}
+	} else {
+		allowedNetworkCount := len(allowedUplinkNetworks)
+		if allowedNetworkCount == 0 {
+			return fmt.Errorf(`No allowed uplink networks in project`)
+		} else if allowedNetworkCount == 1 {
+			// If there is only one allowed uplink network then use it if not specified by user.
+			updatedConfig["network"] = allowedUplinkNetworks[0]
+		} else {
+			return fmt.Errorf(`Option "network" is required`)
+		}
+	}
+
 	// Get bridge MTU to use.
 	bridgeMTU := n.getBridgeMTU()
 	if bridgeMTU == 0 {
@@ -915,7 +940,15 @@ func (n *ovn) setup(update bool) error {
 		}
 
 		// Save to config so the value can be read by instances connecting to network.
-		n.config["bridge.mtu"] = fmt.Sprintf("%d", bridgeMTU)
+		updatedConfig["bridge.mtu"] = fmt.Sprintf("%d", bridgeMTU)
+	}
+
+	// Apply any config dynamically generated to the current config and store back to DB in single transaction.
+	if len(updatedConfig) > 0 {
+		for k, v := range updatedConfig {
+			n.config[k] = v
+		}
+
 		err := n.state.Cluster.Transaction(func(tx *db.ClusterTx) error {
 			err = tx.UpdateNetwork(n.id, n.description, n.config)
 			if err != nil {


More information about the lxc-devel mailing list