fix: bind project container host ports to 0.0.0.0

Story 1130 added HUSKIES_HOST=0.0.0.0 so the server INSIDE a project
container binds to all interfaces, but the host-side `docker -p`
mapping was still `127.0.0.1:{port}:3001` and `127.0.0.1:{ssh_port}:22`
— reachable from the docker host only, blocking remote MCP clients
and out-of-host SSH onto the project container.

Switch host-side mapping to 0.0.0.0 for both the MCP and SSH ports so
project containers spawned via `new project` are reachable from
anywhere that can route to the docker host. Existing containers
created before this commit retain their localhost-only mapping and
need to be recreated to pick up the change.

Add a regression test asserting both -p arguments use 0.0.0.0 and
reject any 127.0.0.1 restriction in the mapping.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Timmy
2026-05-18 00:04:32 +01:00
parent ce688fc0bf
commit 810c8d4d72
@@ -913,6 +913,11 @@ pub async fn handle_new_project(
/// to all interfaces, making Docker port forwarding reachable from the host.
/// Without this the server defaults to `127.0.0.1` inside the container —
/// reachable only from within the container itself, not via `docker -p`.
///
/// Host-side port mappings use `0.0.0.0` so the MCP and SSH endpoints are
/// reachable from outside the docker host (not just from the host itself).
/// `127.0.0.1` had been the conservative default but blocked remote MCP
/// clients and out-of-host SSH onto the project container.
fn project_docker_run_args(
container_name: &str,
port: u16,
@@ -927,9 +932,9 @@ fn project_docker_run_args(
"--name".into(),
container_name.to_string(),
"-p".into(),
format!("127.0.0.1:{port}:3001"),
format!("0.0.0.0:{port}:3001"),
"-p".into(),
format!("127.0.0.1:{ssh_port}:22"),
format!("0.0.0.0:{ssh_port}:22"),
"-e".into(),
"HUSKIES_HOST=0.0.0.0".into(),
"-e".into(),
@@ -1348,6 +1353,39 @@ mod tests {
);
}
#[test]
fn project_docker_args_bind_host_ports_to_all_interfaces() {
// Project containers must be reachable from outside the docker host
// (remote MCP clients, ssh into the project container) — host-side
// mapping must use 0.0.0.0, not 127.0.0.1.
let args = project_docker_run_args(
"huskies-myapp",
3100,
2200,
"ssh-ed25519 AAAA...",
"Test User",
"test@example.com",
);
let pairs: Vec<_> = args.windows(2).collect();
assert!(
pairs
.iter()
.any(|w| w[0] == "-p" && w[1] == "0.0.0.0:3100:3001"),
"expected -p 0.0.0.0:3100:3001 in docker args, got: {args:?}"
);
assert!(
pairs
.iter()
.any(|w| w[0] == "-p" && w[1] == "0.0.0.0:2200:22"),
"expected -p 0.0.0.0:2200:22 in docker args, got: {args:?}"
);
assert!(
!pairs.iter().any(|w| w[0] == "-p"
&& (w[1].starts_with("127.0.0.1:") || w[1] == "127.0.0.1")),
"host-side mapping must not be 127.0.0.1-restricted: {args:?}"
);
}
#[test]
fn interpret_docker_run_error_missing_image_points_at_script() {
let stderr = "Unable to find image 'huskies-project-rust:latest' locally\n\