Skip to content

feat(mxc): add an egress-only AppContainer proxy peer - #4330

Open
pkhodade-NV wants to merge 40 commits into
windowsfrom
feat/mxc-proxy-peer-relay
Open

pkhodade-NV wants to merge 40 commits into
windowsfrom
feat/mxc-proxy-peer-relay

Conversation

@pkhodade-NV

@pkhodade-NV pkhodade-NV commented Oct 8, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Adds an opt-in, egress-only proxy-peer mode to the MXC ProcessContainer driver. Each sandbox gets a separate AppContainer peer identity; MXC directs the workload's proxy traffic only to that peer, and the peer tunnels it over an identity-restricted named pipe to OpenShell's per-sandbox host proxy for policy enforcement.

Native MXC 1.0 validation showed that this directionality cannot safely provide dynamic forwarding. MXC admits workload-to-peer proxy traffic but provides no identity-scoped peer-to-workload or gateway-to-workload path. This PR therefore rejects dynamic forwarding in proxy-peer mode instead of weakening isolation. The existing default network mode continues to support forwarding with its documented broader private-network ingress and host-loopback posture.

This supersedes the loopback-only approach in #4228. MXC 1.0 cannot implement that PR's sandbox-local-only TCP guarantee: denying ingress produces WinSock 10013, while enabling the required ingress/host-loopback policies broadens reachability beyond the sandbox-local listener.

Related Issue

No issue. This is a safety correction and completion of the existing PR based on native MXC 1.0 validation. It supersedes #4228.

Changes

  • Maps proxy-peer sandboxes to MXC schema 1.0.0 using runtimeConfig.networkProxy, processContainer.network.allowedProxyPeer, denied direct egress, and denied host loopback.
  • Keeps the peer egress-only; removes the unworkable reverse peer relay.
  • Returns a typed unsupported-forward error and exposes it as gRPC FailedPrecondition with an actionable diagnostic.
  • Removes the relay-spawner requirement from pc_proxy_peer_path; forwarding examples explicitly leave proxy-peer mode disabled.
  • Documents the residual MXC requirement: ingress.default = allow permits private-network server traffic even though host loopback remains denied.
  • Packages openshell-mxc-peer.exe in both Windows release build lanes and qualification evidence.
  • Updates examples, qualification contracts, architecture, gateway configuration reference, and the Windows build skill.

Testing

  • mise run --jobs 1 pre-commit with the x64 Biome 2.5.4 binary: pass.
  • cargo test -p openshell-driver-mxc --all-targets: pass (228 executed, 14 real-MXC tests ignored in this generic invocation).
  • mise run --skip-tools windows:check:arm64: pass.
  • mise run --skip-tools windows:build:arm64: pass; gateway, CLI, supervisor relay, proxy peer, and Z3 runtime produced.
  • mise run --skip-tools windows:test:arm64: pass (5,073 passed, 28 skipped).
  • mise run --skip-tools windows:test:unsupported:arm64: pass for all selective feature combinations.
  • mise run --skip-tools windows:test:mxc-real:arm64: pass (14 passed; one service-manager privilege probe self-skipped internally because the unelevated host cannot create services).
  • mise run --skip-tools windows:artifacts: pass.
  • mise run --skip-tools windows:qualify:mxc:gb300:contract: pass.
  • mise run --skip-tools windows:e2e:mxc:ws-agent-mock: pass (4/4).
  • mise run --skip-tools windows:e2e:mxc:openclaw-forward-mock: pass.
  • Real MXC 1.0 default-mode WebSocket forwarding lifecycle: pass (7/7, including echo, delete, and port release).
  • Real MXC 1.0 proxy-peer smoke: allowed example.com through policy; denied unlisted example.org; denied direct --noproxy bypass; rejected dynamic forwarding with the expected diagnostic.
  • cargo test -p openshell-supervisor-network: pass (1,248 passed, 2 ignored).
  • cargo test -p openshell-supervisor-relay: pass (17 passed).

The generic mise run test and mise run e2e aggregates are not runnable on this Windows host because they invoke Bash/WSL, Unix-domain-socket, executable-bit, and Unix-style Buf plugin paths. The native Windows and MXC-specific replacements above cover the changed code and behavior.

Checklist

  • Changes are isolated to MXC proxy-peer safety, forwarding behavior, packaging, tests, and documentation.
  • New behavior has unit, integration, build-lane, and native MXC coverage.
  • User-facing and architecture documentation describes the security tradeoff and unsupported forwarding mode.
  • The follow-up is a separate Conventional Commit with DCO sign-off.
  • No generated binaries, result bundles, credentials, or secrets are committed.

Part of #4450

drew and others added 30 commits September 17, 2026 12:06
Signed-off-by: Drew Newberry <anewberry@nvidia.com>
* fix(windows): restore MXC qualification gates

Signed-off-by: Prekshi Vyas <prekshiv@nvidia.com>

* fix(mxc): poll target for full readiness budget

---------

Signed-off-by: Prekshi Vyas <prekshiv@nvidia.com>
* fix(mxc): gate host proxy on explicit network policy

Signed-off-by: Prekshi Vyas <prekshiv@nvidia.com>

* fix(mxc): reject unrestricted egress fallback

Signed-off-by: Prekshi Vyas <prekshiv@nvidia.com>

* chore(mise): refresh lockfile

Signed-off-by: Shailendra Singh <shailendras@nvidia.com>

* fix(mxc): unblock Windows validation

Signed-off-by: Shailendra Singh <shailendras@nvidia.com>

* test(mxc): satisfy Windows clippy

Signed-off-by: Shailendra Singh <shailendras@nvidia.com>

---------

Signed-off-by: Prekshi Vyas <prekshiv@nvidia.com>
Signed-off-by: Shailendra Singh <shailendras@nvidia.com>
Co-authored-by: Shailendra Singh <shailendras@nvidia.com>
Adds real-MXC regression coverage for ProcessContainer token isolation, including an unsandboxed SCM positive control, and documents the AppContainer authorization model.
* fix(mxc): repair Windows inference demos

Signed-off-by: Prekshi Vyas <prekshiv@nvidia.com>

* fix(mxc): address inference demo review feedback

Signed-off-by: Prekshi Vyas <prekshiv@nvidia.com>

---------

Signed-off-by: Prekshi Vyas <prekshiv@nvidia.com>
Signed-off-by: Prekshi Vyas <prekshivyas@nvidia.com>
(cherry picked from commit 3a0efc2)

# Conflicts:
#	crates/openshell-driver-mxc/Cargo.toml
* fix(mxc): stage egress-proxy CA files regardless of env tier

stage_tls_ca_files was only invoked when pc_minimal_env was set, but a
curated ProcessContainer can't read the host proxy's private temp
folder under any env tier. With the default pc_minimal_env=false, the
CA env vars pointed at a path the sandboxed process couldn't read at
all, breaking TLS validation for allow-listed HTTPS requests through
the egress proxy. Extract the staging decision into
resolve_agent_proxy_ca_paths, which takes no env-tier argument, so the
gap can't silently regress; existing tests never exercised this path
since mocked invokers skip host-proxy startup entirely.

