Skip to content

Commit d8cf952

Browse files
author
Marco Napetti
committed
feat: cross-backend secret shim
1 parent abaf068 commit d8cf952

25 files changed

Lines changed: 979 additions & 86 deletions

File tree

.github/workflows/installer-smoke.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ jobs:
3636
./install.sh --install-dir "${INSTALL_DIR}"
3737
"${INSTALL_DIR}/firma" --version
3838
"${INSTALL_DIR}/firma" help config
39-
test -x "${INSTALL_DIR}/firma-secret-shim"
39+
test ! -x "${INSTALL_DIR}/firma-secret-shim"
4040
- name: Re-run (already installed exit path)
4141
env:
4242
FIRMA_NO_BREW: '1'
@@ -82,7 +82,7 @@ jobs:
8282
.\install.ps1 -InstallDir $dir
8383
& (Join-Path $dir 'firma.exe') --version
8484
& (Join-Path $dir 'firma.exe') help config
85-
if (-not (Test-Path (Join-Path $dir 'firma-secret-shim.exe'))) { exit 1 }
85+
if (Test-Path (Join-Path $dir 'firma-secret-shim.exe')) { Write-Host 'shim must not be a user-facing command'; exit 1 }
8686
- name: Re-run (already installed exit path)
8787
shell: pwsh
8888
env:

crates/firma-run/src/backend/firecracker.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use std::process::Child;
22

33
use crate::backend::{
44
BackendKind, EnforcementProof, LaunchSpec, PrepareRequest, SandboxBackend, SandboxHandle,
5+
SecretShimSupport, SecretShimUnsupportedReason,
56
};
67
use crate::config::NetworkPolicy;
78
use crate::error::RunError;
@@ -66,4 +67,10 @@ impl SandboxBackend for FirecrackerBackend {
6667
fn teardown(&self, _handle: SandboxHandle) -> Result<(), RunError> {
6768
Ok(())
6869
}
70+
71+
fn secret_shim_support(&self) -> SecretShimSupport {
72+
SecretShimSupport::Unsupported {
73+
reason: SecretShimUnsupportedReason::NotYetImplemented,
74+
}
75+
}
6976
}

crates/firma-run/src/backend/linux_bwrap/mod.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use self::mount::{BwrapHardening, BwrapMountPlan};
1414
use crate::backend::platform;
1515
use crate::backend::{
1616
BackendKind, EnforcementProof, LaunchSpec, PrepareRequest, SandboxBackend, SandboxHandle,
17-
SandboxInfrastructureKind, SandboxMount, SandboxRuntimeLayout,
17+
SandboxInfrastructureKind, SandboxMount, SandboxRuntimeLayout, SecretShimSupport, ShimTarget,
1818
};
1919
use crate::config::{NetworkPolicy, SandboxIdentityMode};
2020
use crate::error::RunError;
@@ -289,6 +289,12 @@ impl SandboxBackend for BwrapBackend {
289289
remove_runtime_dir(&handle.runtime_dir);
290290
Ok(())
291291
}
292+
293+
fn secret_shim_support(&self) -> SecretShimSupport {
294+
SecretShimSupport::HostBindMount {
295+
guest_target: ShimTarget::host(),
296+
}
297+
}
292298
}
293299

294300
/// Resolve `/etc/resolv.conf` to its canonical on-disk path, following all

crates/firma-run/src/backend/macos_vz.rs

Lines changed: 110 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@ use std::path::{Path, PathBuf};
99
use std::process::{Child, Command};
1010

