Skip to content

Commit 2cabec7

Browse files
committed
test(podman): align credential host alias assertion with isolation
Signed-off-by: Drew Newberry <anewberry@nvidia.com>
1 parent 456cd7a commit 2cabec7

3 files changed

Lines changed: 26 additions & 7 deletions

File tree

‎architecture/sandbox.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ container `/etc/hosts` contents.
3636
The host-networked Podman supervisor cannot use DNS aliases scoped to a
3737
separate Podman fixture network; host-backed fixtures use published ports and
3838
the pinned host alias.
39+
Podman also recognizes `host.containers.internal` as a separate host alias;
40+
driver-specific credential tests use it to verify hostname binding.
3941
The sandbox probes HTTP/2 connection liveness every five seconds and closes
4042
connections that miss a ten-second acknowledgement deadline. Closing a
4143
connection freezes the owned workload process tree and cancels its stream

‎crates/openshell-driver-podman/README.md‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,10 @@ against that pin rather than an untrusted `/etc/hosts` entry.
7373
Tests that run support containers publish their ports on the host and address
7474
them through this alias, since network-scoped Podman DNS aliases are unavailable
7575
to the host-networked supervisor.
76+
Credential endpoint tests use Podman's `host.containers.internal` as the second
77+
host alias when checking that a token bound to `host.openshell.internal` does
78+
not work at a different hostname. Podman's network boundary may deny that
79+
alternate host before the HTTP credential check returns a response.
7680

7781
The channel contains the sandbox bootstrap and sandbox-side TLS identity only.
7882
Supervisor private keys and the runtime descriptor stay in the supervisor's private filesystem.

‎e2e/rust/tests/host_gateway_alias.rs‎

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use std::io::Write;
77
use std::process::Stdio;
88

99
use openshell_e2e::harness::binary::openshell_cmd;
10+
use openshell_e2e::harness::container::is_e2e_driver;
1011
use openshell_e2e::harness::sandbox::SandboxGuard;
1112
use tempfile::{Builder as TempFileBuilder, NamedTempFile};
1213
use tokio::io::AsyncReadExt;
@@ -184,7 +185,7 @@ binaries: [/usr/bin/bash]
184185
Ok(file)
185186
}
186187

187-
fn write_binding_policy(port: u16) -> Result<NamedTempFile, String> {
188+
fn write_binding_policy(port: u16, unbound_host: &str) -> Result<NamedTempFile, String> {
188189
let mut file = NamedTempFile::new().map_err(|e| format!("create binding policy: {e}"))?;
189190
let policy = format!(
190191
r#"version: 1
@@ -211,7 +212,7 @@ network_policies:
211212
protocol: rest
212213
access: full
213214
enforcement: enforce
214-
- host: host.docker.internal
215+
- host: {unbound_host}
215216
port: {port}
216217
path: /**
217218
protocol: rest
@@ -342,6 +343,11 @@ async fn sandbox_reaches_host_openshell_internal_via_host_gateway_alias() {
342343

343344
#[tokio::test]
344345
async fn static_provider_credentials_are_bound_to_profile_endpoints() {
346+
let unbound_host = if is_e2e_driver("podman") {
347+
"host.containers.internal"
348+
} else {
349+
"host.docker.internal"
350+
};
345351
let server = HostServer::start_with_auth_check("", Some("Bearer e2e-bound-secret"))
346352
.await
347353
.expect("start credential echo server");
@@ -357,11 +363,11 @@ async fn static_provider_credentials_are_bound_to_profile_endpoints() {
357363
BINDING_PROFILE_B_ID,
358364
"E2E static endpoint binding B",
359365
"BOUND_TOKEN_B",
360-
"host.docker.internal",
366+
unbound_host,
361367
server.port,
362368
)
363369
.expect("write provider B binding profile");
364-
let policy = write_binding_policy(server.port).expect("write binding policy");
370+
let policy = write_binding_policy(server.port, unbound_host).expect("write binding policy");
365371
let profile_a_path = profile_a.path().to_string_lossy().into_owned();
366372
let profile_b_path = profile_b.path().to_string_lossy().into_owned();
367373
let policy_path = policy.path().to_string_lossy().into_owned();
@@ -420,9 +426,9 @@ http_request() {{
420426
exec 3>&- 3<&-
421427
}}
422428
http_request host.openshell.internal /allowed/check; allowed="$HTTP_BODY"
423-
http_request host.docker.internal /allowed/check; host_denied="$HTTP_STATUS"
429+
http_request {unbound_host} /allowed/check; host_connect_exit=$?; host_denied="$HTTP_STATUS"
424430
http_request host.openshell.internal /other/check; path_denied="$HTTP_STATUS"
425-
printf 'ALLOWED=%s HOST_DENIED=%s PATH_DENIED=%s\n' "$allowed" "$host_denied" "$path_denied"
431+
printf 'ALLOWED=%s HOST_DENIED=%s HOST_CONNECT_EXIT=%s PATH_DENIED=%s\n' "$allowed" "$host_denied" "$host_connect_exit" "$path_denied"
426432
"#,
427433
server.port
428434
);
@@ -455,8 +461,15 @@ printf 'ALLOWED=%s HOST_DENIED=%s PATH_DENIED=%s\n' "$allowed" "$host_denied" "$
455461
"credential should resolve at the bound endpoint:\n{}\nlogs:\n{logs}",
456462
guard.create_output,
457463
);
464+
let host_denied_by_http = guard.create_output.contains("HOST_DENIED=403");
465+
let host_denied_by_podman_boundary = is_e2e_driver("podman")
466+
&& guard.create_output.contains("HOST_CONNECT_EXIT=1")
467+
&& guard.create_output.contains(&format!(
468+
"/dev/tcp/{unbound_host}/{}: Permission denied",
469+
server.port
470+
));
458471
assert!(
459-
guard.create_output.contains("HOST_DENIED=403"),
472+
host_denied_by_http || host_denied_by_podman_boundary,
460473
"same placeholder must be denied at an unbound host:\n{}",
461474
guard.create_output
462475
);

0 commit comments

Comments
 (0)