Note: This bug is displayed in read-only format because the product is no longer active in Red Hat Bugzilla.
RHEL Engineering is moving the tracking of its product development work on RHEL 6 through RHEL 9 to Red Hat Jira (issues.redhat.com). If you're a Red Hat customer, please continue to file support cases via the Red Hat customer portal. If you're not, please head to the "RHEL project" in Red Hat Jira and file new tickets here. Individual Bugzilla bugs in the statuses "NEW", "ASSIGNED", and "POST" are being migrated throughout September 2023. Bugs of Red Hat partners with an assigned Engineering Partner Manager (EPM) are migrated in late September as per pre-agreed dates. Bugs against components "kernel", "kernel-rt", and "kpatch" are only migrated if still in "NEW" or "ASSIGNED". If you cannot log in to RH Jira, please consult article #7032570. That failing, please send an e-mail to the RH Jira admins at rh-issues@redhat.com to troubleshoot your issue as a user management inquiry. The email creates a ServiceNow ticket with Red Hat. Individual Bugzilla bugs that are migrated will be moved to status "CLOSED", resolution "MIGRATED", and set with "MigratedToJIRA" in "Keywords". The link to the successor Jira issue will be found under "Links", have a little "two-footprint" icon next to it, and direct you to the "RHEL project" in Red Hat Jira (issue links are of type "https://issues.redhat.com/browse/RHEL-XXXX", where "X" is a digit). This same link will be available in a blue banner at the top of the page informing you that that bug has been migrated.

Bug 1937707

Summary: some API fails with "QEMU denied access" when libvirt uses polkit as access_driver
Product: Red Hat Enterprise Linux 8 Reporter: Juan Orti Alcaine <jortialc>
Component: DocumentationAssignee: Jiri Herrmann <jherrman>
Documentation sub component: default QA Contact:
Status: CLOSED CURRENTRELEASE Docs Contact:
Severity: medium    
Priority: medium CC: jen, jsuchane, kkoukiou, lmen, mpitt, phrdina, rhel-docs, skobyda, virt-maint, wshi, xchen, yafu, ymao, yunyang
Version: 8.3Keywords: Documentation
Target Milestone: rcFlags: pm-rhel: mirror+
Target Release: ---   
Hardware: x86_64   
OS: Linux   
Whiteboard:
Fixed In Version: Doc Type: If docs needed, set a value
Doc Text:
Story Points: ---
Clone Of: Environment:
Last Closed: 2022-02-01 11:53:52 UTC Type: Bug
Regression: --- Mount Type: ---
Documentation: --- CRM:
Verified Versions: Category: ---
oVirt Team: --- RHEL 7.3 requirements from Atomic Host:
Cloudforms Team: --- Target Upstream Version:
Embargoed:

Description Juan Orti Alcaine 2021-03-11 11:58:35 UTC
Description of problem:
If libvirtd.conf is configured with polkit:

    access_drivers = [ "polkit" ]

cockpit-machines cannot manage the VMs even if connecting as root. I get these messages in the journal:

mar 11 12:50:48 libvirtd[1740]: authentication failed: access denied by policy
mar 11 12:50:48 libvirtd[1740]: access denied: 'QEMU' denied access

Version-Release number of selected component (if applicable):
cockpit-224.2-1.el8.x86_64
cockpit-machines-224.2-1.el8.noarch
polkit-0.115-11.el8.x86_64
libvirt-6.0.0-28.module+el8.3.0+7827+5e65edd7.x86_64

How reproducible:
Always

Steps to Reproduce:
1. Enable polkit in /etc/libvirt/libvirtd.conf

access_drivers = [ "polkit" ]

2. systemctl restart libvirtd.service
3. Connect to the virtual machines manager in Cockpit as root.
4. Try to start a VM

Actual results:
These errors in the journal:

    authentication failed: access denied by policy
    access denied: 'QEMU' denied access

If I connect with virt-manager, I can manage the VMs.

Expected results:
The root user should be allowed to manage everything.

Additional info:

Comment 4 Martin Pitt 2021-04-21 16:02:50 UTC
This works as root with access_drivers = [ "polkit" ]:

