|
1 | 1 | //! Container backend abstraction |
2 | 2 | //! |
3 | 3 | //! 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) |
6 | 6 | //! |
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 |
9 | 22 |
|
10 | 23 | use crate::{ChallengeContainerConfig, ChallengeInstance, ContainerStatus}; |
11 | 24 | use async_trait::async_trait; |
12 | 25 | use secure_container_runtime::{ |
13 | 26 | ContainerConfigBuilder, ContainerState, NetworkMode, SecureContainerClient, |
14 | 27 | }; |
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"; |
16 | 33 |
|
17 | 34 | /// Container backend trait for managing challenge containers |
18 | 35 | #[async_trait] |
@@ -60,12 +77,37 @@ impl SecureBackend { |
60 | 77 | } |
61 | 78 | } |
62 | 79 |
|
63 | | - /// Create from environment |
| 80 | + /// Create from environment or default socket |
64 | 81 | pub fn from_env() -> Option<Self> { |
65 | | - let socket = std::env::var("CONTAINER_BROKER_SOCKET").ok()?; |
66 | 82 | let validator_id = |
67 | 83 | 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() |
69 | 111 | } |
70 | 112 | } |
71 | 113 |
|
@@ -254,20 +296,60 @@ impl ContainerBackend for DirectDockerBackend { |
254 | 296 | } |
255 | 297 |
|
256 | 298 | /// 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) |
257 | 304 | 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) |
259 | 317 | if let Some(secure) = SecureBackend::from_env() { |
260 | | - info!("Using secure container broker"); |
| 318 | + info!("Using secure container broker (production mode)"); |
261 | 319 | return Ok(Box::new(secure)); |
262 | 320 | } |
263 | 321 |
|
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 | + } |
268 | 343 | } |
269 | 344 |
|
270 | 345 | /// Check if running in secure mode (broker available) |
271 | 346 | 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) |
273 | 355 | } |
0 commit comments