Signed-off-by: Prashant Khodade <pkhodade@nvidia.com>
(cherry picked from commit d0346af96aef9a93aca26a7e1825d8678a25567d)

* fix(mxc): complete proxy CA staging isolation (#3535)

* test(mxc): provide workload dir for CA staging

Signed-off-by: Prekshi Vyas <prekshivyas@nvidia.com>

* fix(mxc): isolate staged proxy CA files

Signed-off-by: Prekshi Vyas <prekshiv@nvidia.com>

---------

Signed-off-by: Prekshi Vyas <prekshivyas@nvidia.com>
Signed-off-by: Prekshi Vyas <prekshiv@nvidia.com>

---------

Signed-off-by: Prashant Khodade <pkhodade@nvidia.com>
Signed-off-by: Prekshi Vyas <prekshivyas@nvidia.com>
Signed-off-by: Prekshi Vyas <prekshiv@nvidia.com>
Co-authored-by: Prekshi Vyas <34834085+prekshivyas@users.noreply.github.com>
#3471)

* test(windows): define GB300 MXC qualification contract

Signed-off-by: Prekshi Vyas <prekshiv@nvidia.com>

* test(mxc): harden GB300 qualification provenance

Signed-off-by: Prekshi Vyas <prekshiv@nvidia.com>

* test(windows): package portable GB300 qualification

Signed-off-by: Prekshi Vyas <prekshiv@nvidia.com>

* test(windows): honor external qualification checkout

Signed-off-by: Prekshi Vyas <prekshiv@nvidia.com>

* revert: remove portable GB300 packaging

Signed-off-by: Prekshi Vyas <prekshiv@nvidia.com>

* test(mxc): bind GB300 evidence to exact inputs

Signed-off-by: Prekshi Vyas <prekshiv@nvidia.com>

---------

Signed-off-by: Prekshi Vyas <prekshiv@nvidia.com>
* fix(ocsf): attribute MXC proxy events to sandboxes

NVBug 6783086

Signed-off-by: Prekshi Vyas <prekshiv@nvidia.com>

* fix(ocsf): preserve sandbox context across proxy denials

Signed-off-by: Prekshi Vyas <prekshiv@nvidia.com>

---------

Signed-off-by: Prekshi Vyas <prekshiv@nvidia.com>
* test(mxc): qualify HTTPS L7 enforcement (NVBug 6783374)

Signed-off-by: Prekshi Vyas <prekshiv@nvidia.com>

* docs(mxc): clarify real qualification failures

Signed-off-by: Prekshi Vyas <prekshiv@nvidia.com>

* docs(mxc): align real test skip semantics

Signed-off-by: Shailendra Singh <shailendras@nvidia.com>

---------

Signed-off-by: Prekshi Vyas <prekshiv@nvidia.com>
Signed-off-by: Shailendra Singh <shailendras@nvidia.com>
Co-authored-by: Shailendra Singh <shailendras@nvidia.com>
* fix(mxc): reject a non-absolute wxc_exec_path at gateway startup

wxc_exec_path is the binary that builds every sandbox, but nothing
validated it before spawning: a relative value (including the shipped
default, a bare "wxc-exec.exe") let PATH-lookup or working-directory-
relative resolution execute a decoy binary with the gateway's identity
instead of the approved wxc-exec, turning the containment mechanism
itself into an arbitrary-code-execution primitive.

Add MxcComputeConfig::validate_configuration, wired into the existing
(previously no-op) compute-driver config preflight, rejecting an
empty or non-absolute wxc_exec_path with a clear diagnostic. Change
the default from the relative "wxc-exec.exe" to an empty string so
the field must be explicitly configured -- no usable-but-insecure
fallback survives. Update the architecture doc's stale "else PATH"
discovery claim to match.

Signed-off-by: Prashant Khodade <pkhodade@nvidia.com>
(cherry picked from commit d4192a0072f8f0ca0a4035f10cae07889c2b578f)

* test(mxc): cover wxc_exec_path enforcement at the gateway boundary

MxcComputeConfig::validate_configuration already had unit coverage,
but that only proves the validation function itself is correct -- it
says nothing about whether MxcFactory::validate_config (src/lib.rs)
still calls it. Before this fix, that factory method discarded the
parsed config entirely, so a regression back to that no-op shape would
leave every unit test passing while a relative wxc_exec_path again
reached gateway startup.

Add an integration test that spawns the actual compiled
openshell-gateway binary through its config preflight subcommand,
exercising the real chain: CLI parsing, TOML loading, driver
selection, MxcFactory::validate_config, and
MxcComputeConfig::validate_configuration. Assertions check only
pass/fail, not message content: run_effective_config_preflight
replaces any validation failure with a generic message whenever a
config file is used, to keep file-sourced values out of preflight
diagnostics -- pre-existing, deliberate, and covered by its own tests.

Also document the new required-and-absolute wxc_exec_path constraint
in the gateway config reference and the driver README, which
previously only showed example values without stating the
requirement.

Signed-off-by: Prashant Khodade <pkhodade@nvidia.com>

---------

Signed-off-by: Prashant Khodade <pkhodade@nvidia.com>
…3499)

* feat(mxc): warn when the ETW consumer receives zero provider events

EnableTraceEx2 succeeding only proves the request to enable the
Sandboxing provider succeeded, not that the provider exists on this
host/build or will ever fire. A provider-identity mismatch or a
non-firing provider left the ETW->OCSF audit trail silently empty
across real, successful sandbox lifecycles, with no error, warning,
or diagnostic anywhere.

Track raw provider-matched events received per session and add a
zero-events watchdog on the consumer thread: once real sandbox
activity has happened (register_launch called at least once) and a
grace period elapses with zero events matched, log a warning and
emit a Detection Finding [2004] naming the gap. Gated on actual
activity (not just session uptime) so an idle gateway with
etw_audit=true and no sandboxes created never warns.

Also exposes EtwSession::events_received() alongside the existing
is_capture_alive(), so a status/diagnostics surface can query capture
health directly, not just infer it from tracing output.

The watchdog's decision logic is extracted into a pure function
(should_warn_zero_events) so it's unit-testable without a real ETW
session or elevation.

Signed-off-by: Prashant Khodade <pkhodade@nvidia.com>
(cherry picked from commit 4fcfa716a808eaa91c6476e9c69366f91702b1fa)

* fix(mxc): repair build/lint breaks left by the zero-events watchdog commit

warn_zero_events_received() referenced a nonexistent SESSION_NAME
constant (only SESSION_NAME_PREFIX exists), so the crate failed to
compile. Separately, the consumer thread's decode-or-log match on
Option<DecodedEtwEvent> tripped clippy::single_match_else under -D
warnings. Neither issue is specific to a platform or toolchain
version -- both reproduce on a clean checkout of this branch's tip.

Signed-off-by: Prashant Khodade <pkhodade@nvidia.com>

* fix(mxc): attribute ETW Sandboxing-provider events via identity/CV, not PID

The Sandboxing provider's events are logged under two PIDs that are
never the driver's own wxc-exec PID: a short-lived launcher PID
(wxc-exec.exe exits within seconds even while the sandboxed workload
keeps running) and a shared, constant PID hosted by a system-wide OS
broker service across unrelated sandboxes. Anchoring attribution on
`register_launch`'s wxc_pid therefore left every event unattributable,
so the OCSF audit trail stayed empty despite the provider firing
correctly (confirmed against a raw logman/tracerpt capture running
alongside this consumer).

