bootc_lib/install/
osconfig.rs1use std::borrow::Cow;
2use std::io::Write;
3
4use anyhow::{Context, Result};
5use camino::{Utf8Path, Utf8PathBuf};
6use cap_std::fs::Dir;
7use cap_std_ext::{cap_std, dirext::CapStdExtDirExt};
8use fn_error_context::context;
9use ostree_ext::ostree;
10
11const ETC_TMPFILES: &str = "etc/tmpfiles.d";
12const ROOT_SSH_TMPFILE: &str = "bootc-root-ssh.conf";
13
14#[context("Injecting root authorized_keys")]
15pub(crate) fn inject_root_ssh_authorized_keys(
16 root: &Dir,
17 sepolicy: Option<&ostree::SePolicy>,
18 contents: &str,
19) -> Result<()> {
20 let b64_encoded = ostree_ext::glib::base64_encode(contents.as_bytes());
22
23 let roothome_meta = root.symlink_metadata_optional("root")?;
26 let root_path = if roothome_meta.as_ref().filter(|m| m.is_symlink()).is_some() {
27 let path = root.read_link("root")?;
28 Utf8PathBuf::try_from(path)
29 .context("Reading /root symlink")
30 .map(Cow::Owned)?
31 } else {
32 Cow::Borrowed(Utf8Path::new("root"))
33 };
34
35 let tmpfiles_content =
37 format!("f~ /{root_path}/.ssh/authorized_keys 600 root root - {b64_encoded}\n");
38
39 crate::lsm::ensure_dir_labeled(root, ETC_TMPFILES, None, 0o755.into(), sepolicy)?;
40 let tmpfiles_dir = root.open_dir(ETC_TMPFILES)?;
41 crate::lsm::atomic_replace_labeled(
42 &tmpfiles_dir,
43 ROOT_SSH_TMPFILE,
44 0o644.into(),
45 sepolicy,
46 |w| w.write_all(tmpfiles_content.as_bytes()).map_err(Into::into),
47 )?;
48
49 println!("Injected: {ETC_TMPFILES}/{ROOT_SSH_TMPFILE}");
50 Ok(())
51}
52
53#[cfg(test)]
54mod tests {
55 use super::*;
56
57 #[test]
58 fn test_inject_root_ssh_symlinked() -> Result<()> {
59 let root = &cap_std_ext::cap_tempfile::TempDir::new(cap_std::ambient_authority())?;
60
61 root.create_dir("etc")?;
63 root.symlink("var/roothome", "root")?;
65 inject_root_ssh_authorized_keys(root, None, "ssh-ed25519 ABCDE example@demo\n").unwrap();
66
67 let content = root.read_to_string(format!("etc/tmpfiles.d/{ROOT_SSH_TMPFILE}"))?;
68 assert_eq!(
69 content,
70 "f~ /var/roothome/.ssh/authorized_keys 600 root root - c3NoLWVkMjU1MTkgQUJDREUgZXhhbXBsZUBkZW1vCg==\n"
71 );
72
73 Ok(())
74 }
75
76 #[test]
77 fn test_inject_root_ssh_dir() -> Result<()> {
78 let root = &cap_std_ext::cap_tempfile::TempDir::new(cap_std::ambient_authority())?;
79
80 root.create_dir("etc")?;
81 root.create_dir("root")?;
82 inject_root_ssh_authorized_keys(root, None, "ssh-ed25519 ABCDE example@demo\n").unwrap();
83
84 let content = root.read_to_string(format!("etc/tmpfiles.d/{ROOT_SSH_TMPFILE}"))?;
85 assert_eq!(
86 content,
87 "f~ /root/.ssh/authorized_keys 600 root root - c3NoLWVkMjU1MTkgQUJDREUgZXhhbXBsZUBkZW1vCg==\n"
88 );
89 Ok(())
90 }
91}