The following pull request was submitted through Github. It can be accessed and reviewed at: https://github.com/lxc/lxd/pull/7924
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) ===
From 8ca190120f9461048bb38537a7ba1f1883e11a41 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@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.parr...@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.parr...@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.parr...@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.parr...@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.parr...@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.parr...@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.parr...@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.parr...@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.parr...@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.parr...@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.parr...@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.parr...@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.parr...@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.parr...@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.parr...@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.parr...@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.parr...@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.parr...@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.parr...@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 {
_______________________________________________ lxc-devel mailing list lxc-devel@lists.linuxcontainers.org http://lists.linuxcontainers.org/listinfo/lxc-devel