[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