Enabling polkitd on Ubuntu Core

There has been some interest in using polkit on Ubuntu Core devices, and since we got this working for the Ubuntu Core Desktop prototype builds I thought I’d go over what I think would be needed to get it working in regular Ubuntu Core. This is probably of interest to @valentind and @mvo.

Make the polkit interface available on Core.

The polkit interface described here is already merged to snapd, but the implicit slot is currently only provided on classic systems.

For UCD, we patched snapd to make it implicit on core too. This wouldn’t be appropriate to merge upstream as is, since we don’t want to provide the slot on Ubuntu Core systems that don’t ship with polkit. So I suggest we modify the interface to set:

        implicitOnCore: osutil.IsExecutable("/usr/libexec/polkitd"),

This is sufficient for Core 22. If we wanted to add polkit support to Core 20 or 18, we should also check for the existence of /usr/lib/policykit-1/polkitd.

Add polkitd to core22 base snap

The hook scripts used to build the core22 base snap should be updated to install the polkitd package.

In addition, we need to allow snaps to install their polkit actions. This can be done by editing static/etc/system-image/writable-paths to add:

/usr/share/polkit-1                     auto                    persistent  transition  none

That should be enough to allow installation of snapped system daemons that want to talk to polkitd. That’s not the whole story though, since we also need to let polkitd make authorisation decisions.

This is done via configuration in /etc/polkit-1/localauthority (as documented in the pklocalauthority(8) man page), falling back to defaults provided by the daemon in the .policy file it installed to /usr/share/polkit-1/actions. I’m not sure how interesting local configuration will be, but if we want it we’d need to add /etc/polkit-1/localauthority as a writable path.

In addition to blanket allow or deny policies, authorisation might depend on:

  1. Is the user an administrator?
  2. Can the user authenticate as themselves? (i.e. the familiar password prompts you’d see on the desktop).

Both of these require additional work to enable on Ubuntu Core systems.

Make polkit recognise administrative users.

Polkit needs to be taught about which users on a system are admin users. In addition to root, Ubuntu configures members of the sudo and admin groups as administrators via /etc/polkit-1/localauthority.conf.d/51-ubuntu-admin.conf.

Unfortunately, users created on Ubuntu Core devices are members of neither of these groups. This is because those groups are defined in /etc/group, which is read-only on Core. Snapd manages to get sudo working by dropping sudo configuration fragments in /etc/sudoers.d, but that’s not going to help with polkitd.

While adding similar polkit config fragments could work here, for Ubuntu Core Desktop we instead worked out how to add users to these system groups despite them being read-only. This was done as so:

  1. we patched libnss-extrausers to remove the restriction on creating groups with gid < 500 in /var/lib/extrausers/group
  2. we modified /etc/nsswitch.conf so the group database was configured as:
    group:          files [SUCCESS=merge] extrausers
  3. we modified the initial version of /var/lib/extrausers/group to include the line sudo:x:27: (define the sudo group with the same group ID and an empty user list).

With these changes, rather than taking the first matching group record glibc will consult both databases and merge the membership lists. So we can now add new members to the group without compromising the ability for future core22 releases to add new system groups to the system.

With this in place, snapd would need to learn how to add administrative users to the sudo group. We are using gnome-initial-setup to create user accounts so haven’t tackled this on Core Desktop. It ends up using the shadow utils to manipulate accounts though, and all the --extrausers patches we needed are already in Ubuntu Core 22.04. So it shouldn’t be too difficult for snapd to use too.

Password prompting via a polkit agent

Polkit handles authentication prompts via a polkit agent. A polkit agent registers itself as being responsible for one or more processes, and will be contacted by polkitd if those processes require an action with a auth_* policy.

For Ubuntu Core Desktop, we wanted gnome-shell to be able to act as an agent for all processes in the session, so I created a polkit-agent interface to let the shell do this. It hasn’t yet been merged to master though as the AppArmor policy needs tightening up:

Even with a tighter policy, this is interface must be treated as super privileged. A polkit agent needs to be able to call the setuid /usr/libexec/polkit-agent-helper-1 process, which in turn uses PAM to authenticate the user. With password auth, the agent will be handling the user’s password.

Password prompting on the command line

You’ve probably seen how some systemd utilities can prompt you for a password when run as a regular user. It does this by spawning pkttyagent as a fallback if no other agent is registered for the utility. The code for that is in src/shared/spawn-polkit-agent.c.

Running this from within a snap sandbox would require the polkit-agent interface as described above. But another possibility would be to have snap run spawn the fallback agent outside of the sandbox on behalf of the sandboxed app. If we want to go this route, it could be enabled via another attribute on the app in the snap.yaml file.

One thing to keep in mind is that pkttyagent has its own security problems. Since the snapped process is already running by the time it triggers a authorisation check, it can race the agent to read from the tty and possibly capture the user’s password.


I’m not pointing a finger just to be crystal clear, but it seems this change caused a regression in a very indirect way in the stable release and just wanted to write down some notes.

If I understand the timeline correctly, the polkitd was added to core22 beween the following two builds.

[core22-202311230812] https://launchpad.net/~ubuntu-core-service/+snap/core22/+build/2305181

[core22-202401111629] https://launchpad.net/~ubuntu-core-service/+snap/core22/+build/2353462

diff --git a/core22-202311230812_.manifest b/core22-202401111629_.manifest
index ed4e3c9..96583fa 100644
--- a/core22-202311230812_.manifest
+++ b/core22-202401111629_.manifest
@@ -143,2 +143,4 @@ ii  libplymouth5:amd64             0.9.5+git20211018-1ubuntu3.1~ppa3       amd64
 ii  libpng16-16:amd64              1.6.37-3build5                          amd64        PNG library - runtime (version 1.6)
+ii  libpolkit-agent-1-0:amd64      0.105-33                                amd64        PolicyKit Authentication Agent API
+ii  libpolkit-gobject-1-0:amd64    0.105-33                                amd64        PolicyKit Authorization API
 ii  libpopt0:amd64                 1.18-3build1                            amd64        lib for parsing cmdline parameters
@@ -193,5 +195,6 @@ ii  p11-kit-modules:amd64          0.24.0-6build1                          amd64
 ii  passwd                         1:4.8.1-2ubuntu2.1                      amd64        change and administer password and group data
-ii  perl-base                      5.34.0-3ubuntu1.2                       amd64        minimal Perl system
+ii  perl-base                      5.34.0-3ubuntu1.3                       amd64        minimal Perl system
 ii  plymouth                       0.9.5+git20211018-1ubuntu3.1~ppa3       amd64        boot animation, logger and I/O multiplexer
 ii  plymouth-label-ft              0.9.5+git20211018-1ubuntu3.1~ppa3       amd64        boot animation, logger and I/O multiplexer - label-ft control
+ii  polkitd                        0.105-33                                amd64        framework for managing administrative policies and privileges
 ii  probert-common                 0.0.20                                  all          Hardware probing tool - common

That made the additional command pkttyagent available in the stable update of core22 in the middle of the lifecycle.

$ dpkg -S pkttyagent 
polkitd: /usr/share/man/man1/pkttyagent.1.gz
polkitd: /usr/bin/pkttyagent

And the libvirt component in the MAAS snap tried to use it implicitly after the core22 update, but that actually failed with an AppArmor denial.

$ virsh -c qemu+ssh://maas@HOST/system pool-info pool_virt_images
libvirt: error : cannot execute binary /usr/bin/pkttyagent: Permission denied
apparmor="DENIED" operation="exec" profile="snap.maas.supervisor" name="/usr/bin/pkttyagent"

The error itself was harmless from a MAAS functionality point of view, but a hot fix was required to update the parser of the output.

I’m not so sure what was the best and ideal route of changes like this though.

1 Like