Skip to content

Unlisted hostname request terminates policy boundary instead of returning a clean denial #3577

Description

@drew

This was generated by AI during triage.

Summary

A network request to a hostname that is completely absent from the sandbox policy can terminate the policy boundary instead of returning a normal deny result. The OpenShell exec client then loses its relay before receiving the command's exit status, and the sandbox transitions out of Ready.

The equivalent negative test against an allowed hostname on a disallowed port fails cleanly with EACCES, emits a DENIED audit event, and leaves the sandbox Ready.

Environment

The gateway and supervisor used mutable development tags and may have been from different builds. Version skew is a plausible contributing factor and should be checked during triage.

Policy

version: 1

filesystem_policy:
  include_workdir: true
  read_only: [/usr, /lib, /proc, /dev/urandom, /app, /etc, /var/log]
  read_write: [/sandbox, /tmp, /dev/null]

landlock:
  compatibility: best_effort

network_policies:
  example_http:
    name: example-http
    endpoints:
      - host: example.com
        port: 80
        protocol: tcp
    binaries:
      - { path: "/**" }

Reproduction

  1. Create a sandbox with the policy above and wait until policy version 1 is loaded.

  2. Confirm the allowed request succeeds:

    bash -c 'exec 3<>/dev/tcp/example.com/80; printf "GET / HTTP/1.0\r\nHost: example.com\r\nConnection: close\r\n\r\n" >&3; IFS= read -r line <&3; printf "%s\n" "$line"'

    Observed: HTTP/1.1 200 OK.

  3. Attempt a request to a hostname absent from the policy:

    bash -c 'exec 3<>/dev/tcp/example.org/80'

Actual behavior

The exec client reports:

The service is currently unavailable: exec relay closed before the command reported an exit status

The supervisor reports a mediated-DNS failure followed by loss of its boundary channel:

mediated DNS accept failed; retrying
boundary terminated: boundary lost during wait: transport error

The supervisor terminates and the sandbox transitions from Ready to Stopped/DependenciesNotReady.

Expected behavior

  • The request to example.org:80 is denied normally.
  • The caller receives a deterministic nonzero result such as EACCES.
  • A DENIED audit event records the destination and denial reason.
  • The policy boundary, supervisor, and sandbox remain healthy and Ready.

Control case

With the same policy, connecting to the allowed hostname on an unauthorized port behaves correctly:

/usr/bin/bash: connect: Permission denied

Audit output:

OCSF NET:OPEN [MED] DENIED /usr/bin/bash(0) -> 198.18.0.0:81 [reason:transparent_tcp_mapping_denied]

The sandbox remains Ready afterward.

Suggested validation

  • Add an end-to-end test for DNS resolution/connection to a hostname absent from all network policy endpoints.
  • Assert that the request is denied and audited without terminating the boundary or supervisor.
  • Run the test with matching component builds and, separately, with a supported mixed-version configuration if version skew is expected to work.

Activity

  1. drew commented on Sep 23, 2026

    @drew
    CollaboratorAuthor

    🏗️ build-plan

    Implementation Plan

    Issue type: fix
    Complexity: High
    Confidence: Low — the reported failure is credible, but its initiating fault is not identified.

    Summary

    An unlisted hostname should produce a policy DNS refusal and audit event while the sandbox remains usable. The current DNS evaluator and wire handler already implement those responses; the reported boundary loss may arise elsewhere in the mediation path or from mismatched images. Validate the full path before changing production logic.

    Scope

    • e2e/rust/tests/policy_dns_denial.rs: exercise an unlisted hostname, assert the denial audit and a subsequent successful exec.
    • The production component identified by a failing matched-build reproduction: correct the initiating failure, with a focused regression test.
    • Relevant architecture and published troubleshooting docs if the fix changes behavior or operational requirements.

    Implementation Steps

    1. Run the regression with matching locally built components and inspect the first boundary or mediation error.
    2. Reproduce on AMD64 Kata using matching gateway, supervisor, and sandbox image digests; capture the sandbox exit reason and host or Kata events.
    3. Fix the initiating failure and verify that denial does not interrupt exec or sandbox readiness.

    Test Plan

    • Unit tests: Existing DNS wire test verifies REFUSED and no upstream lookup; add a focused test if the production fault is isolated.
    • Integration tests: Exercise the matched-build mediation path, including the response after DNS refusal.
    • E2E tests: Assert nonzero name resolution, policy_dns_ineligible audit, and successful subsequent exec. Run the Kubernetes lane on AMD64 Kata when available.

    Risks & Open Questions

    • The issue used mutable development tags; component version skew remains possible.
    • The available local host is ARM64 Docker, not the reported AMD64 Kata setup.
    • DNS refusal precedes TCP connect, so EACCES and an audit destination of example.org:80 are not stable expectations. The DNS denial identifies the name at port 53.
    • No process identity, /proc, or LSM behavior change is planned; reassess if the fault is in that area.

    Documentation Impact

    None until a production behavior or operational change is identified.


    Revision 1 — initial validation plan

  2. drew commented on Sep 23, 2026

    @drew
    CollaboratorAuthor

    🏗️ build-from-issue-agent

    Validation update

    I traced the current DNS path. A name absent from all network policy endpoints returns PolicyDnsError::Ineligible; the wire handler converts it to DNS REFUSED and the OCSF event carries DENIED with policy_dns_ineligible. The mediated runtime sends the DNS response through the normal query channel. The existing ineligible_query_is_refused_without_upstream_call unit test covers the evaluator and wire response.

    I added a full-path regression test in a local branch and ran it against gateway, supervisor, and sandbox images built from the same checkout on ARM64 Docker. It passed: unlisted-name resolution failed, the same audit line contained the hostname, DENIED, and policy_dns_ineligible, and a later exec in the same sandbox succeeded. This does not reproduce or resolve the reported AMD64 Kata boundary loss.

    To isolate that loss, please provide the exact gateway, supervisor, and sandbox image digests, plus the sandbox/container exit reason and the first sandbox, Kata, or host error preceding mediated DNS accept failed; retrying. The reported mutable development tags leave version skew unresolved.

    For a DNS-stage refusal, a nonzero name-resolution result is the stable caller outcome; a TCP EACCES or destination audit for example.org:80 is not guaranteed because no TCP connect has occurred.

  3. added this to the OpenShell 0.1.0 milestone on Sep 23, 2026
  4. self-assigned this
    on Sep 23, 2026
  5. removed this from the OpenShell 0.1.0 milestone on Sep 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions