* docs(rfc): RFC 0012 Isolation Backend interface
Introduces RFC 0012, the runtime-selectable Isolation Backend contract: a
pluggable component that establishes and enforces an agent's isolation
boundary across network, filesystem, syscall, and identity, while the
supervisor stays the policy authority (proxy, policy, audit) and the agent's
only egress.
Includes the supporting topology matrix and codebase-grounding notes.
Signed-off-by: Jordan Ganoff <jordan.ganoff@docker.com>
* docs(rfc): complete RFC 0012 contract detail and corrections
Tighten RFC 0012 for review without changing its structure or tone:
- Remove the undefined `BoundaryIntent` from `attach`, and state that `claim`
is the only transition that binds sandbox identity, policy, agent, and
resources; an attached boundary holds no claimed or untrusted workload.
- Add the runtime interface definitions (`BoundaryExec`/`ExecSession`,
`BoundaryPortForward`, `EventSource`) and their semantics: owned distinct
stdio, PTY resize, placement-neutral exit and signals, stable repeated
`wait`, validated loopback targets, and single-consumer events.
- Make boundary confirmation mandatory (`MUST`) and lifetime-long, and state
retry, termination, and cleanup semantics (only attach is auto-retryable; a
lost `start_agent` returns the existing process; descendants are owned;
`wait_terminated` and an idempotent `shutdown`).
- Require trusted admission to supply the expected backend id, contract
version, policy digest, and capabilities that a backend cannot lower; add the
no-silent-weakening policy invariant; mark `VerifiedBoundaryDescriptor`
privately constructed and distinguish envelope from contract version; make
`ResourceBinding` driver-issued, bound, and non-wideable.
- Make the rollout executable and forbid a silent default backend.
topology-matrix: rename "sidecar proxy" to sidecar-assisted (the proxy stays
with the supervisor), frame sidecar and node as composite backends, move the
single-pod outer sandbox to the shared-kernel cell, and note a node enforcer
alone is not `restricted`-compatible.
codebase-grounding: re-pin anchors to the RFC's parent ba21bb32 (driver.rs and
proxy.rs line numbers refreshed) and add a permalink base; correct the event
wording so a denial carries `Evidence` plus request-specific L7 data.
Set state: review and link PR #2048.
Signed-off-by: Jordan Ganoff <jordan.ganoff@docker.com>
* docs(rfc): RFC 0012 cleanup
Contract completion: add MediationIngress/MediatedConnection so the proxy
receives connections placement-neutrally; complete the runtime types
(BoundaryExitStatus/BoundarySignal, BoundaryTerminal::resize, IdentitySource
Send+Sync) and a machine-readable BackendErrorKind; name the AdmittedBoundary-
Requirements admission block retained by VerifiedBoundaryDescriptor; show
claim_id/generation in ClaimContext; require a canonical bounded descriptor
encoding; define capability comparison. Mark MediationIngress, the admission
block, and claim generation as contract additions the in-pod POC does not yet
implement.
Security semantics: make confirmation mandatory and lifetime-long, but correct
the enforcement model: Landlock/seccomp are monotonic while netns/nftables are
mutable, so require the mutation authority fenced from the workload, atomic
default-deny updates, and fail-closed on lease loss rather than calling the
boundary irreversible. "Confirms effective enforcement", not "reads rules
back". Bind Attested identity to sandbox and claim id/generation. Tighten the
execution-domain invariant (admission-visible named capability + audit, whole
descendant trees). Distinguish structural pre-Running ordering from dynamic
post-termination rejection; note adoption must remove legacy bypass paths.
Lifecycle: drop the "establishes before supervisor boot" contradiction; add
shutdown to the supervisor sequence; rewrite the in-pod migration appendix
(policy-free attach, netns/rules at claim/bind, agent().wait(), wait_terminated,
idempotent shutdown). The RFC is implemented only after the in-pod backend
passes its release gates.
Topology: narrow node-enforcer claims to "network-setup capabilities off the
pod"; nuance NetworkPolicy (coarse L3/L4 defense in depth, not a proxy
replacement); drop the proxy-policy-reprograms-boundary claim; label the
containment table a kernel-compromise ceiling.
Grounding: re-pin to the RFC's parent a5161d0 (process.rs and proxy.rs line
numbers refreshed; others unchanged and reverified).
Signed-off-by: Jordan Ganoff <jordan.ganoff@docker.com>
* docs(rfc): simplify isolation backend contract
This removes a lot of detail to keep the RFC succinct.
* removed POC and intermediate stage references
The RFC should describe the end state, and not intermediate milestones or POCs.
* revised based on feedback:
- Introduced topology nomenclature to align with how we've been talking about this in other conversations
- Simplified the explanation in the proposal section
- Clarified binary identity
- Simplified the initial contract to require the minimal interface necessary to satisfy all known topologies
- Confirmed this will work with the proposed mxc (RFC 0013) proposal
* docs(rfc): update RFC 0012 supporting docs for sidecar topology
- Repin codebase-grounding.md to 8eacb477 (sidecar supervisor topology,
#2076); update capabilities line numbers (1534→2538, 1540→2544), init
container line numbers (191→423, 993→1506, 1185→2113), and remove the
stale "no native sidecars today" claim. Add openshell-network-init and
openshell-supervisor-network sidecar entries and expand the rg pattern.
- Add Implementation column to topology-matrix.md; mark Co-located/in-pod
and Same-pod composite as implemented (original topology and #2076
respectively); remaining patterns noted as proposed.
Signed-off-by: Jordan Ganoff <jordan.ganoff@docker.com>
* docs(rfc): fully reground RFC 0012 codebase references to 8eacb477
All line numbers and function names verified against the post-sidecar
state of the codebase (commit 8eacb477, #2076).
Changes:
- process.rs: ProcessHandle::spawn 440→527, netns param 446→535;
drop_privileges call sites 603/700→710/812, enforcement 613/705→721/818-819;
enter_netns_and_sandbox now documented at ssh.rs:1245
- CLONE_NEWNET call sites: process.rs:589→695, ssh.rs:619/1186→653/1262,
supervisor_session.rs:610→735, netns/mod.rs:363→342 (226 unchanged)
- CLONE_NEWNS: was one unshare at :393; now unshare at :449 and a new
setns at :480 added for sidecar mount-namespace entry
- nft fail-open: line 264→265, return Ok(()) range 272-277→277; note
that the sidecar path (netns/mod.rs:477) requires nft and returns an
error if absent, fixing the invariant bug for the sidecar topology
- nft_ruleset.rs: policy accept 41→53; accept rules 43-49→56-92;
reject rules now at 106+
- VM driver MASQUERADE: runtime.rs:417/436→418/437
- Agent command: main.rs:331→601; sleep infinity driver.rs:1886→2937,
clarify it is set via SANDBOX_COMMAND env var
- OPA evaluation: proxy.rs:1611 / evaluate_opa_tcp renamed to
authorize_egress_intent at proxy.rs:1955; NetworkInput built at :2032
- openshell.proto: clarify no lifecycle Attach; note AttachSandboxProvider
(provider record attachment, not isolation lifecycle)
- README.md appendix: update pinned commit reference a5161d0→8eacb477
Signed-off-by: Jordan Ganoff <jordan.ganoff@docker.com>
* docs(rfc): address feedback and clean ups
Signed-off-by: Jordan Ganoff <jordan.ganoff@docker.com>
* docs(rfc): preserve binary identity semantics
Keep RFC 0012 focused on placing binary-aware enforcement behind the Isolation Backend contract instead of requiring a new connection-initiation capture mechanism. Define identity as trusted resolution for an accepted connection before policy evaluation, retain fail-closed attribution requirements, and document the current authorization-time procfs behavior. This preserves RFC 0002's binary gating baseline while allowing future backends to provide stronger identity resolution without changing the contract.
Signed-off-by: Jordan Ganoff <jordan.ganoff@docker.com>
* docs(rfc): simplify isolation backend proposal
Addresses PR feedback: Removed the implementation plan and codebase grounding,
renamed the forwarding primitive to BoundaryLoopbackConnector, and clarified
that conformance tests exercise the deployed topology, including delegated
components and their transport.
---------
Signed-off-by: Jordan Ganoff <jordan.ganoff@docker.com>
* fix(supervisor): canonicalize L7 protocols before OPA
Normalize recognized protocol spellings before supervisor validation and Rego authorization so mixed-case MCP policies cannot take the REST rule path. Reject null MCP configuration instead of silently defaulting it, and cover both behaviors with regression tests.
Signed-off-by: Shiju <shiju@nvidia.com>
* test(policy): cover normalization for every supported protocol
Exercise TCP, REST, WebSocket, GraphQL, SQL, JSON-RPC and MCP through YAML and protobuf loading with lowercase, uppercase and mixed-case names.
Assert canonical names in raw OPA data and returned L7 configuration, preserve TCP L4 behavior, and verify that normalization leaves unrelated data unchanged and is idempotent.
Signed-off-by: Shiju <shiju@nvidia.com>
---------
Signed-off-by: Shiju <shiju@nvidia.com>
Previously, every event from a sandbox reused the sandbox ID as its event
ID. Consumers deduplicating security records could mistake separate events
for the same record, and missing device types or empty image objects could
prevent schema validation.
Give each event its own ID, retain the sandbox association separately, and
classify the environment as Other/Sandbox while keeping the OS separate.
Omit unknown container details instead of emitting empty objects.
Security tooling can now distinguish events from the same sandbox and read
their identity consistently after serialization.
Refs #1055
Signed-off-by: Kris Hicks <khicks@nvidia.com>
* chore(nix): unify native and cross-compilation toolchains
Replace the separate GNU and musl development shells with one shell that
provides explicit toolchains for x86_64 and aarch64 Linux, plus native
Darwin on macOS. Cargo selects the compiler, assembler, archiver, and
linker through target-specific environment variables.
Build GNU targets against a glibc 2.28 sysroot with static GCC runtimes
and use the musl toolchains for static Linux executables. Build Z3 and
AWS-LC with each target's stdenv and expose AWS-LC libraries and Rust
bindings through its target-specific system directory.
Keep Darwin system libraries on the unprocessed Apple SDK and prevent
the Rust toolchain from propagating replacement libraries into the shell.
Include the compiler and libc fixes needed for Darwin-to-Linux builds.
Share dependency and environment wiring through mkToolchain, keep compiler
wrappers in the Linux and Darwin modules, and group the pinned GNU build
environment under glibc-2.28. Document the toolchain boundaries in the build
architecture overview.
Validation: actionlint and nix fmt pass. The toolchain refactor preserves
the shell derivations for x86_64 Linux, aarch64 Linux, and aarch64 Darwin.
Signed-off-by: Simon Scatton <sscatton@nvidia.com>
* ci(nix): use the default shell for binary builds
Remove the dev-shell input and its matrix and workflow plumbing.
Use the host default shell for builds and cache hashing.
Signed-off-by: Simon Scatton <sscatton@nvidia.com>
* ci(nix): build release binaries with explicit Cargo targets
Use target-specific artifact paths and rely on the Nix toolchains for
linkage. Remove post-link rewriting, platform linkage checks, and the
unused interpreter input. Update the build architecture documentation.
Signed-off-by: Simon Scatton <sscatton@nvidia.com>
* refactor(nix): reuse glibc and GCC build recipes
Use a pinned historical Nixpkgs recipe for glibc 2.28 and rebuild GCC 15
against it instead of maintaining separate runtime builds. Keep only
compatibility adjustments needed by the current build tools.
Assemble the sysroot from glibc outputs and static native libraries. Use
standard ELF interpreters and resolve Rust's explicit gcc_s dependency
through the static GCC archives.
Allow 90 minutes for Rust branch checks to accommodate cold toolchain
builds. Validate the Linux release matrix locally; Darwin remains for CI.
Signed-off-by: Simon Scatton <sscatton@nvidia.com>
* fix(nix): disable obsolete RPC tools in glibc
Avoid building rpcgen against the Darwin SDK, which does not expose stat64.
Signed-off-by: Simon Scatton <sscatton@nvidia.com>
---------
Signed-off-by: Simon Scatton <sscatton@nvidia.com>
* feat(docs): add version availability labels
Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
* fix(docs): use supported Python for sync
Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
* feat(docs): publish versioned docs from releases
Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
* fix(docs): format dev version label
Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
* chore(docs): upgrade Fern CLI to 5.112.0
Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
* fix(docs): make release publishing monotonic
Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
* fix(docs): preserve snapshot release identity
Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
* docs(fern): document versioned publishing
Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
* test(docs): cover explicit snapshot rollback
Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
* refactor(docs): use Fern refs for versions
Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
* fix(docs): bundle components for ref versions
Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
* revert(docs): keep complete version copies
Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
* fix(docs): publish latest and dev channels
Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
---------
Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
Keep watching briefly when a compute driver reports ContainerExited without a canonical main-process result. This prevents --no-keep cleanup from deleting the sandbox before the supervisor publishes the authoritative exit status.
Signed-off-by: Shiju <shiju@nvidia.com>
* refactor(inference): remove managed inference routes
Closes#3172
Remove the inference route control plane, inference.local data path, built-in router crate, and SDK surface. Move inference workloads to explicitly imported provider profiles and native endpoints, with migration cleanup and updated tests and documentation.
Signed-off-by: John Myers <9696606+johntmyers@users.noreply.github.com>
* fix(policy): preserve alternate upstream isolation
Restore the provider policy activation guard so legacy OpenAI and Anthropic providers configured for alternate base URLs do not grant egress to the built-in public vendor endpoints.
Signed-off-by: John Myers <9696606+johntmyers@users.noreply.github.com>
---------
Signed-off-by: John Myers <9696606+johntmyers@users.noreply.github.com>
* chore(deps): replace ring with AWS-LC
Signed-off-by: Simon Scatton <sscatton@nvidia.com>
* fix(lint): address warnings after dependency upgrades
Signed-off-by: Simon Scatton <sscatton@nvidia.com>
* fix(tls): limit provider initialization to reqwest clients
Signed-off-by: Simon Scatton <sscatton@nvidia.com>
---------
Signed-off-by: Simon Scatton <sscatton@nvidia.com>
Introduce a single generic applier, options.Apply[T any, O ~func(*T)],
in a new leaf package openshell/v1/internal/options, and route all
option-application sites through it. Nil option entries are now ignored
uniformly instead of panicking; non-nil options still apply in order,
and each site keeps its existing pre-seeding and defaulting.
- Add internal/options package with Apply and unit tests
- Route the 15 application sites (root, types, oidc, fake, gateway, edge)
through options.Apply; exported types.Apply*Options keep their
signatures and delegate to it
- Add one nil-handling test per affected package
- Document the nil-option rule in README.md and the docs book
No public API signature changes.
Closes#3213
Signed-off-by: Roland Huß <rhuss@redhat.com>
* feat(sandbox): support rootfs tar as --from source for VM driver
Accept flat rootfs tar archives (.tar, .tar.gz, .tgz) via the --from
flag for VM-backed gateways. The CLI detects the archive extension,
validates that the gateway uses the VM compute driver, and passes the
tar path through driver_config. The VM driver copies the tar into its
staging area and feeds it into the existing rootfs extraction and ext4
disk creation pipeline, skipping the container image pull/export steps.
Closes#2175
Signed-off-by: Philippe Martin <phmartin@redhat.com>
* fix(sandbox): validate rootfs tar path at the VM driver boundary
The rootfs_tar_path field in driver_config was passed from the API
caller directly to tokio::fs::copy without validation. An authenticated
user bypassing the CLI could supply arbitrary host paths (e.g.
/dev/zero for disk exhaustion, or readable host files for data
exfiltration).
Introduce a trusted staging directory that the VM driver creates on
startup and advertises via GetCapabilities. The CLI now copies the tar
into the staging directory before creating the sandbox, and the driver
validates that the received path is a regular file inside the staging
root and within a configurable size limit (default 10 GiB) before any
I/O.
New VmDriverConfig options:
- rootfs_tar_staging_dir: override the staging directory
(default: <state_dir>/rootfs-tar-staging)
- rootfs_tar_max_bytes: override the size limit (default: 10 GiB)
Addresses GATOR-28b5152e-01.
Signed-off-by: Philippe Martin <phmartin@redhat.com>
* fix(sandbox): request-scoped staging, size pre-check, and cleanup for rootfs tar
Tighten the rootfs tar staging flow to address the remaining GATOR-01
obligations:
- Request-scoped staging: the CLI creates a unique per-request
subdirectory (req-<pid>) under the staging root instead of placing
files directly in the shared directory. The driver enforces that the
tar path is at depth 2 (staging_root/<subdir>/<file>), preventing
cross-request path selection.
- Size pre-check: the driver advertises rootfs_tar_max_bytes via
GetCapabilities. The CLI reads this limit and rejects oversized files
before copying, avoiding disk exhaustion in the staging directory.
- Cleanup: the driver removes the request staging subdirectory after
consuming the tar (on cache hit, copy success, or copy failure),
ensuring staged data does not persist beyond the request.
Signed-off-by: Philippe Martin <phmartin@redhat.com>
* fix(vm): restore rootfs-tar sandboxes from persisted image identity
On restore or restart, the one-shot staged tar archive has already been
cleaned up. Reading the persisted image identity from the sandbox state
directory and resolving the cached disk path directly avoids re-accessing
the deleted staging path.
Addresses GATOR-168b9210-01.
Signed-off-by: Philippe Martin <phmartin@redhat.com>
* fix(cli): use random staging dirs and enforce byte limit during rootfs tar copy
Replace PID-based request staging directories with tempfile-generated
random names to prevent collisions and make paths unpredictable.
Replace bare tokio::fs::copy with a streaming copy loop that enforces
the advertised max_bytes limit during transfer, closing the TOCTOU gap
between the pre-copy size check and the actual copy.
Signed-off-by: Philippe Martin <phmartin@nvidia.com>
Signed-off-by: Philippe Martin <phmartin@redhat.com>
* fix(sandbox): issue rootfs tar staging slots from the gateway
A caller could name any host path in `driver_config.vm.rootfs_tar_path`,
which the privileged VM driver then read. The CLI-side locality check did
not apply to direct API requests.
The gateway now owns staging. `BeginRootfsTarStaging` allocates a
request-scoped directory under the driver-advertised staging root and
returns an opaque single-use token; `CreateSandbox` carries the token, and
the gateway substitutes the path it allocated before dispatching to the
driver. `template.driver_config.<driver>.rootfs_tar_path` is rejected
outright in request validation, so a caller-supplied path never reaches
privileged I/O.
Tokens are bound to the issuing workspace and subject, consumed once, and
expire after 30 minutes. Outstanding slots are capped per caller and
overall, so one caller can neither exhaust the staging filesystem nor
starve others. An RAII guard reclaims the directory on every failure path
after consumption, and an age-gated sweep runs at startup and on each
reconcile pass for directories whose driver died before its own cleanup.
The token is stripped from the public sandbox before persistence: the
stored copy is returned verbatim by GetSandbox, ListSandboxes and
WatchSandbox to every member of the workspace.
Also fixes two defects this exposed:
- The CLI wrote `rootfs_tar_path` at the top level of `driver_config`, but
the gateway forwards only `driver_config.<driver_name>`, silently
dropping unmatched keys. The archive never reached the VM driver, so the
documented `--from ./rootfs.tar` flow did not work at all. Config is now
nested under `vm` and deep-merged, so a caller's existing VM settings
survive instead of being clobbered by a shallow extend.
- Staging previously required `GetGatewayInfo`, which is restricted to
`platform_admin`, making the feature unusable for ordinary users on any
RBAC-enabled gateway. The new RPC matches CreateSandbox at
`sandbox:write` / `workspace_role: user`.
`compute_driver.proto` is unchanged; the gateway reads the staging root
from the capabilities it already stores.
Refs #2175
Signed-off-by: Philippe Martin <phmartin@redhat.com>
* fix(vm): derive rootfs tar cache identity from archive contents
The prepared-disk cache key combined the archive's full path with an mtime
truncated to seconds, then mapped punctuation to `-`. Distinct paths such as
`/tmp/a/b.tar` and `/tmp/a-b.tar` collapsed onto the same key and reused each
other's disk, a rewrite within the same second kept stale contents, and a long
path could exceed filesystem component limits.
Identity is now a SHA-256 of the archive contents. This is also what makes the
cache work at all now that the gateway allocates a fresh staging directory per
request: a path-derived key would miss on every create.
The archive is hashed, the cache checked, and only on a miss copied — so a hit
skips writing a multi-gigabyte file. The copy is hashed as it is written and
rejected if the digest differs from the first pass, which closes the window
where the source changes during staging rather than approximating it with a
re-stat.
Refs #2175
Signed-off-by: Philippe Martin <phmartin@redhat.com>
* fix(vm): decompress gzip rootfs tar archives during staging
`--from` accepts `.tar.gz` and `.tgz`, but the driver staged whatever bytes
it was given and the guest image-prep VM extracts the staged file with a
plain `tar -xpf`. Compressed sources therefore depended on the guest tar
auto-detecting gzip, and the prepared disk was sized from the compressed
length, which is far too small for the expanded rootfs.
Staging now detects gzip from the archive's magic bytes -- the driver only
ever sees a gateway-issued staging path, never the caller's file name -- and
writes an uncompressed tar. The digest still covers the source bytes, so the
"archive changed while staging" check is unaffected, and expansion is bounded
by `rootfs_tar_max_bytes` so a compression bomb cannot fill the host disk.
`extract_rootfs_archive_to` sniffs gzip as well, so the host-side extraction
path matches.
Adds unit coverage for gzip staging, bounded expansion, and gzip extraction,
plus an e2e sandbox created from a gzip-compressed export.
Signed-off-by: Philippe Martin <phmartin@redhat.com>
---------
Signed-off-by: Philippe Martin <phmartin@redhat.com>
Signed-off-by: Philippe Martin <phmartin@nvidia.com>
* feat(e2e): make e2e:kubernetes work transparently on OpenShift
Running `mise run e2e:kubernetes` on OpenShift required manual namespace
creation, SCC grants, Helm value overrides, and cleanup. A separate
`e2e:openshift` task existed but only checked pod readiness without
running the Rust e2e test suite, and even with the suite wired up the
SSH-relay `sandbox connect` path stalled to the ready timeout because
`kubectl port-forward` cannot carry round-trip-heavy SSH over the
internet.
The harness now auto-detects OpenShift via the `route.openshift.io` API
group and, on OpenShift, both configures the cluster and switches the
gateway transport automatically:
- Drives the gateway through a passthrough OpenShift Route secured with
mandatory mTLS instead of port-forward, so the connect suites
(live_policy_update, port_forward, sync, connect-based
sandbox_lifecycle, settings_management) actually pass. Computes the
Route host from the cluster ingress domain, extracts client mTLS
material from the openshell-client-tls secret, waits for the Route to
serve mTLS, asserts a certless caller is rejected at the TLS
handshake, and registers an mTLS CLI gateway pointing at the Route.
- Applies an SCC-compatible Helm values overlay that removes hardcoded
runAsUser/fsGroup, letting OpenShift assign UIDs from the namespace
range.
- Grants the privileged SCC to openshell-sandbox before Helm install
and removes it during cleanup.
- Grants the anyuid SCC to the PostgreSQL fixture service account in
DB scenarios and removes it during cleanup.
- All oc commands use --context to target the correct cluster.
The OpenShift e2e overlay (ci/values-openshift-e2e.yaml) turns TLS back
on, enables the Route, promotes the cert-verified caller to a dev
principal, and forces `image.pullPolicy`/`supervisor.image.pullPolicy`
to Always so runs against the `latest` upstream image use it instead of
a stale copy cached on the cluster nodes. Every OpenShift branch is
gated on OPENSHIFT_DETECTED, so the vanilla-Kubernetes port-forward path
is unchanged.
The Helm template for podSecurityContext is wrapped with {{- with }} so
null values omit the block instead of rendering invalid YAML.
The separate e2e:openshift task and e2e-openshift.sh script are removed
since e2e:kubernetes now covers OpenShift.
TESTING.md is updated with Kubernetes e2e documentation including
OpenShift auto-detection, dropping the e2e-host-gateway feature on
remote clusters, pinning IMAGE_TAG when the CLI and image versions
differ, task variants, and environment variables.
The debug-openshell-cluster skill gains an OpenShift platform row and
two SCC failure patterns (gateway rejected over hardcoded runAsUser,
sandbox missing the privileged SCC) covering the SCC handling and
podSecurityContext behavior this change introduces.
Signed-off-by: Jorge Garcia Oncins <jgarciao@redhat.com>
* fix(e2e): harden OpenShift SCC cleanup, mTLS gate, and Route timeout
Track the anyuid SCC grant for the PostgreSQL fixture with a dedicated
OPENSHIFT_POSTGRES_SCC_GRANTED flag set before the fixture apply, so a
failed apply no longer leaks the binding; cleanup now revokes it whenever
the grant succeeded, independent of deploy state.
Validate the Route server cert in the certless security gate (curl
--cacert instead of -k) and classify curl's exit code so only a TLS
client-auth rejection (35/56) counts as the expected certless rejection;
an unrelated DNS/timeout/TLS failure now fails loudly instead of masking
a potential mTLS hole.
Raise the OpenShift Route timeout in the e2e overlay. The default HAProxy
Route timeout is 30s, which severed long-lived transfers (large sandbox
upload/download, SSH-relay `sandbox connect`) mid-stream and failed the
sync e2e tests. Set both haproxy.router.openshift.io/timeout and
timeout-tunnel to 300s: a passthrough Route proxies in TCP mode, so
timeout-tunnel governs the established tunnel while timeout covers the
pre-tunnel phase.
Document the OpenShift transport exception, oc prerequisites and SCC
grants, and make the skopeo tag-check example copy-safe in TESTING.md.
Signed-off-by: Jorge Garcia Oncins <jgarciao@redhat.com>
---------
Signed-off-by: Jorge Garcia Oncins <jgarciao@redhat.com>
The gateway pod template rendered `securityContext:` unconditionally, so
setting `podSecurityContext: null` (e.g. to let OpenShift's SCC assign the
UID/GID range) produced `securityContext: null` instead of omitting the
block. Wrap the block in `{{- with .Values.podSecurityContext }}` so a null
value omits it and an explicit value renders unchanged.
Add a helm-unittest suite covering the default, explicit, and null cases.
Fixes#3033
Signed-off-by: Jorge Garcia Oncins <jgarciao@redhat.com>
* fix(supervisor): preserve MCP versions in runtime config
Carry canonical MCP version allowlists through both policy-loading paths into the supervisor's typed L7 endpoint configuration. Reject malformed, ambiguous, or cross-protocol version data during policy activation so invalid configuration cannot remove MCP inspection from a selected route.
Signed-off-by: Shiju <shiju@nvidia.com>
* fix(supervisor): accept mixed-case MCP version protocols
Validate materialized MCP version metadata through the parsed L7 protocol so supported mixed-case spellings remain valid. Cover YAML and protobuf ingress with pinned-default regression tests.
Signed-off-by: Shiju <shiju@nvidia.com>
---------
Signed-off-by: Shiju <shiju@nvidia.com>
Evaluate proposal coverage once per observed installed policy snapshot while preserving replacement and deadline behavior.
Signed-off-by: Shiju <shiju@nvidia.com>
Build protobuf-derived allow objects from selectors that are actually present. This keeps protobuf and YAML policy validation aligned for protocols such as GraphQL over WebSocket.
Signed-off-by: Shiju <shiju@nvidia.com>
* feat(middleware): define HTTP response pre-return interface
Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
* docs(middleware): clarify HTTP response interface
Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
* refactor(middleware): align response result actions
Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
* refactor(middleware): expose response reason codes
Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
* refactor(middleware): share session end reasons
Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
* feat(middleware)!: finalize HTTP response pre-return contract
Replace the separate body_end event with HttpResponseBodyUnit.end_of_stream.
Every body-inspecting stage receives exactly one flagged unit, which may be
empty; a zero-byte body is one empty flagged unit and OpenShell never reads
ahead to set the flag.
Defer response trailers from V1 and reserve their field numbers. HTTP/1.0
clients and Content-Length bodies cannot carry trailers and that behavior was
undefined.
Add HttpResponsePreflight.permitted_body_modes, computed once from the
original upstream head so every stage sees the same list, and make an
unlisted selection a failure rather than a downgrade. Add the block_delivery
preflight action as a successful decision enforced regardless of on_error.
Expose Content-Length, Content-Encoding, and Content-Range read-only in
preflight. Cap STREAM_BYTES input units at half of max_payload_bytes and
permit deferring bytes across replacements only for fail_closed bindings,
surfaced as deferral_permitted.
Split PEER_DISCONNECT into DOWNSTREAM_DISCONNECT and UPSTREAM_DISCONNECT and
attribute WebSocket relay failures by direction instead of a generic peer
error. Compile the content-guard example in lint and branch checks so proto
renames cannot break it silently.
BREAKING CHANGE: WebSocketSessionEndReason and WebSocketSessionEnd are
replaced by the shared MiddlewareSessionEndReason and MiddlewareSessionEnd.
NORMAL_CLOSE is now NORMAL, UPSTREAM_REJECTED is now UPSTREAM_FAILURE, and
PEER_DISCONNECT is split into DOWNSTREAM_DISCONNECT and UPSTREAM_DISCONNECT.
Enum numbers are unchanged so binary wire compatibility is preserved;
generated symbols and JSON names change.
Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
* docs(middleware): describe skip as opting out of inspection
Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
* feat(middleware): add body-phase block_delivery and skip_remaining actions
Body results may now stop delivery or opt out of inspecting the rest of the
response after a prefix. One HttpResponseBlockDelivery message is shared by
preflight and body results and documents the difference between blocking
before and after head commitment. Drop the field reservations, since nothing
in this contract has shipped, and renumber session_end to close the gap.
Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
* refactor(middleware): share HTTP body leaf messages across directions
HttpBodyUnit, HttpBodyPassThrough, HttpBodyTransform, HttpBodySkipRemaining,
and HttpBodyMode carry no response-specific semantics, so name them for reuse
by the streaming request hook. Envelopes, results, preflight, and
block_delivery stay response-specific because commitment semantics differ.
Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
* refactor(middleware): keep HTTP body leaf messages response-specific
Reverts the shared HttpBody* naming. A direction-specific payload such as a
response-only semantic mode would otherwise add unreachable variants to the
other direction or force a source-breaking fork after 0.1.0. The streaming
request hook defines its own HttpRequestBody* messages and copies the shape;
SDKs present a direction-neutral body handler over both.
Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
* docs(middleware): simplify response proto comments
Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
* fix(middleware): reject undispatched response bindings
Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
* docs(middleware): simplify phase field comment
Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
* refactor(middleware): rename HTTP response preflight result
Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
* feat(middleware): add HTTP response trailer results
Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
* docs(middleware): define response block delivery
Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
* docs(middleware): define stage-local response body modes
Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
* docs(middleware): define final response body units
Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
* docs(middleware): define streaming response deferral
Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
* docs(middleware): defer whole-body accumulation timeout
Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
* docs(middleware): align response result diagnostics
Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
* test(middleware): cover upstream WebSocket disconnect
Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
* fix(middleware): keep response streams unit-local
Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
* docs(middleware): trim disconnect compatibility note
Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
---------
Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
* feat(ocsf): configurable schema version for SIEM backward compatibility
Add a gateway-configurable OCSF schema version target that downgrades
JSONL output for SIEMs that only support older schema versions. AWS
Security Lake requires v1.1.0, Splunk CIM Add-On targets v1.1-v1.3.
The downgrade filter strips profile-gated fields (ai_model, container,
observation_point_id), removes unknown profiles from metadata.profiles,
rewrites metadata.version, and adds an unmapped.downgraded_from
breadcrumb so auditors can distinguish "no model involved" from "model
attribution stripped."
Supported target versions (1.1, 1.3) are enforced by an allow-list in
the settings registry. Invalid values are rejected with a clear error.
The setting flows to sandboxes via the settings bundle and takes effect
on the next poll cycle. The shorthand log output is unaffected.
Closes#2662
Signed-off-by: Adel Zaalouk <zanetworker@gmail.com>
Signed-off-by: Adel Zaalouk <azaalouk@redhat.com>
* fix(ocsf): align downgrade with schema version
Signed-off-by: John Myers <9696606+johntmyers@users.noreply.github.com>
---------
Signed-off-by: Adel Zaalouk <zanetworker@gmail.com>
Signed-off-by: Adel Zaalouk <azaalouk@redhat.com>
Signed-off-by: John Myers <9696606+johntmyers@users.noreply.github.com>
Co-authored-by: John Myers <9696606+johntmyers@users.noreply.github.com>