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
9 changes: 2 additions & 7 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion crates/openshell-core/src/isolation_registration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use std::path::PathBuf;
use serde::{Deserialize, Serialize};

/// Reserved selection for drivers that launch the built-in sandbox runtime.
pub const BUILTIN_BACKEND_NAME: &str = "openshell-sandbox";
pub use openshell_driver_sdk::legacy_contract::BUILTIN_BACKEND_NAME;

/// Trusted byte-stream endpoint. Generation-specific TLS is supplied by the driver.
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
Expand Down
4 changes: 1 addition & 3 deletions crates/openshell-driver-docker/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,7 @@ path = "src/main.rs"

[dependencies]
openshell-container-runtime = { path = "../openshell-container-runtime" }
openshell-driver-sdk = { path = "../openshell-driver-sdk", features = ["jwt", "otel", "clap"] }
openshell-isolation-interface = { path = "../openshell-isolation-interface" }
openshell-sandbox-backend = { path = "../openshell-sandbox-backend" }
openshell-driver-sdk = { path = "../openshell-driver-sdk", features = ["jwt", "otel", "clap", "legacy-contract"] }
openshell-otel = { path = "../openshell-otel" }

opentelemetry = { workspace = true }
Expand Down
11 changes: 5 additions & 6 deletions crates/openshell-driver-docker/src/isolation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ use std::collections::{BTreeMap, HashMap};
use std::net::IpAddr;
use std::path::PathBuf;

