mirror of
https://github.com/NVIDIA/OpenShell.git
synced 2026-10-21 06:11:31 +08:00
feat(gateway): support runtime image env overrides (#3504)
Closes #3502 Resolve trusted OCI runtime images with driver TOML, process environment, and compiled-default precedence across Docker, Podman, and Kubernetes. Signed-off-by: Evan Lezar <elezar@nvidia.com>
This commit is contained in:
@@ -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(""),
|
||||
@@ -1644,15 +1668,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"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,14 @@ struct Args {
|
||||
#[arg(long, env = "OPENSHELL_DOCKER_DRIVER_CONFIG")]
|
||||
config: PathBuf,
|
||||
|
||||
/// OCI image containing the `openshell-sandbox` runtime binary.
|
||||
#[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_core::config::SUPERVISOR_IMAGE_ENV)]
|
||||
supervisor_image: Option<String>,
|
||||
|
||||
/// Gateway listener address used to derive the supervisor endpoint.
|
||||
#[arg(
|
||||
long,
|
||||
@@ -63,6 +71,12 @@ async fn main() -> Result<()> {
|
||||
docker_config.allow_driver_config = policy.allow_driver_config;
|
||||
docker_config.resource_admission = policy.resource_admission;
|
||||
}
|
||||
if let Some(image) = args.sandbox_runtime_image {
|
||||
docker_config.sandbox_runtime_image = Some(image);
|
||||
}
|
||||
if let Some(image) = args.supervisor_image {
|
||||
docker_config.supervisor_image = Some(image);
|
||||
}
|
||||
let driver = DockerComputeDriver::new(args.gateway_bind, &args.log_level, &docker_config)
|
||||
.await
|
||||
.into_diagnostic()?;
|
||||
|
||||
@@ -2503,6 +2503,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();
|
||||
|
||||
@@ -8865,6 +8865,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 {
|
||||
|
||||
@@ -116,13 +116,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")]
|
||||
|
||||
@@ -2313,6 +2313,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");
|
||||
|
||||
@@ -110,11 +110,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 supervisor-to-gateway TLS.
|
||||
|
||||
@@ -42,6 +42,18 @@ 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 prefer_environment<T>(configured: T, environment: Option<T>) -> T {
|
||||
environment.unwrap_or(configured)
|
||||
}
|
||||
|
||||
/// Install every first-party compute driver linked into the standard gateway.
|
||||
#[must_use]
|
||||
pub fn install_default_compute_drivers() -> ComputeDriverRegistry {
|
||||
@@ -272,6 +284,14 @@ fn kubernetes_config(
|
||||
) -> openshell_core::Result<openshell_driver_kubernetes::KubernetesComputeConfig> {
|
||||
let mut config: openshell_driver_kubernetes::KubernetesComputeConfig =
|
||||
context.driver_config()?;
|
||||
config.sandbox_runtime_image = prefer_environment(
|
||||
config.sandbox_runtime_image,
|
||||
std::env::var(openshell_core::config::SANDBOX_RUNTIME_IMAGE_ENV).ok(),
|
||||
);
|
||||
config.supervisor_image = prefer_environment(
|
||||
config.supervisor_image,
|
||||
std::env::var(openshell_core::config::SUPERVISOR_IMAGE_ENV).ok(),
|
||||
);
|
||||
if let Ok(size) = std::env::var("OPENSHELL_K8S_WORKSPACE_DEFAULT_STORAGE_SIZE") {
|
||||
config.workspace_default_storage_size = size;
|
||||
}
|
||||
@@ -296,7 +316,7 @@ impl openshell_server::ComputeDriverFactory for DockerFactory {
|
||||
&self,
|
||||
context: openshell_server::ComputeDriverConfigContext<'_>,
|
||||
) -> openshell_core::Result<()> {
|
||||
let config: openshell_driver_docker::DockerComputeConfig = context.driver_config()?;
|
||||
let config = docker_config(context)?;
|
||||
config.validate_configuration(context.gateway_bind_address())
|
||||
}
|
||||
|
||||
@@ -304,7 +324,7 @@ 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 mut config = docker_config(context.config_context())?;
|
||||
require_guest_tls_for_local_driver(&context, "docker")?;
|
||||
apply_guest_tls(&mut config.guest_tls_ca, context.guest_tls_ca());
|
||||
let driver = openshell_driver_docker::DockerComputeDriver::new(
|
||||
@@ -321,6 +341,26 @@ impl openshell_server::ComputeDriverFactory for DockerFactory {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(not(target_os = "windows"), feature = "compute-driver-docker"))]
|
||||
fn docker_config(
|
||||
context: openshell_server::ComputeDriverConfigContext<'_>,
|
||||
) -> openshell_core::Result<openshell_driver_docker::DockerComputeConfig> {
|
||||
let mut config: openshell_driver_docker::DockerComputeConfig = context.driver_config()?;
|
||||
config.sandbox_runtime_image = prefer_environment(
|
||||
config.sandbox_runtime_image,
|
||||
std::env::var(openshell_core::config::SANDBOX_RUNTIME_IMAGE_ENV)
|
||||
.ok()
|
||||
.map(Some),
|
||||
);
|
||||
config.supervisor_image = prefer_environment(
|
||||
config.supervisor_image,
|
||||
std::env::var(openshell_core::config::SUPERVISOR_IMAGE_ENV)
|
||||
.ok()
|
||||
.map(Some),
|
||||
);
|
||||
Ok(config)
|
||||
}
|
||||
|
||||
#[cfg(all(not(target_os = "windows"), feature = "compute-driver-podman"))]
|
||||
#[derive(Clone, Copy)]
|
||||
struct PodmanFactory;
|
||||
@@ -364,6 +404,14 @@ fn podman_config(
|
||||
) -> openshell_core::Result<openshell_driver_podman::PodmanComputeConfig> {
|
||||
let mut config: openshell_driver_podman::PodmanComputeConfig = context.driver_config()?;
|
||||
config.gateway_port = context.gateway_port();
|
||||
config.sandbox_runtime_image = prefer_environment(
|
||||
config.sandbox_runtime_image,
|
||||
std::env::var(openshell_core::config::SANDBOX_RUNTIME_IMAGE_ENV).ok(),
|
||||
);
|
||||
config.supervisor_image = prefer_environment(
|
||||
config.supervisor_image,
|
||||
std::env::var(openshell_core::config::SUPERVISOR_IMAGE_ENV).ok(),
|
||||
);
|
||||
if let Ok(path) = std::env::var("OPENSHELL_PODMAN_SOCKET") {
|
||||
config.socket_path = Some(path.into());
|
||||
}
|
||||
@@ -574,6 +622,23 @@ mod windows_tests {
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
#[cfg(all(
|
||||
not(target_os = "windows"),
|
||||
any(
|
||||
feature = "compute-driver-docker",
|
||||
feature = "compute-driver-kubernetes",
|
||||
feature = "compute-driver-podman"
|
||||
)
|
||||
))]
|
||||
fn runtime_image_environment_overrides_configured_value() {
|
||||
assert_eq!(
|
||||
prefer_environment("driver-toml", Some("process-environment")),
|
||||
"process-environment"
|
||||
);
|
||||
assert_eq!(prefer_environment("driver-toml", None), "driver-toml");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn default_registry_contains_exactly_the_enabled_compute_drivers() {
|
||||
let expected: Vec<&str> = vec![
|
||||
|
||||
@@ -175,6 +175,15 @@ need a custom bundle location.
|
||||
The gateway then starts from built-in defaults and reads
|
||||
*~/.config/openshell/gateway.toml* when that file exists.
|
||||
|
||||
The user service also reads *~/.config/openshell/gateway.env* for both config
|
||||
preflight and startup. **OPENSHELL_SANDBOX_RUNTIME_IMAGE** and
|
||||
**OPENSHELL_SUPERVISOR_IMAGE** accept complete tagged or digest-pinned OCI
|
||||
references for the built-in Docker, Podman, and Kubernetes drivers. These
|
||||
variables take precedence over explicit image fields in the selected driver's
|
||||
TOML table, which take precedence over compiled release defaults.
|
||||
These are trusted gateway inputs and cannot be supplied by sandbox requests.
|
||||
Do not embed registry credentials in image references.
|
||||
|
||||
To persist the service across logouts:
|
||||
|
||||
sudo loginctl enable-linger $USER
|
||||
|
||||
@@ -51,6 +51,21 @@ editing the TOML file, add them to `~/.config/openshell/gateway.env`:
|
||||
OPENSHELL_BIND_ADDRESS=192.168.1.10
|
||||
```
|
||||
|
||||
To select exact trusted runtime artifacts across the built-in Docker, Podman,
|
||||
or Kubernetes driver, set complete tagged or digest-pinned references:
|
||||
|
||||
```shell
|
||||
OPENSHELL_SANDBOX_RUNTIME_IMAGE=registry.example.com/openshell/sandbox@sha256:<digest>
|
||||
OPENSHELL_SUPERVISOR_IMAGE=registry.example.com/openshell/supervisor@sha256:<digest>
|
||||
```
|
||||
|
||||
`OPENSHELL_SANDBOX_RUNTIME_IMAGE` and `OPENSHELL_SUPERVISOR_IMAGE` take
|
||||
precedence over explicit image fields in the selected driver's TOML table; the
|
||||
driver TOML takes precedence over the compiled release default. Preflight and
|
||||
startup use the same resolution. These values are trusted operator inputs, not
|
||||
sandbox request fields. Keep registry credentials in Podman's credential store
|
||||
rather than embedding them in image references.
|
||||
|
||||
To override the path to the TOML config file entirely:
|
||||
|
||||
```shell
|
||||
@@ -216,8 +231,8 @@ overrides that persist across package upgrades.
|
||||
| `bind_address` | `127.0.0.1:17670` (gateway default) | Address for the primary gRPC/HTTP API listener. |
|
||||
| `compute_driver` | `"podman"` (RPM default) | When unset, the gateway auto-detects Kubernetes, then Podman, then Docker. The RPM default pins to Podman; legacy `compute_drivers` lists are rejected. |
|
||||
| `[openshell.drivers.podman].default_image` | `nvcr.io/nvidia/base/ubuntu:24.04` | Default sandbox image. |
|
||||
| `[openshell.drivers.podman].sandbox_runtime_image` | `ghcr.io/nvidia/openshell/sandbox:latest` | Static musl sandbox runtime image mounted into Podman workloads. |
|
||||
| `[openshell.drivers.podman].supervisor_image` | `ghcr.io/nvidia/openshell/supervisor:latest` | Dynamic glibc supervisor image used outside the workload. |
|
||||
| `[openshell.drivers.podman].sandbox_runtime_image` | `ghcr.io/nvidia/openshell/sandbox:latest` | Trusted sandbox runtime image. `OPENSHELL_SANDBOX_RUNTIME_IMAGE` overrides this field. |
|
||||
| `[openshell.drivers.podman].supervisor_image` | `ghcr.io/nvidia/openshell/supervisor:latest` | Trusted supervisor image. `OPENSHELL_SUPERVISOR_IMAGE` overrides this field. |
|
||||
| `[openshell.gateway].guest_tls_ca` | auto-generated path | Gateway CA injected into the selected local driver for supervisor-to-gateway TLS. Sandbox identity uses a bearer token. |
|
||||
| `[openshell.gateway.tls]` paths | auto-generated paths | Server TLS certificate, key, and client CA. |
|
||||
| `disable_tls` | unset | Set to `true` to disable TLS. |
|
||||
|
||||
@@ -90,6 +90,21 @@ The script installs a Debian package on Debian and Ubuntu or an RPM package on F
|
||||
|
||||
The gateway runs as a systemd user service at `https://127.0.0.1:17670` and reads `~/.config/openshell/gateway.toml`.
|
||||
|
||||
Both Debian and RPM load `~/.config/openshell/gateway.env` for configuration
|
||||
preflight and gateway startup. Use it to select exact trusted runtime artifacts
|
||||
without creating driver-specific TOML:
|
||||
|
||||
```shell
|
||||
OPENSHELL_SANDBOX_RUNTIME_IMAGE=registry.example.com/openshell/sandbox@sha256:<digest>
|
||||
OPENSHELL_SUPERVISOR_IMAGE=registry.example.com/openshell/supervisor@sha256:<digest>
|
||||
```
|
||||
|
||||
The environment variables take precedence over image fields in the selected
|
||||
Docker, Podman, or Kubernetes TOML table. Restart the user service after
|
||||
editing the file. Treat these variables as trusted operator configuration;
|
||||
sandbox requests cannot set them, and registry credentials must remain in the
|
||||
container runtime's credential store or Kubernetes image-pull secrets.
|
||||
|
||||
```shell
|
||||
systemctl --user status openshell-gateway
|
||||
systemctl --user restart openshell-gateway
|
||||
|
||||
@@ -20,6 +20,37 @@ Gateway CLI flag > gateway OPENSHELL_* env var > TOML file > built-in defa
|
||||
|
||||
On-disk SQLite databases run in WAL mode with `synchronous=FULL`: the gateway keeps `openshell.db-wal` and `openshell.db-shm` next to the database file, the file must live on a local filesystem, and a backup must use `sqlite3 openshell.db ".backup <copy>"` rather than copying `openshell.db` on its own. Every acknowledged write, including SSH session revocations, survives a power loss. The one exception is SSH session issuance, which skips the per-commit sync; a power loss can drop a just-issued session token, and the client must request a new one.
|
||||
|
||||
### Trusted runtime image overrides
|
||||
|
||||
Docker, Podman, and Kubernetes consume two gateway-owned trusted runtime
|
||||
images. Set complete tagged or digest-pinned OCI references for the gateway
|
||||
process when you need to qualify exact artifacts or use a registry mirror:
|
||||
|
||||
```shell
|
||||
OPENSHELL_SANDBOX_RUNTIME_IMAGE=registry.example.com/openshell/sandbox@sha256:<digest>
|
||||
OPENSHELL_SUPERVISOR_IMAGE=registry.example.com/openshell/supervisor:1.2.3
|
||||
```
|
||||
|
||||
These settings follow the gateway's normal environment-over-file precedence:
|
||||
|
||||
```text
|
||||
process environment > [openshell.drivers.<selected>] TOML > compiled release default
|
||||
```
|
||||
|
||||
`openshell-gateway config preflight` and gateway startup use the same effective
|
||||
references. Environment values are handled exactly like the corresponding TOML
|
||||
fields and passed to the selected container runtime; this feature does not add
|
||||
separate OCI-reference validation. Configure registry credentials in Docker,
|
||||
Podman, or Kubernetes image-pull secrets instead of embedding them in a
|
||||
reference.
|
||||
|
||||
The variables affect only the built-in Docker, Podman, and Kubernetes drivers.
|
||||
They do not affect VM, MXC, or extension drivers. They are trusted gateway
|
||||
operator inputs and are never accepted from sandbox create requests. For
|
||||
Debian and RPM user services, place them in
|
||||
`~/.config/openshell/gateway.env`; the service supplies that file to both
|
||||
preflight and gateway startup.
|
||||
|
||||
`name` assigns an operator-facing identity to the gateway installation. Set it with `[openshell.gateway].name`, `--name`, or `OPENSHELL_GATEWAY_NAME`. It defaults to `openshell`; the Helm chart defaults it to the chart fullname so all replicas in one installation share a name. Chart fullnames are only unique within their Kubernetes namespace, so set `server.name` explicitly when one collector receives telemetry from multiple namespaces or clusters. This identity is independent of client-side gateway aliases, TLS names, and `gateway_jwt.gateway_id`.
|
||||
|
||||
## Package-Managed Locations
|
||||
|
||||
@@ -36,6 +36,14 @@ When `compute_driver` is unset, the gateway auto-detects Kubernetes, then Podman
|
||||
|
||||
Configure driver-specific values, such as images, endpoints, and sizing, under `[openshell.drivers.<name>]`. See the [Gateway Configuration File](/how-it-works/gateways/configuration) reference for every option.
|
||||
|
||||
Docker, Podman, and Kubernetes also accept process overrides for the trusted
|
||||
sandbox-runtime and supervisor images through
|
||||
`OPENSHELL_SANDBOX_RUNTIME_IMAGE` and `OPENSHELL_SUPERVISOR_IMAGE`. The
|
||||
environment overrides values in the selected driver's TOML table, and the
|
||||
driver TOML overrides the compiled release defaults. Helm renders Kubernetes
|
||||
image fields into TOML, so they apply when these environment variables are
|
||||
unset. Sandbox requests cannot override either trusted image.
|
||||
|
||||
For TLS-enabled Docker, Podman, and MicroVM gateways, set `guest_tls_ca` in
|
||||
`[openshell.gateway]` or use the package-managed local CA. Remove the retired
|
||||
`guest_tls_cert` and `guest_tls_key` fields. Supervisors receive only the
|
||||
|
||||
@@ -57,7 +57,6 @@
|
||||
|
||||
[openshell.drivers.podman]
|
||||
default_image = "nvcr.io/nvidia/base/ubuntu:24.04"
|
||||
supervisor_image = "{{ openshell_supervisor_image }}"
|
||||
image_pull_policy = "always"
|
||||
network_name = "openshell-test-guest"
|
||||
grpc_endpoint = "http://host.containers.internal:8080"
|
||||
@@ -97,6 +96,7 @@
|
||||
Environment=XDG_CACHE_HOME={{ openshell_gateway_state_root }}/xdg/cache
|
||||
Environment=XDG_DATA_HOME={{ openshell_gateway_state_root }}/xdg/data
|
||||
Environment=XDG_STATE_HOME={{ openshell_gateway_state_root }}/xdg/state
|
||||
Environment=OPENSHELL_SUPERVISOR_IMAGE={{ openshell_supervisor_image }}
|
||||
ExecStart={{ openshell_gateway_bin }} --config {{ openshell_gateway_state_root }}/gateway.toml
|
||||
Restart=on-failure
|
||||
|
||||
|
||||
@@ -40,10 +40,20 @@ assert_file_exists() {
|
||||
service="${ROOT}/deploy/deb/openshell-gateway.service"
|
||||
control="${ROOT}/deploy/deb/control.in"
|
||||
spec="${ROOT}/openshell.spec"
|
||||
package_qualification="${ROOT}/tests/ansible/roles/openshell_packaged_gateway/tasks/main.yaml"
|
||||
tmachine_gateway_service="${ROOT}/tests/ansible/roles/openshell_gateway/templates/gateway.service.j2"
|
||||
tmachine_docker_config="${ROOT}/tests/ansible/roles/openshell_gateway/templates/gateway-docker.toml.j2"
|
||||
tmachine_podman_config="${ROOT}/tests/ansible/roles/openshell_gateway/templates/gateway-podman.toml.j2"
|
||||
development_gateway="${ROOT}/nix/test-guest/provisioners/roles/gateway-podman/tasks/development-gateway.yml"
|
||||
|
||||
assert_file_exists "$service"
|
||||
assert_file_exists "$control"
|
||||
assert_file_exists "$spec"
|
||||
assert_file_exists "$package_qualification"
|
||||
assert_file_exists "$tmachine_gateway_service"
|
||||
assert_file_exists "$tmachine_docker_config"
|
||||
assert_file_exists "$tmachine_podman_config"
|
||||
assert_file_exists "$development_gateway"
|
||||
|
||||
# Debian control files are RFC822-style metadata. Older dpkg-deb releases
|
||||
# reject comment lines as malformed fields, so keep SPDX metadata in the
|
||||
@@ -60,6 +70,8 @@ fi
|
||||
assert_contains \
|
||||
"$service" \
|
||||
'Environment=OPENSHELL_LOCAL_TLS_DIR=%h/.local/state/openshell/tls'
|
||||
assert_contains "$service" 'EnvironmentFile=-%E/openshell/gateway.env'
|
||||
assert_contains "$service" 'ExecStart=/usr/bin/openshell-gateway'
|
||||
assert_contains \
|
||||
"$service" \
|
||||
'ExecStartPre=/usr/bin/openshell-gateway generate-certs --output-dir ${OPENSHELL_LOCAL_TLS_DIR} --server-san host.openshell.internal'
|
||||
@@ -68,6 +80,8 @@ assert_not_contains "$service" '%S/openshell/tls'
|
||||
assert_contains \
|
||||
"$spec" \
|
||||
'Environment=OPENSHELL_LOCAL_TLS_DIR=%%h/.local/state/openshell/tls'
|
||||
assert_contains "$spec" 'EnvironmentFile=-%%E/openshell/gateway.env'
|
||||
assert_contains "$spec" 'ExecStart=/usr/bin/openshell-gateway'
|
||||
assert_contains \
|
||||
"$spec" \
|
||||
'ExecStartPre=/usr/bin/openshell-gateway generate-certs --output-dir ${OPENSHELL_LOCAL_TLS_DIR} --server-san host.openshell.internal'
|
||||
@@ -77,6 +91,28 @@ assert_contains "$spec" '%files prover'
|
||||
assert_contains "$spec" '%{_bindir}/%{name}-prover'
|
||||
assert_not_contains "$spec" '%%S/openshell/tls'
|
||||
|
||||
# Installed-package qualification must select the candidate trusted runtime
|
||||
# images through the same environment file consumed by preflight and startup,
|
||||
# without generating operator-owned gateway TOML.
|
||||
assert_contains "$package_qualification" 'dest: "{{ openshell_gateway_home }}/.config/openshell/gateway.env"'
|
||||
assert_contains "$package_qualification" 'OPENSHELL_COMPUTE_DRIVER={{ openshell_gateway_driver }}'
|
||||
assert_contains "$package_qualification" 'OPENSHELL_SANDBOX_RUNTIME_IMAGE=docker.io/openshell/sandbox:tmachine'
|
||||
assert_contains "$package_qualification" 'OPENSHELL_SUPERVISOR_IMAGE=docker.io/openshell/supervisor:tmachine'
|
||||
assert_not_contains "$package_qualification" 'OPENSHELL_GATEWAY_CONFIG='
|
||||
assert_not_contains "$package_qualification" '/var/lib/openshell-qualification/gateway.toml'
|
||||
|
||||
# Direct tmachine gateway services select trusted runtime images through the
|
||||
# process environment. Driver TOML remains available for settings that do not
|
||||
# have a gateway startup environment variable.
|
||||
assert_contains "$tmachine_gateway_service" 'Environment=OPENSHELL_SANDBOX_RUNTIME_IMAGE=docker.io/openshell/sandbox:tmachine'
|
||||
assert_contains "$tmachine_gateway_service" 'Environment=OPENSHELL_SUPERVISOR_IMAGE=docker.io/openshell/supervisor:tmachine'
|
||||
assert_not_contains "$tmachine_docker_config" 'sandbox_runtime_image ='
|
||||
assert_not_contains "$tmachine_docker_config" 'supervisor_image ='
|
||||
assert_not_contains "$tmachine_podman_config" 'sandbox_runtime_image ='
|
||||
assert_not_contains "$tmachine_podman_config" 'supervisor_image ='
|
||||
assert_contains "$development_gateway" 'Environment=OPENSHELL_SUPERVISOR_IMAGE={{ openshell_supervisor_image }}'
|
||||
assert_not_contains "$development_gateway" 'supervisor_image = "{{ openshell_supervisor_image }}"'
|
||||
|
||||
# Schema-v2 package startup wiring.
|
||||
snap_wrapper="${ROOT}/tasks/scripts/snap-gateway-wrapper.sh"
|
||||
snapcraft="${ROOT}/snapcraft.yaml"
|
||||
|
||||
@@ -7,6 +7,4 @@ socket_path = "{{ openshell_runtime_socket }}"
|
||||
image_pull_policy = "if_not_present"
|
||||
sandbox_label = "tmachine"
|
||||
grpc_endpoint = "http://127.0.0.1:17670"
|
||||
sandbox_runtime_image = "docker.io/openshell/sandbox:tmachine"
|
||||
supervisor_image = "docker.io/openshell/supervisor:tmachine"
|
||||
{% endblock %}
|
||||
|
||||
@@ -5,8 +5,6 @@
|
||||
[openshell.drivers.podman]
|
||||
socket_path = "{{ openshell_runtime_socket }}"
|
||||
image_pull_policy = "if_not_present"
|
||||
sandbox_runtime_image = "docker.io/openshell/sandbox:tmachine"
|
||||
supervisor_image = "docker.io/openshell/supervisor:tmachine"
|
||||
network_name = "tmachine"
|
||||
health_check_interval_secs = 10
|
||||
stop_timeout_secs = 15
|
||||
|
||||
@@ -10,6 +10,8 @@ After=network-online.target
|
||||
User={{ openshell_gateway_user }}
|
||||
Group={{ openshell_gateway_user }}
|
||||
Environment=HOME={{ openshell_gateway_home }}
|
||||
Environment=OPENSHELL_SANDBOX_RUNTIME_IMAGE=docker.io/openshell/sandbox:tmachine
|
||||
Environment=OPENSHELL_SUPERVISOR_IMAGE=docker.io/openshell/supervisor:tmachine
|
||||
{% block service %}{% endblock %}
|
||||
ExecStart=openshell-gateway --config /etc/openshell/gateway.toml
|
||||
Restart=on-failure
|
||||
|
||||
@@ -21,26 +21,6 @@
|
||||
name: openshell_gateway
|
||||
tasks_from: "{{ openshell_gateway_driver }}.yaml"
|
||||
|
||||
# Package defaults select published images. Keep candidate image overrides
|
||||
# separate from the operator-owned gateway.toml in both installers.
|
||||
- name: Create qualification configuration directory
|
||||
become: true
|
||||
ansible.builtin.file:
|
||||
path: /var/lib/openshell-qualification
|
||||
state: directory
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0755"
|
||||
|
||||
- name: Configure candidate runtime images for qualification
|
||||
become: true
|
||||
ansible.builtin.template:
|
||||
src: gateway.toml.j2
|
||||
dest: /var/lib/openshell-qualification/gateway.toml
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
|
||||
- name: Create packaged gateway environment directory
|
||||
become: true
|
||||
ansible.builtin.file:
|
||||
@@ -50,7 +30,10 @@
|
||||
group: "{{ openshell_gateway_user }}"
|
||||
mode: "0700"
|
||||
|
||||
- name: Select qualification gateway configuration
|
||||
# Exercise the package-owned service's environment-file mechanism without
|
||||
# generating operator-owned gateway TOML. Preflight and startup must resolve
|
||||
# the same candidate runtime images.
|
||||
- name: Configure candidate runtime images for qualification
|
||||
become: true
|
||||
ansible.builtin.copy:
|
||||
dest: "{{ openshell_gateway_home }}/.config/openshell/gateway.env"
|
||||
@@ -58,7 +41,9 @@
|
||||
group: "{{ openshell_gateway_user }}"
|
||||
mode: "0600"
|
||||
content: |
|
||||
OPENSHELL_GATEWAY_CONFIG=/var/lib/openshell-qualification/gateway.toml
|
||||
OPENSHELL_COMPUTE_DRIVER={{ openshell_gateway_driver }}
|
||||
OPENSHELL_SANDBOX_RUNTIME_IMAGE=docker.io/openshell/sandbox:tmachine
|
||||
OPENSHELL_SUPERVISOR_IMAGE=docker.io/openshell/supervisor:tmachine
|
||||
|
||||
- name: Start gateway user manager
|
||||
ansible.builtin.include_role:
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
{# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. #}
|
||||
{# SPDX-License-Identifier: Apache-2.0 #}
|
||||
[openshell]
|
||||
version = 2
|
||||
|
||||
[openshell.drivers.{{ openshell_gateway_driver }}]
|
||||
sandbox_runtime_image = "docker.io/openshell/sandbox:tmachine"
|
||||
supervisor_image = "docker.io/openshell/supervisor:tmachine"
|
||||
Reference in New Issue
Block a user