qubes-issues icon indicating copy to clipboard operation
qubes-issues copied to clipboard

Selective `sudo` Access Enabling in VMs Without `qubes-core-agent-passwordless-root` via `qvm-service`

Open adrelanos opened this issue 1 year ago • 9 comments

The problem you're addressing (if any)

In Qubes OS, when the qubes-core-agent-passwordless-root package is removed from a template, App Qubes based on this template lose sudo access, which is the intended behavior for enhanced security.

The typical method of gaining root access in such cases is by using qvm-run -u root vm-name xfce4-terminal, allowing access to a root shell. However, working directly as root presents limitations:

  • GUI applications may not work properly when run as root. For instance, editors like kate may refuse to run as root, and others might exhibit inconsistent behavior.
  • Running processes as root can lead to unintended changes in permissions within the user's home directory, causing problems later.

Ideally, I would like to add the user user to the sudo group to gain root privileges when necessary, without these drawbacks.

However, after running sudo adduser user sudo in the root shell, a re-login or reboot is required for the group membership changes to take effect (which is standard in Linux systems). In Qubes OS, this becomes impractical because:

  • Reboots into App Qubes do not persist the change, as the /etc/group file resets. Thus, adding the user to the sudo group is lost after a reboot.
  • Alternatives like su and newgrp are unavailable due to SUID security hardening (SUID removal) in Kicksecure.

The solution you'd like

Introduce a qvm-service option to control sudo access in a VM.

This could be added via Qubes VM Manager (QVMM) → VM Name → Settings → Services → Add sudo. This would enable sudo access for the user in the VM without relying on the qubes-passwordless-root package.

Technically, this would work by having a systemd unit that runs before "login inside the VM" to execute adduser user sudo. This way, sudo privileges could be granted to the user at VM startup without requiring reboots, re-logins, or direct modifications to the template.

The value to a user, and who that user might be

This feature would be valuable for Qubes OS users who prefer not to install the qubes-passwordless-root package but still need to selectively enable sudo access in certain VMs. It would provide a flexible and lightweight way to grant sudo access per-VM selectively.

Alternatives considered

  • Maintaining Two Templates: One with qubes-passwordless-root installed and one without. While functional, this approach introduces significant overhead and complexity, especially for users managing many Templates.
  • Manual Methods: Manually adding users to sudo each time a VM starts, though this is inefficient and requires repeating the process every time due to lack of persistence. Also I haven't even found a way on how to do that yet.

adrelanos avatar Oct 16 '24 08:10 adrelanos

Is it not simpler to add usermod -a -G sudo user to /rw/config/rc.local?

unman avatar Oct 17 '24 00:10 unman

I should have mentioned... Earlier I tried adduser user sudo in /rw/config/rc.local. That did not work for me. usermod -a -G sudo user should be the same and does not work for me either.

This might be happening because /rw/config/rc.local is maybe parsed only after "login".

An alternative fix to suggest would be: "parse /rw/config/rc.local before login" but then that could break other unknown use cases.

