Skip to content

feat: allow sandbox policy to egress to host.docker.internal without requiring explicit allowed_ips #263

@pimlock

Description

@pimlock

Problem

When a sandbox network policy targets host.docker.internal as a destination, today the process requires two cumbersome steps:

  1. Add a policy rule for the hostname
  2. Wait for the environment to resolve host.docker.internal to an IP, then update the policy to include that resolved IP (via allowed_ips)—otherwise SSRF checks block the connection as private/internal

However, host.docker.internal is a special case in OpenShell. It is deliberately provisioned at cluster startup (via extra_hosts: ["host.docker.internal:host-gateway"] in Docker config, and included in PKI certificates and gateway metadata). Unlike arbitrary private network hosts, its IP is assigned by Docker and not known/controllable at policy authoring time, and it is explicitly intended to be reachable from sandboxes for local cluster routing.

Opportunity

Because host.docker.internal is an explicit and trusted part of the local cluster's topology—and is operator-configured, not open to arbitrary attacker control—it makes sense to loosen the SSRF protection for this one hostname.

Specifically: If a sandbox network policy names host.docker.internal, the proxy should allow CONNECT (and forward-proxy) egress to it without requiring an explicit IP in allowed_ips. The SSRF layer can resolve the current Docker gateway IP dynamically at runtime and allow only that, preserving defense-in-depth while removing an ergonomic obstacle for policy authors.

This change would:

  • Reduce policy friction for a documented, first-class cluster endpoint
  • Avoid brittle policies that break across environments or Docker restarts
  • Align the SSRF logic with intended operator workflows

Proposed Solution

  • Special-case host.docker.internal in the SSRF check path: when this hostname is matched in a policy, resolve its IPs at connect-time and allow only those without further allowed_ips override. (Never broaden to all private IPs, and never allow for arbitrary hostnames.)
  • Optionally: Document this behavior in the networking or policy architecture docs.

Relevant Code/Docs

  • SSRF check: resolve_and_reject_internal() and resolve_and_check_allowed_ips() in crates/navigator-sandbox/src/proxy.rs
  • OPA policy evaluation: crates/navigator-sandbox/data/sandbox-policy.rego
  • Bootstrap/cluster provision: cluster-bootstrap.sh, Docker extra_hosts, PKI SANs
  • Design: architecture/security-policy.md, architecture/sandbox.md

Risks and Mitigations

  • Non-Docker Desktop environments: Not all Linux systems set up host.docker.internal automatically. However, OpenShell already adds it when provisioning the cluster for CI and production.
  • Leakage to true private hosts: Because this exception applies only to the literal host.docker.internal, it doesn't weaken SSRF defenses for any attacker-influenced destinations.

Motivation/Impact

This change will streamline local development and multinode workflows, making sandbox egress policies for the Docker host simpler and more robust.

Alternative approaches

  • Maintain current requirement for explicit allowed_ips, but provide better UX or policy generation tooling to automate the lookup. This is less clean than encoding the topology trust directly in the SSRF enforcement path.

Background

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions