Experimenting with systemd-homed on Ubuntu Core

As an experiment, I tried building a version of the core24 snap with systemd-homed included to see whether it could work as a replacement for the libnss-extrausers code used by Ubuntu Core to manage local user accounts when the regular user database is on read-only storage.

One of the reasons for looking at this is that the extrausers code has been orphanned upstream since December 2016 (and was realistically unmaintained for a few years before that), and has required a bunch of patches to the various user management tools to support managing users in the separate user database. This was a bit of a pain point in getting user creation working on Ubuntu Core Desktop, for instance.

Building

The code is in the systemd-homed-experiment branch of this repository:

https://github.com/jhenstridge/core-base-desktop/tree/systemd-homed-experiment

Apart from some build fixes that aren’t yet merged to the snapcore/core-base repository, the changes are:

  1. Add the systemd-homed and libnss-systemd packages to the build. The latter also causes the systemd NSS module to be included in /etc/nsswitch.conf.
  2. Patch /etc/ssh/sshd_config to set the AuthorizedKeysCommand to look up SSH keys via systemd’s userdbctl.

After building the modified core24 snap by running snapcraft, I can build an Ubuntu Core image using the model file included in the repo:

ubuntu-image snap -i 6G --snap core24_*_amd64.snap ubuntu-core-24-amd64-dangerous.model

This produced a disk image called pc.img, which I started in qemu:

qemu-system-x86_64 -enable-kvm -smp 2 -m 2048 \
  -netdev user,id=mynet0,hostfwd=tcp::8022-:22,hostfwd=tcp::8090-:80 \
  -device virtio-net-pci,netdev=mynet0 \
  -drive file=pc.img,format=raw \
  -drive file=/usr/share/OVMF/OVMF_CODE.fd,if=pflash,format=raw,unit=0,readonly=on \
  -serial mon:stdio

After the system booted and I configured the initial user account via console-conf, I could log in via ssh

ssh -i ~/.ssh/identity-launchpad jamesh@localhost -p 8022

Testing

The initial user account is still created in the extrausers database, so lets create a second account via homed:

sudo homectl create james --real-name="James Henstridge" -G sudo \
  --ssh-authorized-keys="$(cat ~/.ssh/authorized_keys)" --storage=directory

This command prompts me to enter a password for the new user, and then creates /home/james.homedir. There is a user record in /home/james.homedir/.identity which also seems to be duplicated to /var/lib/systemd/home/james.identity.

Some things to note:

  1. I’m adding this new user to to the sudo group. This is possible despite the group being defined in /etc/group, which is read-only. The group membership is recorded in the user’s JSON identity record.
  2. I’ve configured it using the same SSH key as the initial account. The key is also stored in the identity record.
  3. I’m using the directory storage mode, which is about as close to the traditional model as it gets: no encryption or sub-volumes needed (I figure if you want encryption, you can get it from UC’s existing full disk encryption).

The new user is visible to tools that use NSS. For example:

jamesh@localhost:~$ getent passwd james
james:x:60136:60136:James Henstridge:/home/james:/bin/bash

If I try to log in via ssh with public key auth, things don’t quite work though:

$ ssh -i ~/.ssh/identity-launchpad james@localhost -p 8022
Home of user james is currently not active, please log in locally first.
...

I get a shell, but no access to my home directory. This seems to be because some of the other storage methods require the user’s password to gain access to the home directory (i.e. to decrypt it), and I didn’t provide it for this ssh login. There is an open issue about adding support for mounting the user home directory without a password if it isn’t encrypted, so maybe that will change in future.

I can work around this by running sudo homectl activate james. After doing this, an ssh login works as expected and my home directory is available at /home/james. I’ve even got my supplementary group membership:

james@localhost:~$ id
uid=60136(james) gid=60136(james) groups=60136(james),27(sudo)

So the account has sudo access without the need for any drop-in configuration in /etc/sudoers.d. NSS works correctly within a core24 snap sandbox:

james@localhost:~$ test-snapd-sh-core24.sh
$ id
uid=60136(james) gid=60136(james) groups=60136(james),27(sudo)
$ getent passwd james
james:x:60136:60136:James Henstridge:/home/james:/bin/bash
$ 

This is because the relevant AppArmor permissions to talk to the varlink sockets in /run/systemd/userdb are included in the standard include <abstractions/nameservice> profile fragment.

User lookup doesn’t work for snaps in other bases:

james@localhost:~$ test-snapd-sh-core22.sh
$ id
uid=60136 gid=60136 groups=60136,27(sudo)
$ getent passwd james
$ 

This is because the libnss-systemd is not present in core22. I’m unsure if the version from ubuntu 22.04 supports userdb lookups though, so it might not be quite as simple as adding the package. Maybe adding nscd could get this to work, but nscd has it’s own set of problems.

Using NSS to look up group membership doesn’t quite work:

james@localhost:~$ getent group sudo
sudo:x:27:

I have tried doing a build where I use [SUCCESS=merge] to merge the files and systemd group records (as described in the docs), which does work but causes the system to boot very slowly due to systemd-userdbd failing when started earlier on during the boot process. This is probably solvable with a little work.

Main take-aways

While homed is probably workable in environments using password auth, it’s probably not quite ready for environments relying on ssh public key auth. So this might be ready for something like Ubuntu Core Desktop earlier than it is for some embedded use cases.

Either way, this seems like it might be worth-while exploring for a future version of Ubuntu Core:

  1. homectl is basically just a nice looking D-Bus client for systemd-homed, so snapd could make the D-Bus calls directly for account creation. This should be a bit more reliable than shelling out to a perl script to do the job.
  2. It’s actively maintained, with a user base of more than just Ubuntu Core.
  3. The code quality seems higher, but it is also more code than extrausers.
3 Likes