crates/heimdall-broker/src/lib.rs
// SPDX-License-Identifier: AGPL-3.0-only
// Copyright (C) 2026 Ignalina ApS and Vetra Automation AB
//! heimdall-broker: Heimdall's syscall broker (spec §4, layer 2).
//!
//! **Heimdall is a defensive sandbox.** The broker is a Rust supervisor on
//! seccomp `SECCOMP_RET_USER_NOTIF` (kernel ≥ 5.0) with
//! `SECCOMP_IOCTL_NOTIF_ADDFD` (≥ 5.9). The worker's risky calls
//! (`openat`/`openat2`/`open`/`creat`, `socket`, `connect`, `bind`, `execve`)
//! pause the worker; the broker checks them against the job's
//! [`BrokerPolicy`] (in/ read-only, out/ write-only, the declared address:port peers of a
//! fetch job, nothing else) and, when allowed, opens the file or socket
//! ITSELF and injects the fd. The worker's own call never proceeds on a
//! pointer it can still change (TOCTOU-safe). Every decision becomes a
//! [`heimdall_core::BrokerEvent`] for `JobResult.events`; a refused call
//! gets `EACCES` or kills the worker, per policy.
//!
//! Fallbacks (spec §10), found by asking the kernel ([`Support::probe`]):
//! no USER_NOTIF = brokered calls are plain denies ([`Mode::DenyOnly`]); no
//! ADDFD = check and let proceed, with a warning ([`Mode::CheckOnly`]).
//! `Features.broker` is true only in [`Mode::Full`]
//! ([`BrokerReport::broker_feature`]).
//!
//! The filter is built with heimdall-seccomp's BPF builder and served with
//! its USER_NOTIF API ([`heimdall_seccomp::notif`]); it is stacked on the
//! workload's allowlist ([`filter`]). heimdall-container installs it inside
//! each job's container and runs this broker in its shim.
pub mod broker;
pub mod filter;
pub mod policy;
pub mod spawn;
pub mod support;
mod sys;
pub use broker::{errno_name, Broker, BrokerReport, DebugKnobs};
pub use filter::FilterMode;
pub use policy::{Access, BrokerPolicy, ExecPolicy, OnRefuse, OpenPlan, Prefix, Refusal, Resolve};
pub use spawn::{
handoff_pair, install_worker_filter, serve_handoff, spawn_brokered, BrokeredChild,
SpawnOptions, StopSignal,
};
pub use support::{broker_feature, Mode, Support};
/// Why the broker could not start or finish.
#[derive(Debug, thiserror::Error)]
pub enum BrokerError {
#[error("bad broker policy: {0}")]
Policy(String),
#[error("not supported here: {0}")]
Unsupported(String),
#[error("{what}: {source}")]
Io {
what: String,
#[source]
source: std::io::Error,
},
#[error("the broker thread panicked")]
Panicked,
}
/// Would a broker run in full mode on this kernel? (What a runner puts in
/// `Features.broker` before a job; after it, use the job's report.)
pub fn available() -> bool {
broker_feature(Support::probe().mode())
}