wxc-exec never reports its OS-generated `identity`/`__TlgCV__` back to
the driver, so there is nothing to pre-seed `by_identity` with at
registration time the way `by_pid` is pre-seeded. Track launches still
awaiting their first identity/CV in a `pending_launches` queue instead,
and bind opportunistically in `resolve()`: when an event carries a
never-seen identity/CV and has no `by_pid` registration at all (the
real-world shape of these events) and exactly one launch is pending,
it can only be that launch's burst. Zero or multiple pending launches
stay ambiguous and fall through to the existing unresolved-event
buffer/TTL path rather than guess -- misattributing an audit event to
the wrong sandbox_id is worse than dropping it. A PID registration
that does exist (even generation-mismatched) is treated as positive
evidence of an existing PID-reuse race and takes precedence over the
opportunistic path, preserving the existing generation-key guarantees.

Signed-off-by: Prashant Khodade <pkhodade@nvidia.com>

* fix(mxc): surface dropped-unattributed ETW events above debug level

An event that ages out of the unresolved-event buffer unattributed is
a permanent audit-trail gap: the OS action it represents will never
appear in the OCSF log, and nothing retries it afterward. That was
only visible at --log-level debug, so an operator running with the
default level would never see it. Promote it to warn, matching the
severity already used for the zero-events watchdog's own gap warning.

Signed-off-by: Prashant Khodade <pkhodade@nvidia.com>

* fix(mxc): keep ETW attribution fail-closed, rate-limit drop warnings

The prior opportunistic "single pending launch" fallback attributed
any fresh identity/CV from an unregistered or system-broker PID to
the sole pending OpenShell launch. Queue cardinality is not
attribution evidence: unrelated, non-OpenShell AppContainer or UAC
activity shares this same OS Sandboxing provider, so an event
arriving in the five-second window could cross-link an unrelated
identity and emit subsequent events under the wrong sandbox_id,
corrupting the audit trail. Revert it -- attribution requires the
driver-owned wxc-exec PID and its kernel process start key to both
match, exactly as before. Records without that generation-backed
evidence remain unattributed rather than guessed.

The dropped-unattributed warning also moved from one line per record
to a rate-limited, aggregated warning: unattributed drops are
expected, ordinary system-wide activity, not a rare condition, so
warning per record could flood operator logs during a burst of
unrelated AppContainer/UAC activity. UnattributedDropReporter mirrors
the existing OverloadReporter pattern -- warn immediately on the
first drop, then coalesce to one warning with a running count every
30 seconds while drops continue.

Updates the MXC observability documentation to match: the
unattributed-record sentence now explains why fail-closed is
deliberate, documents the coalesced warning cadence, and documents
the mxc-etw-zero-events OCSF Detection Finding [2004], which was not
mentioned anywhere before.

Signed-off-by: Prashant Khodade <pkhodade@nvidia.com>

---------

Signed-off-by: Prashant Khodade <pkhodade@nvidia.com>
…#3498)

* fix(server): keep Error-phase sandbox records through the prune sweep

The periodic store-vs-backend reconciliation sweep deleted any
persisted sandbox not present in the driver's live backend snapshot,
except for Completed and failed-main-process phases. A driver whose
registry is in-process-only and never rehydrates after a restart (no
persistence of its own) reports every previously-known sandbox as
missing on the very first sweep after startup -- including ones
already correctly, terminally marked Error by earlier crash detection
-- so the sweep silently deleted them shortly after gateway restart,
racing any client (GetSandbox/ListSandboxes/DeleteSandbox) working
with the same sandbox in that window.

Treat Error the same as the existing Completed exemption: it is
already a settled, informational terminal state with no live compute
resource to reclaim, so keep the durable record instead of deleting
it.

Signed-off-by: Prashant Khodade <pkhodade@nvidia.com>
(cherry picked from commit 721a1659a822a72e76e3c0dffc6847f17129a3fc)

* fix(server): narrow the prune sweep's Error-phase exemption

