Skip to content

ALL-35: delegate isolated Exa credentials to workers - #237

Open
allisoneer wants to merge 8 commits into
allison-demofrom
codex/all-35-delegated-exa-credential
Open

ALL-35: delegate isolated Exa credentials to workers#237
allisoneer wants to merge 8 commits into
allison-demofrom
codex/all-35-delegated-exa-credential

Conversation

@allisoneer

@allisoneer allisoneer commented Sep 9, 2026

Copy link
Copy Markdown

Summary

  • make explicitly configured Managed NAC workers eligible for credential-gated native web_search and web_fetch
  • snapshot EXA_API_KEY at managed-server startup, remove it from the generic environment before configuration/MCP/background construction, and reserve the snapshot for direct native web plus worker dispatch
  • on Linux, harden the worker before it connects to a mode-0600 Unix socket and authenticate both parent and worker PIDs with SO_PEERCRED; send the bounded credential frame only after post-MCP readiness
  • on non-Linux Unix hosts, retain the inherited anonymous close-on-exec socket and post-MCP readiness ordering
  • keep ordinary local workers, unmanaged direct-session environment/store refresh behavior, traditional-child boundaries, and light-model routing unchanged

Security boundary

  • credential bytes are absent from worker environment, argv, stdin, generic MCP ${EXA_API_KEY} expansion, MCP descendants, and model-controlled commands
  • Linux workers receive no inherited credential descriptor and become non-dumpable with no_new_privs before opening the authenticated parent endpoint
  • the managed image fails closed if CAP_SYS_PTRACE appears in CapInh, CapPrm, CapEff, CapBnd, or CapAmb, or if those fields cannot be parsed exactly
  • the bounded, versioned frame rejects malformed, partial, oversized, trailing, and prematurely closed input without echoing payloads
  • worker web admission uses a dispatch-fixed credential snapshot; no worker fallback consults ambient environment or the credential store
  • model-visible definitions/requests, retained output/events, completed worker episodes, transcripts, snapshots, files, logs, and the durable SQLite store are regression-scanned for fake canaries

Ordinary local/unmanaged servers and workers do not receive these irreversible process controls or delegated credential channels. A same-UID Linux process can still discover, unlink, or flood the socket path and cause denial of service, but cannot authenticate as either peer to read or inject the credential. The boundary assumes an unprivileged deployment without CAP_SYS_PTRACE, privileged-container escape, weakened host inspection controls, kernel compromise, or a compromised NAC process. macOS retains the inherited socket/readiness protections but has no equivalent Linux prctl/procfs isolation guarantee. Stronger mutual isolation requires separate privileges or an external credential broker.

Verification at df43a6e4e26efe3a835513a4c9142c5cc98e64fd

  • make ci — formatting, clippy, source size, 1,215 nac-core tests (1,206 passed, 9 ignored), 162 nac-server library tests, 24 binary tests, 286 web tests, API contract, typecheck, production web build, and static managed-image contract
  • make test-durability
  • make test-managed-image — fresh Linux/amd64 release build plus readiness, restart, SIGTERM, runtime inventory, and Git LFS smoke
  • production-built isolated HTTPS E2E with fake credentials: orchestrator → __worker → native Exa web_search/web_fetch, direct native web, cancellation during a hanging search, malformed Exa JSON, worker cleanup, generic stdio/HTTP MCP isolation, and canary absence from logs, snapshots, transcripts, files, and durable store
  • focused adversarial startup-racer, peer-authentication, MCP-expansion, close-on-exec, protocol framing, cancellation, early-exit, and unmanaged-process-semantics regressions

Review and rollout

Leave this PR OPEN and unmerged for Gerred's explicit review. Gerred should confirm this NAC worker boundary matches the ALL-22 Kubernetes Secret/secretKeyRef delivery before merge or managed-runtime publication.

No real Exa key was requested or used. Tests and E2E use obvious fake canaries and isolated local HTTPS doubles. Platform projection, rotation, restart, shared throttling/429 policy, and enablement remain outside this PR under ALL-22.


Note

High Risk
Changes authentication-adjacent credential handling, process hardening, and worker/MCP startup ordering; mistakes could leak keys to untrusted MCP descendants or break managed deployments.

