fix(sandbox): exempt host gateway from SSRF block for rootless Podman#1279
Open
maxamillion wants to merge 3 commits intoNVIDIA:mainfrom
Open
fix(sandbox): exempt host gateway from SSRF block for rootless Podman#1279maxamillion wants to merge 3 commits intoNVIDIA:mainfrom
maxamillion wants to merge 3 commits intoNVIDIA:mainfrom
Conversation
pasta assigns a link-local IP (169.254.x.x) as the host gateway in rootless Podman containers, which was always blocked by SSRF guards. Read the trusted gateway IP from /etc/hosts at proxy startup (injected by the driver via host.openshell.internal / host.containers.internal aliases) and exempt only that specific IP from the link-local block, while keeping 169.254.169.254 (cloud metadata) hard-blocked regardless.
Extract a public is_link_local_ip(IpAddr) helper in openshell-core::net covering IPv4 169.254.0.0/16, IPv6 fe80::/10, and IPv4-mapped variants. Refactor the inline IPv6 fe80::/10 bitmask in is_always_blocked_ip() and is_internal_ip() to use the new helper, eliminating two copies of the same pattern. Harden the trusted-gateway SSRF exemption in proxy.rs: both detect_trusted_host_gateway() and resolve_and_check_trusted_gateway() now reject always-blocked non-link-local IPs (loopback, unspecified) with a warning. The exemption was intended only for link-local addresses used by rootless Podman with pasta; a /etc/hosts entry mapping host.openshell.internal to 127.0.0.1 or 0.0.0.0 previously bypassed the loopback/unspecified SSRF invariant. Add 169.254. prefix to the generate_security_notes() host checks in mechanistic_mapper and policy server, closing a gap where link-local literal hosts were not flagged as internal/private in advisory notes.
…ion tests When detect_trusted_host_gateway() finds more than one distinct IP for host.openshell.internal, emit a structured warning so operators can diagnose unexpected /etc/hosts state. The first entry still wins (and any runtime mismatch is caught by resolve_and_check_trusted_gateway), but the ambiguity is now observable. Replace inline guard-logic tests with real calls to resolve_and_check_trusted_gateway() using IP literals, which bypass DNS and give deterministic results. This exercises the actual function wiring rather than mirroring the predicate logic manually. Add parse_hosts_file_for_host tests covering: single entry, same-IP deduplication across lines, multiple distinct IPs (documents first-wins ordering), multi-alias lines, missing alias, and comment handling.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Rootless Podman with pasta assigns a link-local IP (
169.254.x.x) as the host gateway — previously always-blocked by SSRF guards — preventing sandbox code from reachinghost.openshell.internal. This PR fixes the SSRF exemption, hardens it against misuse, and adds a sharedis_link_local_ip()helper toopenshell-core.Related Issue
No tracking issue — discovered during rootless Podman networking investigation.
Changes
crates/openshell-core/src/net.rs: Add publicis_link_local_ip(IpAddr) -> boolcovering IPv4169.254.0.0/16, IPv6fe80::/10, and IPv4-mapped variants. Refactor the duplicate inline(v6.segments()[0] & 0xffc0) == 0xfe80bitmask out ofis_always_blocked_ip()andis_internal_ip()(was 3 copies, now 1).crates/openshell-sandbox/src/proxy.rs:/etc/hostsat proxy startup (before user code runs); exempt only that specific IP from the link-local SSRF block.detect_trusted_host_gateway()rejects cloud metadata IPs, always-blocked non-link-local IPs (loopback, unspecified), and emits a structured warning if/etc/hostshas multiple distinct IPs for the alias.resolve_and_check_trusted_gateway()adds defense-in-depth: rejects cloud metadata, mismatch against the pinned IP, always-blocked non-link-local addresses, and control-plane ports.HOST_GATEWAY_ALIASEScovershost.openshell.internal,host.containers.internal,host.docker.internal.CLOUD_METADATA_IPS(169.254.169.254) is never exempted.crates/openshell-sandbox/src/mechanistic_mapper.rsandcrates/openshell-server/src/grpc/policy.rs: Add"169.254."togenerate_security_notes()host prefix checks (advisory notes were missing link-local).Testing
mise run pre-commitpasses (all Rust checks: fmt, clippy, check, helm lint, markdown lint)is_link_local_ip()inopenshell-coreparse_hosts_file_for_hosttests covering: single entry, same-IP deduplication, multiple distinct IPs (documents first-wins ordering), multi-alias lines, missing alias, comment strippingresolve_and_check_trusted_gatewaytests using IP literals (deterministic, no DNS) covering: loopback rejection, unspecified rejection, mismatch rejection, cloud metadata rejectiondetect_trusted_host_gatewayguard predicate testsChecklist