Merge pull request #3612 from stgraber/main
Some checks are pending
Build / Build (${{ matrix.architecture }}) (amd64) (push) Waiting to run
Build / Build (${{ matrix.architecture }}) (arm64) (push) Waiting to run
Tests / Code (oldstable) (push) Waiting to run
Tests / Code (stable) (push) Waiting to run
Tests / Code (tip) (push) Waiting to run
Tests / System (btrfs, stable, ubuntu-24.04, cluster) (push) Waiting to run
Tests / System (btrfs, stable, ubuntu-24.04, standalone_storage) (push) Waiting to run
Tests / System (ceph, stable, ubuntu-24.04, cluster) (push) Waiting to run
Tests / System (ceph, stable, ubuntu-24.04, standalone_storage) (push) Waiting to run
Tests / System (dir, oldstable, ubuntu-24.04, cluster) (push) Waiting to run
Tests / System (dir, oldstable, ubuntu-24.04, standalone_container) (push) Waiting to run
Tests / System (dir, oldstable, ubuntu-24.04, standalone_core) (push) Waiting to run
Tests / System (dir, oldstable, ubuntu-24.04, standalone_network) (push) Waiting to run
Tests / System (dir, oldstable, ubuntu-24.04, standalone_storage) (push) Waiting to run
Tests / System (dir, oldstable, ubuntu-24.04-arm, cluster) (push) Waiting to run
Tests / System (dir, oldstable, ubuntu-24.04-arm, standalone_container) (push) Waiting to run
Tests / System (dir, oldstable, ubuntu-24.04-arm, standalone_core) (push) Waiting to run
Tests / System (dir, oldstable, ubuntu-24.04-arm, standalone_network) (push) Waiting to run
Tests / System (dir, oldstable, ubuntu-24.04-arm, standalone_storage) (push) Waiting to run
Tests / System (dir, stable, ubuntu-24.04, cluster) (push) Waiting to run
Tests / System (dir, stable, ubuntu-24.04, standalone_container) (push) Waiting to run
Tests / System (dir, stable, ubuntu-24.04, standalone_core) (push) Waiting to run
Tests / System (dir, stable, ubuntu-24.04, standalone_network) (push) Waiting to run
Tests / System (dir, stable, ubuntu-24.04, standalone_storage) (push) Waiting to run
Tests / System (dir, stable, ubuntu-24.04-arm, cluster) (push) Waiting to run
Tests / System (dir, stable, ubuntu-24.04-arm, standalone_container) (push) Waiting to run
Tests / System (dir, stable, ubuntu-24.04-arm, standalone_core) (push) Waiting to run
Tests / System (dir, stable, ubuntu-24.04-arm, standalone_network) (push) Waiting to run
Tests / System (dir, stable, ubuntu-24.04-arm, standalone_storage) (push) Waiting to run
Tests / System (dir, tip, ubuntu-24.04, cluster) (push) Waiting to run
Tests / System (dir, tip, ubuntu-24.04, standalone_container) (push) Waiting to run
Tests / System (dir, tip, ubuntu-24.04, standalone_core) (push) Waiting to run
Tests / System (dir, tip, ubuntu-24.04, standalone_network) (push) Waiting to run
Tests / System (dir, tip, ubuntu-24.04, standalone_storage) (push) Waiting to run
Tests / System (dir, tip, ubuntu-24.04-arm, cluster) (push) Waiting to run
Tests / System (dir, tip, ubuntu-24.04-arm, standalone_container) (push) Waiting to run
Tests / System (dir, tip, ubuntu-24.04-arm, standalone_core) (push) Waiting to run
Tests / System (dir, tip, ubuntu-24.04-arm, standalone_network) (push) Waiting to run
Tests / System (dir, tip, ubuntu-24.04-arm, standalone_storage) (push) Waiting to run
Tests / System (linstor, stable, ubuntu-24.04, cluster) (push) Waiting to run
Tests / System (linstor, stable, ubuntu-24.04, standalone_storage) (push) Waiting to run
Tests / System (lvm, stable, ubuntu-24.04, cluster) (push) Waiting to run
Tests / System (lvm, stable, ubuntu-24.04, standalone_storage) (push) Waiting to run
Tests / System (random, stable, ubuntu-24.04, cluster) (push) Waiting to run
Tests / System (random, stable, ubuntu-24.04, standalone_storage) (push) Waiting to run
Tests / System (zfs, stable, ubuntu-24.04, cluster) (push) Waiting to run
Tests / System (zfs, stable, ubuntu-24.04, standalone_storage) (push) Waiting to run
Tests / Client (oldstable, macos-latest) (push) Waiting to run
Tests / Client (oldstable, ubuntu-latest) (push) Waiting to run
Tests / Client (oldstable, windows-latest) (push) Waiting to run
Tests / Client (stable, macos-latest) (push) Waiting to run
Tests / Client (stable, ubuntu-latest) (push) Waiting to run
Tests / Client (stable, windows-latest) (push) Waiting to run
Tests / Documentation (push) Waiting to run

