Skip to content

Commit e8de0ce

Browse files
author
EchoBT
committed
feat: auto-create Docker network and connect validator
- Create 'platform-network' Docker network automatically at startup - Connect validator container to the network if running in Docker - This allows validator to communicate with challenge containers via hostname - Methods to detect container ID from HOSTNAME, cgroup, or mountinfo - Fixes 502 error when proxying to challenge containers
1 parent 9a9c395 commit e8de0ce

File tree

2 files changed

+114
-1
lines changed

2 files changed

+114
-1
lines changed

crates/challenge-orchestrator/src/docker.rs

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,11 +70,112 @@ impl DockerClient {
7070

7171
self.docker.create_network(config).await?;
7272
info!(network = %self.network_name, "Created Docker network");
73+
} else {
74+
debug!(network = %self.network_name, "Docker network already exists");
7375
}
7476

7577
Ok(())
7678
}
7779

80+
/// Connect the current container to the platform network
81+
/// This allows the validator to communicate with challenge containers via hostname
82+
pub async fn connect_self_to_network(&self) -> anyhow::Result<()> {
83+
// Get our container ID from the hostname or cgroup
84+
let container_id = self.get_self_container_id()?;
85+
86+
// Check if already connected
87+
let inspect = self.docker.inspect_container(&container_id, None).await?;
88+
let networks = inspect
89+
.network_settings
90+
.as_ref()
91+
.and_then(|ns| ns.networks.as_ref());
92+
93+
if let Some(nets) = networks {
94+
if nets.contains_key(&self.network_name) {
95+
debug!(
96+
container = %container_id,
97+
network = %self.network_name,
98+
"Container already connected to network"
99+
);
100+
return Ok(());
101+
}
102+
}
103+
104+
// Connect to the network
105+
use bollard::models::EndpointSettings;
106+
use bollard::network::ConnectNetworkOptions;
107+
108+
let config = ConnectNetworkOptions {
109+
container: container_id.clone(),
110+
endpoint_config: EndpointSettings::default(),
111+
};
112+
113+
self.docker
114+
.connect_network(&self.network_name, config)
115+
.await?;
116+
117+
info!(
118+
container = %container_id,
119+
network = %self.network_name,
120+
"Connected validator container to platform network"
121+
);
122+
123+
Ok(())
124+
}
125+
126+
/// Get the container ID of the current process (if running in Docker)
127+
fn get_self_container_id(&self) -> anyhow::Result<String> {
128+
// Method 1: Check hostname (Docker sets hostname to container ID by default)
129+
if let Ok(hostname) = std::env::var("HOSTNAME") {
130+
// Docker container IDs are 12+ hex characters
131+
if hostname.len() >= 12 && hostname.chars().all(|c| c.is_ascii_hexdigit()) {
132+
return Ok(hostname);
133+
}
134+
}
135+
136+
// Method 2: Parse from cgroup (works on Linux)
137+
if let Ok(cgroup) = std::fs::read_to_string("/proc/self/cgroup") {
138+
for line in cgroup.lines() {
139+
// Docker cgroup format: .../docker/<container_id>
140+
if let Some(docker_pos) = line.rfind("/docker/") {
141+
let id = &line[docker_pos + 8..];
142+
if id.len() >= 12 {
143+
return Ok(id[..12].to_string());
144+
}
145+
}
146+
// Kubernetes/containerd format: .../cri-containerd-<container_id>
147+
if let Some(containerd_pos) = line.rfind("cri-containerd-") {
148+
let id = &line[containerd_pos + 15..];
149+
if id.len() >= 12 {
150+
return Ok(id[..12].to_string());
151+
}
152+
}
153+
}
154+
}
155+
156+
// Method 3: Check /.dockerenv file exists
157+
if std::path::Path::new("/.dockerenv").exists() {
158+
// If we're in Docker but can't get ID, try the mountinfo
159+
if let Ok(mountinfo) = std::fs::read_to_string("/proc/self/mountinfo") {
160+
for line in mountinfo.lines() {
161+
if line.contains("/docker/containers/") {
162+
if let Some(start) = line.find("/docker/containers/") {
163+
let rest = &line[start + 19..];
164+
if let Some(end) = rest.find('/') {
165+
let id = &rest[..end];
166+
if id.len() >= 12 {
167+
return Ok(id[..12].to_string());
168+
}
169+
}
170+
}
171+
}
172+
}
173+
}
174+
}
175+
176+
anyhow::bail!("Not running in a Docker container or unable to determine container ID")
177+
}
178+
78179
/// Check if a Docker image is from an allowed registry
79180
/// SECURITY: This prevents pulling/running malicious containers
80181
fn is_image_allowed(image: &str) -> bool {

crates/challenge-orchestrator/src/lib.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,21 @@ pub struct ChallengeOrchestrator {
3232
config: OrchestratorConfig,
3333
}
3434

35+
/// Default network name for Platform containers
36+
pub const PLATFORM_NETWORK: &str = "platform-network";
37+
3538
impl ChallengeOrchestrator {
3639
pub async fn new(config: OrchestratorConfig) -> anyhow::Result<Self> {
37-
let docker = DockerClient::connect().await?;
40+
let docker = DockerClient::connect_with_network(PLATFORM_NETWORK).await?;
41+
42+
// Ensure the Docker network exists
43+
docker.ensure_network().await?;
44+
45+
// Try to connect the current container to the network (if running in Docker)
46+
if let Err(e) = docker.connect_self_to_network().await {
47+
tracing::debug!("Could not connect to network (may not be in Docker): {}", e);
48+
}
49+
3850
let challenges = Arc::new(RwLock::new(HashMap::new()));
3951
let health_monitor = HealthMonitor::new(challenges.clone(), config.health_check_interval);
4052

0 commit comments

Comments
 (0)