no TOTP displayed in systemd-based initramfs
EvilBit opened this issue · 7 comments
Similar to #74, TOTP is not displayed when using systemd initramfs, i.e. following mkinitcpio.conf
hooks on Arch:
HOOKS=(… systemd tpm2-totp …)
I tried writing a small systemd unit file and accompanying installation hook, but no luck.
I checked that all the files are present in the initramfs with lsinitcpio
and tried to trace unit execution with systemd-analyze plot
.
The unit always gets started way too late in the boot process, long after systemd-ask-password-console.service
.
Also, tpm2-totp
seemed to fail on first invocation during initramfs stage with:
tpm2-totp[…]: failed to allocate dbus proxy object: Could not connect: No such file or directory
Could this be a side effect of having tpm2-abrmd
installed? I saw that it uses dbus and usr/lib/libtss2-tcti-tabrmd.so
got included in the initramfs.
On a side note - I haven't researched how to stop the service after the initrd.target
. Right now it keeps lingering in the booted system until stopped manually.
Thanks for writing awesome tpm2 tools ;)
For reference, the systemd unit (commented out parameters are from exploring possible permutations):
tpm2-totp.service
[Unit]
Description=Display attestation using tpm2-totp
#Requires=dev-tpm0.device
#After=dev-tpm0.device
Before=systemd-ask-password-console.service
DefaultDependencies=no
[Service]
Type=exec
#ExecStart=/usr/bin/show-tpm2-totp
ExecStart=/usr/lib/tpm2-totp/show-tpm2-totp
#StandardOutput=kmsg+console
StandardOutput=tty
#[Install]
#WantedBy=sysinit.target
#WantedBy=initrd.target
… and a small install hook:
sd-tpm2-totp
#!/bin/bash
build() {
add_systemd_unit "tpm2-totp.service"
}
NOTE: The unit has to be installed in /usr/lib/systemd/system
, not /etc/systemd/system
, otherwise the add_systemd_unit
function fails silently during mkinitcpio
and the unit doesn't get included in the initramfs.
Thank you for your detailed report! Could you test the following approach, please, which works at least on my machine?
/usr/lib/systemd/system/tpm2-totp.service
:
[Unit]
Description=Display a TOTP during boot
Requires=systemd-vconsole-setup.service dev-tpm0.device
After=systemd-vconsole-setup.service dev-tpm0.device
Conflicts=multi-user.target
DefaultDependencies=no
[Service]
Type=exec
ExecStart=/usr/lib/tpm2-totp/show-tpm2-totp
StandardOutput=tty
[Install]
WantedBy=sysinit.target
/usr/lib/systemd/system/sysinit.target.wants/tpm2-totp.service needs to be a symlink to that unit file:
sudo ln -s ../tpm2-totp.service /usr/lib/systemd/system/sysinit.target.wants/
/etc/initcpio/install/sd-tpm2-totp:
#!/bin/bash
build() {
local mod
if [[ $TPM_MODULES ]]; then
for mod in $TPM_MODULES; do
add_module "$mod"
done
else
add_all_modules /tpm/
fi
add_systemd_unit tpm2-totp.service
add_file /usr/lib/udev/rules.d/*tpm-udev.rules
add_binary tpm2-totp
add_binary /usr/lib/libtss2-tcti-device.so.0
add_binary date
}
/etc/mkinitcpio.conf
needs to have sd-tpm2-totp
anywhere in HOOKS
:
[...]
HOOKS=(base systemd [...] sd-tpm2-totp [...])
[...]
Now regenerate the initramfs and reboot:
sudo mkinitcpio -P
I'll add some more explanation as a separate comment.
The unit always gets started way too late in the boot process, long after
systemd-ask-password-console.service
.
Your unit file generally looks fine, but seems to lack a target specification when it should be executed, which might explain why it is executed too late: the service needs to be started as part of sysinit.target
, which the commented out WantedBy=sysinit.target
is supposed to achieve.
However, mkinitcpio doesn't create the necessary symlink to actually enable it automatically, so you have to create it yourself, see the ln -s
command from my first comment. This is also what we do in tpm2-totp for the plymouth service during installation.
Also,
tpm2-totp
seemed to fail on first invocation during initramfs stage with:
tpm2-totp[…]: failed to allocate dbus proxy object: Could not connect: No such file or directory
Could this be a side effect of havingtpm2-abrmd
installed? I saw that it uses dbus andusr/lib/libtss2-tcti-tabrmd.so
got included in the initramfs.
libtss2-tcti-tabrmd.so
gets pulled in by the sd-encrypt
hook. This is not really useful since I don't think D-Bus is already up at that point, so tpm2-abrmd wouldn't work anyway. On the other hand, it should only generate an annoying, but harmless error message, then continue to try using libtss2-tcti-device.so.0
instead, which should hopefully succeed. I'll look into getting the sd-encrypt
hook fixed; as a workaround, it would be possible to set the environment variable TPM2TOTP_TCTI=device
(e.g. by using Environment="TPM2TOTP_TCTI=device"
in the unit file) to suppress the error message.
On a side note - I haven't researched how to stop the service after the
initrd.target
. Right now it keeps lingering in the booted system until stopped manually.
This should be handled by the Conflicts=multi-user.target
directive in the unit file above: once user space is reached and this target is active, tpm2-totp.service
should get stopped in turn.
Thanks alot, that fixed it :)
The critical missing thing was actually the manual symlink in /usr/lib/systemd/system/sysinit.target.wants/
.
I had enabled the unit using systemctl enable tpm2-totp.service
, but that - of course - only created a symlink in /etc/systemd/system/sysinit.target.wants
, which is not picked up by the systemd
hook (not sure if this behavior is sensible, as well as ignoring units in /etc/systemd/system
- It kinda creates two disjunct modes of managing the system with systemd :/)
The first TOTP gets displayed 1-2 seconds after the LUKS password prompt, so still suboptimal, but at least it's there at all.
systemd-analyze plot
actually shows that the unit get started correctly before systemd-ask-password-console.service
, so that seems to be due to the latency of interacting with the tpm.
On a related note, I'm not entirely sure if StandardOutput=tty
is the most general option, as I'm not sure if it also displays correctly when booting over e.g. serial. If someone knows of a more canonical way of displaying output during boot, please let me know.
Oh, and just to clarify: my sd-tpm2-totp
install hook was used in addition to the original tpm2-totp
hook for testing, so the rest of the original initramfs generation logic was still in place.
BTW, you use the $TPM_MODULES
variable - so should the tpm_tis
module be added to MODULES=(…)
or to this separate variable?
I can try preparing a pull requests in the coming days if you so desire.
Thanks alot, that fixed it :)
Great to hear :)
The critical missing thing was actually the manual symlink in
/usr/lib/systemd/system/sysinit.target.wants/
.
I had enabled the unit usingsystemctl enable tpm2-totp.service
, but that - of course - only created a symlink in/etc/systemd/system/sysinit.target.wants
, which is not picked up by thesystemd
hook (not sure if this behavior is sensible, as well as ignoring units in/etc/systemd/system
- It kinda creates two disjunct modes of managing the system with systemd :/)
Yeah, that's very confusing indeed - I guess this should be fixed in mkinitcpio as well so that units under the /etc
hierarchy are recognised as well.
The first TOTP gets displayed 1-2 seconds after the LUKS password prompt, so still suboptimal, but at least it's there at all.
systemd-analyze plot
actually shows that the unit get started correctly beforesystemd-ask-password-console.service
, so that seems to be due to the latency of interacting with the tpm.
Same here, I think it's just the kernel module taking some time to load (which might have a somewhat low priority during boot).
On a related note, I'm not entirely sure if
StandardOutput=tty
is the most general option, as I'm not sure if it also displays correctly when booting over e.g. serial. If someone knows of a more canonical way of displaying output during boot, please let me know.
I think it should be fine, according to the systemd.exec documentation the standard TTY is /dev/console
, so it should also work over serial (but I am by no means an expert).
BTW, you use the
$TPM_MODULES
variable - so should thetpm_tis
module be added toMODULES=(…)
or to this separate variable?
The $TPM_MODULES
variable controls which kernel modules get included into the initramfs, so you could make the file a little bit smaller by specifying only the modules you need and omit the rest.
In contrast, the mkinitcpio MODULES
array controls which kernel modules actually get loaded during boot: this is done by systemd-modules-load.service
(which must therefore be started before tpm2-totp.service
). It reads configuration files from /etc/modules-load.d/*.conf
containing one module name per line and loads them. mkinitcpio automatically creates such a configuration file from the contents of the MODULES
array and writes it to /etc/modules-load.d/MODULES.conf
.
I am not sure whether there is a better way than specifying the required kernel module statically: usually it is better to just include the necessary modules and let udev to do its job to load them on demand. That's what we do for plymouth-tpm2-totp.service
as well (the dev-tpm0.device
is automagically generated by systemd once /dev/tpm0
appears), but I haven't been able to get that to work here yet.
I can try preparing a pull requests in the coming days if you so desire.
Sure, that would be great 🎉
I am not sure whether there is a better way than specifying the required kernel module statically: usually it is better to just include the necessary modules and let udev to do its job to load them on demand. That's what we do for
plymouth-tpm2-totp.service
as well (thedev-tpm0.device
is automagically generated by systemd once/dev/tpm0
appears), but I haven't been able to get that to work here yet.
Never mind, I just forgot to include the udev rules file in the sd-tpm2-totp
hook, which is needed to create the dev-tpm0.device
device unit. I have updated my first comment, further testing would be much appreciated if you have the time :)
This way, the unit file and install hook look pretty similar to the existing plymouth-tpm2-totp.service.in
and sd-plymouth-tpm2-totp.in
for plymouth.
Thanks for the quick replies :)
Never mind, I just forgot to include the udev rules file in the
sd-tpm2-totp
hook, which is needed to create thedev-tpm0.device
device unit. I have updated my first comment, further testing would be much appreciated if you have the time :)This way, the unit file and install hook look pretty similar to the existing
plymouth-tpm2-totp.service.in
andsd-plymouth-tpm2-totp.in
for plymouth.
Yeah, I realized and implemented that in the meantime as well - taking the plymouth fix from #74 serves as a pretty good skeleton.
Preliminary local PKGBUILD based on tpm2-totp-git
from AUR works fine. I'll polish up my code and try to push later today.
Anything special to consider before making a pull request?
Preliminary local PKGBUILD based on
tpm2-totp-git
from AUR works fine. I'll polish up my code and try to push later today.
Awesome :) I'll start a new release cycle once this has been merged as well.
Anything special to consider before making a pull request?
Keeping the structure similar to #75 seems like a good idea, otherwise nothing special as far as I can think of - we use the Developers Certificate of Origin, so remember to signoff your commits (git commit -s
).
Is the D-Bus/tpm2-abrmd error message still present in your local testing? In that case, I'd include the Environment="TPM2TOTP_TCTI=device:/dev/tpm0"
in the unit file as suggested above as a workaround to silence the error until the sd-encrypt
hook is fixed. (The unit file already depends on dev-tpm0.device
anyway, so the explicit specification of the TPM device is fine.)