brokerd: the podman argument lists
Implemented-By: OpenCode session (model recorded in docs/implementer-log.md)
This commit is contained in:
@@ -8,6 +8,7 @@ pub mod broker;
|
||||
pub mod config;
|
||||
pub mod grants;
|
||||
pub mod ledger;
|
||||
pub mod podman;
|
||||
pub mod policy;
|
||||
pub mod runner;
|
||||
pub mod serve;
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
//! The `podman` argument lists for one call's container and for the `http_fetch` egress proxy.
|
||||
//!
|
||||
//! Nothing is passed through a shell: every argument is its own `OsString`, and the tool's
|
||||
//! arguments go on standard input, never on the command line. Built without running Podman, so a
|
||||
//! runtime that only builds the list can be tested as a golden file. Spec section 6.
|
||||
|
||||
use crate::config::Runner;
|
||||
use crate::runner::RunSpec;
|
||||
use proto::{CallId, SessionId};
|
||||
use std::ffi::OsString;
|
||||
use std::path::Path;
|
||||
|
||||
pub const EGRESS_MOUNT: &str = "/run/egress";
|
||||
pub const EGRESS_SOCKET: &str = "/run/egress/egress.sock";
|
||||
pub const TOOLKIT: &str = "/bin/toolkit";
|
||||
|
||||
/// The container's name: `boxmaker-<session>-<call>-<n>`, so it says whose call it is.
|
||||
pub fn container_name(session: &SessionId, call: CallId, n: u64) -> String {
|
||||
format!("boxmaker-{}-{}-{}", session.as_str(), call.0, n)
|
||||
}
|
||||
|
||||
/// The six hardening flags, common to the tool and the proxy. `pids` and `memory` differ: the tool
|
||||
/// takes the runner's, the proxy its fixed limits.
|
||||
fn hardening(pids: u32, memory: &str) -> Vec<OsString> {
|
||||
[
|
||||
"--read-only",
|
||||
"--cap-drop=all",
|
||||
"--security-opt=no-new-privileges",
|
||||
"--userns=keep-id",
|
||||
]
|
||||
.map(OsString::from)
|
||||
.into_iter()
|
||||
.chain([
|
||||
OsString::from(format!("--pids-limit={pids}")),
|
||||
OsString::from(format!("--memory={memory}")),
|
||||
])
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// A `--volume=<host>:<container>:<mode>` argument, built with `push` so a directory need not be
|
||||
/// UTF-8.
|
||||
fn volume(host: &Path, container: &Path, mode: &str) -> OsString {
|
||||
let mut arg = OsString::new();
|
||||
arg.push("--volume=");
|
||||
arg.push(host);
|
||||
arg.push(":");
|
||||
arg.push(container);
|
||||
arg.push(":");
|
||||
arg.push(mode);
|
||||
arg
|
||||
}
|
||||
|
||||
/// The tool's container. `egress` is the call's egress directory, for `http_fetch` only.
|
||||
pub fn tool_args(
|
||||
spec: &RunSpec,
|
||||
runner: &Runner,
|
||||
name: &str,
|
||||
egress: Option<&Path>,
|
||||
) -> Vec<OsString> {
|
||||
let mut args: Vec<OsString> = [
|
||||
"run",
|
||||
"--rm",
|
||||
"-i",
|
||||
&format!("--name={name}"),
|
||||
"--label=boxmaker=tool",
|
||||
"--network=none",
|
||||
]
|
||||
.map(OsString::from)
|
||||
.into_iter()
|
||||
.collect();
|
||||
args.extend(hardening(runner.pids, &runner.memory));
|
||||
args.push(OsString::from("--tmpfs=/tmp:rw,size=64m,mode=1777"));
|
||||
for mount in spec.mounts() {
|
||||
let mode = if mount.writable { "rw" } else { "ro" };
|
||||
args.push(volume(Path::new(&mount.path), Path::new(&mount.path), mode));
|
||||
}
|
||||
if let Some(dir) = egress {
|
||||
args.push(volume(dir, Path::new(EGRESS_MOUNT), "rw"));
|
||||
}
|
||||
args.push(OsString::from(runner.image.as_str()));
|
||||
args.push(OsString::from(TOOLKIT));
|
||||
args.push(OsString::from(spec.tool().as_str()));
|
||||
args
|
||||
}
|
||||
|
||||
/// The egress proxy's container.
|
||||
pub fn egress_args(runner: &Runner, name: &str, dir: &Path, hosts: &[String]) -> Vec<OsString> {
|
||||
let mut args: Vec<OsString> = [
|
||||
"run",
|
||||
"-d",
|
||||
"--rm",
|
||||
&format!("--name={name}-egress"),
|
||||
"--label=boxmaker=egress",
|
||||
&format!("--network={}", runner.egress_network),
|
||||
]
|
||||
.map(OsString::from)
|
||||
.into_iter()
|
||||
.collect();
|
||||
args.extend(hardening(64, "128m"));
|
||||
args.push(volume(dir, Path::new(EGRESS_MOUNT), "rw"));
|
||||
args.push(OsString::from(runner.image.as_str()));
|
||||
args.push(OsString::from(TOOLKIT));
|
||||
args.push(OsString::from("egress-proxy"));
|
||||
args.push(OsString::from("--socket"));
|
||||
args.push(OsString::from(EGRESS_SOCKET));
|
||||
args.push(OsString::from("--allow"));
|
||||
args.push(OsString::from(hosts.join(",")));
|
||||
args
|
||||
}
|
||||
@@ -5,6 +5,8 @@
|
||||
//!
|
||||
//! ```compile_fail
|
||||
//! let _ = brokerd::runner::RunSpec {
|
||||
//! session: proto::SessionId::new("s1").unwrap(),
|
||||
//! call: proto::CallId(1),
|
||||
//! tool: brokerd::args::ToolName::Shell,
|
||||
//! arguments: todo!(),
|
||||
//! mounts: Vec::new(),
|
||||
@@ -20,7 +22,7 @@
|
||||
|
||||
use crate::args::{ToolArgs, ToolName};
|
||||
use crate::policy::Decision;
|
||||
use proto::ToolResponse;
|
||||
use proto::{CallId, SessionId, ToolResponse};
|
||||
|
||||
/// A directory mounted for one call: a path and whether the runtime may write to it.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
@@ -29,10 +31,13 @@ pub struct Mount {
|
||||
pub writable: bool,
|
||||
}
|
||||
|
||||
/// What one call was turned into before it reached a `Runtime`: the tool, its arguments, the
|
||||
/// directories mounted and the hosts it may reach. Built only here, from a `Decision`.
|
||||
/// What one call was turned into before it reached a `Runtime`: the session and call it is, the
|
||||
/// tool, its arguments, the directories mounted and the hosts it may reach. Built only here, from a
|
||||
/// `Decision`.
|
||||
#[derive(Debug)]
|
||||
pub struct RunSpec {
|
||||
session: SessionId,
|
||||
call: CallId,
|
||||
tool: ToolName,
|
||||
arguments: ToolArgs,
|
||||
mounts: Vec<Mount>,
|
||||
@@ -40,6 +45,12 @@ pub struct RunSpec {
|
||||
}
|
||||
|
||||
impl RunSpec {
|
||||
pub fn session(&self) -> &SessionId {
|
||||
&self.session
|
||||
}
|
||||
pub fn call(&self) -> CallId {
|
||||
self.call
|
||||
}
|
||||
pub fn tool(&self) -> ToolName {
|
||||
self.tool
|
||||
}
|
||||
@@ -108,6 +119,8 @@ pub fn run(decision: Decision, runtime: &dyn Runtime) -> proto::ToolResponse {
|
||||
ToolArgs::HttpFetch(_) => (Vec::new(), Some(decision.hosts().to_vec())),
|
||||
};
|
||||
let spec = RunSpec {
|
||||
session: decision.request().session.clone(),
|
||||
call: decision.request().call,
|
||||
tool: args.tool(),
|
||||
arguments: args.clone(),
|
||||
mounts,
|
||||
|
||||
Reference in New Issue
Block a user