Skip to content
Merged
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
70 changes: 3 additions & 67 deletions crates/openshell-core/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,23 +24,11 @@ pub struct Config {
/// Database URL for persistence.
pub database_url: String,

/// Kubernetes namespace for sandboxes.
#[serde(default = "default_sandbox_namespace")]
pub sandbox_namespace: String,

/// Default container image for sandboxes.
#[serde(default)]
pub sandbox_image: String,

/// Kubernetes `imagePullPolicy` for sandbox pods (e.g. `Always`,
/// `IfNotPresent`, `Never`). Defaults to empty, which lets Kubernetes
/// apply its own default (`:latest` → `Always`, anything else →
/// `IfNotPresent`).
#[serde(default)]
pub sandbox_image_pull_policy: String,

/// gRPC endpoint for sandboxes to connect back to OpenShell.
/// Used by sandbox pods to fetch their policy at startup.
#[serde(default)]
pub grpc_endpoint: String,

Expand Down Expand Up @@ -72,23 +60,7 @@ pub struct Config {
#[serde(default = "default_ssh_session_ttl_secs")]
pub ssh_session_ttl_secs: u64,

/// Kubernetes secret name containing client TLS materials for sandbox pods.
/// When set, sandbox pods get this secret mounted so they can connect to
/// the server over mTLS.
#[serde(default)]
pub client_tls_secret_name: String,

/// Host gateway IP for sandbox pod hostAliases.
/// When set, sandbox pods get hostAliases entries mapping
/// `host.docker.internal` and `host.openshell.internal` to this IP,
/// allowing them to reach services running on the Docker host.
#[serde(default)]
pub host_gateway_ip: String,

/// Sandbox backend: `"kubernetes"` (default) or `"apple-container"`.
///
/// When set to `"apple-container"`, the server manages sandboxes via
/// the container bridge daemon instead of the Kubernetes API.
/// Sandbox backend (e.g. `"apple-container"`).
#[serde(default = "default_sandbox_backend")]
pub sandbox_backend: String,

Expand Down Expand Up @@ -159,9 +131,7 @@ impl Config {
log_level: default_log_level(),
tls,
database_url: String::new(),
sandbox_namespace: default_sandbox_namespace(),
sandbox_image: String::new(),
sandbox_image_pull_policy: String::new(),
grpc_endpoint: String::new(),
ssh_gateway_host: default_ssh_gateway_host(),
ssh_gateway_port: default_ssh_gateway_port(),
Expand All @@ -170,8 +140,6 @@ impl Config {
ssh_handshake_secret: String::new(),
ssh_handshake_skew_secs: default_ssh_handshake_skew_secs(),
ssh_session_ttl_secs: default_ssh_session_ttl_secs(),
client_tls_secret_name: String::new(),
host_gateway_ip: String::new(),
sandbox_backend: default_sandbox_backend(),
bridge_endpoint: String::new(),
bridge_tls: None,
Expand Down Expand Up @@ -199,27 +167,13 @@ impl Config {
self
}

/// Create a new configuration with a sandbox namespace.
#[must_use]
pub fn with_sandbox_namespace(mut self, namespace: impl Into<String>) -> Self {
self.sandbox_namespace = namespace.into();
self
}

/// Create a new configuration with a default sandbox image.
#[must_use]
pub fn with_sandbox_image(mut self, image: impl Into<String>) -> Self {
self.sandbox_image = image.into();
self
}

/// Create a new configuration with a sandbox image pull policy.
#[must_use]
pub fn with_sandbox_image_pull_policy(mut self, policy: impl Into<String>) -> Self {
self.sandbox_image_pull_policy = policy.into();
self
}

/// Create a new configuration with a gRPC endpoint for sandbox callback.
#[must_use]
pub fn with_grpc_endpoint(mut self, endpoint: impl Into<String>) -> Self {
Expand Down Expand Up @@ -276,21 +230,7 @@ impl Config {
self
}

/// Set the Kubernetes secret name for sandbox client TLS materials.
#[must_use]
pub fn with_client_tls_secret_name(mut self, name: impl Into<String>) -> Self {
self.client_tls_secret_name = name.into();
self
}

/// Set the host gateway IP for sandbox pod hostAliases.
#[must_use]
pub fn with_host_gateway_ip(mut self, ip: impl Into<String>) -> Self {
self.host_gateway_ip = ip.into();
self
}

/// Set the sandbox backend (`"kubernetes"` or `"apple-container"`).
/// Set the sandbox backend.
#[must_use]
pub fn with_sandbox_backend(mut self, backend: impl Into<String>) -> Self {
self.sandbox_backend = backend.into();
Expand Down Expand Up @@ -320,10 +260,6 @@ fn default_log_level() -> String {
"info".to_string()
}

fn default_sandbox_namespace() -> String {
"default".to_string()
}

fn default_ssh_gateway_host() -> String {
"127.0.0.1".to_string()
}
Expand All @@ -349,5 +285,5 @@ const fn default_ssh_session_ttl_secs() -> u64 {
}

fn default_sandbox_backend() -> String {
"kubernetes".to_string()
"apple-container".to_string()
}
22 changes: 8 additions & 14 deletions crates/openshell-server/src/grpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ pub const MAX_PAGE_SIZE: u32 = 1000;
// enough for legitimate payloads while capping resource-exhaustion vectors.
// ---------------------------------------------------------------------------

/// Maximum length for a sandbox or provider name (Kubernetes name limit).
/// Maximum length for a sandbox or provider name.
const MAX_NAME_LEN: usize = 253;

/// Maximum number of providers that can be attached to a sandbox.
Expand Down Expand Up @@ -232,12 +232,10 @@ impl OpenShell for OpenShellService {
} else {
request.name.clone()
};
let namespace = self.state.config.sandbox_namespace.clone();

let sandbox = Sandbox {
id: id.clone(),
name: name.clone(),
namespace,
namespace: String::new(),
spec: Some(spec),
status: None,
phase: SandboxPhase::Provisioning as i32,
Expand Down Expand Up @@ -434,7 +432,7 @@ impl OpenShell for OpenShellService {
}

// Replay buffered platform events (best-effort) so late subscribers
// see Kubernetes events (Scheduled, Pulling, etc.) that already fired.
// see provisioning events that already fired.
if follow_events {
for evt in state
.tracing_log_bus
Expand Down Expand Up @@ -3434,14 +3432,10 @@ async fn resolve_sandbox_exec_target(
return Err(Status::failed_precondition("sandbox has no name"));
}

// Kubernetes DNS fallback.
Ok((
format!(
"{}.{}.svc.cluster.local",
sandbox.name, state.config.sandbox_namespace
),
state.config.sandbox_ssh_port,
))
Err(Status::internal(format!(
"sandbox backend returned no exec target for {}",
sandbox.name
)))
}

/// Maximum number of arguments in the command array.
Expand Down Expand Up @@ -3660,7 +3654,7 @@ async fn stream_exec_over_ssh(
);

// Retry loop: the sandbox SSH server may not be accepting connections yet
// even though the pod is marked Ready by Kubernetes. We retry transient
// even though the sandbox is marked ready. We retry transient
// connection errors with exponential backoff.
let (exit_code, proxy_task) = {
let mut last_err: Option<Status> = None;
Expand Down
4 changes: 2 additions & 2 deletions crates/openshell-server/src/http.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,12 @@ async fn health() -> impl IntoResponse {
StatusCode::OK
}

/// Kubernetes liveness probe.
/// Liveness probe.
async fn healthz() -> impl IntoResponse {
StatusCode::OK
}

/// Kubernetes readiness probe with detailed status.
/// Readiness probe with detailed status.
async fn readyz() -> impl IntoResponse {
let response = HealthResponse {
status: "healthy",
Expand Down
34 changes: 1 addition & 33 deletions crates/openshell-server/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,20 +42,11 @@ struct Args {
#[arg(long, env = "OPENSHELL_DB_URL", required = true)]
db_url: String,

/// Kubernetes namespace for sandboxes.
#[arg(long, env = "OPENSHELL_SANDBOX_NAMESPACE", default_value = "default")]
sandbox_namespace: String,

/// Default container image for sandboxes.
#[arg(long, env = "OPENSHELL_SANDBOX_IMAGE")]
sandbox_image: Option<String>,

/// Kubernetes imagePullPolicy for sandbox pods (Always, IfNotPresent, Never).
#[arg(long, env = "OPENSHELL_SANDBOX_IMAGE_PULL_POLICY")]
sandbox_image_pull_policy: Option<String>,

/// gRPC endpoint for sandboxes to callback to `OpenShell`.
/// This should be reachable from within the Kubernetes cluster.
/// gRPC endpoint for sandboxes to connect back to the gateway.
#[arg(long, env = "OPENSHELL_GRPC_ENDPOINT")]
grpc_endpoint: Option<String>,

Expand Down Expand Up @@ -87,16 +78,6 @@ struct Args {
#[arg(long, env = "OPENSHELL_SSH_HANDSHAKE_SKEW_SECS", default_value_t = 300)]
ssh_handshake_skew_secs: u64,

/// Kubernetes secret name containing client TLS materials for sandbox pods.
#[arg(long, env = "OPENSHELL_CLIENT_TLS_SECRET_NAME")]
client_tls_secret_name: Option<String>,

/// Host gateway IP for sandbox pod hostAliases.
/// When set, sandbox pods get hostAliases entries mapping
/// host.docker.internal and host.openshell.internal to this IP.
#[arg(long, env = "OPENSHELL_HOST_GATEWAY_IP")]
host_gateway_ip: Option<String>,

/// Disable TLS entirely — listen on plaintext HTTP.
/// Use this when the gateway sits behind a reverse proxy or tunnel
/// (e.g. Cloudflare Tunnel) that terminates TLS at the edge.
Expand Down Expand Up @@ -178,7 +159,6 @@ async fn main() -> Result<()> {

config = config
.with_database_url(args.db_url)
.with_sandbox_namespace(args.sandbox_namespace)
.with_ssh_gateway_host(args.ssh_gateway_host)
.with_ssh_gateway_port(args.ssh_gateway_port)
.with_ssh_connect_path(args.ssh_connect_path)
Expand All @@ -189,10 +169,6 @@ async fn main() -> Result<()> {
config = config.with_sandbox_image(image);
}

if let Some(policy) = args.sandbox_image_pull_policy {
config = config.with_sandbox_image_pull_policy(policy);
}

if let Some(endpoint) = args.grpc_endpoint {
config = config.with_grpc_endpoint(endpoint);
}
Expand All @@ -201,14 +177,6 @@ async fn main() -> Result<()> {
config = config.with_ssh_handshake_secret(secret);
}

if let Some(name) = args.client_tls_secret_name {
config = config.with_client_tls_secret_name(name);
}

if let Some(ip) = args.host_gateway_ip {
config = config.with_host_gateway_ip(ip);
}

config = config.with_sandbox_backend(&args.sandbox_backend);

if let Some(endpoint) = args.bridge_endpoint {
Expand Down
Loading