The blanket phase == SandboxPhase::Error exemption changed established
missing-backend cleanup for every compute driver, not only the MXC
restart race the PR intended to fix. It also matched
BackendResourceMissing (set by gateway-startup recovery when a
previously-known sandbox's backend resource is already gone),
StartFailed (startup recovery's driver-error case), and
ComputeResourceMissing (this same sweep's own first-pass Error
transition for a Stopping/Stopped/Starting sandbox). All three mark
exactly the orphaned resources this sweep exists to reclaim across
Docker, Podman, VM, Kubernetes, and extension drivers -- exempting
them left orphaned names and gateway-owned records in place
indefinitely and skipped the idempotent driver cleanup for
volumes/secrets until a user explicitly deleted the sandbox.

Add is_missing_compute_resource_reason to inspect the sandbox's Ready
condition and narrow the exemption to a settled Error record only: one
whose reason isn't one of those three. A crashed main process or any
other non-resource failure keeps the exemption (no live resource ever
expected again); a resource-missing reason keeps flowing through the
normal delete-and-cleanup path exactly as before this PR.

Adds regression coverage for BackendResourceMissing and
ComputeResourceMissing confirming they are still pruned with driver
cleanup invoked, and documents the settled-vs-missing-resource
retention distinction in architecture/compute-runtimes.md.

Signed-off-by: Prashant Khodade <pkhodade@nvidia.com>

---------

Signed-off-by: Prashant Khodade <pkhodade@nvidia.com>
* fix(network): normalize Windows policy binary paths

Match Windows executable identities using a stable case-insensitive, separator-normalized representation across policy data, L4 input, and L7 relay evaluation. Preserve exact matching on other platforms and keep the original path for hashing and filesystem access.

NVBug 6782969

Signed-off-by: Prekshi Vyas <prekshiv@nvidia.com>

* fix(network): harden Windows binary matching

Signed-off-by: Prekshi Vyas <prekshiv@nvidia.com>

* fix(ci): scope Windows relay test imports

* fix(network): harden Windows binary path matching

---------

Signed-off-by: Prekshi Vyas <prekshiv@nvidia.com>
* fix(mxc): honor the generic sandbox create -- <COMMAND> syntax

sandbox_config only ever read the command from the MXC-specific
driver_config (--driver-config-json), never DriverSandboxSpec.command
-- the portable field the CLI's documented, driver-agnostic
`sandbox create -- <COMMAND>` syntax actually populates, and the same
field every other compute driver honors. A caller following that
syntax got "driver_config.command must contain a non-empty
executable", a message that reads as if no command was supplied at
all.

Add spec.command as a fallback source when no driver_config is
present, and reword the empty-command error to name both ways to
supply one.

NVBug 6782884

Signed-off-by: Prashant Khodade <pkhodade@nvidia.com>

* docs(mxc): document generic sandbox commands

Signed-off-by: Shailendra Singh <shailendras@nvidia.com>

---------

Signed-off-by: Prashant Khodade <pkhodade@nvidia.com>
Signed-off-by: Shailendra Singh <shailendras@nvidia.com>
Co-authored-by: Shailendra Singh <shailendras@nvidia.com>
* fix(mxc): reject unsupported live policy updates (NVBug 6782891)

Signed-off-by: Prekshi Vyas <prekshiv@nvidia.com>

* fix(mxc): gate all live policy mutations

Signed-off-by: Prekshi Vyas <prekshiv@nvidia.com>

* fix(mxc): gate composed policy mutations

Signed-off-by: Prekshi Vyas <prekshiv@nvidia.com>

* fix(ci): satisfy provider update lint

* fix(ci): order provider validation branches

* test(mxc): make policy synchronization deterministic

* fix(server): scope MXC policy synchronization

* fix(server): serialize provider-backed sandbox creation

* test(server): use valid provider create fixture name

---------

Signed-off-by: Prekshi Vyas <prekshiv@nvidia.com>
* fix(ocsf): attribute MXC proxy events to sandboxes

NVBug 6783086

Signed-off-by: Prekshi Vyas <prekshiv@nvidia.com>

* fix(network): scope Windows egress by socket owner

Resolve each accepted Windows proxy connection to its unique owning PID and executable before evaluating binary-scoped network policy. Fail closed when ownership or process identity cannot be established, and cover allowed and undeclared child processes with a real MXC regression.

* fix(network): preserve sandbox context with socket owners

Signed-off-by: Prekshi Vyas <prekshiv@nvidia.com>
(cherry picked from commit 32ea316)

* fix(network): harden Windows process identity

Signed-off-by: Prekshi Vyas <prekshiv@nvidia.com>

---------

Signed-off-by: Prekshi Vyas <prekshiv@nvidia.com>
#3495)

* fix(core): enforce owner-only Windows ACLs on sensitive files and dirs

set_dir_owner_only/set_file_owner_only were unconditional no-ops on
Windows, so the CLI's mTLS client private key, OIDC/edge tokens, cached
SSH keys, and the gateway's key-encryption key relied entirely on
inherited NTFS ACLs with no OpenShell-applied restriction. Apply an
owner-only DACL via SetEntriesInAclW/SetNamedSecurityInfoW with
PROTECTED_DACL_SECURITY_INFORMATION to strip inherited ACEs, matching
the 0700/0600 guarantee already provided on Unix. is_file_permissions_too_open
now also works on Windows instead of being Unix-only, closing the
detection gap alongside the prevention gap.

Signed-off-by: Prashant Khodade <pkhodade@nvidia.com>
(cherry picked from commit 71560e947f85819efbcddf70ddda94befab62b0b)

* fix(core): treat a NULL DACL as too open in is_file_permissions_too_open

has_foreign_trustee conflated a NULL DACL with an unreadable/invalid
ACL and returned Some(false) (not too open) for both. Per the Win32
contract, a NULL DACL means the object grants full access to everyone
-- the most permissive state possible -- so it must be flagged as too
open. Split the null and invalid-ACL branches: null now returns
Some(true), invalid ACL keeps the existing unreadable-ACL fallback
(None, which the caller maps to false via unwrap_or). Adds a
regression test that constructs a real NULL DACL via a
SetNamedSecurityInfoW helper confined to the windows_acl module,
consistent with the existing unsafe-FFI confinement in that module.

Found by CodeRabbit review on MR !113.

Signed-off-by: Prashant Khodade <pkhodade@nvidia.com>
(cherry picked from commit 46e635a4ef1d6937cdb088f46aa85baee3d6ad28)

* fix(core): close three false-negative gaps in the Windows ACL audit

restrict_to_current_user() updated only the DACL, leaving a foreign
owner's implicit WRITE_DAC right intact -- they could later replace
the DACL we just set. Query OWNER_SECURITY_INFORMATION and take
ownership in the same SetNamedSecurityInfoW call; if the caller can't
(a genuinely foreign-owned object), the call now fails instead of
silently leaving the object insecure.

is_file_permissions_too_open() mapped every Win32 inspection failure
(missing READ_CONTROL, an invalid ACL, a token-query failure) to
"not too open" via unwrap_or(false). Fail closed instead: an
inspection failure is a security false-negative risk, not a green
light.

has_foreign_trustee()'s ACE loop only recognized plain
ACCESS_ALLOWED_ACE_TYPE and treated every other type as non-granting.
Windows also defines access-allowed object, callback, and
callback-object ACE variants that can grant rights to a foreign
trustee; this audit doesn't parse their wider layouts, so their mere
presence is now conservatively flagged as too open instead of
silently skipped.

Also updates architecture/gateway.md, which still described the
SQLite file-tightening behavior only in terms of Unix mode 0o600, to
distinguish it from the owner-only DACL behavior on Windows.

Addresses review comments on PR #3495.

Signed-off-by: Prashant Khodade <pkhodade@nvidia.com>

* fix(core): conditional owner claim and audit owner in Windows ACL helpers

restrict_to_current_user: query the current owner before calling
SetNamedSecurityInfoW. Include OWNER_SECURITY_INFORMATION only when the
path has a foreign owner -- requesting it unconditionally fails with
ACCESS_DENIED (0x80070005) on standard credentials even when the current
user is already the owner, because WRITE_OWNER is not implied by object
ownership. A foreign-owned path still triggers an ownership claim and
fails hard if the claim is denied, preserving the security contract.

has_foreign_trustee: request OWNER_SECURITY_INFORMATION alongside
DACL_SECURITY_INFORMATION and reject paths with a foreign owner
immediately, before inspecting the DACL. A foreign owner has implicit
WRITE_DAC rights and can replace any DACL we set, so a clean DACL is not
sufficient evidence of safety on a foreign-owned object.

architecture/gateway.md: clarify that the Windows path-hardening behavior
sets mode 0o600 on Unix and applies a protected owner-only DACL on
Windows, with conditional ownership claim and fail-hard semantics for
foreign-owned objects.

Signed-off-by: Prashant Khodade <pkhodade@nvidia.com>

---------

Signed-off-by: Prashant Khodade <pkhodade@nvidia.com>
#3548)

* fix(mxc): reject cpu/memory limits instead of silently discarding them

CreateSandbox accepted --cpu/--memory and reached Ready with no Job
Object enforcement and no diagnostic, leaving the SDD's T11
host-exhaustion mitigation silently unmet. MXC's schema does not
expose CPU rate control or memory limiting outside the WSLC backend,
so reject requests carrying cpu/memory limits synchronously at
CreateSandbox, matching the existing fail-closed GPU rejection.

NVBug 6782894

Signed-off-by: Prashant Khodade <pkhodade@nvidia.com>

* fix(server): validate sandbox resource quantities

Signed-off-by: Shailendra Singh <shailendras@nvidia.com>

* docs(skill): clarify MXC resource limit behavior

Signed-off-by: Shailendra Singh <shailendras@nvidia.com>

* chore(go): regenerate protobuf bindings

Signed-off-by: Shailendra Singh <shailendras@nvidia.com>

* fix(go): align generated protobuf comments

Signed-off-by: Shailendra Singh <shailendras@nvidia.com>

---------

Signed-off-by: Prashant Khodade <pkhodade@nvidia.com>
Signed-off-by: Shailendra Singh <shailendras@nvidia.com>
Co-authored-by: Shailendra Singh <shailendras@nvidia.com>
* docs(windows): add runtime architecture overview

Signed-off-by: Shailendra Singh <shailendras@nvidia.com>

* docs(windows): consolidate build documentation

Signed-off-by: Shailendra Singh <shailendras@nvidia.com>

* docs(windows): address architecture review feedback

Signed-off-by: Shailendra Singh <shailendras@nvidia.com>

---------

Signed-off-by: Shailendra Singh <shailendras@nvidia.com>
Signed-off-by: Prekshi Vyas <prekshiv@nvidia.com>
* ci(windows): exercise MXC Ollama demo with mock API

Signed-off-by: Prekshi Vyas <prekshiv@nvidia.com>

* ci(windows): cover both MXC inference demos

Signed-off-by: Prekshi Vyas <prekshiv@nvidia.com>

* fix(mxc): preserve executable extension resolution

Signed-off-by: Prekshi Vyas <prekshiv@nvidia.com>

* test(mxc): use absolute PowerShell in lifecycle checks

Signed-off-by: Prekshi Vyas <prekshiv@nvidia.com>

* test(mxc): make lifecycle write probes deterministic

Signed-off-by: Prekshi Vyas <prekshiv@nvidia.com>

* test(mxc): assert stable lifecycle completion

Signed-off-by: Prekshi Vyas <prekshiv@nvidia.com>

---------

Signed-off-by: Prekshi Vyas <prekshiv@nvidia.com>
Signed-off-by: Prekshi Vyas <prekshiv@nvidia.com>
Signed-off-by: Prekshi Vyas <prekshiv@nvidia.com>
Signed-off-by: Prekshi Vyas <prekshiv@nvidia.com>
Document standalone MXC executable placement and condition system-drive ACL preparation on the AppContainer + DACL tier and probe recommendation. Explain the persistent metadata-only grant and link upstream verification and rollback guidance.

NVBug: 6842834

Signed-off-by: Prekshi Vyas <prekshiv@nvidia.com>
Signed-off-by: Shailendra Singh <shailendras@nvidia.com>
…3787)

* ci(windows): exercise MXC provider credential example

Signed-off-by: Prekshi Vyas <prekshiv@nvidia.com>

* ci(windows): exercise MXC OCSF audit example

Signed-off-by: Prekshi Vyas <prekshiv@nvidia.com>

* ci(windows): enable aggregate MXC example E2E

Signed-off-by: Prekshi Vyas <prekshiv@nvidia.com>

* ci(windows): select native MXC mock target

Signed-off-by: Prekshi Vyas <prekshiv@nvidia.com>

* fix(windows): isolate MXC example CI harnesses

Signed-off-by: Prekshi Vyas <prekshiv@nvidia.com>

---------

Signed-off-by: Prekshi Vyas <prekshiv@nvidia.com>
#3826)

* ci(windows): run MXC host probe, WebSocket agent, and OpenClaw forward examples checks

Add separate hosted CI tasks for the MXC host probe, WebSocket agent,
and OpenClaw forward examples. Use mock workloads to verify gateway,
CLI, driver, and sandbox lifecycle wiring without requiring wxc-exec.

Document that mock passes do not validate forwarding or MXC enforcement.

* fix(tests): enhance environment isolation for WebSocket and OpenClaw mock tests

* fix(mxc): enhance OpenClaw mock validation to require 'Ready' sandbox state

* fix(mxc): restore native process helper in WebSocket example

Restore Invoke-NativeCaptured for the PowerShell argument regression test and delegate CLI execution through it while preserving explicit gateway endpoint selection.

Signed-off-by: Akber Raza <akberr@nvidia.com>

---------

Signed-off-by: Akber Raza <akberr@nvidia.com>
Redact injected environment values and the per-sandbox proxy password in
captured MXC output and decoded relay launch-failure diagnostics before
logging or publishing sandbox failure status.

Match the original text and redact the union of overlapping occurrences.
Preserve control-channel payloads and avoid allocating for unmatched text.
Add regression coverage and document exact-match and length limits.

Signed-off-by: Prashant Khodade <pkhodade@nvidia.com>
Signed-off-by: Shailendra Singh <shailendras@nvidia.com>
pkhodade-NV and others added 5 commits October 1, 2026 10:06
…3659)