# busctl call org.libvirt /org/libvirt/QEMU org.libvirt.Connect ListDomains u 0

As user that works with neither the default access_drivers nor with polkit. However, cockpit-machines usually connects to qemu:///system as root (superuser: try), so calling it as root should reflect this more.

So this needs more in-depth debugging.

Comment 5 Martin Pitt 2021-04-28 06:35:12 UTC
Copy&pasteable setup in a fresh VM, with a tiny pre-defined VM:

  sed -i '/access_drivers/ s/^#//' /etc/libvirt/libvirtd.conf; systemctl restart libvirtd
  virt-install --memory 50 --pxe --virt-type qemu --os-variant alpinelinux3.8 --disk none --wait 0 --name test1

I tested this with current cockpit-machines from upstream master (mostly 243.1) on RHEL 8.4, 8.5, RHEL 9.0, and Fedora 34. In all these cases the Machines page lists the existing VMs, and I can create a new VM (Alpine with PXE boot). Deleting a VM fails with "'QEMU' denied access" on all three OSes.

I also see a lot of these in the journal, but they don't break the UI:

  libvirtd[1909]: access denied: 'QEMU' denied access
  libvirtd[1909]: authentication failed: access denied by policy

Then I downgraded to the respective distro versions: 238.2-1.el8 (RHEL 8.4), 242-1.el8 (RHEL 8.5), 243.1-2.el9, and 243-1.fc34. The latter two are basically the same as current upstream master, the RHEL 8.4/5 versions are older (still from cockpit.git), but I see exactly the same behaviour there (not quite surprisingly, as API wise things didn't change since 242).

Doing VM deletion via the API directly reproduces the issue:

# virsh list --all
 Id   Name    State
-----------------------
 1    test1   running
 2    test2   running

# busctl call org.libvirt /org/libvirt/QEMU org.libvirt.Connect ListDomains u 0
ao 2 "/org/libvirt/QEMU/domain/_b5a8b543_ed63_453c_8b58_8318eb63f0d0" "/org/libvirt/QEMU/domain/_69007a04_04ba_4970_80ac_5dc30f6d6250"

# busctl call org.libvirt /org/libvirt/QEMU/domain/_69007a04_04ba_4970_80ac_5dc30f6d6250 org.libvirt.Domain Destroy u 0
Call failed: access denied: 'QEMU' denied access

@Juan, @Yunming, so I cannot reproduce the rejection on creating the VM, but supposedly it's the same root cause.

Reassigning to libvirtd as there is now a CLI reproducer entirely outside of cockpit. This possibly belongs to libvirt-dbus, please reassign if appropriate.

Comment 6 yafu 2021-05-25 08:03:41 UTC
(In reply to Martin Pitt from comment #5)
> Copy&pasteable setup in a fresh VM, with a tiny pre-defined VM:
> 
>   sed -i '/access_drivers/ s/^#//' /etc/libvirt/libvirtd.conf; systemctl
> restart libvirtd
>   virt-install --memory 50 --pxe --virt-type qemu --os-variant
> alpinelinux3.8 --disk none --wait 0 --name test1
> 
> I tested this with current cockpit-machines from upstream master (mostly
> 243.1) on RHEL 8.4, 8.5, RHEL 9.0, and Fedora 34. In all these cases the
> Machines page lists the existing VMs, and I can create a new VM (Alpine with
> PXE boot). Deleting a VM fails with "'QEMU' denied access" on all three OSes.
> 
> I also see a lot of these in the journal, but they don't break the UI:
> 
>   libvirtd[1909]: access denied: 'QEMU' denied access
>   libvirtd[1909]: authentication failed: access denied by policy
> 
> Then I downgraded to the respective distro versions: 238.2-1.el8 (RHEL 8.4),
> 242-1.el8 (RHEL 8.5), 243.1-2.el9, and 243-1.fc34. The latter two are
> basically the same as current upstream master, the RHEL 8.4/5 versions are
> older (still from cockpit.git), but I see exactly the same behaviour there
> (not quite surprisingly, as API wise things didn't change since 242).
> 
> Doing VM deletion via the API directly reproduces the issue:
> 
> # virsh list --all
>  Id   Name    State
> -----------------------
>  1    test1   running
>  2    test2   running
> 
> # busctl call org.libvirt /org/libvirt/QEMU org.libvirt.Connect ListDomains
> u 0
> ao 2 "/org/libvirt/QEMU/domain/_b5a8b543_ed63_453c_8b58_8318eb63f0d0"
> "/org/libvirt/QEMU/domain/_69007a04_04ba_4970_80ac_5dc30f6d6250"
> 
> # busctl call org.libvirt
> /org/libvirt/QEMU/domain/_69007a04_04ba_4970_80ac_5dc30f6d6250
> org.libvirt.Domain Destroy u 0
> Call failed: access denied: 'QEMU' denied access
> 
> @Juan, @Yunming, so I cannot reproduce the rejection on creating the VM, but
> supposedly it's the same root cause.
> 
> Reassigning to libvirtd as there is now a CLI reproducer entirely outside of
> cockpit. This possibly belongs to libvirt-dbus, please reassign if
> appropriate.


Hi Martin,

I can hit the error with your steps, and could see the error log in libvirtd.log:
2021-05-25 02:22:53.962+0000: 39451: debug : virAccessDriverPolkitCheck:140 : Check action 'org.libvirt.api.domain.stop' for process '39445' time 401995 uid 977
2021-05-25 02:22:53.962+0000: 39451: info : virPolkitCheckAuth:84 : Checking PID 39445 running as 977
2021-05-25 02:22:53.982+0000: 39451: debug : virPolkitCheckAuth:131 : is auth 0  is challenge 0
2021-05-25 02:22:53.982+0000: 39451: error : virPolkitCheckAuth:145 : authentication failed: access denied by policy
2021-05-25 02:22:53.982+0000: 39451: info : virObjectUnref:381 : OBJECT_UNREF: obj=0x55bdc3509df0
2021-05-25 02:22:53.982+0000: 39451: error : virDomainDestroyFlagsEnsureACL:2987 : access denied: 'QEMU' denied access

And from the error log we can see cockpit delete(destroy) guest with user libvirtdbus, which uid is 977. For the non-root user, we need to add polkit rule for api action. So i added it as follows:
#cat /etc/polkit-1/rules.d/100-libvirt-acl.rules
polkit.addRule(function(action, subject) {
if (action.id == "org.libvirt.api.domain.stop" &&
subject.user == "libvirtdbus") {
if (action.lookup("connect_driver") == 'QEMU') {
return polkit.Result.YES;
} else {
return polkit.Result.NO;
}
}
});

Then destroy guest again, the guest destroy successfully:
# busctl call org.libvirt /org/libvirt/QEMU/domain/_c506dacd_1e60_408f_b983_f09b425ec058 org.libvirt.Domain Destroy u 0
# echo $?
0

So i think it's not a bug, it works as expected.

Comment 7 Martin Pitt 2021-05-25 15:19:57 UTC
So this happens because from libvirtd's point of view (the polkit target API) it's libvirt-dbus which is doing the calls, and that is always the libvirtdbus system user which the polkit rules apply to. This is not really useful -- the actual caller that needs to be checked is the one invoking libvirt-dbus' method (which then "forwards" it to libvirtd).

libvirt-dbus ships /usr/share/polkit-1/rules.d/libvirt-dbus.rules which allows anyone calling libvirt-dbus to execute "org.libvirt.unix.manage" operations, but apparently that only allows listing and creation, but does not include "org.libvirt.api.domain.stop"? libvirt-dbus itself does not do any polkit checks against its callers, it just seems to allow calls from root.

So I guess with the current architecture of libvirt-dbus being a separate process/service to libvirtd this is unfixable. To do this cleanly, libvirtd itself would need to expose the D-Bus interface. Unless there is a way to "forward" a polkit API check -- that may be possible with a custom polkit agent?


So Juan, the summary is: libvirt polkit mode and cockpit-machines (via libvirt-dbus) are incompatible. If you want to hand out access to non-root/wheel users, there is currently no way to do that with cockpit-machines.

Comment 8 Martin Pitt 2021-05-25 15:23:48 UTC
A bandaid fix for libvirt-dbus is then to actually do exactly what yafu proposed above: Add the other operations to /usr/share/polkit-1/rules.d/libvirt-dbus.rules , not just "org.libvirt.unix.manage". As libvirt-dbus access is restricted to root, there is no point in restricting what libvirt-dbus can do. With that, cockpit-machines/libvirt-dbus would at least work for actual root/wheel users in polkit mode. That doesn't require any architectural changes.

Comment 9 Juan Orti Alcaine 2021-06-01 08:59:29 UTC
Thanks for the explanation, and based on that, would it make sense to expand the supplied polkit policy in /usr/share/polkit-1/rules.d/libvirt-dbus.rules to allow all actions?

Something like this:

polkit.addRule(function(action, subject) {
    if ((action.id == "org.libvirt.unix.manage" ||
         action.id.indexOf("org.libvirt.api.connect.") == 0 ||
         action.id.indexOf("org.libvirt.api.domain.") == 0 ||
         action.id.indexOf("org.libvirt.api.interface.") == 0 ||
         action.id.indexOf("org.libvirt.api.network.") == 0 ||
         action.id.indexOf("org.libvirt.api.node-device.") == 0 ||
         action.id.indexOf("org.libvirt.api.nwfilter.") == 0 ||
         action.id.indexOf("org.libvirt.api.secret.") == 0 ||
         action.id.indexOf("org.libvirt.api.storage-pool.") == 0 ||
         action.id.indexOf("org.libvirt.api.storage-vol.") == 0 ) &&
        subject.user == "libvirtdbus") {
        return polkit.Result.YES;
    }
});

Comment 10 Martin Pitt 2021-06-01 09:25:14 UTC
Juan: In principle yes; I'd even simplify it too

    if (subject.user == "libvirtdbus" &&
        (action.id == "org.libvirt.unix.manage" || action.id.indexOf("org.libvirt.api.") == 0))

or even

    if (subject.user == "libvirtdbus" && action.id.indexOf("org.libvirt.") == 0)

as conceptually libvirt-dbus should be able to call *all* libvirt APIs?

Comment 11 Juan Orti Alcaine 2021-06-01 10:41:26 UTC
Martin, I've tested your last example allowing all "org.libvirt." and works like a charm. Now root and the members of the libvirt group can manage the VMs from Cockpit.

Can we update the supplied policy?

Comment 14 Pavel Hrdina 2021-06-17 15:35:47 UTC
As Martin mentioned in one of the comments using libvirt polkit policy together with libvirt-dbus isn't compatible. With the current state without any changes for system connection it works like this:

   polkit disabled:

      - root user: both virsh and libvirt-dbus works with all APIs

      - non-root user: using virsh requires authentication (if authenticated all APIs) and libvirt-dbus access denied

      - non-root user with libvirt group: both virsh and libvirt-dbus works with all APIs

   polkit enabled:

      - root user: virsh works for all APIs but libvirt-dbus only read-only APIs

      - non-root user: using virsh requires authentication (if authenticated APIs allowed for the authenticated user) and libvirt-dbus access denied

      - non-root user with libvirt group: both virsh and libvirt-dbus only read-only APIs

Now if we consider the situation with polkit enabled:

   - any change to polkit rules for specific user (except for libvirtdbus) will be available only for virsh usage

   - any change to polkit rules for libvirtdbus user will affect both root user and any user with libvirt group added


With the change proposed by Comment 9 or Comment 10 we would be able to use all APIs with root user but also with all users that are part of libvirt group which could break expectation for system administrators enabling polkit for libvirt. Users with libvirt group would not be able to use virsh to make changes to VMs but would be able to use libvirt-dbus to make changes to VMs which ti me sounds like security hole.


I don't think we should change the default policy rules shipped by libvirt-dbus, instead we need to properly document all limitations and offer some examples how to control access to VMs with libvirt-dbus and cockpit-machines.

Comment 15 Jaroslav Suchanek 2021-06-21 11:51:38 UTC
I am changing the component to 'Documentation' per comment 14. Thanks.

Comment 26 Jaroslav Suchanek 2022-01-28 08:59:13 UTC
Overall, looks good to me.