Skip to content

Conversation

@allisonkarlitskaya
Copy link
Member

This was surprisingly easy: we just needed to enable our already-existing polkit agent for "run0" as we have it for "pkexec".

This will help us in our beautiful post-setuid future.

Closes #21723

The patch is trivial, but there's gonna need to be some discussion here...

@allisonkarlitskaya
Copy link
Member Author

Screenshot From 2025-08-27 17-05-41 Screenshot From 2025-08-27 17-04-29

Copy link
Member

@martinpitt martinpitt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Obviously needs integration tests, but I suppose you asked for review to discuss the design?

Comment on lines 38 to 39
"spawn": [
"run0",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we do any kind of "does that binary exist?" check anywhere? I thought we didn't, and on non-bleeding-edge distros that would just fail superuser.

Also, I don't think we are ready to replace sudo as default yet. Adding run0 as an option is great and desirable, but breaking existing setups with custom sudo rules very much isn't. And they are very popular, e.g. a lot of cloud instances have them, or even with NOPASSWD.

If we do this, I think we'll have to bite the bullet of adding an availability check to the manifest, and check if sudo is the "good" one.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need something like path-exists maybe? For sure rhel-8-10 won't have it run0. I would love to opt in to run0 for, for example my own server would love to use run0. So maybe we should figure out either:

  • a way to fallback in case of a broken sudo
  • a way to set your preferred superuser provider, although that brings in my configuration

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We already have a way to do that (https://cockpit-project.org/guide/latest/packages.html#manifest-overrides), but it's not very well documented . Check testMultipleBridgeConfig.

Copy link
Member

@jelly jelly Sep 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, but that wouldn't work with Cockpit client ;-)

@martinpitt
Copy link
Member

TBH the only way I see us migrating away from sudo by default is to keep sudo on upgrades, and switching to run0 (if available) or pkexec on new installs. This is a bit involved, but we e.g. did it for /etc/cockpit/disallowed-users. I. e. we can switch the default upstream, and put something into deb postinst script or rpm %post section to create a manifest override on upgrades which switches sudo back to the default on upgrade. WDYT?

@allisonkarlitskaya
Copy link
Member Author

What's the specific concern about not using sudo? Do you imagine someone has added cockpit-bridge --privileged to their sudoers file? Anyone who has the ability to do that has full root already...

Or is it more about adding users who aren't in the wheel group?

@allisonkarlitskaya
Copy link
Member Author

My take: we should use run0 if it exists, otherwise fall back to sudo. The exact mechanism by which we implement that is a "smop" as pitti likes to say :)

But yes: an override could work for existing installs. I just want to make sure we all agree about if and why it's necessary.

@martinpitt
Copy link
Member

martinpitt commented Aug 28, 2025

I'm less concerned about command restriction sudo rules, but more about custom sudo rules like you commonly find on e.g. cloud images (as said above):

# cat /etc/sudoers.d/90-cloud-init-users 
# Created by cloud-init v. 24.2 on Mon, 25 Aug 2025 07:11:02 +0000

# User rules for fedora
fedora ALL=(ALL) NOPASSWD:ALL

I.e. which aren't based on our standard wheel/sudo groups, but direct names. run0/pkexec don't catch these.

@jelly
Copy link
Member

jelly commented Sep 3, 2025

Shouldn't we just merge this with a new test in check-superuser where we force it to use run0? And for now don't make run0 the default solution.

@allisonkarlitskaya
Copy link
Member Author

Shouldn't we just merge this with a new test in check-superuser where we force it to use run0? And for now don't make run0 the default solution.

Yes. This is a good first step.

Copy link
Member

@martinpitt martinpitt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! Some small questions, but this looks very sane (needs test cases of course)

Comment on lines 151 to +154
if (ids.length == 0)
start("sudo");
start(proxy.Bridges[0]);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't checked how this all hangs together, but this reads odd .. Is it guaranteed that Bridges[0] exists here, if there are no IDs? I.e. does "id" just mean "labelled bridge" here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes. The Methods (ie: the ids in this function) are the bridges that have labels. The Bridges are the names of all bridges, labels or not.

It's actually possible that we'd have no bridges (if none of run0, sudo, or pkexec appear in the path) so maybe we should indeed check for that.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AFAIK we don't currently check if the bridge actually exists, no? So this could only happen with a manifest override that removes all superuser bridges. That's actually not an unreasonable thing to do for things like e.g. clouddot (remember that still?) or foreman integration, or other custom setups.

Comment on lines 210 to 242
self.stop()
self.shutdown()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does that make a practical difference? Does it matter here, or is it unrelated cleanup similar to the Optional[] typing changes? (I don't mind, just want to understand it)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was a typing thing: the .stop() method is wrapped as a D-Bus method and the decorator erases the typing information. Since .stop() just calls .shutdown() directly anyway (which is correctly typed) we can avoid the error about calling an untyped function. I should have explained that in the commit message better, but the inclusion of the typing stuff here at all was an unintentional rebase fail..

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense, thanks!

b.wait_not_present(".pf-v6-c-modal-box:contains('Switch to administrative access')")
b.check_superuser_indicator("Administrative access")

# Verify run0 is being used and not sudo or pkexec
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no check for "not using sudo" here, actually.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shakes fist claude!

If the argv[0] of a bridge doesn't exist in the path then remove it from
the list.

In the shell superuser dialog we first look if `Methods` are available
(which is when the user provides `"label"` keys on their superuser
entries, which is not the default).  If not, or if the number of labels
is zero, we fall back to using the string "sudo".

With the change to remove methods that are not available, "sudo" may no
longer be the first available method, so fall back to the first
available method.  Also add an explicit error path for the case that no
methods are available.

Make a minor UI tweak: now that we have an error dialog for no methods
being available, always show the indicator (even if there are no methods
available) so that the user can still see "Limited access" and click on
it to find out why.

We need to change a type annotation: I'm not sure why this ended up
being declared unknown, but the D-Bus type of the thing is `as` so
`string[]` is definitely the right choice here.

Use Claude to write a testcase.

Assisted-by: Claude <[email protected]>
If we detect a version of sudo which lacks support for the -A (askpass)
flag then we exclude it from the list of available authentication
methods.

This prevents us from trying to use sudo-rs, which lacks askpass support
(which we depend on).

See trifectatechfoundation/sudo-rs#1249
This was surprisingly easy: we just needed to enable our
already-existing polkit agent for "run0" as we have it for "pkexec".

Add run0 in the order below sudo but above pkexec.  This means that on
systems without a working sudo (such as Ubuntu 25.10) but run0 present,
we will choose run0.

The eventual goal will be to default to run0 but let's try this as a
first step.

This will help us in our beautiful post-setuid future.

Closes cockpit-project#21723
Add tests to verify run0 works as a fallback when sudo is unavailable
or broken:
- test_run0_no_sudo: sudo removed entirely
- test_run0_broken_sudo: sudo doesn't support askpass
- test_run0_wrong_password: verify wrong password handling with run0

Assisted-by: Claude <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support run0 for authentication

4 participants