Skip to content

Commit 3552f53

Browse files
committed
test(e2e): cover Podman token exchange grants
Signed-off-by: Gordon Sim <gsim@redhat.com>
1 parent 350284c commit 3552f53

27 files changed

Lines changed: 1862 additions & 396 deletions

crates/openshell-core/src/driver_utils.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,9 @@ pub const TLS_KEY_MOUNT_PATH: &str = "/etc/openshell/tls/client/tls.key";
7272
/// Container-side mount path for the per-sandbox JWT token.
7373
pub const SANDBOX_TOKEN_MOUNT_PATH: &str = "/etc/openshell/auth/sandbox.jwt";
7474

75+
/// Container-side directory where the provider SPIFFE Workload API socket is mounted.
76+
pub const PROVIDER_SPIFFE_WORKLOAD_API_SOCKET_MOUNT_DIR: &str = "/spiffe-workload-api";
77+
7578
/// Return the XDG state path for a driver's sandbox JWT token file.
7679
///
7780
/// The resulting path is `$XDG_STATE_HOME/openshell/<driver_subdir>[/<namespace>]/<sandbox_id>/sandbox.jwt`.

crates/openshell-driver-podman/src/config.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,9 @@ pub struct PodmanComputeConfig {
126126
/// `template.driver_config`.
127127
#[serde(default)]
128128
pub enable_bind_mounts: bool,
129+
/// Host path to a SPIFFE Workload API Unix socket exposed to sandbox
130+
/// supervisors for provider token exchange client assertions.
131+
pub provider_spiffe_workload_api_socket: Option<PathBuf>,
129132
/// Health check interval in seconds for sandbox containers.
130133
///
131134
/// Podman runs the health check command at this interval to determine
@@ -261,6 +264,7 @@ impl Default for PodmanComputeConfig {
261264
guest_tls_key: None,
262265
sandbox_pids_limit: DEFAULT_SANDBOX_PIDS_LIMIT,
263266
enable_bind_mounts: false,
267+
provider_spiffe_workload_api_socket: None,
264268
health_check_interval_secs: DEFAULT_HEALTH_CHECK_INTERVAL_SECS,
265269
}
266270
}
@@ -284,6 +288,10 @@ impl std::fmt::Debug for PodmanComputeConfig {
284288
.field("guest_tls_key", &self.guest_tls_key)
285289
.field("sandbox_pids_limit", &self.sandbox_pids_limit)
286290
.field("enable_bind_mounts", &self.enable_bind_mounts)
291+
.field(
292+
"provider_spiffe_workload_api_socket",
293+
&self.provider_spiffe_workload_api_socket,
294+
)
287295
.field(
288296
"health_check_interval_secs",
289297
&self.health_check_interval_secs,

crates/openshell-driver-podman/src/container.rs

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ const TLS_CA_MOUNT_PATH: &str = openshell_core::driver_utils::TLS_CA_MOUNT_PATH;
5656
const TLS_CERT_MOUNT_PATH: &str = openshell_core::driver_utils::TLS_CERT_MOUNT_PATH;
5757
const TLS_KEY_MOUNT_PATH: &str = openshell_core::driver_utils::TLS_KEY_MOUNT_PATH;
5858
const SANDBOX_TOKEN_MOUNT_PATH: &str = openshell_core::driver_utils::SANDBOX_TOKEN_MOUNT_PATH;
59+
const PROVIDER_SPIFFE_WORKLOAD_API_SOCKET_MOUNT_DIR: &str =
60+
openshell_core::driver_utils::PROVIDER_SPIFFE_WORKLOAD_API_SOCKET_MOUNT_DIR;
5961

6062
/// Directory inside sandbox containers where the supervisor binary is mounted.
6163
const SUPERVISOR_MOUNT_DIR: &str = openshell_core::driver_utils::SUPERVISOR_CONTAINER_DIR;
@@ -406,6 +408,13 @@ fn build_env(
406408
);
407409
}
408410

411+
if let Some(socket_path) = provider_spiffe_workload_api_socket_env_value(config) {
412+
env.insert(
413+
openshell_core::sandbox_env::PROVIDER_SPIFFE_WORKLOAD_API_SOCKET.into(),
414+
socket_path,
415+
);
416+
}
417+
409418
env.remove(openshell_core::sandbox_env::SANDBOX_TOKEN);
410419
env.remove(openshell_core::sandbox_env::SANDBOX_TOKEN_FILE);
411420

@@ -1013,6 +1022,18 @@ pub fn build_container_spec_with_token_and_gpu_devices(
10131022
options: ro,
10141023
});
10151024
}
1025+
if let Some(path) = provider_spiffe_workload_api_socket_mount_source(config) {
1026+
let mut ro = vec!["ro".into(), "rbind".into()];
1027+
if is_selinux_enabled() {
1028+
ro.push("z".into());
1029+
}
1030+
m.push(Mount {
1031+
kind: "bind".into(),
1032+
source: path.display().to_string(),
1033+
destination: PROVIDER_SPIFFE_WORKLOAD_API_SOCKET_MOUNT_DIR.into(),
1034+
options: ro,
1035+
});
1036+
}
10161037
m.extend(user_mounts.mounts);
10171038
m
10181039
},
@@ -1029,6 +1050,33 @@ pub fn build_container_spec_with_token_and_gpu_devices(
10291050
Ok(serde_json::to_value(container_spec).expect("ContainerSpec serialization cannot fail"))
10301051
}
10311052