use openshell_isolation_interface::contract::{
use openshell_driver_sdk::legacy_contract::GPU_RESOURCE_CLAIM;
use openshell_driver_sdk::legacy_contract::{
BackendError, ProvisioningEvidence, ResolvedWorkloadIdentity,
};
use openshell_sandbox_backend::GPU_RESOURCE_CLAIM;
use openshell_sandbox_backend::boundary_protocol::{
use openshell_driver_sdk::legacy_contract::{
BoundaryConfig, BoundaryListener, GatewayVerificationKey, SandboxRuntimeDescriptor,
SandboxTlsClientConfig, SandboxTlsServerConfig, SandboxTransport,
};
Expand Down Expand Up @@ -167,9 +167,8 @@ mod tests {
#[test]
fn provisioning_binds_container_and_image_claims() {
let session_id = openshell_driver_sdk::SandboxSessionId::new();
let tls =
openshell_sandbox_backend::boundary_protocol::generate_sandbox_tls_material(session_id)
.unwrap();
let tls = openshell_driver_sdk::legacy_contract::generate_sandbox_tls_material(session_id)
.unwrap();
let provisioned = DockerBoundarySpec {
boundary_id: "sandbox-1".to_string(),
generation: "generation-1".to_string(),
Expand Down
37 changes: 25 additions & 12 deletions crates/openshell-driver-docker/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@ use openshell_driver_sdk::driver_utils::{
SUPERVISOR_EXIT_WORKSPACE_VALIDATION_FAILED,
};
use openshell_driver_sdk::gpu::{driver_gpu_requirements, effective_driver_gpu_count};
use openshell_driver_sdk::legacy_contract::ResolvedWorkloadIdentity;
use openshell_driver_sdk::legacy_contract::{
BoundaryConfig, GatewayVerificationKey, SandboxRuntimeDescriptor, SandboxTlsClientConfig,
SandboxTlsServerConfig, generate_sandbox_tls_material,
};
use openshell_driver_sdk::progress::{
PROGRESS_STEP_PULLING_IMAGE, PROGRESS_STEP_REQUESTING_SANDBOX, PROGRESS_STEP_STARTING_SANDBOX,
format_bytes, mark_progress_active, mark_progress_complete, mark_progress_detail,
Expand All @@ -64,11 +69,6 @@ use openshell_driver_sdk::{
AppArmorProfile, Error, ImagePullPolicy, NetworkSupervisorTrustBundle, Result as CoreResult,
UpstreamProxyConfig,
};
use openshell_isolation_interface::contract::ResolvedWorkloadIdentity;
use openshell_sandbox_backend::boundary_protocol::{
BoundaryConfig, GatewayVerificationKey, SandboxRuntimeDescriptor, SandboxTlsClientConfig,
SandboxTlsServerConfig, generate_sandbox_tls_material,
};
use opentelemetry::trace::TraceContextExt as _;
use sha2::{Digest as _, Sha256};
use std::borrow::Cow;
Expand Down Expand Up @@ -127,9 +127,10 @@ const SUPERVISOR_PROXY_CA_BUNDLE_MOUNT_PATH: &str =
"/.openshell/supervisor/upstream-proxy-ca-bundle.pem";
const PROVIDER_SPIFFE_WORKLOAD_API_SOCKET_MOUNT_DIR: &str =
openshell_container_runtime::driver_utils::PROVIDER_SPIFFE_WORKLOAD_API_SOCKET_MOUNT_DIR;
const DRIVER_ADMITTED_BACKEND: &str = openshell_sandbox_backend::BACKEND_NAME;
const DRIVER_ADMITTED_BACKEND: &str = openshell_driver_sdk::legacy_contract::BUILTIN_BACKEND_NAME;
const LABEL_ISOLATION_BACKEND: &str = "openshell.ai/isolation-backend";
const LABEL_ISOLATION_BACKEND_OPEN_SHELL: &str = openshell_sandbox_backend::BACKEND_NAME;
const LABEL_ISOLATION_BACKEND_OPEN_SHELL: &str =
openshell_driver_sdk::legacy_contract::BUILTIN_BACKEND_NAME;
const LABEL_ISOLATION_ROLE: &str = "openshell.ai/isolation-role";
const LABEL_ISOLATION_ROLE_SANDBOX: &str = "sandbox";
const LABEL_ISOLATION_ROLE_SUPERVISOR: &str = "supervisor";
Expand Down Expand Up @@ -5083,8 +5084,14 @@ async fn stage_docker_supervisor_bundle(
Some(RemoveContainerOptionsBuilder::default().force(true).build()),
)
.await;
let runtime_initiated = read_docker_runtime_descriptor(&sandbox.id, config).await?
.is_some_and(|descriptor| matches!(descriptor.transport, openshell_sandbox_backend::boundary_protocol::SandboxTransport::RuntimeInitiatedUnix { .. }));
let runtime_initiated = read_docker_runtime_descriptor(&sandbox.id, config)
.await?
.is_some_and(|descriptor| {
matches!(
descriptor.transport,
openshell_driver_sdk::legacy_contract::SandboxTransport::RuntimeInitiatedUnix { .. }
)
});
let mut mounts = vec![Mount {
target: Some(SUPERVISOR_STATE_MOUNT_PATH.to_string()),
source: Some(docker_supervisor_volume_name(sandbox, config)),
Expand Down Expand Up @@ -5523,8 +5530,14 @@ async fn spawn_docker_control_process(
&config.upstream_proxy,
config.proxy_ca_bundle.is_some(),
));
let runtime_initiated = read_docker_runtime_descriptor(&sandbox.id, config).await?
.is_some_and(|descriptor| matches!(descriptor.transport, openshell_sandbox_backend::boundary_protocol::SandboxTransport::RuntimeInitiatedUnix { .. }));
let runtime_initiated = read_docker_runtime_descriptor(&sandbox.id, config)
.await?
.is_some_and(|descriptor| {
matches!(
descriptor.transport,
openshell_driver_sdk::legacy_contract::SandboxTransport::RuntimeInitiatedUnix { .. }
)
});
command.extend(docker_network_trust_supervisor_args(
config.network_trust.as_ref(),
));
Expand Down Expand Up @@ -6368,7 +6381,7 @@ fn build_container_create_body_for_image(
// exactly as written so short names never expand to host domains.
dns_search: Some(vec![".".to_string()]),
tmpfs: Some(HashMap::from([(
openshell_sandbox_backend::SUPERVISOR_CA_RUNTIME_DIR.to_string(),
openshell_driver_sdk::legacy_contract::SUPERVISOR_CA_RUNTIME_DIR.to_string(),
format!(
"rw,noexec,nosuid,nodev,size=1m,uid={},gid={},mode=0755",
workload_identity.uid, workload_identity.gid
Expand Down
2 changes: 1 addition & 1 deletion crates/openshell-driver-docker/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3783,7 +3783,7 @@ fn build_container_create_body_limits_writable_runtime_storage_to_supervisor_ca(

assert_eq!(tmpfs.len(), 1);
assert_eq!(
tmpfs.get(openshell_sandbox_backend::SUPERVISOR_CA_RUNTIME_DIR),
tmpfs.get(openshell_driver_sdk::legacy_contract::SUPERVISOR_CA_RUNTIME_DIR),
Some(&"rw,noexec,nosuid,nodev,size=1m,uid=1000,gid=1000,mode=0755".to_string())
);
assert!(!tmpfs.contains_key("/run"));
Expand Down
4 changes: 1 addition & 3 deletions crates/openshell-driver-podman/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,9 @@ name = "openshell-driver-podman"
path = "src/main.rs"

[dependencies]
openshell-driver-sdk = { path = "../openshell-driver-sdk", features = ["jwt", "otel", "clap"] }
openshell-driver-sdk = { path = "../openshell-driver-sdk", features = ["jwt", "otel", "clap", "legacy-contract"] }
openshell-container-runtime = { path = "../openshell-container-runtime" }
openshell-otel = { path = "../openshell-otel" }
openshell-isolation-interface = { path = "../openshell-isolation-interface" }
openshell-sandbox-backend = { path = "../openshell-sandbox-backend" }
tar = "0.4"
tempfile = "3"
uuid = { workspace = true }
Expand Down
17 changes: 9 additions & 8 deletions crates/openshell-driver-podman/src/container.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ const CONTAINER_PREFIX: &str = "openshell-";
const VOLUME_PREFIX: &str = "openshell-sandbox-";
const PODMAN_WORKLOAD_CONTROL_PATHS: &[&str] = &[
"/.openshell",
openshell_sandbox_backend::SUPERVISOR_CA_RUNTIME_ROOT,
openshell_driver_sdk::legacy_contract::SUPERVISOR_CA_RUNTIME_ROOT,
];

/// Secret name prefix for per-sandbox gateway JWTs.
Expand Down Expand Up @@ -1591,7 +1591,7 @@ pub struct IsolationSpecInput<'a> {
/// Gateway-owned immutable destination-trust artifact. It is deliberately
/// mounted only into the trusted supervisor spec.
pub network_trust: Option<&'a NetworkSupervisorTrustBundle>,
pub identity: &'a openshell_isolation_interface::contract::ResolvedWorkloadIdentity,
pub identity: &'a openshell_driver_sdk::legacy_contract::ResolvedWorkloadIdentity,
}

pub struct IsolationSpecs {
Expand Down Expand Up @@ -1686,7 +1686,7 @@ pub fn build_isolation_specs(
workload.mounts.push(Mount {
kind: "tmpfs".into(),
source: "tmpfs".into(),
destination: openshell_sandbox_backend::SUPERVISOR_CA_RUNTIME_ROOT.into(),
destination: openshell_driver_sdk::legacy_contract::SUPERVISOR_CA_RUNTIME_ROOT.into(),
options: vec![
"rw".into(),
"noexec".into(),
Expand Down Expand Up @@ -1744,7 +1744,7 @@ pub fn build_isolation_specs(
}
supervisor.env.insert(
openshell_driver_sdk::sandbox_env::ADMITTED_ISOLATION_BACKEND.into(),
openshell_sandbox_backend::BACKEND_NAME.into(),
openshell_driver_sdk::legacy_contract::BUILTIN_BACKEND_NAME.into(),
);
// The supervisor runs as the resolved non-root workload identity. Keep
// generated interception CA material under its writable /run tmpfs rather
Expand Down Expand Up @@ -1986,7 +1986,7 @@ mod tests {
name: "trust-pair".into(),
..Default::default()
};
let identity = openshell_isolation_interface::contract::ResolvedWorkloadIdentity::new(
let identity = openshell_driver_sdk::legacy_contract::ResolvedWorkloadIdentity::new(
1000,
1001,
Vec::new(),
Expand Down Expand Up @@ -2092,7 +2092,7 @@ mod tests {
"openshell-sandbox".into(),
));
config.userns = Some("auto".into());
let identity = openshell_isolation_interface::contract::ResolvedWorkloadIdentity::new(
let identity = openshell_driver_sdk::legacy_contract::ResolvedWorkloadIdentity::new(
1000,
1001,
vec![2000],
Expand Down Expand Up @@ -2181,7 +2181,7 @@ mod tests {
assert_eq!(specs.supervisor.apparmor_profile, None);

let default_identity =
openshell_isolation_interface::contract::ResolvedWorkloadIdentity::new(
openshell_driver_sdk::legacy_contract::ResolvedWorkloadIdentity::new(
1000,
1000,
Vec::new(),
Expand Down Expand Up @@ -2247,7 +2247,8 @@ mod tests {
.mounts
.iter()
.find(|mount| {
mount.destination == openshell_sandbox_backend::SUPERVISOR_CA_RUNTIME_ROOT
mount.destination
== openshell_driver_sdk::legacy_contract::SUPERVISOR_CA_RUNTIME_ROOT
})
.expect("workload supervisor CA mount");
assert_eq!(supervisor_ca_mount.kind, "tmpfs");
Expand Down
4 changes: 2 additions & 2 deletions crates/openshell-driver-podman/src/driver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1670,7 +1670,7 @@ impl PodmanComputeDriver {
sandbox: &DriverSandbox,
image: &str,
image_user: &str,
) -> Result<openshell_isolation_interface::contract::ResolvedWorkloadIdentity, ComputeDriverError>
) -> Result<openshell_driver_sdk::legacy_contract::ResolvedWorkloadIdentity, ComputeDriverError>
{
#[derive(serde::Serialize)]
struct InspectionSpec<'a> {
Expand Down Expand Up @@ -4470,7 +4470,7 @@ mod tests {

fn restart_responses() -> Vec<StubResponse> {
let mut archive = tar::Builder::new(Vec::new());
let identity = openshell_isolation_interface::contract::ResolvedWorkloadIdentity::new(
let identity = openshell_driver_sdk::legacy_contract::ResolvedWorkloadIdentity::new(
1000,
1001,
vec![],
Expand Down
8 changes: 4 additions & 4 deletions crates/openshell-driver-podman/src/isolation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,14 @@ use std::io::Read;
use std::path::PathBuf;

use openshell_driver_sdk::ComputeDriverError;
use openshell_driver_sdk::proto::compute::v1::DriverSandbox;
use openshell_isolation_interface::contract::{ProvisioningEvidence, ResolvedWorkloadIdentity};
use openshell_sandbox_backend::ALLOW_EXTRA_SUPPLEMENTARY_GROUPS_RESOURCE_CLAIM;
use openshell_sandbox_backend::boundary_protocol::{
use openshell_driver_sdk::legacy_contract::ALLOW_EXTRA_SUPPLEMENTARY_GROUPS_RESOURCE_CLAIM;
use openshell_driver_sdk::legacy_contract::{
BoundaryConfig, BoundaryListener, GatewayVerificationKey, SandboxRuntimeDescriptor,
SandboxTlsClientConfig, SandboxTlsServerConfig, SandboxTransport,
generate_sandbox_tls_material,
};
use openshell_driver_sdk::legacy_contract::{ProvisioningEvidence, ResolvedWorkloadIdentity};
use openshell_driver_sdk::proto::compute::v1::DriverSandbox;
use serde::{Deserialize, Serialize};

pub const LABEL_ROLE: &str = "openshell.ai/isolation-role";
Expand Down
4 changes: 4 additions & 0 deletions crates/openshell-driver-sdk/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ base64 = { workspace = true }
clap = { workspace = true, optional = true }
jsonwebtoken = { workspace = true, optional = true }
zeroize = { workspace = true, optional = true }
rcgen = { workspace = true, optional = true }
prost = { workspace = true }
prost-types = { workspace = true }
tonic = { workspace = true, features = ["channel"] }
Expand Down Expand Up @@ -51,6 +52,9 @@ default = []
telemetry = []
## Strict Ed25519 JWT issuance and verification for authenticated sandbox sessions.
jwt = ["dep:openshell-crypto", "dep:jsonwebtoken", "dep:zeroize"]
## The Rust-defined form of the documents a driver hands to the supervisor and the
## sandbox boundary. Legacy: removed when its replacement ships.
legacy-contract = ["jwt", "dep:rcgen"]
## Conversions to `openshell-otel` types.
otel = ["dep:openshell-otel"]
## Command-line flags shared by compute drivers.
Expand Down
48 changes: 48 additions & 0 deletions crates/openshell-driver-sdk/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,56 @@ only by the Docker, Podman, and VM drivers belongs in
- `error`, `paths`, `net`, `settings`, `telemetry`, `time`, `progress`,
`image`, `sandbox_session`, `sandbox_generation`, `proto_struct`,
`external_driver_socket`, `e2fsprogs`: small helpers.
- `legacy_contract` (feature `legacy-contract`): the Rust-defined documents a
driver hands to the supervisor and the sandbox boundary. See below.

## Dependencies

The crate depends only on workspace crates and crates.io, and never on
`openshell-core`.

## Legacy driver contract

A compute driver provisions two things for the supervisor and the sandbox
boundary besides the `ComputeDriver` gRPC service: the JSON documents below, and
a few constants. `legacy_contract` holds the Rust types for them so that a
driver does not depend on `openshell-core` or `openshell-sandbox-backend`.

| Type | Produced by | Read by |
|---|---|---|
| `jwt::SandboxLaunchAuthentication` | Gateway | Driver |
| `jwt::SupervisorAuthBundle` | Gateway, inside the launch authentication | Supervisor |
| `SandboxRuntimeDescriptor` | Driver | Supervisor |
| `BoundaryConfig` | Driver | Sandbox boundary |

The module also holds the backend name, the resource claim keys and the
supervisor CA paths that drivers set, and `generate_sandbox_tls_material`.

This is the format that exists today, not a specification to build on. It is
expected to be replaced by a language-neutral one, so the module accepts no new
fields and is removed once the replacement has shipped and no supported release
reads it. Readers reject unknown fields, so a writer must not add any, and the
documents carry no version.

`tests/legacy_contract/examples/` holds the golden documents, and
`tests/legacy_contract.rs` checks that each decodes and re-encodes to the same
JSON and that readers reject invalid ones. A change that alters an encoding
fails there.

### Replacing it

Options considered for the replacement are JSON Schema for these documents, and
protobuf messages written as proto3 JSON. JSON Schema describes today's bytes
exactly but is a second description of concepts that protos already define;
protobuf avoids that, and a driver that serializes generated types would emit
proto3 JSON, which existing readers reject. Nothing is decided.

A lower-risk path than reading both formats is to write both: a driver writes
the new format under a new file name beside the legacy one, and the supervisor
and sandbox prefer the new file when present. Every document is regenerated at
each create and restart, so there is no stored data to migrate. The gateway's
launch authentication travels over gRPC, so it can gain a typed message field
beside the opaque bytes instead of a new JSON form. Docker rewrites its own
`BoundaryConfig` and descriptor in place at restart; it would first need to keep
its restart state privately, as Podman does, so that a driver never parses a
file written by an older one.
Loading
Loading