| 1 |
|
| 2 |
|
| 3 |
|
| 4 |
|
| 5 |
|
| 6 |
|
| 7 |
|
| 8 |
|
| 9 |
|
| 10 |
|
| 11 |
|
| 12 |
|
| 13 |
|
| 14 |
|
| 15 |
|
| 16 |
use std::path::PathBuf; |
| 17 |
|
| 18 |
use makenotwork::constants::{KEYS_REBUILD_MARKER, SCAN_SPOOL_DIR}; |
| 19 |
|
| 20 |
|
| 21 |
const STATE_DIRECTORY_ROOT: &str = "/var/lib/"; |
| 22 |
|
| 23 |
fn bootstrap_script() -> Option<String> { |
| 24 |
let path: PathBuf = PathBuf::from(env!("CARGO_MANIFEST_DIR")) |
| 25 |
.join("../sando/deploy/bootstrap-node.sh") |
| 26 |
.components() |
| 27 |
.collect(); |
| 28 |
std::fs::read_to_string(path).ok() |
| 29 |
} |
| 30 |
|
| 31 |
|
| 32 |
fn declared_state_directories(script: &str) -> Vec<String> { |
| 33 |
script |
| 34 |
.lines() |
| 35 |
.filter_map(|line| line.trim().strip_prefix("StateDirectory=")) |
| 36 |
.flat_map(|value| value.split_whitespace()) |
| 37 |
.map(|entry| format!("{STATE_DIRECTORY_ROOT}{entry}")) |
| 38 |
.collect() |
| 39 |
} |
| 40 |
|
| 41 |
#[test] |
| 42 |
fn every_provisioned_path_the_code_names_is_written_by_bootstrap() { |
| 43 |
let Some(script) = bootstrap_script() else { |
| 44 |
|
| 45 |
|
| 46 |
return; |
| 47 |
}; |
| 48 |
|
| 49 |
let declared = declared_state_directories(&script); |
| 50 |
assert!( |
| 51 |
!declared.is_empty(), |
| 52 |
"bootstrap-node.sh declares no StateDirectory; the unit template moved" |
| 53 |
); |
| 54 |
|
| 55 |
for (name, path) in [ |
| 56 |
("SCAN_SPOOL_DIR", SCAN_SPOOL_DIR), |
| 57 |
("KEYS_REBUILD_MARKER", KEYS_REBUILD_MARKER), |
| 58 |
] { |
| 59 |
assert!( |
| 60 |
declared.iter().any(|dir| path.starts_with(dir)), |
| 61 |
"{name} = {path} is under no StateDirectory bootstrap-node.sh declares \ |
| 62 |
({declared:?}). The sandbox makes it read-only, so the first write EPERMs." |
| 63 |
); |
| 64 |
} |
| 65 |
} |
| 66 |
|
| 67 |
#[test] |
| 68 |
fn the_rebuild_marker_the_unit_watches_is_the_one_the_server_writes() { |
| 69 |
let Some(script) = bootstrap_script() else { |
| 70 |
return; |
| 71 |
}; |
| 72 |
|
| 73 |
assert!( |
| 74 |
script.contains(&format!("KEYS_MARKER=\"{KEYS_REBUILD_MARKER}\"")), |
| 75 |
"the .path unit watches a different file than KEYS_REBUILD_MARKER ({KEYS_REBUILD_MARKER}); \ |
| 76 |
adding an SSH key would never reach sshd" |
| 77 |
); |
| 78 |
assert!( |
| 79 |
script.contains("Unit=makenotwork-rebuild-keys.service"), |
| 80 |
"the marker is watched by no unit, so nothing rebuilds authorized_keys" |
| 81 |
); |
| 82 |
let sudo_rebuild = script |
| 83 |
.lines() |
| 84 |
.find(|line| line.contains("NOPASSWD") && line.contains("rebuild-keys")); |
| 85 |
assert!( |
| 86 |
sudo_rebuild.is_none(), |
| 87 |
"bootstrap installs a sudoers rule for rebuild-keys ({sudo_rebuild:?}); the unit's \ |
| 88 |
sandbox implies NoNewPrivileges, so sudo cannot run from inside it" |
| 89 |
); |
| 90 |
} |
| 91 |
|
| 92 |
#[test] |
| 93 |
fn every_declared_state_directory_has_a_constant_that_names_it() { |
| 94 |
let Some(script) = bootstrap_script() else { |
| 95 |
return; |
| 96 |
}; |
| 97 |
|
| 98 |
for dir in declared_state_directories(&script) { |
| 99 |
assert!( |
| 100 |
[SCAN_SPOOL_DIR, KEYS_REBUILD_MARKER] |
| 101 |
.iter() |
| 102 |
.any(|path| path.starts_with(&dir)), |
| 103 |
"bootstrap-node.sh provisions {dir} and no constant names it. Either the code \ |
| 104 |
that used it was deleted, or a constant drifted off the provisioned path." |
| 105 |
); |
| 106 |
} |
| 107 |
} |
| 108 |
|