Own the patched runsc artifact and bind recovery identity

This commit is contained in:
ms2k 2026-07-26 18:01:03 +09:00
commit 2c420012bd
6 changed files with 1967 additions and 130 deletions

View file

@ -40,6 +40,13 @@ use validation::{
};
pub const NETWORK_NAMESPACE_PATH: &str = "/proc/self/ns/net";
pub const RUNSC_BUILD_ID: &str = "904ab3c9bd4c99a27181076f2c9ea2fecc041db5a728c999540d6664174d2cd4";
pub const RUNSC_BUILD_ID_BYTES: [u8; 32] = [
144, 74, 179, 201, 189, 76, 153, 162, 113, 129, 7, 111, 44, 158, 162, 254, 204, 4, 29, 181,
167, 40, 201, 153, 84, 13, 102, 100, 23, 77, 44, 212,
];
pub const RUNSC_EXECUTABLE_NAME: &str =
"lazaret-runsc-904ab3c9bd4c99a27181076f2c9ea2fecc041db5a728c999540d6664174d2cd4";
pub const DOMAIN_OPEN_FILE_LIMIT: u64 = 524_288;
const OCI_VERSION: &str = "1.2.1";
@ -445,6 +452,7 @@ pub fn stage_host_terminal(
pub struct SandboxBuildRequest<'a> {
pub configuration: &'a ResolvedDomainConfiguration,
pub runsc_program: &'a Path,
pub container_id: ContainerId,
pub expected_network_namespace: NamespaceIdentity,
pub root_process: RootProcess,
@ -647,6 +655,7 @@ fn build_sandbox_bundle_from_source(
request: SandboxBuildRequest<'_>,
workspace_source: PathBuf,
) -> Result<SandboxBundle, SandboxBuildError> {
validate_absolute_normalized(request.runsc_program)?;
require_network_namespace(request.expected_network_namespace)?;
request
.configuration
@ -694,6 +703,7 @@ fn build_sandbox_bundle_from_source(
let specification_json = canonical_specification_json(&specification)?;
let bundle_authorization = Sha256::digest(&specification_json).into();
let command = build_runsc_command(
request.runsc_program,
&paths,
request.configuration.runsc_dcache(),
bundle_authorization,

View file

@ -277,12 +277,13 @@ pub(super) fn build_linux() -> Result<oci_spec::runtime::Linux, SandboxBuildErro
}
pub(super) fn build_runsc_command(
program: &Path,
paths: &RuntimePaths,
dcache: u32,
bundle_authorization: [u8; 32],
) -> RunscCommand {
RunscCommand::new(
"runsc",
program,
[
OsString::from(format!(
"-root={}",

View file

@ -4,7 +4,7 @@ use std::io::{self, Read};
use std::os::fd::{AsFd, AsRawFd, OwnedFd};
use std::os::unix::ffi::{OsStrExt, OsStringExt};
use std::os::unix::fs::FileExt;
use std::path::PathBuf;
use std::path::{Component, Path, PathBuf};
use std::time::{Duration, Instant};
use lazaret_core::codec::{decode, encode};
@ -419,7 +419,7 @@ impl RuntimeSupervisorLaunch {
command: &RunscCommand,
) -> io::Result<Self> {
if ownership.slot != HostProcessOwnershipSlot::SandboxRuntimeSupervisor
|| command.program() != OsStr::new("runsc")
|| !is_absolute_normalized(command.program())
|| !command
.arguments()
.iter()
@ -558,6 +558,14 @@ impl RuntimeSupervisorLaunch {
}
}
fn is_absolute_normalized(value: &OsStr) -> bool {
let path = Path::new(value);
path.is_absolute()
&& path
.components()
.all(|component| matches!(component, Component::RootDir | Component::Normal(_)))
}
/// Runs the Runtime Supervisor's private early role before ordinary binary dispatch.
///
/// # Errors

View file

@ -1,10 +1,11 @@
use super::{
AsRawFd, ConfigurationFingerprint, CoordinatorId, DerivedPolicyDigest, Deserialize, DomainId,
File, FileType, GenerationId, HostProcessOwnershipSlot, MAX_RUNTIME_HANDOFF_BYTES,
NetworkSidecarId, OsStr, OsStrExt, OsString, OsStringExt, PathBuf, ProcessOwnershipContext,
Read, RuntimeArguments, RuntimeGitRoutingArguments, RuntimeGitRoutingLaunch,
RuntimeNetworkArguments, RuntimeStateAnchorArguments, RuntimeSupervisorLaunch, Serialize,
StateAnchorLaunchAuthority, decode, encode, fstat, io, require_write_sealed,
NetworkSidecarId, OsStr, OsStrExt, OsString, OsStringExt, Path, PathBuf,
ProcessOwnershipContext, Read, RuntimeArguments, RuntimeGitRoutingArguments,
RuntimeGitRoutingLaunch, RuntimeNetworkArguments, RuntimeStateAnchorArguments,
RuntimeSupervisorLaunch, Serialize, StateAnchorLaunchAuthority, decode, encode, fstat, io,
require_write_sealed,
};
#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
@ -280,7 +281,9 @@ pub(super) fn parse_runtime_handoff(arguments: &[OsString]) -> io::Result<Runtim
let RuntimeSupervisorHandoff::V1(handoff) =
decode::<RuntimeSupervisorHandoff>(&encoded).map_err(io::Error::other)?;
validate_runtime_handoff(handoff_descriptor, &handoff)?;
if handoff.runsc_program.as_slice() != b"runsc" {
if !super::is_absolute_normalized(
Path::new(OsStr::from_bytes(&handoff.runsc_program)).as_os_str(),
) {
return Err(io::Error::other("Runtime Supervisor target is not runsc"));
}
if !handoff
@ -450,7 +453,7 @@ mod handoff_tests {
state_anchor: None,
git_routing: None,
network: None,
runsc_program: b"runsc".to_vec(),
runsc_program: b"/usr/bin/runsc".to_vec(),
runsc_arguments: arguments,
})
}
@ -520,7 +523,7 @@ mod handoff_tests {
configuration_fingerprint: ConfigurationFingerprint::new([5; 32]),
network_sidecar_id: NetworkSidecarId::new([6; 32]),
}),
runsc_program: b"runsc".to_vec(),
runsc_program: b"/usr/bin/runsc".to_vec(),
runsc_arguments: vec![
b"-root=/runtime".to_vec(),
b"run".to_vec(),
@ -531,8 +534,8 @@ mod handoff_tests {
assert_eq!(
Sha256::digest(&encoded).as_slice(),
[
123, 0, 18, 227, 164, 11, 96, 15, 4, 201, 99, 25, 198, 122, 175, 149, 170, 165, 12,
40, 209, 64, 119, 67, 10, 109, 128, 44, 201, 103, 14, 182,
160, 106, 3, 13, 33, 111, 190, 97, 46, 192, 143, 140, 37, 66, 201, 110, 197, 41,
25, 200, 73, 147, 49, 163, 36, 226, 10, 166, 255, 62, 61, 135,
]
);
assert_eq!(
@ -620,7 +623,7 @@ mod handoff_tests {
configuration_fingerprint: None,
network_sidecar_id: None,
runsc_command: RunscCommand::new(
"runsc",
"/usr/bin/runsc",
[OsString::from("run"), OsString::from("container")],
[8; 32],
),

View file

@ -422,7 +422,7 @@ fn runtime_supervisor_and_runsc_keep_distinct_edge_and_stdio_policies() {
policy_digest: None,
configuration_fingerprint: None,
runsc_command: RunscCommand::new(
"runsc",
"/usr/bin/runsc",
[OsString::from("run"), OsString::from("container")],
[0; 32],
),
@ -450,7 +450,7 @@ fn runtime_supervisor_and_runsc_keep_distinct_edge_and_stdio_policies() {
state_anchor: None,
git_routing: None,
network: None,
runsc_program: "runsc".into(),
runsc_program: "/usr/bin/runsc".into(),
runsc_arguments: ["-root=/runtime", "run", "container"]
.map(OsString::from)
.into(),
@ -462,7 +462,7 @@ fn runtime_supervisor_and_runsc_keep_distinct_edge_and_stdio_policies() {
ChildStandardIoPolicy::Silenced
);
let delete = runsc_delete_target(&runsc_arguments).unwrap();
assert_eq!(delete.program(), "runsc");
assert_eq!(delete.program(), "/usr/bin/runsc");
assert_eq!(
delete.arguments(),
["-root=/runtime", "delete", "--force", "container"]
@ -482,7 +482,7 @@ fn runtime_handoff_preserves_exact_ownership_and_runsc_arguments() {
let launch = RuntimeSupervisorLaunch::from_runsc_command(
ownership,
&RunscCommand::new(
"runsc",
"/usr/bin/runsc",
[
OsString::from("-root=/runtime"),
OsString::from("run"),

View file

@ -143,6 +143,7 @@ env = {{ UNSELECTED_SECRET = "env:UNSELECTED_HOST_SECRET" }}
fn request(&self, root_process: RootProcess) -> SandboxBuildRequest<'_> {
SandboxBuildRequest {
configuration: &self.configuration,
runsc_program: Path::new("/usr/bin/runsc"),
container_id: ContainerId::new([0x11; 32]),
expected_network_namespace: current_network_namespace(),
root_process,
@ -352,7 +353,7 @@ fn routed_workspace_source_replaces_only_the_workspace_bind_source() {
}
fn assert_runtime_command(bundle: &SandboxBundle, fixture: &Fixture) {
assert_eq!(bundle.command().program(), OsStr::new("runsc"));
assert_eq!(bundle.command().program(), OsStr::new("/usr/bin/runsc"));
let runtime_domain = String::from_utf8(
fixture
.configuration
@ -421,6 +422,7 @@ fn configured_dcache_is_fingerprinted_and_passed_to_runsc() {
);
let bundle = build_sandbox_bundle(SandboxBuildRequest {
configuration: &configuration,
runsc_program: Path::new("/usr/bin/runsc"),
container_id: ContainerId::new([0x11; 32]),
expected_network_namespace: current_network_namespace(),
root_process: root_process([]),
@ -650,6 +652,7 @@ fn hostile_terminal_names_sources_and_changed_entries_fail_closed() {
.unwrap();
let error = build_sandbox_bundle(SandboxBuildRequest {
configuration: &fixture.configuration,
runsc_program: Path::new("/usr/bin/runsc"),
container_id: ContainerId::new([0x11; 32]),
expected_network_namespace: current_network_namespace(),
root_process: root_process([]),
@ -684,6 +687,7 @@ fn invalid_processes_and_wrong_network_identity_fail_closed() {
}
let error = build_sandbox_bundle(SandboxBuildRequest {
configuration: &fixture.configuration,
runsc_program: Path::new("/usr/bin/runsc"),
container_id: ContainerId::new([0x11; 32]),
expected_network_namespace: NamespaceIdentity {
device: DeviceId::new(u64::MAX),