Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions architecture/build.md
Original file line number Diff line number Diff line change
Expand Up @@ -292,11 +292,12 @@ for direct executable installation on every environment. Release Dev and
Release Tag run Ubuntu conformance through the Debian package, while Fedora
continues using direct executable installation until RPM coverage is available.
The Debian qualification profile keeps candidate-image overrides outside the
operator-owned gateway configuration: it writes a harness-owned file under
`/var/lib/openshell-qualification` and selects it through the packaged systemd
unit's `gateway.env` hook. Ordinary package installations continue to use the
gateway's built-in runtime-image defaults unless the operator configures an
override.
operator-owned gateway configuration. It supplies the Docker selector and exact
sandbox-runtime and supervisor references through the packaged systemd unit's
`gateway.env` hook, so config preflight and actual startup resolve the same
artifacts without generating `gateway.toml`. Ordinary package installations
continue to use the gateway's compiled runtime-image defaults unless the
operator supplies process defaults or explicit driver TOML values.

## Python Wheel Packaging

Expand Down
57 changes: 48 additions & 9 deletions crates/openshell-core/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,24 +89,48 @@ pub const DEFAULT_SUPERVISOR_IMAGE_REPO: &str = "ghcr.io/nvidia/openshell/superv
/// Default OCI repository for the sandbox runtime image (no tag).
pub const DEFAULT_SANDBOX_RUNTIME_IMAGE_REPO: &str = "ghcr.io/nvidia/openshell/sandbox";

