//! Seal: the paths the systemd unit provisions are the paths the binary writes. //! //! `sando/deploy/bootstrap-node.sh` is the only writer of the makenotwork unit, //! and the unit's sandbox makes every path outside `ReadWritePaths` and the //! `StateDirectory` set read-only. So a constant in `makenotwork::constants` //! that names a directory the unit does not provision is not a compile error //! and not a failing request: it is an EPERM on a live box, at the moment a //! creator adds an SSH key or the scanner spools an upload. //! //! Both directions are checked. A path the code names must appear in the //! script, and every `StateDirectory` entry the script declares must be named //! by a constant, so a directory provisioned for nothing is caught too. //! //! Run with: cargo test --test unit_paths_seal use std::path::PathBuf; use makenotwork::constants::{KEYS_REBUILD_MARKER, SCAN_SPOOL_DIR}; /// systemd puts every `StateDirectory=` entry under this root. const STATE_DIRECTORY_ROOT: &str = "/var/lib/"; fn bootstrap_script() -> Option { let path: PathBuf = PathBuf::from(env!("CARGO_MANIFEST_DIR")) .join("../sando/deploy/bootstrap-node.sh") .components() .collect(); std::fs::read_to_string(path).ok() } /// The `StateDirectory=` entries the script declares, as absolute paths. fn declared_state_directories(script: &str) -> Vec { script .lines() .filter_map(|line| line.trim().strip_prefix("StateDirectory=")) .flat_map(|value| value.split_whitespace()) .map(|entry| format!("{STATE_DIRECTORY_ROOT}{entry}")) .collect() } #[test] fn every_provisioned_path_the_code_names_is_written_by_bootstrap() { let Some(script) = bootstrap_script() else { // A checkout of the server crate alone has no sibling sando. Nothing to // seal against, and failing here would only punish that layout. return; }; let declared = declared_state_directories(&script); assert!( !declared.is_empty(), "bootstrap-node.sh declares no StateDirectory; the unit template moved" ); for (name, path) in [ ("SCAN_SPOOL_DIR", SCAN_SPOOL_DIR), ("KEYS_REBUILD_MARKER", KEYS_REBUILD_MARKER), ] { assert!( declared.iter().any(|dir| path.starts_with(dir)), "{name} = {path} is under no StateDirectory bootstrap-node.sh declares \ ({declared:?}). The sandbox makes it read-only, so the first write EPERMs." ); } } #[test] fn the_rebuild_marker_the_unit_watches_is_the_one_the_server_writes() { let Some(script) = bootstrap_script() else { return; }; assert!( script.contains(&format!("KEYS_MARKER=\"{KEYS_REBUILD_MARKER}\"")), "the .path unit watches a different file than KEYS_REBUILD_MARKER ({KEYS_REBUILD_MARKER}); \ adding an SSH key would never reach sshd" ); assert!( script.contains("Unit=makenotwork-rebuild-keys.service"), "the marker is watched by no unit, so nothing rebuilds authorized_keys" ); let sudo_rebuild = script .lines() .find(|line| line.contains("NOPASSWD") && line.contains("rebuild-keys")); assert!( sudo_rebuild.is_none(), "bootstrap installs a sudoers rule for rebuild-keys ({sudo_rebuild:?}); the unit's \ sandbox implies NoNewPrivileges, so sudo cannot run from inside it" ); } #[test] fn every_declared_state_directory_has_a_constant_that_names_it() { let Some(script) = bootstrap_script() else { return; }; for dir in declared_state_directories(&script) { assert!( [SCAN_SPOOL_DIR, KEYS_REBUILD_MARKER] .iter() .any(|path| path.starts_with(&dir)), "bootstrap-node.sh provisions {dir} and no constant names it. Either the code \ that used it was deleted, or a constant drifted off the provisioned path." ); } }