Implement security.nesting for virtual machines
This commit is contained in:
Serge Hallyn 2026-07-08 19:14:55 -05:00 committed by GitHub
commit bc9cc1586d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 69 additions and 20 deletions

View File

@ -1677,6 +1677,15 @@ func projectValidateConfig(s *state.State, config map[string]string) error {
// shortdesc: Whether to prevent using low-level VM options
"restricted.virtual-machines.lowlevel": isEitherAllowOrBlock,
// gendoc:generate(entity=project, group=restricted, key=restricted.virtual-machines.nesting)
// Possible values are `allow` or `block`.
// When set to `block`, {config:option}`instance-security:security.nesting` must be set to `false` on all virtual machines, turning off nested virtualization.
// ---
// type: string
// defaultdesc: `allow`
// shortdesc: Whether to prevent using nested virtualization
"restricted.virtual-machines.nesting": isEitherAllowOrBlock,
// gendoc:generate(entity=project, group=restricted, key=restricted.devices.unix-char)
// Possible values are `allow` or `block`.
// ---

View File

@ -3232,3 +3232,10 @@ and `physical` networks as an alternative to `bgp.peers.NAME.address`.
When set, the BGP session is established over the given interface using
BGP unnumbered (the peer's IPv6 link-local address is discovered
automatically and IPv4 routes are exchanged using extended next-hop).
## `projects_restricted_virtual_machines_nesting`
This adds a new `restricted.virtual-machines.nesting` project configuration key.
When set to `block`, all virtual machines in the project must have
`security.nesting` set to `false`, turning off nested virtualization.

View File

@ -2686,12 +2686,12 @@ If specified, the idmap used for this instance is unique among instances that ha
```
```{config:option} security.nesting instance-security
:condition: "container"
:defaultdesc: "`false`"
:liveupdate: "yes"
:shortdesc: "Whether to support running Incus (nested) inside the instance"
:defaultdesc: "`false` (containers), `true` (VMs)"
:liveupdate: "yes (containers only)"
:shortdesc: "Whether to allow nesting inside of the instance"
:type: "bool"
For containers, this controls whether Incus (nested) can be run inside of the instance.
For virtual machines, setting this to `false` disables nested virtualization (turns off the `svm` and `vmx` CPU flags).
```
```{config:option} security.privileged instance-security
@ -4905,6 +4905,14 @@ Possible values are `allow` or `block`.
When set to `allow`, low-level VM options like {config:option}`instance-raw:raw.qemu`, `volatile.*`, etc. can be used.
```
```{config:option} restricted.virtual-machines.nesting project-restricted
:defaultdesc: "`allow`"
:shortdesc: "Whether to prevent using nested virtualization"
:type: "string"
Possible values are `allow` or `block`.
When set to `block`, {config:option}`instance-security:security.nesting` must be set to `false` on all virtual machines, turning off nested virtualization.
```
<!-- config group project-restricted end -->
<!-- config group project-specific start -->
```{config:option} backups.compression_algorithm project-specific

View File

@ -310,6 +310,16 @@ var InstanceConfigKeysAny = map[string]func(value string) error{
// shortdesc: Whether `/dev/incus` is present in the instance
"security.guestapi": validate.Optional(validate.IsBool),
// gendoc:generate(entity=instance, group=security, key=security.nesting)
// For containers, this controls whether Incus (nested) can be run inside of the instance.
// For virtual machines, setting this to `false` disables nested virtualization (turns off the `svm` and `vmx` CPU flags).
// ---
// type: bool
// defaultdesc: `false` (containers), `true` (VMs)
// liveupdate: yes (containers only)
// shortdesc: Whether to allow nesting inside of the instance
"security.nesting": validate.Optional(validate.IsBool),
// gendoc:generate(entity=instance, group=security, key=security.protection.delete)
//
// ---
@ -935,16 +945,6 @@ var InstanceConfigKeysContainer = map[string]func(value string) error{
// shortdesc: The size of the idmap to use
"security.idmap.size": validate.Optional(validate.IsUint32),
// gendoc:generate(entity=instance, group=security, key=security.nesting)
//
// ---
// type: bool
// defaultdesc: `false`
// liveupdate: yes
// condition: container
// shortdesc: Whether to support running Incus (nested) inside the instance
"security.nesting": validate.Optional(validate.IsBool),
// gendoc:generate(entity=instance, group=security, key=security.privileged)
//
// ---

View File

@ -282,6 +282,11 @@ func (d *qemu) cpuType(bs *qemuBootState) (string, error) {
}
}
// Turn off nested virtualization when security.nesting is disabled.
if d.architecture == osarch.ARCH_64BIT_INTEL_X86 && util.IsFalse(d.expandedConfig["security.nesting"]) {
cpuExtensions = append(cpuExtensions, "-svm", "-vmx")
}
// Get the feature flags.
info := DriverStatuses()[instancetype.VM].Info
_, nested := info.Features["nested"]

View File

@ -2984,11 +2984,10 @@
},
{
"security.nesting": {
"condition": "container",
"defaultdesc": "`false`",
"liveupdate": "yes",
"longdesc": "",
"shortdesc": "Whether to support running Incus (nested) inside the instance",
"defaultdesc": "`false` (containers), `true` (VMs)",
"liveupdate": "yes (containers only)",
"longdesc": "For containers, this controls whether Incus (nested) can be run inside of the instance.\nFor virtual machines, setting this to `false` disables nested virtualization (turns off the `svm` and `vmx` CPU flags).",
"shortdesc": "Whether to allow nesting inside of the instance",
"type": "bool"
}
},
@ -5497,6 +5496,14 @@
"shortdesc": "Whether to prevent using low-level VM options",
"type": "string"
}
},
{
"restricted.virtual-machines.nesting": {
"defaultdesc": "`allow`",
"longdesc": "Possible values are `allow` or `block`.\nWhen set to `block`, {config:option}`instance-security:security.nesting` must be set to `false` on all virtual machines, turning off nested virtualization.",
"shortdesc": "Whether to prevent using nested virtualization",
"type": "string"
}
}
]
},

View File

@ -584,6 +584,7 @@ func checkRestrictions(project api.Project, instances []api.Instance, profiles [
allowContainerLowLevel := false
allowVMLowLevel := false
blockVMNesting := false
var allowedIDMapHostUIDs, allowedIDMapHostGIDs []idmap.Entry
for i := range allRestrictions {
@ -645,6 +646,11 @@ func checkRestrictions(project api.Project, instances []api.Instance, profiles [
allowVMLowLevel = true
}
case "restricted.virtual-machines.nesting":
if restrictionValue == "block" {
blockVMNesting = true
}
case "restricted.devices.unix-char":
devicesChecks["unix-char"] = func(device map[string]string) error {
if restrictionValue != "allow" {
@ -816,6 +822,11 @@ func checkRestrictions(project api.Project, instances []api.Instance, profiles [
isContainerOrProfile := instType == instancetype.Container || instType == instancetype.Any
isVMOrProfile := instType == instancetype.VM || instType == instancetype.Any
// VM nesting is on by default, so when blocked, require it to be explicitly disabled.
if blockVMNesting && instType == instancetype.VM && !util.IsFalse(config["security.nesting"]) {
return fmt.Errorf(`Virtual machine nesting is forbidden on %s %q of project %q ("security.nesting" must be set to "false")`, entityTypeLabel, entityName, project.Name)
}
for key, value := range config {
if ((isContainerOrProfile && !allowContainerLowLevel) || (isVMOrProfile && !allowVMLowLevel)) && key == "raw.idmap" {
// If the low-level raw.idmap is used check whether the raw.idmap host IDs
@ -954,6 +965,7 @@ var allRestrictions = map[string]string{
"restricted.containers.lowlevel": "block",
"restricted.containers.privilege": "unprivileged",
"restricted.virtual-machines.lowlevel": "block",
"restricted.virtual-machines.nesting": "allow",
"restricted.devices.unix-char": "block",
"restricted.devices.unix-block": "block",
"restricted.devices.unix-hotplug": "block",

View File

@ -551,6 +551,7 @@ var APIExtensions = []string{
"infiniband_sriov_guid",
"instance_selinux",
"network_bgp_peer_interface",
"projects_restricted_virtual_machines_nesting",
}
// APIExtensionsCount returns the number of available API extensions.