* docs(mxc): add missing demo examples for runbook and mTLS scenario

Add three files present in the internal mirror but absent from the
Windows branch:

- mxc-demo-runbook.md: operator runbook for the MXC demo kit
- README-mtls.txt: usage notes for the mTLS scenario
- run-mtls-test.ps1: PowerShell test runner for the mTLS scenario

These are required by the package-demo.ps1 packager script and are
referenced by the mxc-kit documentation.

No issue required: mechanical sync of missing demo assets.

Signed-off-by: Prashant Khodade <pkhodade@nvidia.com>

* fix(mxc): align demo workflows with current contracts

Signed-off-by: Shailendra Singh <shailendras@nvidia.com>

---------

Signed-off-by: Prashant Khodade <pkhodade@nvidia.com>
Signed-off-by: Shailendra Singh <shailendras@nvidia.com>
Co-authored-by: Shailendra Singh <shailendras@nvidia.com>
Signed-off-by: nv-vankit <nv-vankit@users.noreply.github.com>
Co-authored-by: nv-vankit <nv-vankit@users.noreply.github.com>
* fix(mxc): refresh stale demo gateway aliases (NVBug 6870039)

Signed-off-by: nv-vankit <nv-vankit@users.noreply.github.com>

* fix(mxc): isolate demo CLI gateway state

Signed-off-by: nv-vankit <nv-vankit@users.noreply.github.com>

* fix(mxc): remove empty gateway overrides

Signed-off-by: nv-vankit <nv-vankit@users.noreply.github.com>

* fix(mxc): preserve exact runner environment state

Signed-off-by: Shailendra Singh <shailendras@nvidia.com>

---------

Signed-off-by: nv-vankit <nv-vankit@users.noreply.github.com>
Signed-off-by: Shailendra Singh <shailendras@nvidia.com>
Co-authored-by: nv-vankit <nv-vankit@users.noreply.github.com>
Co-authored-by: Shailendra Singh <shailendras@nvidia.com>
* feat(mxc): migrate schema to 1.0.0

Signed-off-by: Shailendra Singh <shailendras@nvidia.com>

* test(mxc): gate governed egress by host capability

Signed-off-by: Shailendra Singh <shailendras@nvidia.com>

* feat(mxc): enforce 1.0-only configuration

Signed-off-by: Shailendra Singh <shailendras@nvidia.com>

* fix(mxc): align RC3 qualification contracts

Signed-off-by: Shailendra Singh <shailendras@nvidia.com>

* docs(mxc): replace RC3 release references

Signed-off-by: Shailendra Singh <shailendras@nvidia.com>

* docs(mxc): clarify 1.0 example evidence

Signed-off-by: Shailendra Singh <shailendras@nvidia.com>

* fix(mxc): migrate standalone mapper to schema 1.0

Signed-off-by: Shailendra Singh <shailendras@nvidia.com>

* test(mxc): handle backend capability validation

Signed-off-by: Shailendra Singh <shailendras@nvidia.com>

* fix(mxc): reject isolation filesystem grants

