Commit Graph
529 Commits
Author SHA1 Message Date
ddurst-nvidia a3aed62cb2 chore(openshell-core): discover proto files in build script (#1122)
Walk ../../proto recursively instead of maintaining a hard-coded list. Sort paths for deterministic codegen and keep cargo:rerun-if-changed scoped to the proto tree.

Signed-off-by: ddurst <267424412+ddurst-nvidia@users.noreply.github.com>
2026-05-01 12:04:55 -07:00
John T. Myers 55f0e37127 chore(ci): label non-maintainer issues for triage (#1120)
Signed-off-by: John Myers <9696606+johntmyers@users.noreply.github.com>
2026-05-01 11:08:56 -07:00
John T. Myers 8e28209587 chore(ci): label maintainer issues by repo permission (#1116)
Signed-off-by: John Myers <9696606+johntmyers@users.noreply.github.com>
2026-05-01 09:09:30 -07:00
Drew Newberry 230824e74a fix(install): refresh dev gateway registration (#1110) 2026-05-01 08:34:00 -07:00
Drew Newberry fcefdd53b7 feat(driver-docker): use host networking for sandboxes (#1080) 2026-05-01 08:09:11 -07:00
Drew Newberry 9751872b2d feat(release): add Debian package publishing (#1069) 2026-05-01 07:51:45 -07:00
John T. Myers b39af3da85 chore(ci): label mon maintainer issues for triage (#1102)
Signed-off-by: John Myers <9696606+johntmyers@users.noreply.github.com>
2026-05-01 07:18:49 -07:00
jtoelke2 dd98eb25ef ci: drop duplicate shadow e2e workflow (#1104)
Signed-off-by: Jonas Toelke <jtoelke@nvidia.com>
2026-04-30 22:31:03 -05:00
Taylor Mutch 888f1dde86 chore: sync Cargo.lock (#1084) 2026-04-30 20:09:01 -07:00
Piotr Mlocek f46b296daf test(e2e): skip docker gpu test in rust suite (#1103)
Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
2026-04-30 18:59:43 -07:00
Seth Jennings ea4915ad30 feat(server): add feat: auto-detection of compute driver at startup (#1088)
When no drivers are explicitly configured, the server now automatically
detects the appropriate compute driver by checking the runtime environment:

- Kubernetes: detected via KUBERNETES_SERVICE_HOST env var (set inside pods)
- Podman: detected by checking if podman binary is available on PATH
- Docker: detected by checking if docker binary is available on PATH

Priority order: Kubernetes → Podman → Docker. VM is never auto-detected
and must be selected explicitly via --drivers vm.

The Auto variant is internal-only and does not serialize to config files.
The default --drivers value is now empty, triggering auto-detection.
2026-04-30 17:39:26 -07:00
Evan Lezar 0c0f3e32e1 feat(docker): enable CDI GPU sandboxes (#1036) 2026-04-30 13:23:57 -07:00
Evan Lezar 182cbc6750 fix(docker): set apparmor=unconfined on sandbox containers (#1078)
Docker's default AppArmor profile blocks mount(2) with MS_SHARED even
when SYS_ADMIN is granted, which prevents ip-netns from creating
network namespaces required for proxy-mode network isolation. The
sandbox enforces its own isolation (seccomp, Landlock, network
namespaces), so the host AppArmor profile provides no meaningful
additional defence inside a container that already holds SYS_ADMIN.
2026-04-30 13:23:36 -07:00
Drew Newberry 51351e4745 chore(ci): update checkout action to v6 (#1086)
Signed-off-by: Drew Newberry <anewberry@nvidia.com>
2026-04-30 13:22:47 -07:00
Mrunal Patel 084505425b feat(auth): add OIDC/Keycloak authentication with RBAC and scope-based permissions (#935)
* feat(auth): add OIDC/Keycloak authentication with RBAC

Add OAuth2/OIDC authentication to the gateway server with role-based
access control, CLI login flows, and full deployment plumbing.

Server: JWT validation against configurable OIDC issuer (oidc.rs),
JWKS key caching with TTL and rotation handling, method classification
(unauthenticated/sandbox-secret/dual-auth/bearer), identity extraction
with provider-agnostic Identity type, and RBAC enforcement via
AuthzPolicy with configurable admin/user roles and auth-only mode.

CLI: browser-based Authorization Code + PKCE flow, Client Credentials
flow for CI/automation, token storage with refresh, gateway add/login/
logout commands, OIDC bearer token injection over mTLS transport,
discovery endpoint for auto-configuration.

Security: sandbox-secret scope restriction on UpdateConfig (policy
sync only), anti-spoofing header stripping, dual-auth fallthrough
from sandbox-secret to Bearer token.

Deployment: OIDC config wired through DeployOptions, Docker env vars,
Helm values/templates, HelmChart manifest, cluster-entrypoint.sh, and
bootstrap scripts. Keycloak dev server script with pre-configured
realm (test users, roles, PKCE client, CI client).

Tested with Keycloak. The roles claim path and role names are
configurable to support other OIDC providers.

* feat(auth): add OAuth2 scope-based fine-grained permissions

Add opt-in scope enforcement on top of existing OIDC role-based access
control. When --oidc-scopes-claim is set, the server extracts scopes
from the JWT and checks them per-method against an exhaustive scope map.

Scopes: sandbox:read, sandbox:write, provider:read, provider:write,
config:read, config:write, inference:read, inference:write, and
openshell:all (wildcard). Methods not in the scope map require
openshell:all. Scopes layer on top of roles and cannot escalate
privilege. Auth-only mode (empty role names) still enforces scopes
when enabled.

Server: scopes_claim in OidcConfig, scope extraction from JWT
(space-delimited and JSON array formats), standard OIDC scope
filtering, scope check in AuthzPolicy after role check.

CLI: --oidc-scopes on gateway add/start stored in metadata and
consumed by gateway login, --oidc-scopes-claim on gateway start
forwarded to server, scopes parameter in browser and client
credentials OAuth2 flows with openid deduplication.

Deployment: oidc_scopes_claim wired through DeployOptions, docker.rs,
Helm, bootstrap scripts, and cluster entrypoint.

Keycloak: realm config updated with built-in OIDC scopes and 9
OpenShell client scopes as optional on openshell-cli and openshell:all
as default on openshell-ci.

* fix(auth): address branch review findings

Add GetInferenceBundle to sandbox-secret methods so sandbox inference
route refresh works under OIDC. Make GetSandboxConfig dual-auth so CLI
users can read sandbox settings with Bearer tokens.

Preserve OIDC gateway metadata on restart — a bare gateway start
without --oidc-* flags no longer erases the stored OIDC registration.

Document CI client ID requirement (openshell-ci vs openshell-cli) in
the testing guide. Add security note about auth-only mode blast radius
for GitHub Actions.

* fix(auth): complete review findings for OIDC auth boundary

Move OpenShell/GetSandboxConfig from sandbox-secret-only to dual-auth
so CLI users can read sandbox settings with Bearer tokens while sandbox
supervisors continue using the shared secret.

Add sandbox secret interceptor to the inference bundle fetch path so
GetInferenceBundle works under OIDC-enabled gateways. Extract shared
interceptor constructor to avoid duplication.

Add GetSandboxConfig to the config:read scope map so scope enforcement
applies consistently when scopes are enabled.

Refactor OIDC metadata preservation into apply_oidc_gateway_metadata()
with explicit resume semantics — only preserve existing OIDC metadata
on real resume paths, not on fresh deployments.

Update architecture docs and testing guide to reflect the corrected
method classifications and add new test coverage for interceptor
injection, scope requirements, metadata preservation, and dual-auth
classification.

* refactor(auth): use oauth2 crate for CLI OIDC flows

Replace hand-written PKCE generation, authorization URL construction,
token exchange, client credentials, and token refresh with the oauth2
crate's typed API.

Eliminates sha2, hex, and getrandom dependencies from the CLI. The
custom urlencoded() helper and manual form POST logic are replaced by
BasicClient methods with proper type-state safety.

Discovery and the callback server remain custom since the oauth2 crate
does not provide OIDC discovery or a localhost redirect listener.

* refactor(auth): move server auth modules into auth/ directory

Group oidc.rs, authz.rs, identity.rs, and the auth HTTP endpoints
under src/auth/ module directory. No behavioral changes.

  auth/mod.rs      — module root, re-exports HTTP router
  auth/oidc.rs     — JWT validation, JWKS caching, method classification
  auth/authz.rs    — role and scope authorization policy
  auth/identity.rs — provider-agnostic Identity type
  auth/http.rs     — /auth/connect and /auth/oidc-config endpoints

* fix(auth): use RequestBody auth type for client credentials flow

The oauth2 crate defaults to BasicAuth (HTTP Basic header) but Keycloak
and most OIDC providers expect client_secret_post (credentials in the
request body). Set AuthType::RequestBody explicitly to match the
pre-refactor behavior.

Also re-export Identity, IdentityProvider, and JwksCache from the auth
module so ServerState's public API remains nameable by external consumers.

* fix(auth): forward OPENSHELL_OIDC_SCOPES through cluster bootstrap

Pass --oidc-scopes to gateway start so the metadata includes requested
scopes after cluster bootstrap. Without this, users had to manually
edit metadata.json to set scopes for gateway login.

Usage: OPENSHELL_OIDC_SCOPES="openshell:all" mise run cluster

* test(auth): add OIDC e2e tests for RBAC, scopes, and client credentials

Add 10 end-to-end tests covering OIDC authentication against a live
K3s cluster with Keycloak:

RBAC (5 tests): admin can create providers, user cannot, user can list
sandboxes, unauthenticated requests rejected, health probe works
without auth.

Scopes (4 tests): sandbox-scoped token can list sandboxes but not
providers, openshell:all grants full access, no-scopes token denied.

Client credentials (1 test): CI token via client_credentials grant.

Tests are opt-in via OPENSHELL_E2E_OIDC=1 and OPENSHELL_E2E_OIDC_SCOPES=1
env vars. They derive the Keycloak URL from gateway metadata to match
the server's configured issuer.

Run with:

  OPENSHELL_E2E_OIDC=1 OPENSHELL_E2E_OIDC_SCOPES=1 \
  PYTHONPATH=python uv run pytest e2e/python/oidc/ -v

* fix(docs): fix markdown lint errors in OIDC architecture docs

Add blank lines before lists and fenced code blocks to satisfy
markdownlint MD031 and MD032 rules.
2026-04-30 10:37:23 -07:00
jtoelke2 5c77b06124 ci: add OS-49 phase 5 shadow workflows (#1075)
Signed-off-by: Jonas Toelke <jtoelke@nvidia.com>
2026-04-30 12:18:08 -05:00
Alexander Watson ebbd9dee5a docs(examples): add multi-agent notepad demo (#991)
Signed-off-by: Alexander Watson <zredlined@gmail.com>
2026-04-30 09:25:19 -07:00
jtoelke2 78f0b6fff7 ci(rust): keep sccache stats non-blocking 2026-04-29 21:20:43 -05:00
jtoelke2 a656ed7b91 ci(docker): use prebuilt Rust binaries by default (#1027)
* ci(docker): use prebuilt Rust binaries by default

Flip Docker image builds to consume staged native Rust artifacts, remove in-Docker Rust build stages, and publish per-arch images with a manifest merge.

Add local staging support for prebuilt gateway and sandbox binaries so development image builds continue to work without CI artifacts.

Signed-off-by: Jonas Toelke <jtoelke@nvidia.com>

* ci(docker): address prebuilt build review feedback

* ci(rust): allow existing vfio complexity

* ci(rust): pin toolchain to 1.95

---------

Signed-off-by: Jonas Toelke <jtoelke@nvidia.com>
2026-04-29 19:23:02 -05:00
Piotr Mlocek ee2de81bc9 fix(sandbox): preserve encoded slash policy from proto (#1073) 2026-04-29 16:36:49 -07:00
John T. Myers 0914f3f4f7 fix(sandbox): log L7 parse denials (#1072)
Signed-off-by: John Myers <9696606+johntmyers@users.noreply.github.com>
2026-04-29 16:15:37 -07:00
Drew Newberry 24724742a8 ci(rust): enforce -D warnings on clippy (#1008) 2026-04-29 12:12:53 -07:00
vince-brisebois 2adddaa6b2 feat: Adding qemu vm driver support with GPU pass-through (#992)
* Adding qemu vm driver support with GPU pass-through

Signed-off-by: Vincent Caux-Brisebois <vcauxbrisebo@nvidia.com>

* Add GPU rootfs variant, harden VFIO binding, and fix networking and supervisor reliability issues discovered during GPU VM bring-up.

Signed-off-by: Vincent Caux-Brisebois <vcauxbrisebo@nvidia.com>

---------

Signed-off-by: Vincent Caux-Brisebois <vcauxbrisebo@nvidia.com>
2026-04-29 09:18:27 -07:00
Florent BENOIT c0ffa933ed feat(openshell-vm): allow to have tty with exec (#939)
add support on both side (rust and python)

fixes https://github.com/NVIDIA/OpenShell/issues/936

Signed-off-by: Florent Benoit <fbenoit@redhat.com>
2026-04-29 07:49:45 -07:00
Mesut Oezdil 4510b0d1bc fix(net): catch IPv4-mapped blocked ranges in is_always_blocked_net (#1032)
* fix(net): catch IPv4-mapped blocked ranges in is_always_blocked_net

The IPv6 branch only checked whether the network address itself mapped
to a blocked IPv4 address. A broader prefix like ::ffff:168.0.0.0/103
has a public network address but spans ::ffff:169.254.0.0, so the old
code accepted it at policy load time while is_always_blocked_ip silently
rejected every connection at runtime.

Add three containment checks for the IPv4-mapped loopback, link-local,
and unspecified representatives. The existing network-address check is
kept because it handles single-host entries (/128) whose network address
is already in a blocked range.

Five new tests cover: single-host loopback and link-local mapped
addresses, broad prefixes that span each blocked range without starting
there, and a public single-host address that must not be blocked.

* fix(net): address clippy warnings in is_always_blocked_net

Use Ipv4Addr::LOCALHOST instead of Ipv4Addr::new(127, 0, 0, 1) and
collapse the nested if let / if into is_some_and.
2026-04-29 07:40:38 -07:00
John T. MyersandJohn Myers 20ffc7253b fix(cli): preserve directory basename for filtered uploads (#1028)
Signed-off-by: John Myers <johntmyers@users.noreply.github.com>
Co-authored-by: John Myers <johntmyers@users.noreply.github.com>
2026-04-28 18:42:53 -07:00
John T. Myers d414e69a20 refactor(server): unify policy persistence in objects table (#972)
* refactor(server): unify policy persistence in objects table

Signed-off-by: John Myers <9696606+johntmyers@users.noreply.github.com>

* fix(server): clean sandbox-owned records on reconcile delete

* refactor(server): use protos for stored policy records

* refactor(server): move policy persistence into policy_store

* fix(server): restore compute runtime merge compatibility

* fix(server): validate draft chunk sandbox ownership

---------

Signed-off-by: John Myers <9696606+johntmyers@users.noreply.github.com>
2026-04-28 16:15:14 -07:00
jtoelke2 3e69c36f84 feat(ci): add shadow-rust-native-build workflow for OS-49 Phase 4 (PR 4a) (#973)
Builds openshell-gateway and openshell-sandbox natively per-arch on the
nv-gha-runners shared CPU pool with a GHA-backed sccache, and uploads
the resulting binaries as artifacts shaped for Dockerfile.images'
BINARY_SOURCE=prebuilt path (added in #945).

Dispatch manually 4-5 times after merge to collect cold + warm numbers
and compare against the Rust portion of docker-build.yml's ARC baseline.
PR 4c will wire these artifacts into the real pipeline once numbers land.

Refs OS-128.

Signed-off-by: Jonas Toelke <jtoelke@nvidia.com>
2026-04-28 15:32:55 -05:00
Taylor Mutch cd5c16d765 chore(tools): sync mise version to v2026.4.25 (#1013)
* chore(tools): sync mise.lock after upgrade

* chore(tools): Update mise min_version and version in CI dockerfile
2026-04-28 12:49:54 -07:00
Piotr Mlocek c49ae09d5b fix(ci): grant actions:read and contents:read to E2E label helper (#995) 2026-04-28 10:16:27 -07:00
Drew Newberry 5975805424 feat(server): add bundled docker compute driver (#888) 2026-04-27 17:38:02 -07:00
Florent BENOIT 385855c824 fix(podman): use podman machine socket path on macOS (#999)
The default socket path assumed Linux conventions ($XDG_RUNTIME_DIR or
/run/user/{uid}), which do not exist on macOS. Use the podman machine
socket at $HOME/.local/share/containers/podman/machine/podman.sock
on macOS and show a targeted "podman machine start" hint when missing.

Signed-off-by: Florent Benoit <fbenoit@redhat.com>
2026-04-27 16:16:12 -07:00
Piotr Mlocek b264cb83d8 chore(mise): add lockfile with multi-platform support and version pin (#946) 2026-04-27 16:07:15 -07:00
John T. Myers c890f0ee9e chore(ci): relax agent diagnostic gate (#1001)
Signed-off-by: John Myers <9696606+johntmyers@users.noreply.github.com>
2026-04-27 15:56:58 -07:00
John T. MyersandJohn Myers 2646b8c6c8 fix(sandbox): deny ambiguous socket ownership (#958)
Signed-off-by: John Myers <johntmyers@users.noreply.github.com>
Co-authored-by: John Myers <johntmyers@users.noreply.github.com>
2026-04-27 15:12:45 -07:00
Miyoung Choi cde20dc312 docs: weekly documentation refresh (#993)
* docs(release-notes): refresh weekly documentation

Signed-off-by: Miyoung Choi <miyoungc@nvidia.com>

* docs(release-notes): remove weekly refresh entry

Signed-off-by: Miyoung Choi <miyoungc@nvidia.com>

---------

Signed-off-by: Miyoung Choi <miyoungc@nvidia.com>
2026-04-27 12:57:05 -07:00
Florent BENOIT aee744374a fix(deps): add missing cargo-zigbuild dep for macOS cross-compilation (#986)
Signed-off-by: Florent Benoit <fbenoit@redhat.com>
2026-04-27 12:36:08 -07:00
Piotr Mlocek c4286648bb ci(e2e): replace label dispatcher with comment-only helper (#990) 2026-04-27 11:11:52 -07:00
Piotr Mlocek e703b597cd ci(e2e): add label dispatcher and contributor CI docs (#975) 2026-04-27 09:49:01 -07:00
Derek Carr de9dce0436 fix(e2e): use high UID range to avoid host user conflicts (#978)
Change sandbox user UID from 1000 to 1000660000 in custom image examples and E2E tests.
Using a high UID range (1000000000+) prevents conflicts with host users when running
without user namespace remapping, where container UIDs map directly to host UIDs.

This resolves fork failures caused by RLIMIT_NPROC enforcement when the host user
already has many threads running.

Signed-off-by: Derek Carr <decarr@redhat.com>
2026-04-27 09:46:39 -07:00
Drew Newberry e5360b3944 docs(rfc): add core architecture RFC (#836) 2026-04-27 08:05:11 -07:00
John T. Myers 30115bdd1b fix(ci): patch CI container vulnerability toolchain (#959) 2026-04-27 07:56:53 -07:00
Derek Carr 5e28ea3a4b feat(server): add object meta convention to top-level objects (#919)
- adds filterable label selectors on resources

Closes #864

Signed-off-by: Derek Carr <decarr@redhat.com>
2026-04-27 07:16:07 -07:00
Derek Carr f8fb382146 fix(scripts): handle docker cleanup when no containers are running (#977)
The docker-cleanup.sh script failed when no containers were running because
grep -v returned exit code 1 on empty input, causing the script to abort
due to set -euo pipefail.

Add || true to the volume detection pipeline so the script succeeds when
there are no running containers (in_use_volumes will be empty, which is
the correct behavior).

Signed-off-by: Derek Carr <decarr@redhat.com>
2026-04-26 23:02:40 -07:00
John T. Myers bb5bdb483e fix(ci): ignore local artifacts in license checks (#974)
Signed-off-by: John Myers <9696606+johntmyers@users.noreply.github.com>
2026-04-24 15:07:41 -07:00
jtoelke2 55b0266ed5 fix(ci): make buildkitd-config opt-in for setup-buildx (#970)
#966 hard-coded `buildkitd-config: /etc/buildkit/buildkitd.toml` inside
the `driver: local` branch of the setup-buildx composite action. The only
caller using that driver is shadow-docker-build.yml, which runs inside
the ghcr.io/nvidia/openshell/ci:latest container — so the host-side
buildkitd.toml was invisible to docker/setup-buildx-action and every
matrix job failed at "Set up buildx".

Revert the hard-coded path and expose it as an opt-in input on the
action (empty default, passed through to both the remote and local
branches). Wire shadow-docker-build.yml to bind-mount /etc/buildkit
into the ci container and pass the path explicitly, so the action can
read the file from inside the container. Remote-driver callers are
unaffected (empty input is a no-op).

Signed-off-by: Jonas Toelke <jtoelke@nvidia.com>
2026-04-24 16:35:14 -05:00
Piotr Mlocek a01b6dd820 fix(docs): scope fenced code language linting (#965) 2026-04-24 14:23:31 -07:00
jtoelke2 daa7d7d2b9 fix(ci): use nv-gha-runners buildkit mirror to avoid Docker Hub rate limit (#966)
Signed-off-by: Jonas Toelke <jtoelke@nvidia.com>
2026-04-24 13:53:59 -07:00
jtoelke2 d331ed5117 feat(ci): add shadow-docker-build workflow for OS-49 Phase 3 (#964)
Signed-off-by: Jonas Toelke <jtoelke@nvidia.com>
2026-04-24 12:33:25 -07:00
Drew Newberry 25c827d271 test(e2e): fix filtered upload path assertion (#963) 2026-04-24 11:46:04 -07:00