Overview
Managed NAC orchestrator workers can now get native web_search / web_fetch when the server has a usable Exa key, without putting that key in the worker environment or MCP expansion paths.

The agent replaces a single web_retrieval_eligible flag with NativeWebCapabilities (direct vs worker vs disabled) and a new worker_credentials path: on managed startup the server snapshots EXA_API_KEY, strips it from the process environment before config/MCP/background work, and uses the snapshot only for direct native web and worker dispatch. Managed hosts spawn workers with a private Unix channel (Linux: mode-0600 socket + mutual SO_PEERCRED; other Unix: inherited close-on-exec fd); credentials are sent only after the worker signals post-MCP readiness, then applied via set_worker_web_credential. Worker stdout/stderr and debug output are redacted; ordinary local workers stay unchanged.

Hardening and ops: Linux managed server/worker processes opt into no_new_privs and non-dumpable mode; the managed image entrypoint rejects CAP_SYS_PTRACE. Docs, contract scripts, debug-only Exa TLS/proxy hooks, an E2E Exa double, and broad adversarial/integration tests cover MCP isolation, framing, cancellation, and canary absence from retained state.

Reviewed by Cursor Bugbot for commit cf5914b. Bugbot is set up for automated code reviews on this repo. Configure here.

@allisoneer

Copy link
Copy Markdown
Author

Security review changes are in progress. The initial process-environment inheritance is too broad because worker startup-time MCP configuration and stdio MCP descendants could observe EXA_API_KEY before native-tool admission. I am replacing it with a post-MCP private worker control channel and adding MCP descendant/config-expansion plus durable-store leak regressions. Keep this PR OPEN and unmerged; it is not ready for Gerred review until the follow-up commit and verification land.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale comment

Security review found one high-severity issue: EXA_API_KEY is re-injected into the trusted __worker process environment, which worker exec_command (brokerless) and host stdio MCP can recover despite child-env stripping and parent stdout/stderr redaction.

Open in Web View Automation 

Sent by Cursor Security Agent: Security Reviewer

Comment thread crates/nac-core/src/tools/thread/worker.rs Outdated
@allisoneer allisoneer changed the title ALL-35: delegate environment-injected Exa credentials to workers ALL-35: delegate isolated Exa credentials to workers Sep 9, 2026
@allisoneer

Copy link
Copy Markdown
Author

Security review follow-up is complete in signed commit 2ecfb88.

The worker no longer inherits EXA_API_KEY. The parent writes a versioned credential frame to the existing private stdin control pipe, and the worker consumes it only after startup-time MCP construction. The worker then installs a dispatch-fixed native-web capability snapshot without consulting its environment or credential store.

New regression coverage verifies:

  • ${EXA_API_KEY} MCP config expansion fails inside the worker
  • an MCP stdio descendant sees the key as unset
  • argv and model-visible tool definitions/requests contain no key
  • retained stdout/stderr redact an intentionally echoed fake canary
  • ordinary model-controlled shell and PTY commands still see the key as unset
  • completed worker episodes, transcript rows, and every file in the SQLite store directory contain no fake canary
  • cancellation continues to use the same control pipe correctly

Final local verification is green: focused worker suites, 1,189 nac-core tests (1,180 passed / 9 ignored), and the complete make ci lane including Rust workspace tests, 268 web tests, API contract checks, lint, typecheck, production build, and managed image contract.

The PR remains open and unmerged for Gerred's explicit review against ALL-22.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale comment

Agentic security review found one HIGH issue: native Exa credentials are still recoverable from the worker stdin pipe via /proc/<pid>/fd/0 during MCP setup, even after environment stripping.

Open in Web View Automation 

Sent by Cursor Security Agent: Security Reviewer

Comment thread crates/nac-core/src/tools/thread/worker.rs Outdated
@allisoneer

Copy link
Copy Markdown
Author

Final hosted verification is complete: every PR check is green, including macOS and Linux builds, core/server tests, contract, lint, managed-image quality and build-smoke, CLA, Cursor Bugbot, and Cursor Security Agent. The PR remains open and unmerged for Gerred.

@allisoneer

Copy link
Copy Markdown
Author

Security follow-up reopened: the current stdin control frame may be recoverable by a same-UID startup-time MCP child through procfs while bytes are buffered. I am replacing it with a dedicated non-inherited channel plus a post-MCP readiness handshake and adversarial procfs coverage. Keep this PR open and unmerged; it is not ready for Gerred review.