Signed-off-by: Shailendra Singh <shailendras@nvidia.com>

---------

Signed-off-by: Shailendra Singh <shailendras@nvidia.com>
Add an opt-in "proxy-peer" mode to the MXC ProcessContainer driver for
sandboxes running with MXC networkProxy, where in-sandbox loopback dials
are blocked.

- Gateway spawns a per-sandbox AppContainer peer (openshell-mxc-peer)
  that listens on loopback for the MXC networkProxy / allowedProxyPeer
  and bridges to the gateway over ACL'd named pipes.
- Ingress: forward listener backed by the peer (start_peer_relay) with
  the same nonce-authenticated contract as the control-channel relay.
- Egress: peer tunnels proxy connections to the host egress proxy;
  tunnelled connections are aliased via ForwardedClients so identity
  resolution works and the per-sandbox password check is skipped.
- Supervisor relay keeps the HTTP(S)_PROXY variables MXC injects when
  it clears the environment for the launched target.
- New driver option pc_proxy_peer_path with validation; README section.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Signed-off-by: Prashant S Khodade <pkhodade@nvidia.com>
@copy-pr-bot

copy-pr-bot Bot commented Oct 8, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

pkhodade-NV and others added 2 commits October 8, 2026 05:52
Resolve conflicts with the schema 1.0.0 migration (#4252): drop the
peer-specific 0.9.0-alpha schema override (1.0.0 supports
allowedProxyPeer), follow the removal of default_configuration_id and
the hand-written Default impl, and update the peer config test to the
renamed MxcNetwork field.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Signed-off-by: Prashant S Khodade <pkhodade@nvidia.com>
Signed-off-by: Shailendra Singh <shailendras@nvidia.com>
@shailendra-nv shailendra-nv changed the title feat(mxc): relay ingress and egress through an AppContainer proxy peer feat(mxc): add an egress-only AppContainer proxy peer Oct 9, 2026
@github-actions

github-actions Bot commented Oct 9, 2026

Copy link
Copy Markdown

@shailendra-nv

Copy link
Copy Markdown
Collaborator

/ok to test

@copy-pr-bot

copy-pr-bot Bot commented Oct 9, 2026

Copy link
Copy Markdown

/ok to test

@shailendra-nv, there was an error processing your request: E1

See the following link for more information: https://docs.gha-runners.nvidia.com/cpr/e/1/

@shailendra-nv

Copy link
Copy Markdown
Collaborator

/ok to test c44279a

@shailendra-nv shailendra-nv left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Requesting changes for three issues in the new proxy-peer path and its relay integration:

  1. The unpackaged AppContainer peer has no inbound firewall authorization setup or enforced deployment prerequisite. On profiles that block inbound apps without matching rules, the helper can report ready while permitted egress times out.
  2. Registry ownership keeps peer processes, listeners, pipe endpoints, and profiles alive after stop, workload completion, and post-peer startup failures. Cleanup currently depends on explicit sandbox deletion.
  3. Proxy environment inheritance also runs for existing non-peer relay launches, adding ambient proxy values that the explicit launch environment previously excluded and changing workload routing.

Each inline comment identifies the triggering configuration, previous versus proposed behavior, and a suggested fix and regression test.

Reviewed head c44279ab9899c1e407a18bb3cadac738c339831a against base d89e4359b810d3d7205bb7d4a706f3c773a59e1f. I reviewed all 31 changed files and traced configuration validation, MXC 1.0 serialization, profile and helper creation, pipe ACLs and framing, tunnel authentication and original-process attribution, relay environment handling, startup/stop/completion/delete paths, forwarding rejection through the gateway/RPC boundary, Windows build artifacts, examples, qualification contracts, published docs, and related skill updates.

The egress-only design is coherent: the peer stays a byte tunnel while policy evaluation, TLS handling, and credential substitution remain in the host proxy. Dynamic forwarding is rejected before opening a listener, with an actionable FailedPrecondition. The documentation consistently records the remaining private-network ingress exposure.

Validation:

  • Ran cargo test --locked --offline -p openshell-supervisor-relay --all-targets --target aarch64-pc-windows-msvc using an isolated target directory: 7 unit tests and 10 control-channel integration tests passed.
  • Reproduced ambient proxy propagation through the compiled ARM64 relay's actual launch protocol with a synthetic value absent from the supplied launch environment.
  • Checked the firewall prerequisite against Microsoft's MXC networking contract, which explicitly requires authorization scoped to an unpackaged proxy's AppContainer SID, executable, and port.
  • The broader three-package native test attempt waited on another build's shared Cargo cache lock; I stopped only that waiting review command and ran the relay suite in isolation. I did not independently reproduce the peer firewall or full MXC terminal-state cleanup scenarios. Those two findings are based on the source paths and the upstream deployment contract. The PR's reported native MXC smoke remains author-provided evidence.

Please address the three inline findings and add coverage for restrictive firewall deployment, peer cleanup before retained-sandbox deletion, and non-peer launches preserving the explicit environment.

Comment on lines +515 to +524
handle.proxy_addr = SocketAddr::from(([127, 0, 0, 1], ready));
let egress_enabled = egress.is_some();
if let (Some(first), Some(tunnel)) = (handle.egress_first.take(), egress) {
let task = tokio::spawn(egress_accept_loop(
Arc::clone(&handle.descriptor),
egress_pipe_name(&handle.prefix),
first,
tunnel,
handle.proxy_addr,
));

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Authorize the unpackaged AppContainer listener before accepting readiness.

Before this PR, governed traffic reached the gateway's host proxy. With proxy-peer mode enabled, the workload must first reach this new per-sandbox AppContainer listener. READY proves that the helper bound its port, but neither helper startup nor the driver provisions inbound firewall authorization for the new profile SID and ephemeral port. On a Windows Firewall profile that blocks inbound traffic to apps without a matching allow rule, the sandbox can become ready while otherwise permitted HTTP(S) requests time out.

MXC's proxy deployment contract explicitly says allowedProxyPeer and privateNetworkClientServer do not bypass that firewall policy; an unpackaged AppContainer proxy needs authorization scoped to its SID, executable, and port. Please provide lifecycle-managed authorization, or an enforceable deployment prerequisite with an actionable failure before reporting readiness. Add coverage on a host with the firewall enabled and no existing peer allow rule. The reported successful smoke on one host does not establish that prerequisite on other hosts.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Implemented firewall authorization before publishing peer readiness in 701efe93. The gateway installs a uniquely owned inbound TCP rule scoped to the helper executable, AppContainer SID, ephemeral port, and local/remote 127.0.0.1. It checks active-profile policy and reports an actionable startup error when rule management is denied. Termination removes the owned rule; preparation/startup failures release the profile and process. The deployment prerequisite and orphan cleanup are documented.

Native COM tests inspect the complete rule scope and verify that a permission-denied installation leaves no rule. The actual AppContainer helper was run on this firewall-enabled host: standard-token startup failed before readiness with 0x80070005, and the test proved its profile was removed by recreating it.

Validation still pending: successful elevated rule installation/removal and pc_peer_https_egress_reads_injected_ca_bundle. A standard-token attempt of the positive HTTPS test was blocked by firewall permission. I am leaving this finding open until that positive test actually executes successfully; the negative-path pass is not connectivity evidence.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Correction after checking the Windows API contract: Microsoft's INetFwRules::Add documentation explicitly says adding rules with LocalAppPackageId is unsupported. The implementation in 701efe9 sets that property and then calls this Add API. The COM-property test and permission-denied startup tests therefore do not establish a supported successful rule installation. This firewall fix needs revision; elevating the test alone is insufficient to close the finding.

The production code calls the firewall API directly and reports a permission error; the UAC prompt was only my manual test launcher. An unattended standard-user harness embedding the gateway cannot use this peer mode as implemented. A noninteractive deployment needs an already authorized backend or a small installed privileged service/broker with an authenticated, restricted interface. This PR does not implement such a broker. Each peer uses a distinct AppContainer SID and ephemeral port, so a fixed preinstalled rule is not a direct replacement without an identity/port design change.

The HTTPS integration test is pc_peer_https_egress_reads_injected_ca_bundle in crates/openshell-driver-mxc/tests/wxc_exec_real.rs:1009; install/removal is checked separately by firewall_install_has_explicit_permission_failure_or_owned_cleanup in src/peer_firewall.rs:133. The firewall finding remains open.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

The fresh rerun isolates the failure to INetFwRules::Add returning 0x80070005 before peer HTTPS starts. The firewall unit test passes its expected-denial/no-leftover-rule branch; successful supported installation/removal is still unverified.

The new BaseContainer no-UAC qualifications both passed under TokenElevation=0, including the real OpenShell HTTPS/CA/L7 path with pc_proxy_peer_path empty and a separate native MXC runtimeConfig.networkProxy proof. Commit 572de53 adds those tests and corrects the diagnostics/docs.

Full results: #4330 (comment)

This firewall finding remains open: the peer writer still uses the unsupported INetFwRules::Add plus LocalAppPackageId combination. UAC approval alone is not a verified remedy. Default BaseContainer host-proxy use does not require this optional peer or this firewall API.

Comment on lines +1847 to +1851
{
let mut registry = registry.lock().await;
if let Some(entry) = registry.get_mut(&sandbox_id) {
entry.peer = Some(Arc::clone(&peer));
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Release the peer when its workload reaches a terminal state.

This registry reference keeps PeerHandle alive after the lifecycle task returns. Before this change, stopping a ProcessContainer terminated/reaped its workload and released its host proxy. With peer mode, stop_sandbox still takes the host proxy but leaves entry.peer; monitor_exec also clears only the host proxy on successful and nonzero exits. Startup failures after this registration retain the peer as well. The helper's control pipe therefore remains open, leaving openshell-mxc-peer.exe, its listener, pipe endpoints, and AppContainer profile alive until an explicit delete.

Run a short command, stop a long-running workload, or fail launch after peer startup to trigger this. Keeping completed/stopped/failed sandboxes for inspection then accumulates running helpers. MXC rejects restart, so these peers cannot be reused by restarting the workload. Please take and terminate the peer on confirmed termination and post-spawn failure paths, preserving cleanup ownership across timeout/retry paths. Add a lifecycle regression that checks helper exit and resource release before deleting the retained sandbox.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Implemented terminal-state cleanup in 701efe93. Confirmed stop, successful/nonzero workload exit, and failure before workload launch now take and explicitly terminate the registered peer before reporting completion. This closes its control pipe even while a forward/other owner retains an Arc, deletes its AppContainer profile, and releases its owned firewall rule. A timed-out delete preserves peer ownership until termination is confirmed and the retry succeeds. Failed launch after workload publication retains the peer until the monitor confirms termination.

Added native-resource regressions: terminal_workload_releases_peer_before_sandbox_deletion, failure_before_workload_launch_releases_peer, launch_failure_retains_peer_until_monitor_confirms_termination, and timed_out_delete_preserves_peer_until_confirmed_retry; the existing stop regression also checks peer cleanup. Fixtures own a real child, secured named pipe, and AppContainer profile. Assertions verify child exit, pipe rebinding, and profile recreation while retaining the sandbox/peer Arc.

All passed in the final affected-package run (1,506 passed, 23 ignored) and the targeted peer/relay rerun (26 passed). MXC Nextest has four mock-workload stdio leak annotations; all four reproduced on the original PR head, and the newly added lifecycle tests have none. The native peer resource assertions passed. Removal of an installed firewall rule still awaits the elevated positive check described in the firewall thread.

Comment on lines +350 to +356
let inherited = inherited_proxy_env(|key| std::env::var(key).ok(), &child_env);
if !inherited.is_empty() {
eprintln!(
"[openshell-supervisor-relay] inheriting {} proxy env var(s) set for this container",
inherited.len()
);
child_env.extend(inherited);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Gate ambient proxy inheritance on proxy-peer mode.

This runs for every relay launch, including the existing IsolationSession backend with egress_proxy = false. That backend's exec_config_json sets inheritDefaultEnv = true, so the relay can receive HTTPS_PROXY from the agent user's default environment even when the sandbox launch request omitted it. Before this PR, the nonempty launch environment went through env_clear().envs(child_env) and the target received only the driver/request environment. Now these lines add ambient proxy values back before clearing the environment, so the same target unexpectedly observes and uses a proxy that was never supplied in its launch request.

A default HTTPS_PROXY plus IsolationSession relay wrapping is enough to change the behavior; proxy-peer mode is not required. Please pass explicit launch metadata identifying MXC proxy-peer injection and inherit these variables only for that mode. Add coverage for a non-peer launch preserving the supplied environment and a peer launch inheriting the MXC endpoint.

Verified through the compiled ARM64 relay at this commit: I set a synthetic HTTPS_PROXY only in the relay process, sent a nonempty launch environment containing SYSTEMROOT and a test marker but no proxy variables, and launched cmd.exe /d /c echo %HTTPS_PROXY%. The target printed the ambient proxy value. This exercises the actual launch protocol without MXC; the IsolationSession applicability follows from the existing inheritDefaultEnv configuration.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Implemented explicit launch metadata in 701efe93. The driver sets inherit_proxy_env true only for an actual MXC proxy-peer launch. The relay defaults it to false, so curated non-peer/IsolationSession launches do not acquire ambient HTTP(S) proxy settings. Peer launches retain the injected endpoint, explicit child values keep precedence, and ambient NO_PROXY is excluded. Protocol version 5 makes stale v4 relay binaries fail explicitly; staging guidance was updated.

Four added tests drive the compiled relay and inspect a real child's environment: curated_launch_without_peer_metadata_drops_ambient_proxy, non_peer_launch_explicitly_drops_ambient_proxy, peer_launch_preserves_mxc_proxy_without_no_proxy_bypass, and peer_launch_keeps_explicit_proxy_override. All four passed. The full relay suite passed 21 tests (7 unit and 14 blackbox), and the gateway version-mismatch test explicitly rejects v4.

Signed-off-by: Shailendra Singh <shailendras@nvidia.com>
@shailendra-nv

Copy link
Copy Markdown
Collaborator

Review fixes pushed in 701efe93: scoped firewall authorization before peer readiness, peer cleanup on confirmed terminal states with retry ownership, and explicit relay proxy-inheritance metadata (protocol v5). Added native resource regressions, four compiled-relay environment tests, a native firewall scope/ownership test, and an ignored real peer HTTPS/L7 integration test. Architecture, gateway reference, crate README, and Windows/debug skills are updated. Each review thread has a reply with the corresponding tests.

Validation on native Windows ARM64 (separate executions, with overlapping coverage):

Execution Total Passed Skipped Failed Result
Windows Rust workspace 5,113 5,083 30 0 PASS, six Nextest leak annotations
Final affected packages/all targets 1,529 1,506 23 0 PASS
Focused MXC Nextest 251 235 16 0 PASS, four mock workload leak annotations
Real MXC suite, elevated peer test excluded 14 13 1 0 PASS for executed proofs
Actual AppContainer peer startup denial/cleanup 1 1 0 0 PASS, negative path
Final peer/relay regression rerun 26 26 0 0 PASS, one baseline mock-stop annotation; other tests filtered
TypeScript SDK 109 109 0 0 PASS
Broad Python suite 259 234 0 25 FAIL in unchanged Unix-oriented tests on Windows

The full pre-commit check and the installed commit hook both passed, including workspace/e2e Clippy, Rust format, Python lint/format/proto, SDK lint/format, Markdown, Mermaid, protobuf and license checks. The real MXC token probe printed SKIP because this token lacks SC_MANAGER_CREATE_SERVICE; it is counted as skipped rather than as an enforcement proof.

The four focused MXC leak annotations were reproduced on original head c44279ab in a baseline run: 4 passed, all four flagged for inherited mock workload stdio. The newly added lifecycle regressions have no leak annotations and verify actual peer child exit, pipe rebinding, and profile recreation with a retained Arc. The other two workspace annotations were not baseline-compared.

Reproduction commands from this revision (native MSVC environment; the real tests use OPENSHELL_WXC_EXEC_PATH):

mise run --skip-tools pre-commit
mise run --skip-tools test:rust
cargo test --locked --offline -p openshell-driver-mxc -p openshell-supervisor-network -p openshell-supervisor-relay --all-targets --target aarch64-pc-windows-msvc
cargo test --locked --offline -p openshell-driver-mxc --test wxc_exec_real --target aarch64-pc-windows-msvc -- --ignored --skip pc_peer_https_egress_reads_injected_ca_bundle --test-threads=1 --nocapture

Tooling limits: this host's ARM64 Biome crashed, so lint/format used the same pinned x64 version under Windows emulation; Python used the existing uv-managed x64 interpreter. SDK code generation used an ignored temporary Buf template that invokes the same plugin through Node because the shipped Unix plugin path does not execute on Windows. The aggregate test task failed in shell parity; suites were run independently. No Python, shell-parity, SDK source or tracked tooling dependency changes were made.

Still pending before calling the firewall finding fully validated: successful elevated firewall install/removal and the new real peer HTTPS/L7 test. The standard token was denied with 0x80070005; the actual helper's negative startup test proved actionable failure before readiness and profile cleanup, but does not prove peer connectivity. An attempted positive test was blocked by this permission, and elevation produced no completed positive result. The firewall review thread remains open.

With an elevated token and a staged AppContainer-readable helper:

$env:OPENSHELL_MXC_PEER_EXE='C:\path\to\openshell-mxc-peer.exe'
cargo test --locked --offline -p openshell-driver-mxc --test wxc_exec_real --target aarch64-pc-windows-msvc -- --exact pc_peer_https_egress_reads_injected_ca_bundle --ignored --test-threads=1 --nocapture

Logs and the detailed evidence report are retained locally under D:\OpenShell\target\pr4330-*. This is not an all-green completion; the elevated positive check and the reported broad Windows test limitations remain outstanding.

Signed-off-by: Shailendra Singh <shailendras@nvidia.com>
@shailendra-nv

Copy link
Copy Markdown
Collaborator

Re-ran the tests on Windows ARM64 using a non-elevated token (TokenElevation=0) and real MXC. MXC selected base-container with no DACL augmentation.

The default BaseContainer host-proxy path works without UAC. The reproduced failure belongs to the optional unpackaged AppContainer peer.

Original check Rerun result Exact interpretation
peer_firewall::tests::firewall_install_has_explicit_permission_failure_or_owned_cleanup PASS, permission-denial branch INetFwRules::Add returns 0x80070005. The test verifies the diagnostic and absence of a leftover rule. It does not establish successful installation/removal.
pc_peer_https_egress_reads_injected_ca_bundle FAIL, exit 101 INetFwRules::Add returns E_ACCESSDENIED / 0x80070005 during peer authorization. Sandbox state is ProvisionFailed before the HTTPS workload starts. The missing workload diagnostic file follows from that startup failure.

The actual AppContainer-helper startup/cleanup negative test also passed: failure occurs before readiness and the profile is removed.

There is a separate implementation defect: Microsoft documents that INetFwRules::Add with LocalAppPackageId is unsupported. The peer writer still uses that combination. Approving UAC is not a verified fix; successful peer authorization/connectivity remains unresolved. The error now identifies the API call and states this limitation.

Commit 572de53b adds two strict qualifications, both executed and passed:

  • pc_basecontainer_https_proxy_without_elevation: actual OpenShell driver and host proxy; real HTTPS GET, injected CA certificate verified, HTTPS POST denied with 403 by L7 policy; peer disabled.
  • pc_basecontainer_runtime_proxy_without_elevation: independently sets MXC runtimeConfig.networkProxy against an ordinary loopback listener without allowedProxyPeer; MXC injects HTTP_PROXY and delivers the request/response without elevation.

The tests require OPENSHELL_MXC_QUALIFY_NO_UAC=1 for strict execution, assert the host token and BaseContainer probe, and fail on missing prerequisites or a skipped workload. A negative check confirmed missing MXC fails qualification. Without opt-in, the generic real-MXC lane preserves its existing skip behavior.

For an unattended harness on a BaseContainer host supporting host loopback:

[openshell.drivers.mxc]
backend = "process_container"
egress_proxy = true
egress_proxy_addr = "127.0.0.1:18080"
pc_proxy_peer_path = ""

Provide a sandbox cwd and explicit network policy. BaseContainer is the MXC tier, not a gateway backend name. The default driver uses proxy environment variables and retains its documented broader host-loopback access; the native runtime-proxy test validates a separate MXC contract. No automatic fallback weakens a requested peer policy.

Validation: 256 MXC/relay regression tests passed, 18 ignored; all six Nextest leak annotations reproduce on the original PR. Real-MXC suite excluding optional peer HTTPS: 15 executed proofs passed plus one explicit token-test skip. Full pre-commit and signed-off commit hook passed. The peer firewall review finding remains open.

The test ends 'sandbox create' with '-- exit'. In a supervisor-less MXC
sandbox the CLI's post-create exec fails with exit 255 and
'CreateProcessW failed error:2 posix_spawnp: No such file or directory',
which the harness's benign-message list did not cover, so the script
aborted before the forward and health checks even though the sandbox was
created and ready. Treat that specific outcome as benign.

Signed-off-by: Prashant S Khodade <pkhodade@nvidia.com>

This branch has not been deployed

No deployments
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.

6 participants