On 1/14/26 00:35, Erik Huelsmann wrote:
> Hi,
>
> Several months ago, I ran into issue #135 which says that Qemu under
> AppArmor can't access LVM volume disks.
The problem here is, that AppArmor driver in libvirt generates the
profile in qemuSecurityGenLabel() phase. Long story short, starting up a
libvirt domain is broken down into several steps. One of them is
qemuProcessPrepareDomain() where domain XML is filled with (parts of)
live data. In this specific case, disk source is translated (from
storage pool/vol to a filepath). But it's not just that, various other
paths are generated (e.g. socket paths, render nodes for graphics,
etc.).
Then, the next step is qemuProcessPrepareHost() where those files from
previous step are created, their seclabels are (possibly) set. This has
a caveat though.
Initially, libvirt cared about SELinux only (leave traditional uid/gid
perms (we call them DAC) aside for a brief moment). Here. an unique
seclabel is generated in the domain prepare phase, so that any helper
process that's used to create files in host prepare phase can run under
that label (and thus be restricted).
AppArmor does not work this way. So when an aforementioned helper
process wants to run (say /usr/bin/swtpm_setup) it already needs profile
to be defined. As "obvious" workaround, AppArmor generates the profile
in seclabel generation phase. But by that time path are not populated!
IMO, quick and dirty fix would be to generate seclabels at the end of
qemuProcessPrepareDomain() instead of beginning (see below). The only
downside of this is that seclabels won't be available upfront, e.g. if
we want to copy them into a device specific seclabel (we do NOT do that
though).
diff --git i/src/qemu/qemu_process.c w/src/qemu/qemu_process.c
index a53bb40783..00b9a732f3 100644
--- i/src/qemu/qemu_process.c
+++ w/src/qemu/qemu_process.c
@@ -7024,15 +7024,6 @@ qemuProcessPrepareDomain(virQEMUDriver *driver,
return -1;
if (!(flags & VIR_QEMU_PROCESS_START_PRETEND)) {
- /* If you are using a SecurityDriver with dynamic labelling,
- then generate a security label for isolation */
- VIR_DEBUG("Generating domain security label (if required)");
- if (qemuSecurityGenLabel(driver->securityManager, vm->def) < 0) {
- virDomainAuditSecurityLabel(vm, false);
- return -1;
- }
- virDomainAuditSecurityLabel(vm, true);
-
if (qemuProcessPrepareDomainNUMAPlacement(vm) < 0)
return -1;
}
@@ -7154,6 +7145,17 @@ qemuProcessPrepareDomain(virQEMUDriver *driver,
}
}
+ /* Keep this as the very last step because AppArmor already generates
+ * profile at this point. IOW, we need all the paths filled in. */
+ if (!(flags & VIR_QEMU_PROCESS_START_PRETEND)) {
+ VIR_DEBUG("Generating domain security label (if required)");
+ if (qemuSecurityGenLabel(driver->securityManager, vm->def) < 0) {
+ virDomainAuditSecurityLabel(vm, false);
+ return -1;
+ }
+ virDomainAuditSecurityLabel(vm, true);
+ }
+
return 0;
}
Another alternative is to move profile generation into its separate
step. So then we'd have:
qemuProcessPrepareDomain():
1) gen seclabel /* here only SELinux/DAC drivers would do something useful.
For AppArmor it'd be a NOP. */
2) generate all the paths
3) write profile /* only AppArmor would act upon, for SELinux and AppArmor
it'd be nop. */
Let me see if that'd would fix the issue.
Michal