From 247cbdc566c6b431ccb62f1ee43fb5db1d8479c0 Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Wed, 20 Mar 2024 16:55:29 +0100 Subject: [PATCH] osconfig: install root ssh keys to /var/roothome The current location via tmpfiles.d to install the root ssh keys seems to be not working. There is an error that `/root/.ssh` does not exist from `systemd-tmpfiles-setup` and indeed the authorized_keys file is on in `/root/.ssh`. It seems like everything else in the firstboot in tmpfiles.d is refering to `/var/roothome` instead of `/root` and switching to this for the location of the ssh keys solves the issue. Co-authored-by: Colin Walters --- lib/src/install/osconfig.rs | 43 +++++++++++++++++++++++++++++++++---- 1 file changed, 39 insertions(+), 4 deletions(-) diff --git a/lib/src/install/osconfig.rs b/lib/src/install/osconfig.rs index 3083c8ec..24bca491 100644 --- a/lib/src/install/osconfig.rs +++ b/lib/src/install/osconfig.rs @@ -1,8 +1,10 @@ +use std::borrow::Cow; use std::io::Write; -use anyhow::Result; +use anyhow::{Context, Result}; +use camino::{Utf8Path, Utf8PathBuf}; use cap_std::fs::Dir; -use cap_std_ext::cap_std; +use cap_std_ext::{cap_std, dirext::CapStdExtDirExt}; use fn_error_context::context; use ostree_ext::ostree; @@ -17,8 +19,22 @@ pub(crate) fn inject_root_ssh_authorized_keys( ) -> Result<()> { // While not documented right now, this one looks like it does not newline wrap let b64_encoded = ostree_ext::glib::base64_encode(contents.as_bytes()); + + // Eagerly resolve the path of /root in order to avoid tmpfiles.d clashes/problems. + // If it's local state (i.e. /root -> /var/roothome) then we resolve that symlink now. + let roothome_meta = root.symlink_metadata_optional("root")?; + let root_path = if roothome_meta.as_ref().filter(|m| m.is_symlink()).is_some() { + let path = root.read_link("root")?; + Utf8PathBuf::try_from(path) + .context("Reading /root symlink") + .map(Cow::Owned)? + } else { + Cow::Borrowed(Utf8Path::new("root")) + }; + // See the example in https://systemd.io/CREDENTIALS/ - let tmpfiles_content = format!("f~ /root/.ssh/authorized_keys 600 root root - {b64_encoded}\n"); + let tmpfiles_content = + format!("f~ /{root_path}/.ssh/authorized_keys 600 root root - {b64_encoded}\n"); crate::lsm::ensure_dir_labeled(root, ETC_TMPFILES, None, 0o755.into(), sepolicy)?; let tmpfiles_dir = root.open_dir(ETC_TMPFILES)?; @@ -35,11 +51,30 @@ pub(crate) fn inject_root_ssh_authorized_keys( } #[test] -fn test_inject_root_ssh() -> Result<()> { +fn test_inject_root_ssh_symlinked() -> Result<()> { let root = &cap_std_ext::cap_tempfile::TempDir::new(cap_std::ambient_authority())?; // The code expects this to exist, reasonably so root.create_dir("etc")?; + // Test with a symlink + root.symlink("var/roothome", "root")?; + inject_root_ssh_authorized_keys(root, None, "ssh-ed25519 ABCDE example@demo\n").unwrap(); + + let content = root.read_to_string(format!("etc/tmpfiles.d/{ROOT_SSH_TMPFILE}"))?; + assert_eq!( + content, + "f~ /var/roothome/.ssh/authorized_keys 600 root root - c3NoLWVkMjU1MTkgQUJDREUgZXhhbXBsZUBkZW1vCg==\n" + ); + + Ok(()) +} + +#[test] +fn test_inject_root_ssh_dir() -> Result<()> { + let root = &cap_std_ext::cap_tempfile::TempDir::new(cap_std::ambient_authority())?; + + root.create_dir("etc")?; + root.create_dir("root")?; inject_root_ssh_authorized_keys(root, None, "ssh-ed25519 ABCDE example@demo\n").unwrap(); let content = root.read_to_string(format!("etc/tmpfiles.d/{ROOT_SSH_TMPFILE}"))?;