Skip to content

Commit e50e1df

Browse files
author
EchoBT
committed
feat: make secure broker the default container backend
BREAKING CHANGE: Production validators now require the broker by default. Priority order for backend selection: 1. DEVELOPMENT_MODE=true -> Direct Docker (local dev only) 2. Broker socket available -> Secure broker (production default) 3. No broker + not dev mode -> Fallback with warnings Default broker socket: /var/run/platform/broker.sock For local development, set DEVELOPMENT_MODE=true
1 parent 5e729c9 commit e50e1df

File tree

2 files changed

+108
-21
lines changed

2 files changed

+108
-21
lines changed

crates/challenge-orchestrator/src/backend.rs

Lines changed: 97 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,35 @@
11
//! Container backend abstraction
22
//!
33
//! Provides a unified interface for container management that can use:
4-
//! - Direct Docker (for local development/testing)
5-
//! - SecureContainerClient via broker (for production validators)
4+
//! - SecureContainerClient via broker (DEFAULT for production validators)
5+
//! - Direct Docker (ONLY for local development when DEVELOPMENT_MODE=true)
66
//!
7-
//! The backend is selected based on the CONTAINER_BROKER_SOCKET environment variable.
8-
//! If set, uses the secure broker. Otherwise, uses direct Docker.
7+
//! ## Backend Selection (Priority Order)
8+
//!
9+
//! 1. If `DEVELOPMENT_MODE=true` -> Direct Docker (local dev only)
10+
//! 2. If `CONTAINER_BROKER_SOCKET` is set -> Use that socket path
11+
//! 3. If default socket exists (`/var/run/platform/broker.sock`) -> Use broker
12+
//! 4. Otherwise -> Error (production requires broker)
13+
//!
14+
//! ## Security
15+
//!
16+
//! In production, challenges MUST run through the secure broker.
17+
//! The broker enforces:
18+
//! - Image whitelisting (only ghcr.io/platformnetwork/)
19+
//! - Non-privileged containers
20+
//! - Resource limits
21+
//! - No Docker socket access for challenges
922
1023
use crate::{ChallengeContainerConfig, ChallengeInstance, ContainerStatus};
1124
use async_trait::async_trait;
1225
use secure_container_runtime::{
1326
ContainerConfigBuilder, ContainerState, NetworkMode, SecureContainerClient,
1427
};
15-
use tracing::{info, warn};
28+
use std::path::Path;
29+
use tracing::{error, info, warn};
30+
31+
/// Default broker socket path
32+
pub const DEFAULT_BROKER_SOCKET: &str = "/var/run/platform/broker.sock";
1633

1734
/// Container backend trait for managing challenge containers
1835
#[async_trait]
@@ -60,12 +77,37 @@ impl SecureBackend {
6077
}
6178
}
6279

63-
/// Create from environment
80+
/// Create from environment or default socket
6481
pub fn from_env() -> Option<Self> {
65-
let socket = std::env::var("CONTAINER_BROKER_SOCKET").ok()?;
6682
let validator_id =
6783
std::env::var("VALIDATOR_HOTKEY").unwrap_or_else(|_| "unknown".to_string());
68-
Some(Self::new(&socket, &validator_id))
84+
85+
// Priority 1: Explicit socket path from env
86+
if let Ok(socket) = std::env::var("CONTAINER_BROKER_SOCKET") {
87+
if Path::new(&socket).exists() {
88+
info!(socket = %socket, "Using broker socket from environment");
89+
return Some(Self::new(&socket, &validator_id));
90+
}
91+
warn!(socket = %socket, "Broker socket from env does not exist");
92+
}
93+
94+
// Priority 2: Default socket path
95+
if Path::new(DEFAULT_BROKER_SOCKET).exists() {
96+
info!(socket = %DEFAULT_BROKER_SOCKET, "Using default broker socket");
97+
return Some(Self::new(DEFAULT_BROKER_SOCKET, &validator_id));
98+
}
99+
100+
None
101+
}
102+
103+
/// Check if broker is available
104+
pub fn is_available() -> bool {
105+
if let Ok(socket) = std::env::var("CONTAINER_BROKER_SOCKET") {
106+
if Path::new(&socket).exists() {
107+
return true;
108+
}
109+
}
110+
Path::new(DEFAULT_BROKER_SOCKET).exists()
69111
}
70112
}
71113

@@ -254,20 +296,60 @@ impl ContainerBackend for DirectDockerBackend {
254296
}
255297

