cockpit-project / cockpit

Cockpit is a web-based graphical interface for servers.
http://www.cockpit-project.org/
GNU Lesser General Public License v2.1
11.23k stars 1.11k forks source link

cockpit attempts sudo in futile situations #18324

Open allisonkarlitskaya opened 1 year ago

allisonkarlitskaya commented 1 year ago

The problem

I am logged in to cockpit on a Silverblue machine with account (lis) that's not in wheel. I see the "Limited access" button at the top. I know the password to the admin account (which is in wheel) and I'm used to being prompted for this when I'm using GNOME.

I press "Limited access" expecting that I will be asked for admin's password.

Instead, I get asked for my own password in a futile attempt to use sudo.

In some sense this is reasonable. Cockpit has no way to know that I'm not in the sudoers file. It could find out, however, that pkexec is possible for my situation.

The desired solution

In a situation where it might be possible for a non-admin user to get admin access via entering the password of another user who does have the access (a common polkit configuration) then this should be presented as an option when clicking "Limited access".

This should not come at the cost of preventing access to sudo via the user's own password, unless we can definitively rule out sudo as being possible, given the right password (which I think isn't possible).

Feasibility research

Frustratingly, trying to execute pkexec in the Terminal tab fails:

[lis@srv ~]$ pkexec ls /root
Error executing command as another user: Not authorized

Doing this via SSH looks more promising, but gets a different failure:

[lis@srv ~]$ pkexec ls /root
==== AUTHENTICATING FOR org.freedesktop.policykit.exec ====
Authentication is needed to run `/usr/bin/ls' as the super user
Authenticating as: Administrator (admin)
Password: 
polkit-agent-helper-1: error response to PolicyKit daemon: GDBus.Error:org.freedesktop.PolicyKit1.Error.Failed: No session for cookie
==== AUTHENTICATION FAILED ====
Error executing command as another user: Not authorized

In both places, this relatively contorted alternative seems to work:

[lis@srv ~]$ (sleep 1; pkexec ls /root)& pkttyagent 
[1] 38748
==== AUTHENTICATING FOR org.freedesktop.policykit.exec ====
Authentication is needed to run `/usr/bin/ls' as the super user
Authenticating as: Administrator (admin)
Password: 
==== AUTHENTICATION COMPLETE ====
anaconda-ks.cfg

And we could detect if maybe this might be possible:

[lis@srv ~]$ pkcheck -a org.freedesktop.policykit.exec -p $$; echo exit-status $? 
Authorization requires authentication and -u wasn't passed.
exit-status 2

[lis@srv ~]$ pkcheck -a org.freedesktop.policykit.exec -p $$ -u; echo exit-status $? 
Authorization requires authentication but no agent is available.
exit-status 2

...and for the record, not that it's really very useful, but this works:

[lis@srv ~]$ (sleep 1; pkcheck -a org.freedesktop.policykit.exec -p $$ -u; echo exit-status $?)& pkttyagent 
[1] 38835
==== AUTHENTICATING FOR org.freedesktop.policykit.exec ====
Authentication is required to run a program as another user
Authenticating as: Administrator (admin)
Password: 
==== AUTHENTICATION COMPLETE ====
exit-status 0

and (easier) this too:

[lis@srv ~]$ pkcheck --enable-internal-agent -a org.freedesktop.policykit.exec -p $$ -u; echo exit-status $?
==== AUTHENTICATING FOR org.freedesktop.policykit.exec ====
Authentication is required to run a program as another user
Authenticating as: Administrator (admin)
Password: 
==== AUTHENTICATION COMPLETE ====
exit-status 0

In terms of convincing Cockpit to do the right thing, I tried creating an override in ~/.config/cockpit/shell.override.json containing

{
    "bridges": [
        {
            "privileged": true,
            "spawn": [
                "pkexec",
                "--disable-internal-agent",
                "cockpit-bridge",
                "--privileged"
            ]
        }
    ]
}

That got me:

(!) Problem becoming administrator No such superuser bridge [ Close ]

which isn't so nice. Knowing a little bit about this stuff, I decided that maybe adding a "label": field would help. It did, but not much:

(!) Problem becoming administrator Error executing command as another user: Not authorized This incident has been reported. [ Close ]

Further considerations

I thought it might be useful to be able to show the name of the user that we'd be trying to authenticate as as part of a potential future interaction with the user:

Switch to administrative access: ( ) enter your own password (sudo) ( ) enter password for user admin (polkit)

but it's very difficult to discover whose password polkit is going to request.

I poked around the D-Bus API and available commandline tools to try to get polkit to tell me about the users it might select. The default config on Fedora does that via /etc/polkit-1/rules.d/50-default.rules:

polkit.addAdminRule(function(action, subject) {
    return ["unix-group:wheel"];
});

Frustratingly, this file is not accessible to normal users, and I couldn't find any other way to access the information. There is a pkla-admin-identities as a compatibility shim for old the old-style rules, but it returns nothing on Fedora, since the config is via Javascript. There's also no guarantee that this tool would even be used, anyway, as part of the JS config.

One option would be to attempt to initiate a pkexec authentication, catch it with the agent to check the username, then immediately cancel the interaction without a password attempt. That might have logging implications, and we've gotten in trouble with stuff like that in the past, but it might be worth looking into.

Another UI option that doesn't involve knowing the name of the admin user ahead of time and preserves the same UI flow (and number of clicks) for existing users of sudo might be something like:

Switch to administrative access: Password for lis: __ » other options

and a local storage item to remember if you elected "other options" (and possibly also remember if it actually worked).

martinpitt commented 1 year ago

This information actually is part of the polkit agent protocol. polkitd sends the agent a list of possible "admin" identities, e.g. like this on my system:

[('unix-user', {'uid': systemd_ctypes.Variant(1001, 'u')}), ('unix-user', {'uid': systemd_ctypes.Variant(1000, 'u')})]

The agent can use this to implement a selector, and let the user choose which admin user to authenticate as. Of course this requires quite some UI plumbing (including a possible fallback from sudo to polkit), but it seems possible in principle.