1111
use crate::backend::{
12-
BackendKind, EnforcementProof, LaunchSpec, NetworkConfinement, PrepareRequest, SandboxBackend,
13-
SandboxHandle, SandboxMount, SandboxRuntimeLayout,
12+
BackendKind, BrokerBridgeKind, EnforcementProof, LaunchSpec, NetworkConfinement,
13+
PrepareRequest, SandboxBackend, SandboxHandle, SandboxMount, SandboxRuntimeLayout,
14+
SecretShimSupport, SecretShimUnsupportedReason, ShimTarget,
1415
};
1516
use crate::config::{MountSpec, NetworkPolicy, SidecarEndpoint};
1617
use crate::error::RunError;
@@ -22,7 +23,7 @@ const VZ_GUEST_KERNEL_ENV: &str = "FIRMA_RUN_VZ_GUEST_KERNEL";
2223
const VZ_GUEST_INITRD_ENV: &str = "FIRMA_RUN_VZ_GUEST_INITRD";
2324
const VZ_GUEST_ROOTFS_ENV: &str = "FIRMA_RUN_VZ_GUEST_ROOTFS";
2425
const VZ_GUEST_LAUNCH_CONTRACT_VERSION: u32 = 1;
25-
const VZ_GUEST_SECRET_ENV_KEYS: &[&str] = &["FIRMA_CAPABILITY_TOKEN"];
26+
const VZ_GUEST_SECRET_ENV_KEYS: &[&str] = &["FIRMA_CAPABILITY_TOKEN", "FIRMA_BROKER_ADDR"];
2627
const VZ_GUEST_HOST_NETWORK_ENV_KEYS: &[&str] = &[
2728
"HTTP_PROXY",
2829
"HTTPS_PROXY",
@@ -40,6 +41,7 @@ const VZ_GUEST_DNS_STUB_ADDR: &str = "127.0.0.1:1053";
4041
const VZ_GUEST_SIDECAR_VSOCK_PORT: u32 = 18080;
4142
const VZ_GUEST_COMMAND_PTY_VSOCK_PORT: u32 = 18081;
4243
const VZ_GUEST_COMMAND_PTY_CONTROL_VSOCK_PORT: u32 = 18082;
44+
const VZ_GUEST_BROKER_VSOCK_PORT: u32 = 18083;
4345

4446
/// Host paths owned by the VZ guest-launch handoff.
4547
struct VzGuestLayout {
@@ -269,7 +271,7 @@ impl SandboxBackend for VzBackend {
269271

270272
let mode = vz_structural_mode();
271273
if mode == VzStructuralMode::VzGuest {
272-
return start_vz_guest_runner(handle, launch);
274+
return start_vz_guest_runner(handle, launch, &self.secret_shim_support());
273275
}
274276

275277
let mut command = Command::new("sandbox-exec");
@@ -309,6 +311,23 @@ impl SandboxBackend for VzBackend {
309311
remove_runtime_dir(&handle.runtime_dir);
310312
Ok(())
311313
}
314+
315+
fn secret_shim_support(&self) -> SecretShimSupport {
316+
let mode = vz_structural_mode();
317+
match mode {
318+
VzStructuralMode::Compatibility | VzStructuralMode::SandboxExecNetworkDeny => {
319+
SecretShimSupport::Unsupported {
320+
reason: SecretShimUnsupportedReason::HostCallable,
321+
}
322+
}
323+
VzStructuralMode::VzGuest => SecretShimSupport::IsolatedGuest {
324+
guest_target: ShimTarget::linux_x86_64_musl(),
325+
broker_bridge: BrokerBridgeKind::VsockPort {
326+
port: VZ_GUEST_BROKER_VSOCK_PORT,
327+
},
328+
},
329+
}
330+
}
312331
}
313332

314333
/// Create the VZ runtime tree with owner-only custody.
@@ -377,6 +396,8 @@ struct VzGuestLaunchContract {
377396
mounts: Vec<MountSpec>,
378397
network: VzGuestNetworkContract,
379398
invariants: Vec<VzGuestInvariantContract>,
399+
#[serde(skip_serializing_if = "Option::is_none")]
400+
secret_shims: Option<VzGuestSecretShimsContract>,
380401
}
381402

382403
impl VzGuestLaunchContract {
@@ -385,12 +406,14 @@ impl VzGuestLaunchContract {
385406
handle: &SandboxHandle,
386407
launch: &LaunchSpec,
387408
inputs: VzGuestLaunchInputs,
409+
shim_support: &SecretShimSupport,
388410
) -> Result<Self, RunError> {
389411
Self::from_launch_with_terminal_snapshot(
390412
handle,
391413
launch,
392414
inputs,
393415
TerminalSnapshot::from_host(),
416+
shim_support,
394417
)
395418
}
396419

@@ -399,10 +422,26 @@ impl VzGuestLaunchContract {
399422
launch: &LaunchSpec,
400423
inputs: VzGuestLaunchInputs,
401424
terminal_snapshot: TerminalSnapshot,
425+
shim_support: &SecretShimSupport,
402426
) -> Result<Self, RunError> {
403427
let terminal =
404428
VzGuestTerminalContract::from_launch_with_terminal_snapshot(launch, terminal_snapshot);
405429

430+
let secret_shims = match shim_support {
431+
SecretShimSupport::IsolatedGuest {
432+
guest_target,
433+
broker_bridge,
434+
} => Some(VzGuestSecretShimsContract {
435+
guest_target_triple: guest_target.triple.to_string(),
436+
provider_names: vz_guest_shim_provider_names(&launch.env),
437+
broker_vsock_port: match broker_bridge {
438+
BrokerBridgeKind::VsockPort { port } => *port,
439+
},
440+
shim_share_directory: handle.runtime_dir.join("vz-guest").join("secret-shims"),
441+
}),
442+
SecretShimSupport::HostBindMount { .. } | SecretShimSupport::Unsupported { .. } => None,
443+
};
444+
406445
Ok(Self {
407446
version: VZ_GUEST_LAUNCH_CONTRACT_VERSION,
408447
sandbox_id: handle.identity.sandbox_id.to_string(),
@@ -434,6 +473,7 @@ impl VzGuestLaunchContract {
434473
handle.identity.full_attribution_headers(),
435474
)?,
436475
invariants: VzGuestInvariantContract::required_set(handle.network_policy.fail_closed),
476+
secret_shims,
437477
})
438478
}
439479
}
@@ -714,18 +754,42 @@ enum VzGuestInvariantMode {
714754
DisabledByPolicy,
715755
}
716756

717-
fn start_vz_guest_runner(handle: &SandboxHandle, launch: &LaunchSpec) -> Result<Child, RunError> {
757+
#[derive(Debug, serde::Serialize)]
758+
#[serde(rename_all = "snake_case")]
759+
struct VzGuestSecretShimsContract {
760+
guest_target_triple: String,
761+
provider_names: Vec<String>,
762+
broker_vsock_port: u32,
763+
shim_share_directory: PathBuf,
764+
}
765+
766+
/// Extracts the provider names that need shims from the launch environment.
767+
///
768+
/// Provider names are communicated via the `FIRMA_SECRET_PROVIDER_NAMES` env
769+
/// var set by `secret_shims::prepare` when it sets up the broker.
770+
fn vz_guest_shim_provider_names(env: &BTreeMap<String, String>) -> Vec<String> {
771+
env.get("FIRMA_SECRET_PROVIDER_NAMES")
772+
.map(|value| value.split(',').map(str::to_string).collect())
773+
.unwrap_or_default()
774+
}
775+
776+
fn start_vz_guest_runner(
777+
handle: &SandboxHandle,
778+
launch: &LaunchSpec,
779+
shim_support: &SecretShimSupport,
780+
) -> Result<Child, RunError> {
718781
let inputs = VzGuestLaunchInputs::from_env()?;
719-
start_vz_guest_runner_with_inputs(handle, launch, inputs)
782+
start_vz_guest_runner_with_inputs(handle, launch, inputs, shim_support)
720783
}
721784

722785
fn start_vz_guest_runner_with_inputs(
723786
handle: &SandboxHandle,
724787
launch: &LaunchSpec,
725788
inputs: VzGuestLaunchInputs,
789+
shim_support: &SecretShimSupport,
726790
) -> Result<Child, RunError> {
727791
let runner = inputs.runner.clone();
728-
let contract = VzGuestLaunchContract::from_launch(handle, launch, inputs)?;
792+
let contract = VzGuestLaunchContract::from_launch(handle, launch, inputs, shim_support)?;
729793
let contract_path = write_vz_guest_launch_contract(handle, &contract)?;
730794

731795
tracing::info!(
@@ -1026,7 +1090,8 @@ mod tests {
10261090
use crate::identity::RunIdentity;
10271091

10281092
use super::{
1029-
GuestPtyRequest, GuestTerminalSelection, TerminalSize, TerminalSnapshot,
1093+
BrokerBridgeKind, GuestPtyRequest, GuestTerminalSelection, SecretShimSupport, ShimTarget,
1094+
TerminalSize, TerminalSnapshot, VZ_GUEST_BROKER_VSOCK_PORT,
10301095
VZ_GUEST_COMMAND_PTY_CONTROL_VSOCK_PORT, VZ_GUEST_COMMAND_PTY_VSOCK_PORT,
10311096
VzGuestLaunchContract, VzGuestLaunchInputs, VzGuestLayout, VzGuestTerminalContract,
10321097
VzStructuralMode, build_sandbox_profile, loopback_port_from, vz_structural_mode_from_flags,
@@ -1101,6 +1166,12 @@ mod tests {
11011166
initrd: PathBuf::from("/var/lib/firma/vz/initrd.img"),
11021167
rootfs: PathBuf::from("/var/lib/firma/vz/rootfs.img"),
11031168
},
1169+
&SecretShimSupport::IsolatedGuest {
1170+
guest_target: ShimTarget::linux_x86_64_musl(),
1171+
broker_bridge: BrokerBridgeKind::VsockPort {
1172+
port: VZ_GUEST_BROKER_VSOCK_PORT,
1173+
},
1174+
},
11041175
)
11051176
.expect("guest contract should build from prepared launch")
11061177
}
@@ -1212,6 +1283,12 @@ mod tests {
12121283
rootfs: PathBuf::from("/var/lib/firma/vz/rootfs.img"),
12131284
},
12141285
terminal_snapshot,
1286+
&SecretShimSupport::IsolatedGuest {
1287+
guest_target: ShimTarget::linux_x86_64_musl(),
1288+
broker_bridge: BrokerBridgeKind::VsockPort {
1289+
port: VZ_GUEST_BROKER_VSOCK_PORT,
1290+
},
1291+
},
12151292
)
12161293
.expect("guest contract should build from prepared launch");
12171294

@@ -1257,6 +1334,7 @@ mod tests {
12571334
"runner",
12581335
"runtime_dir",
12591336
"sandbox_id",
1337+
"secret_shims",
12601338
"terminal",
12611339
"version",
12621340
])
@@ -1687,6 +1765,12 @@ mod tests {
16871765
initrd: PathBuf::from("/var/lib/firma/vz/initrd.img"),
16881766
rootfs: PathBuf::from("/var/lib/firma/vz/rootfs.img"),
16891767
},
1768+
&SecretShimSupport::IsolatedGuest {
1769+
guest_target: ShimTarget::linux_x86_64_musl(),
1770+
broker_bridge: BrokerBridgeKind::VsockPort {
1771+
port: VZ_GUEST_BROKER_VSOCK_PORT,
1772+
},
1773+
},
16901774
)
16911775
.expect("guest contract should build from prepared launch");
16921776

@@ -1813,6 +1897,12 @@ mod tests {
18131897
initrd: PathBuf::from("/var/lib/firma/vz/initrd.img"),
18141898
rootfs: PathBuf::from("/var/lib/firma/vz/rootfs.img"),
18151899
},
1900+
&SecretShimSupport::IsolatedGuest {
1901+
guest_target: ShimTarget::linux_x86_64_musl(),
1902+
broker_bridge: BrokerBridgeKind::VsockPort {
1903+
port: VZ_GUEST_BROKER_VSOCK_PORT,
1904+
},
1905+
},
18161906
)
18171907
.expect_err("non-loopback sidecar endpoint must fail closed");
18181908

@@ -1881,6 +1971,12 @@ mod tests {
18811971
initrd: PathBuf::from("/var/lib/firma/vz/initrd.img"),
18821972
rootfs: PathBuf::from("/var/lib/firma/vz/rootfs.img"),
18831973
},
1974+
&SecretShimSupport::IsolatedGuest {
1975+
guest_target: ShimTarget::linux_x86_64_musl(),
1976+
broker_bridge: BrokerBridgeKind::VsockPort {
1977+
port: VZ_GUEST_BROKER_VSOCK_PORT,
1978+
},
1979+
},
18841980
)
18851981
.expect("guest contract should build from prepared launch");
18861982

@@ -1965,6 +2061,12 @@ mod tests {
19652061
initrd: initrd.clone(),
19662062
rootfs: rootfs.clone(),
19672063
},
2064+
&SecretShimSupport::IsolatedGuest {
2065+
guest_target: ShimTarget::linux_x86_64_musl(),
2066+
broker_bridge: BrokerBridgeKind::VsockPort {
2067+
port: VZ_GUEST_BROKER_VSOCK_PORT,
2068+
},
2069+
},
19682070
)
19692071
.expect("fake VZ runner should spawn");
19702072
let status = child.wait().expect("fake VZ runner should exit");

0 commit comments

Comments
 (0)