An alternative feature request would be: "implement /rw/config/rc.local-pre-login" (so the adduser command to add user to group sudo could be executed before "login""

adrelanos avatar Oct 17 '24 07:10 adrelanos

(Edited/fixed wrong path in above comment.)

adrelanos avatar Oct 17 '24 10:10 adrelanos

https://github.com/QubesOS/qubes-core-agent-linux/pull/386 might allow implementing it this way

marmarek avatar Oct 17 '24 10:10 marmarek

Alternative implementation: #2695 (then it's up to the policy: always allow, ask, deny)

marmarek avatar Oct 17 '24 10:10 marmarek

On Thu, Oct 17, 2024 at 12:17:51AM -0700, Patrick Schleizer wrote:

I should have mentioned... Earlier I tried adduser user sudo in /rw/config.rc.local. That did not work for me. usermod -a -G sudo user should be the same and does not work for me either. It might be helpful if you could say in what way it "does not work". It works for me with debian-12-minimal template based qubes, allowing use of eg sudo su.

The sudo group is present in minimal templates. Why can you not leverage that?

unman avatar Oct 17 '24 12:10 unman

It might be helpful if you could say in what way it "does not work".

user gets added to group sudo but a terminal running as user (started from Qubes start menu) still cannot use sudo. Group changes did not take affect. I would guess because it happened after "login".

Using su would be required beforehand. (But that is disabled due to SUID hardening and also cumbersome.)

As for the mixed results, I am expected a race condition. What happens first?

  • user addgroup to sudo before "login" -> functional
  • user addgroup to sudo after "login" -> broken

adrelanos avatar Oct 18 '24 07:10 adrelanos

I can only say that with a debian-minimal template, this works for me. (No race condition - it works every time.) I use it sometimes for other users and none of them report the problem you have. Again, what template have you tested with? If you start the AppVM, and open a terminal, what error is reported when you try to use sudo su?

unman avatar Oct 19 '24 12:10 unman

As for the mixed results, I am expected a race condition. What happens first?

* user addgroup to sudo before "login" -> functional

* user addgroup to sudo after "login" -> broken

Does it work reliably if you add a Before=systemd-user-sessions.service ordering to qubes-misc-post.service?

rustybird avatar Oct 20 '24 12:10 rustybird

/rw/config/rc.local is working now. The only problem was, that /rw/config/rc.local wasn't executable.

adrelanos avatar Oct 22 '24 09:10 adrelanos

Technically, this would work by having a systemd unit that runs before "login inside the VM" to execute adduser user sudo.

It might be easier to link/unlink the sudoers.d file than adding/removing group membership. As you noted group membership can be tricky (like having one shell with and one without, also group membership sticks around until all processes terminate).

HW42 avatar Oct 24 '24 01:10 HW42

There is an issue with any /rw/config/rc.local based solution. It cannot be used inside the Template. Because if the Template does adduser user sudo (or similar) then this change will apply to any App Qube based on that Template.

Therefore, ideally sudo access for user user would be based on a qvm-service so it one day can be implemented as a checkbox inside QVMM.

adrelanos avatar Oct 25 '24 15:10 adrelanos

What about using qrexec policy?

DemiMarie avatar Jan 09 '25 01:01 DemiMarie

@DemiMarie I'm not quite sure how qrexec would help - qrexec runs commands as user user (unless there's some setting for choosing the user to run things as that I'm not aware of), so it would be very tricky (maybe impossible) to use this in a way that prevented user user from gaining root access. Maybe you're thinking of something I'm not though.

@HW42 That sounds like a good idea to me. A similar trick can be used for polkit at the same time so pkexec and similar tools will also work.

ArrayBolt3 avatar Feb 07 '25 22:02 ArrayBolt3

@DemiMarie I'm not quite sure how qrexec would help - qrexec runs commands as user user (unless there's some setting for choosing the user to run things as that I'm not aware of), so it would be very tricky (maybe impossible) to use this in a way that prevented user user from gaining root access. Maybe you're thinking of something I'm not though.

I'm not sure what Demi had in mind, but technically a qrexec service can be configured to run as other user. See https://github.com/QubesOS/qubes-core-qrexec/tree/main/qubes-rpc-config (and there is also a optional "user=" argument settable via qrexec policy.

marmarek avatar Feb 07 '25 22:02 marmarek

@adrelanos, @HW42 This is how I currently think this would be implemented if deleting the config files rather than changing groups:

  • qvm-services aren't taken into account for TemplateVMs anyway, so for the TemplateVM it can just keep qubes-core-agent-passwordless-root installed (meaning it would be installed in all AppVMs as well). Thus full privileges would be enabled by default in all VMs.
  • The service would disable unrestricted privilege escalation when enabled, and leave privilege escalation enabled otherwise.
  • When the "disable-passwordless-root" service is enabled, it will delete /etc/polkit-1/rules.d/00-qubes-allow-all.rules, /etc/sudoers.d/qubes, and /usr/share/pam-configs/su.qubes (list copied from https://github.com/QubesOS/qubes-core-agent-linux/blob/main/debian/qubes-core-agent-passwordless-root.install) during VM bootup, then run pam-auth-update to commit the change. This will be reverted on reboot because of the ephemeral nature of AppVMs, and TemplateVMs will never be affected.

The only apparent issue I can see with this is that if someone does sudo apt full-upgrade (or the dnf equivalent thereof) in an AppVM, and a qubes-core-agent-passwordless-root upgrade comes along with, the files may end up being brought back. In Debian-based VMs this mostly isn't a problem since everything under /etc is a conffile by default and thus dpkg will see that the user intentionally deleted these files and refrain from recreating them, however /usr/share/pam-configs/su.qubes will probably get remade and pam-auth-update re-called, and if that happens then passwordless su will come back. I don't know how Fedora behaves when it comes to user-removed package-owned files. There usually aren't legitimate reasons to run an OS update inside of an AppVM, but there may be users who do it without knowing any better, and there's probably a few people out there who do it intentionally, knowing the implications.

One workaround might be to create something that watches the /etc/sudoers.d, /etc/polkit-1, and /usr/share/pam-configs directories and would immediately re-delete any improper files that showed up, re-running pam-auth-update if needed. But that seems like an awful lot of overhead and an awfully hacky way to do this. This makes me wonder if maybe the groups-based solution is better after all.

ArrayBolt3 avatar Feb 07 '25 23:02 ArrayBolt3

@marmarek Thanks, I looked for those docs for a while and didn't find them last night. Will add that to my bookmarks.

ArrayBolt3 avatar Feb 07 '25 23:02 ArrayBolt3

As for the groups-based idea, maybe an overkill, but what about a small NSS module that lists "user" as a member of a specific group or not, based on qvm-service presence? That would avoid issues of "leaking" permissions from template to appvm, but also avoids dynamic changes of any of the /etc and /usr files. One still needs to effectively reboot to drop the privilege (and also to gain it - although sg may be enough in some cases).

marmarek avatar Feb 07 '25 23:02 marmarek

@ArrayBolt3, that would be indeed brittle, but that's not quite what I had in mind. What I meant is that there's a early running service that queries the feature and then generates the sudo (or pkexec) config files based on the setting.

@marmarek:

[...] what about a small NSS module that lists "user" as a member of a specific group or not, based on qvm-service presence?

Neat idea, but might be "too clever" (For example IIRC managing NSS config is distro specific, making it more annoying to do.).

HW42 avatar Feb 08 '25 03:02 HW42

@DemiMarie I'm not quite sure how qrexec would help - qrexec runs commands as user user (unless there's some setting for choosing the user to run things as that I'm not aware of), so it would be very tricky (maybe impossible) to use this in a way that prevented user user from gaining root access. Maybe you're thinking of something I'm not though.

I was thinking of using pam_exec to make a qrexec call, but @HW42 pointed out that this is too brittle.

DemiMarie avatar Feb 08 '25 04:02 DemiMarie

Note there is quite a bit of overlap with https://github.com/QubesOS/qubes-issues/issues/2695, which IMO is more flexible approach.

marmarek avatar Feb 08 '25 10:02 marmarek

Note there is quite a bit of overlap with https://github.com/QubesOS/qubes-issues/issues/2695, which IMO is more flexible approach.

Hmm... that's true. Perhaps we could just make it a tri-state rather than a check box:

  • Allow root escalation automatically
  • Allow root escalation, but require dom0 permission first
  • Deny root escalation always

Perhaps this feature would replace qubes-core-agent-passwordless-linux, so that we don't have the problem of upgrades potentially breaking things?

ArrayBolt3 avatar Feb 09 '25 01:02 ArrayBolt3

Hmm... that's true. Perhaps we could just make it a tri-state rather than a check box:

That's exactly what that solution can do, it's called qrexec action, respectively: allow, ask, deny.

marmarek avatar Feb 09 '25 02:02 marmarek

Oh nice! So then the setting in the GUI could just tweak the qrexec policy in dom0?

ArrayBolt3 avatar Feb 09 '25 02:02 ArrayBolt3