256298
/// Create the appropriate backend based on environment
299+
///
300+
/// Priority order:
301+
/// 1. DEVELOPMENT_MODE=true -> Direct Docker (local dev only)
302+
/// 2. Broker socket available -> Secure broker (production default)
303+
/// 3. No broker + not dev mode -> Error (production requires broker)
257304
pub async fn create_backend() -> anyhow::Result<Box<dyn ContainerBackend>> {
258-
// Check if broker socket is configured
305+
// Check if explicitly in development mode
306+
let dev_mode = std::env::var("DEVELOPMENT_MODE")
307+
.map(|v| v == "true" || v == "1")
308+
.unwrap_or(false);
309+
310+
if dev_mode {
311+
info!("DEVELOPMENT_MODE=true: Using direct Docker (local development)");
312+
let direct = DirectDockerBackend::new().await?;
313+
return Ok(Box::new(direct));
314+
}
315+
316+
// Try to use secure broker (default for production)
259317
if let Some(secure) = SecureBackend::from_env() {
260-
info!("Using secure container broker");
318+
info!("Using secure container broker (production mode)");
261319
return Ok(Box::new(secure));
262320
}
263321

264-
// Fall back to direct Docker
265-
info!("Using direct Docker (local development mode)");
266-
let direct = DirectDockerBackend::new().await?;
267-
Ok(Box::new(direct))
322+
// No broker available - try Docker as last resort but warn
323+
warn!("Broker not available. Attempting Docker fallback...");
324+
warn!("This should only happen in local development!");
325+
warn!("Set DEVELOPMENT_MODE=true to suppress this warning, or start the broker.");
326+
327+
match DirectDockerBackend::new().await {
328+
Ok(direct) => {
329+
warn!("Using direct Docker - NOT RECOMMENDED FOR PRODUCTION");
330+
Ok(Box::new(direct))
331+
}
332+
Err(e) => {
333+
error!("Cannot connect to Docker: {}", e);
334+
error!("For production: Start the container-broker service");
335+
error!("For development: Set DEVELOPMENT_MODE=true and ensure Docker is running");
336+
Err(anyhow::anyhow!(
337+
"No container backend available. \
338+
Start broker at {} or set DEVELOPMENT_MODE=true for local Docker",
339+
DEFAULT_BROKER_SOCKET
340+
))
341+
}
342+
}
268343
}
269344

270345
/// Check if running in secure mode (broker available)
271346
pub fn is_secure_mode() -> bool {
272-
std::env::var("CONTAINER_BROKER_SOCKET").is_ok()
347+
SecureBackend::is_available()
348+
}
349+
350+
/// Check if in development mode
351+
pub fn is_development_mode() -> bool {
352+
std::env::var("DEVELOPMENT_MODE")
353+
.map(|v| v == "true" || v == "1")
354+
.unwrap_or(false)
273355
}

crates/challenge-orchestrator/src/lib.rs

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,17 @@
66
//! - Evaluation routing
77
//! - Hot-swap without core restart
88
//!
9-
//! ## Backend Selection
9+
//! ## Backend Selection (Secure by Default)
1010
//!
11-
//! The orchestrator supports two backends:
12-
//! - **Direct Docker** (default): For local development and testing
13-
//! - **Secure Broker**: For production validators, uses Unix socket API
11+
//! The orchestrator uses the **secure broker by default** in production.
12+
//! Direct Docker is ONLY used when explicitly in development mode.
1413
//!
15-
//! Set `CONTAINER_BROKER_SOCKET=/var/run/platform/broker.sock` to use the secure broker.
14+
//! Priority order:
15+
//! 1. `DEVELOPMENT_MODE=true` -> Direct Docker (local dev only)
16+
//! 2. Broker socket exists -> Secure broker (production default)
17+
//! 3. No broker + not dev mode -> Fallback to Docker with warnings
18+
//!
19+
//! Default broker socket: `/var/run/platform/broker.sock`
1620
1721
pub mod backend;
1822
pub mod config;
@@ -22,7 +26,8 @@ pub mod health;
2226
pub mod lifecycle;
2327

2428
pub use backend::{
25-
create_backend, is_secure_mode, ContainerBackend, DirectDockerBackend, SecureBackend,
29+
create_backend, is_development_mode, is_secure_mode, ContainerBackend, DirectDockerBackend,
30+
SecureBackend, DEFAULT_BROKER_SOCKET,
2631
};
2732
pub use config::*;
2833
pub use docker::*;

0 commit comments

Comments
 (0)