liminal-server 0.24.5

Standalone server for the liminal messaging bus
Documentation
//! Filesystem boundary and exact digest contracts, without executing a command.

use super::{TemplatePolicy, TemplateSources};
use crate::services::lifecycle::site::test_support::TestResult;
use serde_json::json;
use std::os::unix::fs::PermissionsExt;

fn policy(root: &std::path::Path) -> Result<TemplatePolicy, serde_json::Error> {
    serde_json::from_value(json!({"roots":[root],"command_bytes":1_048_576,"records":8,
        "arguments":32,"environment_names":16,"text_bytes":1024,"stop_timeout_ms":2000}))
}

#[test]
fn only_regular_executables_inside_canonical_roots_are_proposable() -> TestResult {
    let root = tempfile::tempdir()?;
    let outside = tempfile::tempdir()?;
    let policy = policy(root.path())?;
    let command = root.path().join("program");
    std::fs::write(&command, b"approved command bytes")?;
    let refused = policy
        .digest("echo", &command)
        .err()
        .ok_or("non executable admitted")?;
    assert!(refused.to_string().contains("has no execute bit"));
    std::fs::set_permissions(&command, std::fs::Permissions::from_mode(0o700))?;
    let (_, approved) = policy.digest("echo", &command)?;
    assert_eq!(approved.len(), 64);
    let refused = policy
        .digest("echo", root.path())
        .err()
        .ok_or("directory admitted")?;
    assert!(refused.to_string().contains("not a regular file"));
    let foreign = outside.path().join("program");
    std::fs::write(&foreign, b"foreign")?;
    std::fs::set_permissions(&foreign, std::fs::Permissions::from_mode(0o700))?;
    let link = root.path().join("escape");
    std::os::unix::fs::symlink(&foreign, &link)?;
    assert!(
        policy
            .digest("echo", &foreign)
            .err()
            .ok_or("foreign command admitted")?
            .to_string()
            .contains("outside [auth.lys.templates] roots")
    );
    assert!(policy.digest("echo", &link).is_err());
    std::fs::write(&command, b"changed command bytes")?;
    assert_ne!(policy.digest("echo", &command)?.1, approved);
    Ok(())
}

#[test]
fn legacy_configuration_and_root_policy_have_unambiguous_toml_shapes() -> TestResult {
    #[derive(serde::Deserialize)]
    struct Config {
        templates: TemplateSources,
    }
    let legacy: Config = toml::from_str(
        r#"[[templates]]
name="echo"
command="/usr/bin/true"
subjects=["echo"]
stop_timeout_ms=1000
"#,
    )?;
    assert_eq!(legacy.templates.len(), 1);
    assert!(legacy.templates.policy.is_none());
    let root = tempfile::tempdir()?;
    let table: Config = toml::from_str(&format!(
        r#"[templates]
roots=["{}"]
command_bytes=1048576
records=8
arguments=16
environment_names=8
text_bytes=1024
stop_timeout_ms=2000
[[templates.configured]]
name="echo"
command="/usr/bin/true"
subjects=["echo"]
stop_timeout_ms=1000
"#,
        root.path().display()
    ))?;
    assert_eq!(table.templates.len(), 1);
    assert!(table.templates.policy.is_some());
    assert!(toml::from_str::<Config>("[templates]\nroots=[]").is_err());
    Ok(())
}

#[test]
fn args_and_environment_names_never_appear_in_configuration_debug() {
    let template = crate::config::types::ServiceTemplateConfig {
        name: "echo".into(),
        command: "/usr/bin/true".into(),
        args: vec!["private argument".into()],
        subjects: vec!["echo".into()],
        env: vec!["PRIVATE_ENV".into()],
        stop_timeout_ms: 1000,
    };
    let debug = format!("{template:?}");
    assert!(!debug.contains("private argument"));
    assert!(!debug.contains("PRIVATE_ENV"));
}

#[test]
fn approved_template_is_live_without_restart_and_changed_command_never_starts() -> TestResult {
    use crate::services::lifecycle::ServiceState;
    use crate::services::lifecycle::site::{
        SiteChange,
        test_support::{Fixture, spec},
    };
    let root = tempfile::tempdir()?;
    let command = root.path().join("program");
    std::fs::write(&command, b"#!/bin/sh\nexit 0\n")?;
    std::fs::set_permissions(&command, std::fs::Permissions::from_mode(0o700))?;
    let fixture = Fixture::with_policy(Some(policy(root.path())?))?;
    fixture.execute("create", SiteChange::CreateSite(spec()))?;
    fixture.execute(
        "bind",
        SiteChange::BindBuilder {
            agent: "builder".into(),
        },
    )?;
    let proposal = fixture.lifecycle.propose_template(
        "echo",
        &command,
        vec!["exact argument".into()],
        vec!["LANG".into()],
        1000,
    )?;
    let approved = proposal.digest.clone();
    fixture.execute("template", SiteChange::CreateTemplate(proposal))?;
    let list = fixture.lifecycle.list(|_| true)?;
    assert_eq!(list["templates"][0]["name"], "echo");
    fixture.execute("complete", SiteChange::Complete)?;
    assert_eq!(
        fixture.lifecycle.record("echo")?.state,
        ServiceState::Asleep
    );
    assert_eq!(
        fixture
            .issuer
            .grants
            .lock()
            .map_err(|_| "fixture grants poisoned")?
            .len(),
        1
    );
    std::fs::write(&command, b"#!/bin/sh\nexit 7\n")?;
    let refused = fixture
        .lifecycle
        .deploy("echo", "person-owner")
        .err()
        .ok_or("changed approved command started")?;
    let words = refused.to_string();
    assert!(words.contains("is not the file approved on 10"));
    assert!(words.contains(&format!("digest {approved} !=")));
    assert_eq!(
        fixture.lifecycle.list(|_| true)?["services"][0]["running"],
        false
    );
    let registry = fixture.lifecycle.lock()?;
    let template = registry
        .construction
        .templates
        .get("echo")
        .ok_or("live template missing")?;
    assert!(template.transitions[0].receipt.is_some());
    drop(registry);
    Ok(())
}

#[test]
fn a_path_replacement_after_staging_cannot_change_the_executed_bytes() -> TestResult {
    use crate::services::lifecycle::site::{
        SiteChange,
        test_support::{Fixture, spec},
    };
    let root = tempfile::tempdir()?;
    let command = root.path().join("program");
    let approved = b"#!/bin/sh\nexit 0\n";
    std::fs::write(&command, approved)?;
    std::fs::set_permissions(&command, std::fs::Permissions::from_mode(0o700))?;
    let fixture = Fixture::with_policy(Some(policy(root.path())?))?;
    fixture.execute("create", SiteChange::CreateSite(spec()))?;
    let proposal = fixture
        .lifecycle
        .propose_template("echo", &command, vec![], vec![], 1000)?;
    fixture.execute("template", SiteChange::CreateTemplate(proposal))?;
    let template = fixture
        .lifecycle
        .lock()?
        .construction
        .templates
        .get("echo")
        .cloned()
        .ok_or("template missing")?;
    let staged = fixture.lifecycle.stage_template(&template)?;
    let staged_path = staged.command().to_path_buf();
    std::fs::remove_file(&command)?;
    std::fs::write(&command, b"#!/bin/sh\nexit 7\n")?;
    assert_eq!(std::fs::read(&staged_path)?, approved);
    assert_eq!(
        std::fs::metadata(&staged_path)?.permissions().mode() & 0o777,
        0o500
    );
    drop(staged);
    assert!(!staged_path.exists());
    Ok(())
}