Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/holger

jobs/runner/src/lib.rs

//! # `holger-job-runner`: the runners behind Holger's job API
//!
//! Holger's untrusted work (verifying a bundle that came through the inbox, the
//! scans) is a [`heimdall_core::Job`] (Heimdall's own job API, nordisk-heimdall-core 0.2: the signed wire v2), run by
//! an `Arc<dyn heimdall_core::JobRunner>` chosen once at boot. The caller accepts
//! only a [`heimdall_core::SignedResult`] that [`accept`] believes: the pinned
//! per-boot key, the job id it issued, and exactly the listed outputs with their
//! hashes. It never opens the job's input itself.
//!
//! | Runner | What it is | Where |
//! |---|---|---|
//! | [`ContainerRunner`] | the INTERIM runner: Holger's own sandbox (`server/sandbox`: podman, its API socket, crun, the sidecar), a fresh container per job, no network, a read-only root, the engine's default seccomp | every build |
//! | `InProcessRunner` | the job's program in the caller's own process | a test build only (feature `test-runner`) |
//! | `heimdall::HeimdallJobs` | Holger's worker under Heimdall's protected runner (`heimdall-container`: Landlock, Heimdall's seccomp profile, the image verified at every start), or `youki-plain` where Heimdall cannot start | feature `heimdall`, `engine: heimdall` |
//!
//! [`ContainerRunner::features`] says truthfully what the interim runner does not
//! have yet (no generated syscall profile, no broker, no Landlock, no wasm), and
//! Holger shows it on its Security page and in the CLI.
//!
//! **The worker's side** ([`wire`]): the job runs `holger-scan-worker` over
//! `/in/job.json` (a [`wire::WireJob`]); its input directory is `/in/i`, the
//! policy's read paths `/in/r0`, `/in/r1`, …, and its one writable directory
//! `/out`. It answers ONE [`wire::Answer`] on stdout: the verdict and the names of
//! the files it wrote. The runner lists and hashes those files itself, outside the
//! job, and signs the result with a key the job never sees.

pub mod accept;
pub mod collect;
pub mod container;
pub mod fetch_wire;
#[cfg(any(feature = "heimdall", feature = "heimdall-wasm"))]
pub mod guests;
#[cfg(feature = "heimdall")]
pub mod heimdall;
#[cfg(feature = "test-runner")]
pub mod in_process;
pub mod key;
#[cfg(feature = "heimdall")]
pub mod seccomp;
pub mod wire;

pub use accept::{accept, Rejected};
pub use container::ContainerRunner;
pub use heimdall_core;
#[cfg(feature = "heimdall-wasm")]
pub use heimdall_run;
#[cfg(feature = "test-runner")]
pub use in_process::InProcessRunner;
pub use key::{key_words, ResultKey};

use heimdall_core::{ImageRef, JobKind};

/// The worker image's name every Holger job names. The runner decides which image
/// that is (the configured, digest-pinned or locally minted `holger-scan-worker`,
/// or the crun rootfs verified at boot), so the digest here is empty.
pub const WORKER: &str = "holger-scan-worker";

/// The worker program that looks at the box from inside (`server_lib::sandbox::PROBE_PROGRAM`):
/// it keeps Heimdall's fixed seccomp profile (`heimdall::HeimdallJobs::with_seccomp`).
pub const PROBE_PROGRAM: &str = "probe";

/// A job of Holger's worker running `argv` (`["verify-bundle"]`, `["scan"]`).
pub fn worker(argv: &[&str]) -> JobKind {
    JobKind::Native {
        image: ImageRef {
            name: WORKER.into(),
            digest: String::new(),
        },
        argv: argv.iter().map(|a| a.to_string()).collect(),
    }
}

/// The plain words for a runner's features: `(what, has it, the words)`, the rows
/// the Security page and the CLI print.
pub fn feature_rows(f: &heimdall_core::Features) -> Vec<(&'static str, bool, String)> {
    use heimdall_core::Protection;
    vec![
        (
            "runner",
            f.runner == "heimdall",
            format!(
                "{}{}",
                f.runner,
                if f.runner == "heimdall" {
                    ""
                } else {
                    " (interim, not Heimdall)"
                }
            ),
        ),
        (
            "container",
            f.container,
            if f.container {
                "a fresh container per job: namespaces, a read-only root, no network".into()
            } else {
                "no container per job".into()
            },
        ),
        (
            "seccomp",
            // Heimdall's fixed profile is Heimdall's own protection (lane HOL-HEIMDALL-02);
            // every other runner's `Default` is its engine's.
            matches!(f.seccomp, Protection::Generated { .. })
                || (f.runner == "heimdall" && f.seccomp == Protection::Default),
            match &f.seccomp {
                Protection::None => "no seccomp filter of its own".into(),
                Protection::Default if f.runner == "heimdall" => {
                    "Heimdall's fixed, hand-reviewed seccomp profile (stricter than the engine default; the dangerous calls kill the job), not one generated for the worker".into()
                }
                Protection::Default => {
                    "the engine's default seccomp profile, not one generated for the worker".into()
                }
                Protection::Generated { profile_digest } => {
                    format!("a generated profile ({profile_digest})")
                }
            },
        ),
        (
            "landlock",
            f.landlock,
            if f.landlock {
                "Landlock: only the job's in and out".into()
            } else {
                "no Landlock".into()
            },
        ),
        (
            "broker",
            f.broker,
            if f.broker {
                "the syscall broker answers the risky calls".into()
            } else {
                "no syscall broker".into()
            },
        ),
        (
            "wasm",
            f.wasm,
            if f.wasm {
                "parsers as wasm guests".into()
            } else {
                "no wasm guests: the parsers run native".into()
            },
        ),
        (
            "image",
            f.image_verified_each_start,
            if f.image_verified_each_start {
                "the image is verified at every start".into()
            } else {
                "the image is not verified at every start".into()
            },
        ),
    ]
}

#[cfg(all(test, feature = "heimdall"))]
mod heimdall_tests;
#[cfg(test)]
mod tests;