lib

Core libraries for Radroots
git clone https://radroots.dev/git/lib.git
Log | Files | Refs | README

lib.rs (6009B)


      1 #![forbid(unsafe_code)]
      2 
      3 mod cli;
      4 mod error;
      5 mod managed;
      6 mod model;
      7 #[cfg(any(target_os = "linux", target_os = "macos"))]
      8 mod status;
      9 
     10 pub use cli::{ManagedCliCommand, ManagedCliInvocation};
     11 pub use error::RadrootsRuntimeManagerError;
     12 pub use managed::{ManagedRuntimeContext, ManagedRuntimeTarget, resolve_runtime_target};
     13 pub use model::{
     14     InstanceMetadataContract, LifecycleContract, ManagementDefaults, ManagementModeContract,
     15     RadrootsRuntimeManagementContract, RuntimeGroups,
     16 };
     17 #[cfg(any(target_os = "linux", target_os = "macos"))]
     18 pub use status::{ManagedRuntimeStatusClient, ManagedServiceStatusV1};
     19 
     20 pub const RUNTIME_MANAGEMENT_SCHEMA: &str = "radroots-runtime-management";
     21 pub const RUNTIME_MANAGEMENT_SCHEMA_VERSION: u32 = 1;
     22 pub const RUNTIME_MANAGEMENT_CONTRACT_MAX_UTF8_BYTES: usize = 1_048_576;
     23 
     24 pub(crate) const HARDENED_MANAGEMENT_CONTRACT: &str =
     25     include_str!("../tests/fixtures/hardened_service_management.v1.toml");
     26 
     27 pub fn parse_contract_str(
     28     raw: &str,
     29 ) -> Result<RadrootsRuntimeManagementContract, RadrootsRuntimeManagerError> {
     30     if raw.len() > RUNTIME_MANAGEMENT_CONTRACT_MAX_UTF8_BYTES {
     31         return Err(RadrootsRuntimeManagerError::ContractTooLarge);
     32     }
     33     let contract = toml::from_str::<RadrootsRuntimeManagementContract>(raw)
     34         .map_err(|_| RadrootsRuntimeManagerError::Parse)?;
     35     if contract.schema != RUNTIME_MANAGEMENT_SCHEMA {
     36         return Err(RadrootsRuntimeManagerError::UnexpectedSchema);
     37     }
     38     if contract.schema_version != RUNTIME_MANAGEMENT_SCHEMA_VERSION {
     39         return Err(RadrootsRuntimeManagerError::UnexpectedSchemaVersion);
     40     }
     41     validate_hardened_management_contract(&contract)?;
     42     Ok(contract)
     43 }
     44 
     45 pub(crate) fn validate_hardened_management_contract(
     46     contract: &RadrootsRuntimeManagementContract,
     47 ) -> Result<(), RadrootsRuntimeManagerError> {
     48     let expected =
     49         toml::from_str::<RadrootsRuntimeManagementContract>(HARDENED_MANAGEMENT_CONTRACT)
     50             .map_err(|_| RadrootsRuntimeManagerError::InvalidContract)?;
     51     if contract != &expected {
     52         return Err(RadrootsRuntimeManagerError::InvalidContract);
     53     }
     54     Ok(())
     55 }
     56 
     57 #[cfg(test)]
     58 mod tests {
     59     use std::error::Error as _;
     60 
     61     use super::{
     62         HARDENED_MANAGEMENT_CONTRACT, RUNTIME_MANAGEMENT_CONTRACT_MAX_UTF8_BYTES,
     63         RUNTIME_MANAGEMENT_SCHEMA, RadrootsRuntimeManagerError, parse_contract_str,
     64     };
     65 
     66     const CONTRACT: &str = HARDENED_MANAGEMENT_CONTRACT;
     67 
     68     #[test]
     69     fn contract_parser_accepts_only_the_exact_static_inventory() {
     70         let contract = parse_contract_str(CONTRACT).expect("contract");
     71         assert_eq!(contract.schema, RUNTIME_MANAGEMENT_SCHEMA);
     72         assert_eq!(contract.service_targets.len(), 2);
     73         assert_eq!(contract.managed_runtime_targets.active, ["myc", "rhi"]);
     74 
     75         for raw in [
     76             CONTRACT.replace("schema_version = 1", "schema_version = 2"),
     77             CONTRACT.replace(
     78                 "active = [\"myc\", \"rhi\"]",
     79                 "active = [\"myc\"]\ndefined = [\"rhi\"]",
     80             ),
     81             CONTRACT.replace(
     82                 "actions = [\"config_init\", \"config_validate\", \"state_init\", \"run\", \"status\", \"doctor\"]",
     83                 "actions = [\"start\"]",
     84             ),
     85             format!("{CONTRACT}\nunknown = true\n"),
     86         ] {
     87             assert!(parse_contract_str(&raw).is_err());
     88         }
     89         assert!(parse_contract_str("schema = [").is_err());
     90     }
     91 
     92     #[test]
     93     fn contract_parser_caps_the_complete_document_before_toml_parsing() {
     94         let mut exact = CONTRACT.to_owned();
     95         exact.push('#');
     96         exact.extend(std::iter::repeat_n(
     97             'x',
     98             RUNTIME_MANAGEMENT_CONTRACT_MAX_UTF8_BYTES - exact.len(),
     99         ));
    100         assert_eq!(exact.len(), RUNTIME_MANAGEMENT_CONTRACT_MAX_UTF8_BYTES);
    101         parse_contract_str(&exact).expect("exact maximum contract remains admissible");
    102 
    103         exact.push('x');
    104         assert_eq!(
    105             parse_contract_str(&exact),
    106             Err(RadrootsRuntimeManagerError::ContractTooLarge)
    107         );
    108 
    109         let very_large = format!("{}#{}", CONTRACT, "x".repeat(4 * 1024 * 1024));
    110         assert_eq!(
    111             parse_contract_str(&very_large),
    112             Err(RadrootsRuntimeManagerError::ContractTooLarge)
    113         );
    114     }
    115 
    116     #[test]
    117     fn contract_errors_are_value_free_and_source_free() {
    118         for (raw, secret) in [
    119             (
    120                 CONTRACT.replace(
    121                     "schema = \"radroots-runtime-management\"",
    122                     "schema = \"/sensitive/root/secret-schema\"",
    123                 ),
    124                 "/sensitive/root/secret-schema",
    125             ),
    126             (
    127                 "credential = 'secret-value'\ninvalid = [".to_owned(),
    128                 "secret-value",
    129             ),
    130         ] {
    131             let error = parse_contract_str(&raw).expect_err("invalid contract");
    132             let rendered = format!("{error} {error:?}");
    133             assert!(!rendered.contains(secret));
    134             assert!(error.source().is_none());
    135         }
    136     }
    137 
    138     #[test]
    139     fn root_surface_exposes_no_legacy_runtime_authority() {
    140         let source = include_str!("lib.rs")
    141             .split("\n#[cfg(test)]")
    142             .next()
    143             .expect("production source");
    144         for forbidden in [
    145             "mod lifecycle",
    146             "mod paths",
    147             "mod registry",
    148             "ManagedRuntimeArtifactName",
    149             "ManagedRuntimeInstancePaths",
    150             "ManagedRuntimeSharedPaths",
    151             "ManagedRuntimeInstanceRegistry",
    152             "ManagedRuntimeInstanceRecord",
    153             "ManagedRuntimeLifecycleAction",
    154             "load_registry",
    155             "save_registry",
    156             "start_process",
    157             "stop_process",
    158             "install_binary",
    159             "extract_binary_archive",
    160             "remove_instance_artifacts",
    161             "write_instance_config",
    162         ] {
    163             assert!(!source.contains(forbidden), "root retained `{forbidden}`");
    164         }
    165     }
    166 }