1053+
fn provider_spiffe_workload_api_socket_env_value(config: &PodmanComputeConfig) -> Option<String> {
1054+
let host_path = config.provider_spiffe_workload_api_socket.as_ref()?;
1055+
let raw = host_path.to_str()?;
1056+
if raw.starts_with("tcp:") {
1057+
return Some(raw.to_string());
1058+
}
1059+
let host_path = raw
1060+
.strip_prefix("unix:")
1061+
.map_or(host_path.as_path(), Path::new);
1062+
let file_name = host_path.file_name()?.to_str()?;
1063+
Some(format!(
1064+
"{PROVIDER_SPIFFE_WORKLOAD_API_SOCKET_MOUNT_DIR}/{file_name}"
1065+
))
1066+
}
1067+
1068+
fn provider_spiffe_workload_api_socket_mount_source(config: &PodmanComputeConfig) -> Option<&Path> {
1069+
let host_path = config.provider_spiffe_workload_api_socket.as_ref()?;
1070+
let raw = host_path.to_str()?;
1071+
if raw.starts_with("tcp:") {
1072+
return None;
1073+
}
1074+
let host_path = raw
1075+
.strip_prefix("unix:")
1076+
.map_or(host_path.as_path(), Path::new);
1077+
host_path.parent()
1078+
}
1079+
10321080
fn hostadd_entries(config: &PodmanComputeConfig) -> Vec<String> {
10331081
let host_gateway_ip = config.host_gateway_ip.trim();
10341082
if host_gateway_ip.is_empty() {
@@ -2234,6 +2282,33 @@ mod tests {
22342282
}));
22352283
}
22362284

2285+
#[test]
2286+
fn container_spec_includes_provider_spiffe_socket_when_configured() {
2287+
let sandbox = test_sandbox("spiffe-id", "spiffe-name");
2288+
let mut config = test_config();
2289+
config.provider_spiffe_workload_api_socket =
2290+
Some(std::path::PathBuf::from("/host/spire-agent.sock"));
2291+
2292+
let spec = build_container_spec(&sandbox, &config);
2293+
2294+
let env_map = spec["env"].as_object().expect("env should be an object");
2295+
assert_eq!(
2296+
env_map
2297+
.get(openshell_core::sandbox_env::PROVIDER_SPIFFE_WORKLOAD_API_SOCKET)
2298+
.and_then(|v| v.as_str()),
2299+
Some("/spiffe-workload-api/spire-agent.sock"),
2300+
);
2301+
2302+
let mounts = spec["mounts"]
2303+
.as_array()
2304+
.expect("mounts should be an array");
2305+
assert!(mounts.iter().any(|m| {
2306+
m["type"].as_str() == Some("bind")
2307+
&& m["source"].as_str() == Some("/host")
2308+
&& m["destination"].as_str() == Some(PROVIDER_SPIFFE_WORKLOAD_API_SOCKET_MOUNT_DIR)
2309+
}));
2310+
}
2311+
22372312
#[test]
22382313
fn container_spec_omits_tls_without_config() {
22392314
let sandbox = test_sandbox("notls-id", "notls-name");

0 commit comments

Comments
 (0)