Skip to content

Commit b2adf35

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

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
@@ -55,6 +55,8 @@ const TLS_CA_MOUNT_PATH: &str = openshell_core::driver_utils::TLS_CA_MOUNT_PATH;
5555
const TLS_CERT_MOUNT_PATH: &str = openshell_core::driver_utils::TLS_CERT_MOUNT_PATH;
5656
const TLS_KEY_MOUNT_PATH: &str = openshell_core::driver_utils::TLS_KEY_MOUNT_PATH;
5757
const SANDBOX_TOKEN_MOUNT_PATH: &str = openshell_core::driver_utils::SANDBOX_TOKEN_MOUNT_PATH;
58+
const PROVIDER_SPIFFE_WORKLOAD_API_SOCKET_MOUNT_DIR: &str =
59+
openshell_core::driver_utils::PROVIDER_SPIFFE_WORKLOAD_API_SOCKET_MOUNT_DIR;
5860

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

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

@@ -1019,6 +1028,18 @@ pub fn build_container_spec_with_token_and_gpu_default(
10191028
options: ro,
10201029
});
10211030
}
1031+
if let Some(path) = provider_spiffe_workload_api_socket_mount_source(config) {
1032+
let mut ro = vec!["ro".into(), "rbind".into()];
1033+
if is_selinux_enabled() {
1034+
ro.push("z".into());
1035+
}
1036+
m.push(Mount {
1037+
kind: "bind".into(),
1038+
source: path.display().to_string(),
1039+
destination: PROVIDER_SPIFFE_WORKLOAD_API_SOCKET_MOUNT_DIR.into(),
1040+
options: ro,
1041+
});
1042+
}
10221043
m.extend(user_mounts.mounts);
10231044
m
10241045
},
@@ -1035,6 +1056,33 @@ pub fn build_container_spec_with_token_and_gpu_default(
10351056
Ok(serde_json::to_value(container_spec).expect("ContainerSpec serialization cannot fail"))
10361057
}
10371058

1059+
fn provider_spiffe_workload_api_socket_env_value(config: &PodmanComputeConfig) -> Option<String> {
1060+
let host_path = config.provider_spiffe_workload_api_socket.as_ref()?;
1061+
let raw = host_path.to_str()?;
1062+
if raw.starts_with("tcp:") {
1063+
return Some(raw.to_string());
1064+
}
1065+
let host_path = raw
1066+
.strip_prefix("unix:")
1067+
.map_or(host_path.as_path(), Path::new);
1068+
let file_name = host_path.file_name()?.to_str()?;
1069+
Some(format!(
1070+
"{PROVIDER_SPIFFE_WORKLOAD_API_SOCKET_MOUNT_DIR}/{file_name}"
1071+
))
1072+
}
1073+
1074+
fn provider_spiffe_workload_api_socket_mount_source(config: &PodmanComputeConfig) -> Option<&Path> {
1075+
let host_path = config.provider_spiffe_workload_api_socket.as_ref()?;
1076+
let raw = host_path.to_str()?;
1077+
if raw.starts_with("tcp:") {
1078+
return None;
1079+
}
1080+
let host_path = raw
1081+
.strip_prefix("unix:")
1082+
.map_or(host_path.as_path(), Path::new);
1083+
host_path.parent()
1084+
}
1085+
10381086
fn hostadd_entries(config: &PodmanComputeConfig) -> Vec<String> {
10391087
let host_gateway_ip = config.host_gateway_ip.trim();
10401088
if host_gateway_ip.is_empty() {
@@ -2180,6 +2228,33 @@ mod tests {
21802228
}));
21812229
}
21822230

2231+
#[test]
2232+
fn container_spec_includes_provider_spiffe_socket_when_configured() {
2233+
let sandbox = test_sandbox("spiffe-id", "spiffe-name");
2234+
let mut config = test_config();
2235+
config.provider_spiffe_workload_api_socket =
2236+
Some(std::path::PathBuf::from("/host/spire-agent.sock"));
2237+
2238+
let spec = build_container_spec(&sandbox, &config);
2239+
2240+
let env_map = spec["env"].as_object().expect("env should be an object");
2241+
assert_eq!(
2242+
env_map
2243+
.get(openshell_core::sandbox_env::PROVIDER_SPIFFE_WORKLOAD_API_SOCKET)
2244+
.and_then(|v| v.as_str()),
2245+
Some("/spiffe-workload-api/spire-agent.sock"),
2246+
);
2247+
2248+
let mounts = spec["mounts"]
2249+
.as_array()
2250+
.expect("mounts should be an array");
2251+
assert!(mounts.iter().any(|m| {
2252+
m["type"].as_str() == Some("bind")
2253+
&& m["source"].as_str() == Some("/host")
2254+
&& m["destination"].as_str() == Some(PROVIDER_SPIFFE_WORKLOAD_API_SOCKET_MOUNT_DIR)
2255+
}));
2256+
}
2257+
21832258
#[test]
21842259
fn container_spec_omits_tls_without_config() {
21852260
let sandbox = test_sandbox("notls-id", "notls-name");

0 commit comments

Comments
 (0)