/// Return the default sandbox runtime image reference with a version-pinned tag.
#[must_use]
pub fn default_sandbox_runtime_image() -> String {
/// Process-level default for the trusted sandbox runtime image.
pub const SANDBOX_RUNTIME_IMAGE_ENV: &str = "OPENSHELL_SANDBOX_RUNTIME_IMAGE";

/// Process-level default for the trusted supervisor image.
pub const SUPERVISOR_IMAGE_ENV: &str = "OPENSHELL_SUPERVISOR_IMAGE";

fn compiled_sandbox_runtime_image() -> String {
format!(
"{DEFAULT_SANDBOX_RUNTIME_IMAGE_REPO}:{}",
default_supervisor_image_tag()
)
}

/// Return the default supervisor image reference with a version-pinned tag.
#[must_use]
pub fn default_supervisor_image() -> String {
fn compiled_supervisor_image() -> String {
format!(
"{DEFAULT_SUPERVISOR_IMAGE_REPO}:{}",
default_supervisor_image_tag()
)
}

fn runtime_image_default(environment_value: Option<String>, compiled_default: String) -> String {
environment_value.unwrap_or(compiled_default)
}

/// Return the process-configured sandbox runtime image, or the compiled default.
#[must_use]
pub fn default_sandbox_runtime_image() -> String {
runtime_image_default(
std::env::var(SANDBOX_RUNTIME_IMAGE_ENV).ok(),
compiled_sandbox_runtime_image(),
)
}

/// Return the process-configured supervisor image, or the compiled default.
#[must_use]
pub fn default_supervisor_image() -> String {
runtime_image_default(
std::env::var(SUPERVISOR_IMAGE_ENV).ok(),
compiled_supervisor_image(),
)
}

fn default_supervisor_image_tag() -> String {
resolve_supervisor_image_tag(&[
option_env!("OPENSHELL_IMAGE_TAG").unwrap_or(""),
Expand Down Expand Up @@ -1617,15 +1641,30 @@ mod tests {

#[test]
fn default_supervisor_image_is_version_pinned() {
use super::{default_sandbox_runtime_image, default_supervisor_image};
let image = default_supervisor_image();
use super::{compiled_sandbox_runtime_image, compiled_supervisor_image};
let image = compiled_supervisor_image();
assert!(image.starts_with("ghcr.io/nvidia/openshell/supervisor:"));
let tag = image.rsplit_once(':').unwrap().1;
assert!(!tag.is_empty());

let sandbox_image = default_sandbox_runtime_image();
let sandbox_image = compiled_sandbox_runtime_image();
assert!(sandbox_image.starts_with("ghcr.io/nvidia/openshell/sandbox:"));
let sandbox_tag = sandbox_image.rsplit_once(':').unwrap().1;
assert!(!sandbox_tag.is_empty());
}

#[test]
fn runtime_image_environment_value_replaces_compiled_default() {
use super::runtime_image_default;

let digest = format!("registry.example.com/sandbox@sha256:{}", "a".repeat(64));
assert_eq!(
runtime_image_default(Some(digest.clone()), "compiled:default".to_string()),
digest
);
assert_eq!(
runtime_image_default(None, "compiled:default".to_string()),
"compiled:default"
);
}
}
17 changes: 17 additions & 0 deletions crates/openshell-driver-docker/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2133,6 +2133,23 @@ fn validate_sandbox_rejects_unknown_driver_config_fields() {
assert!(err.message().contains("unknown field"));
}

#[test]
fn sandbox_driver_config_rejects_trusted_runtime_image_overrides() {
for field in ["sandbox_runtime_image", "supervisor_image"] {
let template = DriverSandboxTemplate {
driver_config: Some(json_struct(serde_json::json!({
(field): "registry.example.com/openshell/runtime:untrusted"
}))),
..Default::default()
};

let error = DockerSandboxDriverConfig::from_template(&template)
.expect_err("sandbox requests must not select trusted runtime images");
assert!(error.contains("unknown field"), "{error}");
assert!(error.contains(field), "{error}");
}
}

#[test]
fn validate_sandbox_accepts_gpu_count_request_shape() {
let mut config = runtime_config();
Expand Down
17 changes: 17 additions & 0 deletions crates/openshell-driver-kubernetes/src/driver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8019,6 +8019,23 @@ mod tests {
assert!(err.contains("unknown field"));
}

#[test]
fn sandbox_driver_config_rejects_trusted_runtime_image_overrides() {
for field in ["sandbox_runtime_image", "supervisor_image"] {
let template = SandboxTemplate {
driver_config: Some(json_struct(serde_json::json!({
(field): "registry.example.com/openshell/runtime:untrusted"
}))),
..Default::default()
};

let error = KubernetesSandboxDriverConfig::from_template(&template)
.expect_err("sandbox requests must not select trusted runtime images");
assert!(error.contains("unknown field"), "{error}");
assert!(error.contains(field), "{error}");
}
}

#[test]
fn driver_config_for_spec_rejects_unknown_fields() {
let sandbox = Sandbox {
Expand Down
4 changes: 2 additions & 2 deletions crates/openshell-driver-kubernetes/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,13 +109,13 @@ struct Args {
#[arg(long, env = "OPENSHELL_HOST_GATEWAY_IP")]
host_gateway_ip: Option<String>,

#[arg(long, env = "OPENSHELL_SANDBOX_RUNTIME_IMAGE")]
#[arg(long, env = openshell_core::config::SANDBOX_RUNTIME_IMAGE_ENV)]
sandbox_runtime_image: Option<String>,

#[arg(long, env = "OPENSHELL_SANDBOX_RUNTIME_IMAGE_PULL_POLICY")]
sandbox_runtime_image_pull_policy: Option<KubernetesImagePullPolicy>,

#[arg(long, env = "OPENSHELL_SUPERVISOR_IMAGE")]
#[arg(long, env = openshell_core::config::SUPERVISOR_IMAGE_ENV)]
supervisor_image: Option<String>,

#[arg(long, env = "OPENSHELL_SUPERVISOR_IMAGE_PULL_POLICY")]
Expand Down
19 changes: 19 additions & 0 deletions crates/openshell-driver-podman/src/container.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2183,6 +2183,25 @@ mod tests {
assert!(err.to_string().contains("unknown field"));
}

#[test]
fn sandbox_driver_config_rejects_trusted_runtime_image_overrides() {
use openshell_core::proto::compute::v1::DriverSandboxTemplate;

for field in ["sandbox_runtime_image", "supervisor_image"] {
let template = DriverSandboxTemplate {
driver_config: Some(json_struct(serde_json::json!({
(field): "registry.example.com/openshell/runtime:untrusted"
}))),
..Default::default()
};

let error = PodmanSandboxDriverConfig::from_template(&template)
.expect_err("sandbox requests must not select trusted runtime images");
assert!(error.to_string().contains("unknown field"), "{error}");
assert!(error.to_string().contains(field), "{error}");
}
}

#[test]
fn container_spec_defaults_drop_capabilities_and_keep_runtime_seccomp() {
let sandbox = test_sandbox("test-id", "test-name");
Expand Down
4 changes: 2 additions & 2 deletions crates/openshell-driver-podman/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,11 +102,11 @@ struct Args {
health_check_interval_secs: Option<NonZeroU64>,

/// OCI image containing the `openshell-sandbox` runtime binary.
#[arg(long, env = "OPENSHELL_SANDBOX_RUNTIME_IMAGE")]
#[arg(long, env = openshell_core::config::SANDBOX_RUNTIME_IMAGE_ENV)]
sandbox_runtime_image: Option<String>,

/// OCI image containing the `openshell-supervisor` control binary.
#[arg(long, env = "OPENSHELL_SUPERVISOR_IMAGE")]
#[arg(long, env = openshell_core::config::SUPERVISOR_IMAGE_ENV)]
supervisor_image: Option<String>,

/// Host path to the CA certificate for sandbox mTLS.
Expand Down
3 changes: 1 addition & 2 deletions crates/openshell-gateway/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ openshell-otel = { path = "../openshell-otel", optional = true }
async-trait = "0.1"
miette = { workspace = true }
tokio = { workspace = true }
tracing = { workspace = true }

[target.'cfg(not(target_os = "windows"))'.dependencies]
openshell-driver-docker = { path = "../openshell-driver-docker", optional = true }
Expand All @@ -33,7 +34,6 @@ serde = { workspace = true, optional = true }
rustix = { workspace = true, optional = true }
tonic = { workspace = true, optional = true }
tower = { workspace = true, optional = true }
tracing = { workspace = true, optional = true }

[target.'cfg(target_os = "windows")'.dependencies]
openshell-driver-mxc = { path = "../openshell-driver-mxc", optional = true }
Expand All @@ -60,7 +60,6 @@ compute-driver-vm = [
"dep:rustix",
"dep:tonic",
"dep:tower",
"dep:tracing",
]
telemetry = ["openshell-core/telemetry", "openshell-server/telemetry"]
## Convenience alias: every default feature except `telemetry`. Build a
Expand Down
99 changes: 96 additions & 3 deletions crates/openshell-gateway/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,66 @@ use openshell_core::telemetry::TelemetryComputeDriver;
use openshell_server::ComputeDriverRegistration;
use openshell_server::ComputeDriverRegistry;

#[cfg(all(
not(target_os = "windows"),
any(
feature = "compute-driver-docker",
feature = "compute-driver-kubernetes",
feature = "compute-driver-podman"
)
))]
fn runtime_image_source(
context: openshell_server::ComputeDriverConfigContext<'_>,
field: &str,
environment_variable: &str,
) -> &'static str {
if context.driver_config_field_is_explicit(field) {
"driver_toml"
} else if std::env::var(environment_variable).is_ok() {
"process_environment"
} else {
"compiled_default"
}
}

#[cfg(all(
not(target_os = "windows"),
any(
feature = "compute-driver-docker",
feature = "compute-driver-kubernetes",
feature = "compute-driver-podman"
)
))]
fn log_trusted_runtime_images(
context: openshell_server::ComputeDriverConfigContext<'_>,
driver_name: &str,
sandbox_runtime_image: &str,
supervisor_image: &str,
sandbox_runtime_active: bool,
) {
tracing::info!(
compute_driver = driver_name,
image = sandbox_runtime_image,
configuration_source = runtime_image_source(
context,
"sandbox_runtime_image",
openshell_core::config::SANDBOX_RUNTIME_IMAGE_ENV,
),
active = sandbox_runtime_active,
"resolved trusted sandbox runtime image"
);
tracing::info!(
compute_driver = driver_name,
image = supervisor_image,
configuration_source = runtime_image_source(
context,
"supervisor_image",
openshell_core::config::SUPERVISOR_IMAGE_ENV,
),
"resolved trusted supervisor image"
);
}

/// Install every first-party compute driver linked into the standard gateway.
#[must_use]
pub fn install_default_compute_drivers() -> ComputeDriverRegistry {
Expand Down Expand Up @@ -253,7 +313,15 @@ impl openshell_server::ComputeDriverFactory for KubernetesFactory {
&self,
context: openshell_server::ComputeDriverBuildContext<'_>,
) -> openshell_core::Result<openshell_server::ComputeDriverInstance> {
let config = kubernetes_config(context.config_context())?;
let config_context = context.config_context();
let config = kubernetes_config(config_context)?;
log_trusted_runtime_images(
config_context,
"kubernetes",
&config.sandbox_runtime_image,
&config.supervisor_image,
true,
);
let driver = openshell_driver_kubernetes::KubernetesComputeDriver::new(
config,
context.shutdown_receiver(),
Expand Down Expand Up @@ -305,7 +373,24 @@ impl openshell_server::ComputeDriverFactory for DockerFactory {
&self,
context: openshell_server::ComputeDriverBuildContext<'_>,
) -> openshell_core::Result<openshell_server::ComputeDriverInstance> {
let mut config: openshell_driver_docker::DockerComputeConfig = context.driver_config()?;
let config_context = context.config_context();
let mut config: openshell_driver_docker::DockerComputeConfig =
config_context.driver_config()?;
let sandbox_runtime_image = config
.sandbox_runtime_image
.clone()
.unwrap_or_else(openshell_core::config::default_sandbox_runtime_image);
let supervisor_image = config
.supervisor_image
.clone()
.unwrap_or_else(openshell_core::config::default_supervisor_image);
log_trusted_runtime_images(
config_context,
"docker",
&sandbox_runtime_image,
&supervisor_image,
config.supervisor_bin.is_none(),
);
require_guest_tls_for_local_driver(&context, "docker")?;
apply_guest_tls(
&mut config.guest_tls_ca,
Expand Down Expand Up @@ -351,7 +436,15 @@ impl openshell_server::ComputeDriverFactory for PodmanFactory {
&self,
context: openshell_server::ComputeDriverBuildContext<'_>,
) -> openshell_core::Result<openshell_server::ComputeDriverInstance> {
let mut config = podman_config(context.config_context())?;
let config_context = context.config_context();
let mut config = podman_config(config_context)?;
log_trusted_runtime_images(
config_context,
"podman",
&config.sandbox_runtime_image,
&config.supervisor_image,
true,
);
require_guest_tls_for_local_driver(&context, "podman")?;
apply_guest_tls(
&mut config.guest_tls_ca,
Expand Down
Loading
Loading