Commit Graph
725 Commits
Author SHA1 Message Date
Aaron Erickson 7baf03ba8b test(sandbox): prove loopback proxy netns boundary 2026-05-21 23:45:18 -07:00
Aaron Erickson 083c066318 fix(sandbox): harden managed loopback proxy 2026-05-21 10:08:03 -07:00
Aaron Erickson b75abad200 fix(sandbox): add managed loopback proxy 2026-05-21 09:40:01 -07:00
mjamivandJohn Myers 528fb29147 fix(sandbox): allow first-label L7 host wildcards (#1304)
* fix(sandbox): allow first-label L7 host wildcards

* docs(sandbox): document L7 host wildcard contract + add OPA runtime tests

- Add Host Wildcards section to architecture/security-policy.md
  describing accepted (first-label *, **, intra-label *-X) and
  rejected (bare, TLD, non-first-label, recursive-in-label) forms,
  and noting that wildcards never cross '.' boundaries.
- Expand the policy-schema.mdx 'host' field description to reflect
  the same contract instead of only mentioning '*.example.com'.
- Add OPA runtime tests asserting '*-aiplatform.googleapis.com'
  matches 'us-central1-aiplatform.googleapis.com' and does not match
  'us-central1.aiplatform.googleapis.com' (cross-dot boundary). Locks
  validator/runtime alignment for intra-label wildcards.

* chore: update mise lockfile

* test(server): tolerate serialized inference upserts

---------

Co-authored-by: John Myers <9696606+johntmyers@users.noreply.github.com>
2026-05-21 09:32:37 -07:00
Derek Carr 2d9e5326f8 fix(sandbox): skip fork-exec socket ambiguity test on SELinux-enforcing hosts (#1449)
Exec'ing /bin/sleep (SELinux label bin_t) from a user_home_t test binary
causes /proc/<pid>/exe readlink to return ENOENT on SELinux-enforcing
hosts due to the cross-domain boundary. Skip the test at runtime when
getenforce reports Enforcing.

Also adds a ChildGuard drop guard for safe child cleanup on panic and
increases the exec-detection deadline from 2s to 5s.

Signed-off-by: Derek Carr <decarr@redhat.com>
2026-05-21 09:13:57 -07:00
Eric Curtin e3f009fb9a refactor(server): extract shared relay-await and sandbox-scan helpers (#1495) v0.0.46 2026-05-21 07:23:36 -07:00
Florent BENOIT b93a3d8661 fix(scripts): use portable lowercase in normalize_bool for Bash 3.2 (#1493) 2026-05-21 07:15:07 -07:00
Seth Jennings c143c81fbf fix(cli): add auth and TLS support to completion client (#1489) 2026-05-21 06:52:23 -07:00
Drew NewberryandTaylor Mutch 77e6c7a12a test(server): cover service endpoint plaintext security (#1352)
* test(server): cover service endpoint plaintext security

* test(server): align tls test with from_files Option<&Path> signature

TlsAcceptor::from_files now accepts the client CA path as Option<&Path>
(per the require_client_auth refactor on main). Wrap the helper's CA
path in Some(...) so the new plaintext-service-http tests compile after
rebasing onto current main.

---------

Co-authored-by: Taylor Mutch <taylormutch@gmail.com>
2026-05-20 14:44:16 -07:00
Mesut Oezdil 2b13bfa47b fix(ci): eliminate image-tag race between concurrent workflows (#1413)
- Add publish-manifest input to docker-build.yml (default true); single-arch
  branch callers set it false so the merge job is skipped and the shared
  bare :SHA tag in GHCR is never written by branch workflows
- branch-kubernetes-e2e: retag :SHA-amd64 to :SHA before kind load so
  Helm's image.tag matches what is loaded in kind containerd
- branch-e2e: pass image-tag as :SHA-arm64 to e2e-test so the arch-specific
  GHCR tag is used directly without depending on the bare tag
- bare :SHA in GHCR is now written only by test-gpu.yml (multi-arch build),
  eliminating the last-writer-wins race across concurrent workflows
2026-05-20 14:06:25 -07:00
Adel Zaalouk bdaa08fbb0 fix(server): add ConnectSupervisor and RelayStream to SANDBOX_METHODS (#1475) 2026-05-20 10:01:56 -07:00
dependabot[bot] 3cde651834 chore(deps): bump azure/setup-helm from 4 to 5 (#1468)
Bumps [azure/setup-helm](https://github.com/azure/setup-helm) from 4 to 5.
- [Release notes](https://github.com/azure/setup-helm/releases)
- [Changelog](https://github.com/Azure/setup-helm/blob/main/CHANGELOG.md)
- [Commits](https://github.com/azure/setup-helm/compare/v4...v5)

---
updated-dependencies:
- dependency-name: azure/setup-helm
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-20 09:35:48 -07:00
Taylor Mutch c600b11ff2 docs(agents): add release canary testing skill (#1440)
* ci(canary): add kind-based helm chart smoke test

Signed-off-by: Taylor Mutch <taylormutch@gmail.com>

* docs(agents): add release canary testing skill

Signed-off-by: Taylor Mutch <taylormutch@gmail.com>

---------

Signed-off-by: Taylor Mutch <taylormutch@gmail.com>
2026-05-20 09:34:57 -07:00
Eric Curtin b332ffdf7d refactor: deduplicate shared driver and provider constants (#1474)
Move default_sandbox_image() and SUPERVISOR_IMAGE_BINARY_PATH into
openshell-core so all compute drivers share a single authoritative
definition. Eliminates four identical copies of default_sandbox_image()
and two identical copies of SUPERVISOR_IMAGE_BINARY_PATH.

Implement ProviderPlugin for ProviderDiscoverySpec to remove the
repeated three-method delegation boilerplate from eight simple
provider modules. Each now declares only a SPEC constant; the blanket
impl handles id(), discover_existing(), and credential_env_vars().
2026-05-20 09:24:57 -07:00
John T. Myers 14c5329f91 docs(providers): add Providers v2 guide (#1442)
* docs(providers): add Providers v2 guide

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

* docs(providers): clarify built-in profile location

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

* docs(providers): remove refresh-backed profile bullet

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

* docs(providers): clarify persisted query registry key

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

* docs(providers): simplify binary schema example

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

* docs(providers): add Microsoft Graph refresh tutorial

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

* docs(providers): link Microsoft Graph refresh tutorial

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

* docs(providers): announce providers v2

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

---------

Signed-off-by: John Myers <9696606+johntmyers@users.noreply.github.com>
2026-05-20 08:57:24 -07:00
Evan Lezar be6ac9e61c docs(agents): add Docker GPU CDI debug hints (#1448)
Signed-off-by: Evan Lezar <elezar@nvidia.com>
v0.0.45
2026-05-20 14:43:54 +02:00
Evan Lezar cade0bb0ac test(e2e): default GPU probe image (#1450)
Signed-off-by: Evan Lezar <elezar@nvidia.com>
2026-05-20 13:25:09 +02:00
Taylor Mutch 2a065a5ff2 ci(canary): add kind-based helm chart smoke test (#1336) 2026-05-19 17:25:57 -07:00
Derek Carr 3c8739372f feat(agents): add LSM compatibility checks to review and spike skills (#1451)
Teach the principal-engineer-reviewer agent, build-from-issue skill, and
create-spike skill to assess Linux Security Module impact.  Code that
touches /proc filesystem access, process identity, or binary execution
can behave differently on SELinux-enforcing or AppArmor hosts — for
example, readlink on /proc/<pid>/exe returns ENOENT across SELinux
domain boundaries, breaking tests that fork+exec into system binaries.

Signed-off-by: Derek Carr <decarr@redhat.com>
2026-05-19 17:19:27 -07:00
Piotr Mlocek 3b53184971 test(persistence): make CAS conflict test deterministic (#1464) 2026-05-19 16:46:52 -07:00
Zygmunt Krynicki 0a8b35c31f fix(build): install binaries built in part build tree (#1462)
The earlier build only worked if the tree had pre-compiled binaries left
over from non-snap build.

Signed-off-by: Zygmunt Krynicki <zygmunt.krynicki@canonical.com>
2026-05-19 15:33:25 -07:00
dependabot[bot] 37ca269072 chore(deps): bump softprops/action-gh-release from 2.6.2 to 3.0.0 (#1458)
Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 2.6.2 to 3.0.0.
- [Release notes](https://github.com/softprops/action-gh-release/releases)
- [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md)
- [Commits](https://github.com/softprops/action-gh-release/compare/3bb12739c298aeb8a4eeaf626c5b8d85266b0e65...b4309332981a82ec1c5618f44dd2e27cc8bfbfda)

---
updated-dependencies:
- dependency-name: softprops/action-gh-release
  dependency-version: 3.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-19 15:17:39 -07:00
dependabot[bot] 2cef120dc1 chore(deps): bump actions/download-artifact from 4.3.0 to 8.0.1 (#1459)
Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 4.3.0 to 8.0.1.
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](https://github.com/actions/download-artifact/compare/d3f86a106a0bac45b974a628896c90dbdf5c8093...3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c)

---
updated-dependencies:
- dependency-name: actions/download-artifact
  dependency-version: 8.0.1
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-19 15:17:23 -07:00
Piotr Mlocek 2a5a44989a fix(ci): require PR checks to pass (#1461) 2026-05-19 14:46:23 -07:00
Eric Curtin 10af3e609f refactor: deduplicate shared test helpers (#1399)
Extract four categories of copy-pasted test code into single
canonical locations:

- POLICY_OBJECT_TYPE / DRAFT_CHUNK_OBJECT_TYPE constants that were
  defined identically in both persistence/sqlite.rs and
  persistence/postgres.rs are now owned by persistence/mod.rs.

- test_server_state() was duplicated across grpc/provider.rs,
  grpc/policy.rs, and grpc/sandbox.rs. A shared grpc::test_support
  module in grpc/mod.rs now owns the single implementation; each
  submodule imports it.

- The TestOpenShell stub (full OpenShell trait impl), install_rustls_provider(),
  PkiBundle, generate_pki(), and start_test_server() were copied across
  up to five openshell-server integration test files. A new
  tests/common/mod.rs module owns them; each test file uses mod common.

- StubResponse, unique_socket_path(), and spawn_podman_stub() were
  duplicated between openshell-driver-podman/src/driver.rs and
  openshell-driver-podman/src/grpc.rs. A new src/test_utils.rs
  (cfg(test)-gated) owns the shared helpers.
2026-05-19 12:26:12 -07:00
Seth Jennings c527341d8a feat(k8s): make default workspace PVC storage size configurable (#1436)
The Kubernetes driver hardcoded the workspace PVC size to 2Gi. Add a
workspace_default_storage_size field to KubernetesComputeConfig so
operators can tune it via TOML config, Helm values, or the
OPENSHELL_K8S_WORKSPACE_DEFAULT_STORAGE_SIZE environment variable.
2026-05-19 11:42:16 -07:00
John T. Myers d255cdd9c9 feat(providers): add credential refresh foundation (#1349)
* feat(providers): add credential refresh foundation

* feat(providers): mint oauth refresh token credentials

* fix(providers): allow delegated refresh bootstrap

* fix(providers): guard refresh credential modes

* fix(providers): refine refresh lifecycle UX

* fix(cli): restore provider test gateway response import

* fix(server): clean auth endpoint test qualifications

* fix(providers): tighten refresh authorization and collisions

* chore(providers): trim bundled v2 profiles

* fix(providers): resolve refresh rebase fallout

* fix(cli): accept rfc3339 credential expiry

* test(providers): update inferred claude provider type

* test(providers): avoid removed outlook default profile

* test(providers): isolate attach limit fixtures
v0.0.44
2026-05-19 07:42:10 -07:00
Florencio Cano f9435b4d42 chore(ci): pin all GitHub Actions to SHA digests (#1233) 2026-05-18 22:36:48 -07:00
Russell Bryant 04a39cabbd fix(build): add z3 include path for RHEL/Fedora bindgen compatibility (#1388)
On RHEL and Fedora, the z3 header is installed to /usr/include/z3/
rather than /usr/include/. The z3-sys crate's bindgen step fails
because pkg-config doesn't find z3 on these distros. Add the extra
include path via BINDGEN_EXTRA_CLANG_ARGS in mise.toml — the -I
flag is silently ignored on systems where the path doesn't exist.
2026-05-18 20:16:27 -07:00
Alec Fong d620d65f6e feat(sandbox): inject DENO_CERT into sandbox child environment (#1441)
Sandbox processes running on Deno couldn't TLS-verify connections to
services signed by OpenShell's per-sandbox ephemeral CA because Deno
doesn't consult `SSL_CERT_FILE` / `NODE_EXTRA_CA_CERTS`. Deno reads its
own `DENO_CERT` env var (additive PEM bundle, same shape as
`NODE_EXTRA_CA_CERTS`) for that.

Add `DENO_CERT` to `child_env::tls_env_vars` pointing at the standalone
OpenShell CA cert (not the combined system+sandbox bundle, since DENO_CERT
is additive). The three callers in `process.rs` / `ssh.rs` iterate the
array, so the size bump from 5 to 6 entries flows through without
caller changes.

Updates the security best-practices doc to enumerate `DENO_CERT`
alongside the other trust-store env vars.

Tracks: brevdev/dex-ui#5

Signed-off-by: Alec Fong <alecf@nvidia.com>
2026-05-18 20:06:25 -07:00
Russell Bryant 65a3a7c2ec test(e2e): close Podman driver test coverage gaps (#1439)
* test(e2e): close Podman driver test coverage gaps

Add podman_gateway_resume test following the VM pattern (no
container-state assertions since Podman keeps containers running across
gateway restarts). Widen websocket_conformance feature gate from
e2e-docker to e2e-host-gateway so it runs on both Docker and Podman.
Fix e2e-podman.sh to default to the e2e-podman feature so Podman-
specific and host-gateway tests are actually included in Podman CI runs.

* refactor(e2e): extract shared CLI helpers from gateway_resume tests

Move run_cli, wait_for_healthy, sandbox_names, and
wait_for_sandbox_exec_contains into a new harness::cli module. All three
gateway_resume variants (Docker, Podman, VM) now import these shared
helpers instead of defining identical copies. Docker-specific container
introspection functions remain local to gateway_resume.rs.
2026-05-18 17:09:16 -07:00
Adam Miller 436c59a2a1 fix(rpm): restore 0.0.0.0 bind address for Podman via default gateway.toml (#1438)
* fix(rpm): restore 0.0.0.0 bind address for Podman via default gateway.toml

The gateway binary default changed to 127.0.0.1 in recent commits
(b61a98db, f257ed01). This breaks the Podman compute driver because
sandbox containers reach the gateway over the host network bridge and
cannot connect to the loopback address.

Ship a default TOML config template that the RPM systemd unit seeds
into ~/.config/openshell/gateway.toml on first start. The template
sets bind_address = "0.0.0.0:17670" and pins compute_drivers =
["podman"] to prevent unexpected driver selection when Docker is also
installed. The binary default remains 127.0.0.1 (secure-by-default
for non-RPM installs).

Changes:
- deploy/rpm/gateway.toml.default: new default config template
- openshell.spec: install template to %{_datadir}/openshell-gateway/;
  add ExecStartPre to seed ~/.config/openshell/gateway.toml on first
  start; add %check assertions for template presence and unit reference
- deploy/rpm/CONFIGURATION.md: document default config, override paths,
  and updated bind address throughout
- deploy/rpm/QUICKSTART.md: update bind address note for RPM installs
- crates/openshell-server/src/config_file.rs: contract test that parses
  the RPM template through load() and asserts bind_address=0.0.0.0
  and compute_drivers=[podman]
- e2e/with-podman-gateway.sh: start from RPM template as base config
  so e2e exercises the same TOML path RPM users get on first start

* fix(rpm): restore openshell_python_version macro in dist-info metadata

* fix(rpm): reset Packit-managed version fields to match main baseline

Version, Source0, and Source1 were stamped to 0.0.43 by Packit CI
during branch builds. Reset to 0.0.37 (current main baseline) so
the spec diff only contains our intentional changes. Packit's
fix-spec-file action will re-stamp these fields at build time.

* Revert "fix(rpm): reset Packit-managed version fields to match main baseline"

This reverts commit 8ef9d7ad8e.

* fix(rpm): introduce openshell_version macro; remove hardcoded versions

Add %global openshell_version as the single source of truth for the
package version. Version:, Source0:, Source1:, openshell_cargo_version,
and openshell_python_version all expand from this one macro.

Update .packit.yaml fix-spec-file to patch %global openshell_version
instead of the Version:, Source0:, and Source1: lines individually.
2026-05-18 16:48:35 -07:00
Russell Bryant c5d1d76d94 refactor(sandbox): replace iptables with nftables for network policy enforcement (#1401)
Migrate all sandbox and VM driver network policy enforcement from
iptables to nftables. nftables provides atomic ruleset loading, a
cleaner rule syntax, and is the standard netfilter interface in modern
kernels.

Sandbox bypass enforcement (openshell-sandbox):
- Replace iptables chain of individual rule insertions with a single
  atomic nftables ruleset load via nft -f
- New nft_ruleset module with pure functions for ruleset generation
  and unit tests
- Combine log and reject rules in one inet family table (handles both
  IPv4 and IPv6 in a single ruleset)
- Fall back to reject-only ruleset when kernel lacks nft_log support
- Enable net.netfilter.nf_log_all_netns so log rules work from
  non-init network namespaces
- Use temp file for nft ruleset loading instead of stdin for
  compatibility with minimal VM guest environments

VM TAP networking (openshell-driver-vm):
- Replace iptables NAT/forwarding rules with nftables equivalents
- New nft_ruleset module for TAP network rule generation with unit
  tests
- Atomic table-per-TAP-device lifecycle (create/destroy)
- Host-side rules provide NAT infrastructure and defense-in-depth
  isolation (input chain restricts VM to gateway port only, forward
  chain blocks unsolicited inbound); primary security enforcement
  happens inside the VM guest via the sandbox supervisor's own rules

VM init script:
- Load nft kernel modules at sandbox init
- Enable nf_log_all_netns sysctl for bypass detection logging

OCSF / docs:
- Update firewall rule engine references from iptables to nftables
- Document host firewall interaction model and two-layer enforcement
  architecture in VM driver README and compute drivers reference

Closes #1335

Signed-off-by: Russell Bryant <rbryant@redhat.com>
2026-05-18 16:37:17 -07:00
Mesut Oezdil 702bb56f91 ci: extend artifact attestations to all release binaries (#1398)
The VM driver artifacts were already attested. Expand the attest step
to cover CLI, gateway, supervisor, deb, rpm, and wheel artifacts so all
release binaries can be verified with gh attestation verify.

Signed-off-by: mesutoezdil <mesudozdil@gmail.com>
2026-05-18 15:39:39 -07:00
Taylor Mutch a7cd1608f3 docs(helm): add chart readme generation (#1437)
Signed-off-by: Taylor Mutch <taylormutch@gmail.com>
2026-05-18 15:26:17 -07:00
Russell Bryant 3cd238ab1a feat(e2e): enable mTLS for Podman compute driver (#1430)
* feat(e2e): enable mTLS for Podman compute driver e2e harness

The Podman driver already supported mTLS certificate injection into
sandbox containers, but the e2e test harness explicitly blocked HTTPS
and ran plaintext-only gateways. Generate ephemeral PKI (CA, server,
client certs) with host.containers.internal in the server SANs, wire
guest_tls_* paths into the TOML config, replace --disable-tls with
--tls-cert/--tls-key/--tls-client-ca, and switch to mTLS gateway
registration. The health check remains on the plaintext health port.

Closes #1428

* refactor(e2e): extract shared PKI generation into gateway-common.sh

The Docker and Podman e2e scripts had near-identical 35-line PKI
generation blocks differing only in the host-gateway SAN. Extract
into e2e_generate_pki(pki_dir, host_alias) in gateway-common.sh,
which also eliminates the cd/cd pattern by using absolute paths
throughout.

* refactor(e2e): use generate-certs instead of raw openssl for PKI

Replace the hand-rolled openssl PKI generation in e2e_generate_pki()
with the gateway's built-in generate-certs --output-dir command. This
uses the same PKI generation as production deployments (Helm and RPM).

The helper now takes the gateway binary path and passes extra SANs
via --server-san (host.openshell.internal always, plus the driver's
host alias for Podman). The default SAN list already includes
host.docker.internal and localhost.

Update all cert path references to match the generate-certs output
layout (server/tls.{crt,key}, client/tls.{crt,key}) and remove the
openssl preflight checks from both gateway scripts.
2026-05-18 15:22:43 -07:00
mjamiv a54758cdaa test(sandbox): cover inference stream truncation errors (#1418) 2026-05-18 15:07:03 -07:00
John T. Myers dbba580e84 fix(security): refresh CI and gateway image dependencies (#1432)
Refresh the CI image tool pins so Go-built tools are rebuilt with patched Go releases and move the sandbox Python runtime to 3.14.5.

Rebase the gateway runtime to a pinned distroless Debian 13 image with glibc 2.41-12+deb13u3 while preserving the existing UID/GID 1000 runtime identity for upgrade compatibility. Update rustls-webpki to 0.103.13 and clarify Linux k3d guidance now that k3d is not installed through mise on Linux.

Signed-off-by: John Myers <9696606+johntmyers@users.noreply.github.com>
2026-05-18 14:25:08 -07:00
Drew Newberry f257ed0193 refactor(packaging): rely on gateway runtime defaults (#1415)
* fix(packaging): use gateway TOML config in packages

* refactor(packaging): rely on gateway runtime defaults
2026-05-18 14:13:19 -07:00
Derek Carr 7f16d60ef1 feat(persistence): implement optimistic concurrency control with CAS (#1292)
Add resource_version-based optimistic concurrency control to the
persistence layer. Every write now requires an explicit WriteCondition
(MustCreate or MatchResourceVersion), enforced at compile time by
gating unconditional put/put_message behind #[cfg(test)].

- Add WriteCondition enum and put_if for conditional writes
- Add update_message_cas for atomic read-modify-write operations
- Add list_messages/list_messages_with_selector helpers that hydrate
  resource_version from authoritative DB rows
- Convert all production write paths to CAS-aware methods
- Gate put/put_message behind #[cfg(test)] to prevent non-CAS writes
- Use structured PersistenceError::UniqueViolation matching instead of
  string matching for duplicate detection
- Hydrate resource_version from WriteResult directly on creates,
  eliminating unnecessary read-after-write round trips

Signed-off-by: Derek Carr <decarr@redhat.com>
2026-05-18 16:35:46 -04:00
Evan Lezar 555680cefc fix(docker): fall back to host arch for local builds (#1420)
(cherry picked from commit 392ab51f58fc18e03e721cdfa21f666091c67b80)

Signed-off-by: Evan Lezar <elezar@nvidia.com>
2026-05-18 14:24:36 -05:00
Derek Carr b4c7bc46e1 fix(sandbox): stabilize forked socket owner test (#1417)
Increase the polling deadline from 2s to 5s to tolerate loaded CI
machines, and add a ChildGuard drop guard so the forked child is always
reaped even if the test panics.

Signed-off-by: Derek Carr <decarr@redhat.com>
2026-05-18 09:57:34 -07:00
Adam MillerandTaylor Mutch 71209e6ac6 feat(rpm): replace init-pki.sh with openshell-gateway generate-certs (#1426)
* feat(rpm): replace init-pki.sh with openshell-gateway generate-certs

Cuts the RPM gateway over to the unified Rust certgen path. The systemd
user unit's first ExecStartPre now invokes:

  /usr/bin/openshell-gateway generate-certs --output-dir %S/openshell/tls

producing the same six-PEM layout init-pki.sh built (ca.{crt,key},
server/tls.{crt,key}, client/tls.{crt,key}) and the same CLI mTLS copy
under $XDG_CONFIG_HOME/openshell/gateways/openshell/mtls/. None of the
OPENSHELL_TLS_* / OPENSHELL_PODMAN_TLS_* paths in the unit change.

Adds host.containers.internal to the gateway's built-in SAN list so
podman containers reaching their host validate cleanly with no
per-deployment --server-san flag. Docker (host.docker.internal) and
Kubernetes (cluster.local DNS) were already covered.

Drops 197 lines of openssl shell, the install/file lines for the script
itself, and updates the docs (man page, RPM CONFIGURATION.md, env-file
generator comment) to point at the new entrypoint. The %S state dir,
unit security hardening, and consumer paths are untouched.

* docs(certgen): remove stale init-pki.sh references in comments

---------

Co-authored-by: Taylor Mutch <taylormutch@gmail.com>
2026-05-18 11:34:53 -05:00
Florent BENOIT 0cbd2d6c96 feat(cli): add -o json/yaml output format to sandbox list (#1422)
Support machine-readable output for `openshell sandbox list` via
`-o json` and `-o yaml` flags, matching the pattern established for
provider profile commands. Rename ProviderProfileOutput to shared
OutputFormat enum.

Signed-off-by: Florent Benoit <fbenoit@redhat.com>
2026-05-18 09:06:48 -07:00
Drew Newberry 09bd8a9c66 fix(vm): preserve guest TLS hostname (#1416) v0.0.43 2026-05-17 13:05:32 -07:00
Drew Newberry f819f7dcb5 fix(vm): restore sandboxes after gateway restart (#1407) 2026-05-15 15:08:59 -07:00
Drew Newberry 403c754842 fix(ci): skip helm plugin verification in CI image (#1411)
Signed-off-by: Drew Newberry <anewberry@nvidia.com>
2026-05-15 14:05:17 -07:00
Drew Newberry 910d3f09f2 feat(vm): boot sandboxes from ext4 root disks (#1263)
* feat(vm): boot sandboxes from ext4 root disks

* feat(vm): boot sandboxes with writable overlay disk

* fix(sandbox): improve sandbox provisioning behavior

* fix(vm): repair sandbox image provisioning

* fix(vm): clean up rebase fallout

* perf(vm): cache empty overlay images

* perf(vm): skip repeated sandbox ownership repair

* fix(vm): address lifecycle review issues

* fix(vm): repair gateway vm startup

* wip
2026-05-15 13:23:10 -07:00
Taylor MutchandDrew Newberry b61a98dbad feat(gateway): add TOML configuration file (RFC 0003) (#1317)
* feat(gateway): add TOML configuration file (RFC 0003)

Introduces an opt-in --config / OPENSHELL_GATEWAY_CONFIG flag that loads a
TOML file with gateway-wide settings and per-driver tables. Source
precedence is CLI > env > file > built-in default, implemented via clap's
ValueSource so existing flags and env vars keep their priority.

Driver crates (kubernetes, docker, podman, vm) now derive Deserialize on
their config structs. SupervisorSideloadMethod gains Deserialize with
kebab-case rename. A per-driver inheritance allowlist on the loader side
overlays [openshell.gateway] shared defaults (default_image,
supervisor_image, image_pull_policy, guest_tls_*, ssh_handshake_skew_secs,
client_tls_secret_name, host_gateway_ip, enable_user_namespaces) onto
each [openshell.drivers.<name>] table before deserialization.

The Helm chart renders a new gateway-config ConfigMap and mounts it at
/etc/openshell/gateway.toml. The migrated OPENSHELL_* env entries are
dropped from the StatefulSet — only the Secret-backed
OPENSHELL_SSH_HANDSHAKE_SECRET remains. database_url stays on --db-url.

Adds examples/gateway/gateway.example.toml and updates architecture/gateway.md
with the source precedence and inheritance rules.

* docs(gateway): drop ssh_handshake_skew_secs and ssh_handshake_secret from examples

Both fields are scheduled for removal. Remove the example values and the
env-only note so the gateway.toml example and the architecture doc stop
recommending settings that will not exist much longer.

* docs(rfc): correct OPENSHELL_CONFIG to OPENSHELL_GATEWAY_CONFIG in RFC 0003

* docs(gateway): add per-driver TOML example configurations

Adds focused single-driver examples next to the comprehensive
gateway.example.toml: kubernetes, docker, podman, and microvm. Each one
demonstrates the realistic settings for that driver plus how shared
[openshell.gateway] defaults inherit into the driver table.

A new unit test (`checked_in_examples_parse`) loads every example through
the config_file loader so schema drift fails CI rather than silently
shipping a broken example.

* refactor(gateway): drop image_pull_policy from shared inheritance

Kubernetes and Podman use mutually-incompatible vocabularies for the same
TOML key:

  - Kubernetes: `Always | IfNotPresent | Never` (free-form string passed
    verbatim to the K8s API).
  - Podman: `always | missing | never | newer` (strict lowercase enum
    deserialised into `ImagePullPolicy`).

No value means the same thing in both drivers. Sharing the key at
`[openshell.gateway]` scope and inheriting it into every active driver's
table meant any value safe for one driver was either wrong or silently
dropped for the other (`IfNotPresent` → `ImagePullPolicy::Missing` after
`.unwrap_or_default()`). Operators run one driver per gateway, so the
"shared default" never pays for itself.

Make `image_pull_policy` driver-local:

  - Remove the field from `GatewayFileSection` and from
    `inheritable_keys()` for both Kubernetes and Podman.
  - Drop the file→`RunArgs` merge for the gateway-scope key.
  - Stop unconditionally clobbering the driver value with
    `config.sandbox_image_pull_policy` in the runtime wiring — only apply
    the CLI/env override when it was set (and, for Podman, only when it
    parses into the lowercase enum).
  - Move the key under `[openshell.drivers.kubernetes]` and
    `[openshell.drivers.podman]` in every example, the RFC, the
    architecture doc, and the Helm-rendered gateway ConfigMap.

The supervisor pull policy follows the same shape: it is K8s-only and
moves into `[openshell.drivers.kubernetes]` alongside `image_pull_policy`
in the Helm template.

* fix(gateway): address review feedback on TOML configuration

Resolves the P1 and P2 issues raised in PR #1317:

- Helm gateway ConfigMap moves `grpc_endpoint` under
  `[openshell.drivers.kubernetes]` so the default install no longer fails
  the gateway's `deny_unknown_fields` schema check.
- `kubernetes_config_from_file` and `podman_config_from_file` only let
  the gateway-wide CLI/env `grpc_endpoint` overwrite the driver-table
  value when it was actually supplied, preserving file-only configs.
- Kubernetes driver default `image_pull_policy` is now empty (was Podman
  vocabulary "missing"), so default deployments let the Kubernetes API
  apply its own policy instead of being rejected.
- New `disable_tls` gateway field plumbs `.Values.server.disableTls`
  through the TOML ConfigMap instead of relying on env vars dropped from
  the StatefulSet.
- StatefulSet pod template now carries a `checksum/gateway-config`
  annotation so `helm upgrade` rolls pods when the ConfigMap changes.
- Auxiliary listener resolution preserves the full `SocketAddr` from
  `health_bind_address` / `metrics_bind_address`, so a loopback-pinned
  health port is not silently relocated onto the public bind address.
- `ssh_session_ttl_secs` from the file is now applied to `Config` (it
  was previously accepted by the loader but never read).

New regression coverage: cli-level merge tests for the new fields plus
helm-unittest assertions for the ConfigMap shape, checksum annotation,
and `disable_tls` rendering.

* docs(gateway): consolidate gateway TOML examples into docs reference

Replaces the per-driver example files under examples/gateway/ with a
single published reference page at docs/reference/gateway-config.mdx
covering source precedence, layout, the full example, and the four
per-driver examples (Kubernetes, Docker, Podman, microVM). Drew flagged
during PR #1317 review that the examples belong with the user-facing
docs rather than in a sibling examples/ directory.

The cross-references in architecture/gateway.md and RFC 0003 are updated
to point at the new docs page; the round-trip test in config_file.rs is
removed (schema coverage stays on the inline parses_full_example test
and per-field merge tests — doc-snippet drift belongs in a separate
docs-lint, not in a cross-tree Rust unit test).

* refactor(core): move DEFAULT_K8S_NAMESPACE into K8s driver

The constant is Kubernetes-specific (used only by KubernetesComputeConfig's
Default impl) and does not belong in openshell-core. Relocate it to the
driver crate that owns the K8s vocabulary; openshell-core retains only
truly cross-cutting defaults.

* refactor(core): move Podman bridge default into Podman driver

DEFAULT_NETWORK_NAME is Podman vocabulary, consumed only by the Podman
driver. Also drops the unused DEFAULT_IMAGE_PULL_POLICY constant.

* docs(auth): scrub remaining SSH handshake secret references

Sweeps the trailing mentions left after the rebase: the gateway
config-file module doc, the Helm gateway-config ConfigMap header,
the gateway-config.mdx env-only note, and the RPM systemd unit
comment for init-gateway-env.sh.

* docs(gateway): clarify OPENSHELL_GRPC_ENDPOINT applies to all drivers

The previous comment implied the callback endpoint was Kubernetes-only,
but the value is propagated to every compute driver (Kubernetes, Docker,
Podman, VM) and must be reachable from wherever the sandbox runs.

* refactor(gateway): move driver options into config (#1394)

Signed-off-by: Drew Newberry <anewberry@nvidia.com>

* fix(e2e): regenerate gateway config via TOML for docker + podman harnesses

The gateway CLI flags moved into TOML config tables in 560550d2 (#1394),
which made every existing e2e/with-{docker,podman}-gateway.sh invocation
fail with "unexpected argument '--sandbox-namespace'" (and a long tail
of similar driver-specific options) before the gateway could even bind.

Replace the obsolete CLI flags with a synthesized
`[openshell.drivers.<driver>]` table written to `${STATE_DIR}/gateway.toml`
and passed via the new `--config` flag. Only the gateway-wide flags
that survived 560550d2 (bind-address, port, drivers, db-url, tls-*,
disable-tls, log-level, health-port) stay on the command line.

Both scripts get a small `toml_string` helper to properly TOML-quote
the values (the previous `%q` printf format produced bash-escape, not
TOML-escape). The Docker harness also corrects two field names that
diverged from the driver schema: `docker_network_name` →
`network_name`, and the supervisor binary/image plumbing now reads
through to `supervisor_bin` / `supervisor_image` in the same table.

The Podman harness drops `--ssh-gateway-port` (deleted in 560550d2 —
gRPC + SSH are multiplexed on the same port now) and substitutes
`network_name` + `gateway_port` for the obsolete `--sandbox-namespace`
(which the Podman driver never had as a typed field).

* fix(core): swap bind-only 0.0.0.0 SSH gateway host for cluster URL host

CLI's resolve_ssh_gateway treated 0.0.0.0 as a loopback "keep as-is"
when the cluster URL was also loopback, so the SSH proxy connected to
0.0.0.0:port. The unspecified address is never a valid connect target
and is not present in any TLS cert SAN, which produced BadCertificate
TLS handshake failures during `openshell sandbox create -- ...` in
docker/podman e2e (e.g. bypass_detection).

Resolution: when the server returns 0.0.0.0 or :: as the gateway host
and both endpoints are loopback, fall back to the cluster URL's host
(which the CLI is already using to reach the gateway, so it must
resolve and match the cert).

* fix(e2e): repair podman harness on macOS

Podman 5.x with the applehv/libkrun provider no longer creates the legacy
~/.local/share/containers/podman/machine/podman.sock symlink, and
`podman system service` is a Linux-only subcommand — the macOS client
delegates the API service to the VM. Both assumptions in the harness
were stale, so the script tried to start a temporary service that podman
rejected with "unknown flag: --time".

- Discover the macOS socket via `podman machine inspect` instead of the
  hardcoded path.
- On Darwin, fail fast with a "start podman machine" message rather than
  attempting the Linux-only `podman system service` fallback.
- Write socket_path into [openshell.drivers.podman] so the in-process
  driver picks up the discovered socket; the driver reads TOML only
  after the config refactor (560550d2), so OPENSHELL_PODMAN_SOCKET alone
  was no longer enough.

* fix(server): use clone_from for TLS client CA assignment

clippy 1.95.0 rejects assigning the result of `Clone::clone()` to an
existing variable under `-D warnings` (`assigning_clones`). Switch to
`clone_from(&...)` to satisfy the lint and avoid the redundant
allocation.

---------

Signed-off-by: Drew Newberry <anewberry@nvidia.com>
Co-authored-by: Drew Newberry <anewberry@nvidia.com>
2026-05-15 12:43:48 -07:00
Mesut Oezdil 283defd285 fix(sandbox): allow HEAD where GET is permitted in L7 policy (#1382)
* fix(sandbox): allow HEAD where GET is permitted in L7 policy

RFC 9110 §9.3.2 defines HEAD as identical to GET except no response body.
method_matches in sandbox-policy.rego rejected HEAD even when GET was
explicitly allowed, breaking package managers that probe with HEAD before
fetching.

Signed-off-by: mesutoezdil <mesudozdil@gmail.com>

* test(sandbox): fix binary mismatch in HEAD/POST test and add deny-rule regression

l7_head_denied_when_only_post_allowed used /bin/sh in the fixture but
l7_input() sends /usr/bin/curl, so requests were denied by binary policy
before the method rule could be evaluated. A regression allowing HEAD to
match POST would have passed undetected.

Fixes the fixture binary and adds l7_head_blocked_by_deny_rule_targeting_get
to cover the deny_rules path of method_matches(), per review feedback.

Signed-off-by: mesutoezdil <mesudozdil@gmail.com>

---------

Signed-off-by: mesutoezdil <mesudozdil@gmail.com>
2026-05-15 12:25:07 -07:00