@allisoneer

Copy link
Copy Markdown
Author

The reopened HIGH security follow-up is resolved in signed commit a72da97.

The retired stdin transport has been replaced by an anonymous Unix socketpair. Its inherited worker endpoint is marked close-on-exec before MCP construction, stdin is cancellation-only, and the parent does not write credential bytes until the worker completes MCP construction and sends the readiness frame. The receiver accepts exactly one bounded/versioned frame and requires EOF, rejecting malformed, partial, oversized, trailing, or closed-peer input without echoing payloads.

Linux server/worker processes additionally set no_new_privs and become non-dumpable before untrusted descendants start. The managed entrypoint fails closed if CAP_SYS_PTRACE is effective. This is explicitly an unprivileged same-UID boundary, not protection from privileged containers, kernel compromise, or a compromised NAC process.

Adversarial regressions cover environment, argv, stdin, inherited descriptors, /proc/<pid>/fd, pidfd_getfd, MCP descendants, intentionally echoed stdout/stderr, event delivery, model requests, transcripts, SQLite files, readiness races, cancellation-before-ready, timeout, peer close, and descendant cleanup. The prior stdin-pipe procfs vulnerability is reproduced as a characterization test.

Local verification is green, including the complete make ci lane (1,186 nac-core tests, 159 nac-server tests, 268 web tests, generated contracts, lint, typecheck, production build, and managed-image contract). Hosted Linux checks are now running against this exact commit. Keep the PR open and unmerged for Gerred's explicit review.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

Comment thread crates/nac-server/src/main.rs Outdated
@allisoneer
allisoneer force-pushed the codex/all-35-delegated-exa-credential branch from d1bc89b to eaf1ffb Compare September 9, 2026 05:56
@allisoneer

Copy link
Copy Markdown
Author

Final review repairs are pushed in signed commit eaf1ffb after rebasing onto current allison-demo (96bc452).

  • Native web capability source ownership now lives in agent/web_capabilities.rs, preserving the newly landed light-model behavior while keeping agent/mod.rs below the source-size limit.
  • Irreversible Linux no_new_privs/non-dumpable controls and credential socket delegation are now opt-in to an explicitly configured Managed NAC host. Ordinary local/unmanaged servers and workers retain their prior subprocess semantics and credential-free worker behavior.
  • Linux subprocess regressions prove unmanaged controls remain unchanged and managed hardening occurs before descendants; the existing worker adversary proves the post-exec worker reapplies the boundary before MCP construction.
  • Documentation now explicitly limits the same-UID process-inspection guarantee to Linux. macOS keeps the close-on-exec socket and readiness ordering but requires privilege separation or an external broker for same-user mutual isolation.

Post-rebase local make ci is green: 1,202 nac-core tests, 162 nac-server tests, 268 web tests, source-size, lint, generated contracts, typecheck, production build, and managed-image contract. Fresh hosted Linux and independent exact-head security review are running now. Keep this PR open and unmerged for Gerred.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale comment

Comment thread crates/nac-core/src/worker_credentials.rs Outdated
@allisoneer

Copy link
Copy Markdown
Author

Exact-head verification for df43a6e4e26efe3a835513a4c9142c5cc98e64fd:

  • make ci passed (formatting, clippy, source-size, all Rust/web tests, API contract, typecheck, production assets, static managed-image contract).
  • make test-durability passed.
  • make test-managed-image passed after a fresh Linux/amd64 release build, including readiness/restart/SIGTERM and Git LFS smoke.
  • Production-container E2E passed against isolated local HTTPS model/Exa/MCP doubles using a fake canary: orchestrator → __worker native web_search/web_fetch, direct native web, cancellation, malformed Exa JSON, worker cleanup, generic stdio/HTTP MCP isolation, and no canary in logs, snapshots, transcripts, files, or the durable store.
  • The E2E CA/private key, logs, snapshots, and helper state were deleted afterward. No real Exa credential was used.

The branch is signed and clean. This PR remains intentionally open and unmerged pending all hosted checks, a fresh exact-head security review, and Gerred's explicit review.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit df43a6e. Configure here.

Comment thread crates/nac-core/src/agent/web_capabilities.rs Outdated
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant