fix(security): authenticate extension services - #2638
Conversation
Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
|
Auto-sync is disabled for draft pull requests in this repository. Workflows must be run manually. Contributors can view more details about this message here. |
Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
This should become an extension SDK package. Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
6763e56 to
4dc58b4
Compare
|
/ok to test 4dc58b4 |
|
🌿 Preview your docs: https://nvidia-preview-pr-2638.docs.buildwithfern.com/openshell |
Follow-up hardening on the alpha extension authentication mechanism. Claim contract: - Extension tokens carry an explicit `typ` of `openshell-ext+jwt`. They share a signing key with sandbox-to-gateway admission tokens and were otherwise separated by audience alone, so a verifier that neglects to check `aud` could accept a gateway credential. The header is a second, independent discriminator. - Publish OIDC-shaped discovery at `/.well-known/openid-configuration` so a service configured with only the gateway URL can learn the exact expected issuer and the JWKS location. It is shaped, not compliant: `issuer` is the gateway identity, not the serving URL. Audience agreement: - `MiddlewareManifest` and `InterceptorManifest` gain `expected_audience`. The audience is otherwise configured independently on each side of the boundary, where a mismatch surfaces only as an opaque authentication failure on every call. OpenShell now compares the two and fails at startup. An empty field keeps the check off for existing services. Compatibility: - Add `allow_insecure_transport` per registration. Enabling gateway JWT signing previously made any plaintext endpoint a hard startup failure, including the endpoint form used in our own documentation. The opt-out attaches no credential, is refused by the gateway if a supervisor asks for one, and warns at every startup. - Make the transport requirement kind-aware. A middleware endpoint must be reachable from every sandbox supervisor, so only interceptors may use a gateway-local Unix socket. Credential lifecycle: - Replace the process-global slot map with a supervisor-owned `ExtensionCredentialStore` shared explicitly across the gateway connections the supervisor opens, removing test-order coupling. - Rotate only when a credential is missing or has passed four fifths of its lifetime. Configuration polling ran every ten seconds against fifteen-minute credentials, so each poll re-ran gateway effective-policy resolution and re-minted the gateway token. - Bound credential minting per sandbox, since each request resolves the caller's effective policy. Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
Restore the RFC 0009 and 0010 bodies to their accepted text and move every extension-authentication update into appendices instead. An RFC records a decision at a point in time; superseding detail belongs alongside it rather than rewritten into it. RFC 0009's appendix carries the shared contract: claims, authorization, key distribution, the `allow_insecure_transport` replacement for the body's `allow_insecure`, and residual risks. RFC 0010's records only what differs for interceptors and links to it. The existing protocol-extensions appendix, which parked the phase 2 transport question, now points forward to what was built. Also document the audience handshake, the discovery endpoint, the `typ` requirement, and `jti` replay guidance in the extensibility and gateway configuration pages, and correct the middleware transport guidance: middleware endpoints must be reachable from sandbox supervisors, so Unix sockets are not an option there. Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
|
/ok to test 32f93e1 |
|
On the JWKS bootstrap follow-up: operator provisioning the key seems the better default. Binding the listener early to serve only well-known paths adds a readiness state whose only job is to hand out material an out-of-band channel already has to deliver for a cold start — the first-contact service still needs something before the gateway is reachable. Treating the certgen-written key as the supported bootstrap keeps one trust path instead of two. The well-known endpoints are still worth keeping as the steady-state convenience for services that can already reach the gateway; the question is only which one is authoritative for first contact. |
Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
pimlock
left a comment
There was a problem hiding this comment.
gator-agent
PR Review Status
Validation: Project-valid maintainer-authored implementation of linked validated issue #2623.
Head SHA: ca8cbf4667dc58b9b3ce7a65c581775baf1b815e
Base SHA: cd4d90579c542ef5b35c74eaad51acc896c53775
Merge base SHA: cd4d90579c542ef5b35c74eaad51acc896c53775
Patch ID: a39b0015ff6d36e22d1b5b15607b59ff365dd314
Gator payload: 5
Review mode: initial
Previous reviewed SHA: none
Review budget exhausted: no
Maintainer decision required: no
Blocking findings:
GATOR-ca8cbf46-01: no-signer gateways regress from supported unauthenticated supervisor middleware to a permanently degraded registry.GATOR-ca8cbf46-02: the governance interceptor example has a malformedInterceptorManifestliteral and cannot compile.
Carried findings:
- None
Non-blocking suggestions:
- Update
debug-openshell-clusterto inspect and diagnose the new extension-authentication fields and failure modes.
Docs: Fern docs and gateway configuration references are updated for the direct UX changes.
Next state: gator:in-review
Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
|
@lbelyaev Agreed, and I incorporated this direction in |
pimlock
left a comment
There was a problem hiding this comment.
gator-agent
PR Review Status
Thanks @pimlock. I checked the latest author delta against the two prior findings: the advertised extension-authentication capability restores the no-signer legacy connector path, and the governance interceptor manifest literal is now well-formed with dedicated check/format coverage.
Validation: Project-valid maintainer-authored implementation of linked validated issue #2623.
Head SHA: dfe26f88282f9a14a4272a773a69303da22f05a6
Base SHA: cd4d90579c542ef5b35c74eaad51acc896c53775
Merge base SHA: cd4d90579c542ef5b35c74eaad51acc896c53775
Patch ID: def29f76fb406d6530e0e7ba7f011ac84a6367ca
Gator payload: 5
Review mode: follow_up
Previous reviewed SHA: ca8cbf4667dc58b9b3ce7a65c581775baf1b815e
Review budget exhausted: no
Maintainer decision required: no
Blocking findings:
- No blocking findings remain.
Carried findings:
GATOR-ca8cbf46-01: resolved by the explicit gateway capability and legacy unauthenticated connector selection.GATOR-ca8cbf46-02: resolved by movingexpected_audienceinside the manifest literal and adding example checks.
Docs: Fern docs, gateway configuration references, architecture notes, and RFC appendices cover the direct UX and trust-bootstrap changes.
Test dispatch: test:e2e is required because this changes gateway/supervisor interaction and extension credential flow, but applying that label was not authorized for this cycle. No workflow was rerun and no /ok to test command was posted.
Next state: gator:blocked
Blocked reason: test_dispatch_required
Next action: the operator must separately authorize applying test:e2e; after the required workflow is actually queued, running, or complete, gator can enter pipeline watch.
|
Label |
Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
pimlock
left a comment
There was a problem hiding this comment.
gator-agent
PR Review Status
Thanks @drew, I checked the token-confusion path you identified at the sandbox authentication boundary. Thanks @pimlock, I reviewed the follow-up delta that rejects extension-typed JWTs before authenticator fallthrough, reserves the gateway sandbox audience from extension minting, and adds targeted regressions for both invariants. The concern is resolved on this head.
Validation: Project-valid maintainer-authored implementation of linked validated issue #2623.
Head SHA: 5569ccac4cd8e735f11ff8dea7f5e348c2fbc2f9
Base SHA: cd4d90579c542ef5b35c74eaad51acc896c53775
Merge base SHA: cd4d90579c542ef5b35c74eaad51acc896c53775
Patch ID: 1aa823e573b0294b3a85ea39fd96b90e04573cc1
Gator payload: 5
Review mode: follow_up
Previous reviewed SHA: dfe26f88282f9a14a4272a773a69303da22f05a6
Review budget exhausted: no
Maintainer decision required: no
Blocking findings:
- No blocking findings remain.
Carried findings:
GATOR-ca8cbf46-01: remains resolved by the explicit gateway capability and legacy unauthenticated connector selection.GATOR-ca8cbf46-02: remains resolved by the corrected manifest literal and dedicated example checks.
Trusted maintainer feedback:
- @drew's sandbox-token confusion concern is resolved by the current author delta.
Docs: Fern docs, gateway configuration references, architecture notes, and RFC appendices cover the direct UX and trust-bootstrap changes.
Test dispatch: test:e2e is present, and the required current-head Branch Checks and E2E workflows are queued or running. No workflow was rerun and no /ok to test command was posted by gator.
Next state: gator:watch-pipeline
Monitoring CompleteMonitoring is complete because this PR has merged. Head SHA: Final status: the PR reached I removed the active |
Summary
Establish the first extension-authentication contract for OpenShell. Remote supervisor middleware and gateway-interceptor RPCs receive short-lived, exact-audience gateway-signed JWTs through a shared extension client foundation.
This is intentionally an alpha integration. It wires the mechanism into middleware and interceptors now, while keeping the identity, claims, credential rotation, and transport contracts reusable by future extension points.
Related Issue
Closes #2623
Security Fix
Remote extension services previously could not cryptographically distinguish OpenShell gateway or sandbox-supervisor calls from direct network callers. This change adds authenticated OpenShell caller identity and policy-constrained token distribution while preserving the existing Ed25519 signing authority and sandbox refresh API.
These tokens identify the calling OpenShell gateway or sandbox supervisor. They do not represent an end user, delegated user authority, or durable agent identity.
Severity Assessment
Changes
openshell-extension-core, a shared foundation for extension identity, claims, bearer rotation, and TLS/custom-CA transport used by both supervisor middleware and gateway interceptors.ExtensionKindnon-exhaustive so future extension points can join the shared model without breaking downstream consumers. New kinds still require explicit registration, authorization, and transport wiring before OpenShell accepts them.RefreshSandboxToken, held in a supervisor-owned store that rotates slots in place./.well-known/jwks.jsonand/.well-known/openid-configuration.expected_audiencevalue toDescribemanifests as a post-authentication consistency assertion. Operator configuration remains authoritative, and a strict verifier may reject a wrong audience before returning the manifest.allow_insecure_transportper registration for deployments that keep a plaintext endpoint.Current alpha coverage
Describe,Evaluate, and provider-profile snapshot RPCs.expected_audiencedoes not bootstrap or discover the audience. It can detect configuration drift only after authenticatedDescribesucceeds.jtiidentifies that token instance for correlation and future explicit revocation; rejecting repeatedjtivalues would reject legitimate requests.Review notes
Compatibility. The gateway now advertises extension-auth capability to supervisors. Without
gateway_jwt, supervisors keep the legacy unauthenticated connector and never request extension credentials. With signing configured, credential or authenticated-connection failures remain fail closed. Authenticated network endpoints usehttps://;allow_insecure_transport = trueremains the explicit per-registration plaintext opt-out.Rotation cadence. Configuration polling runs every 10s while credentials last 15 minutes. Supervisors reuse their installed slots and rotate only when one is missing or has passed four fifths of its lifetime, so a routine poll does not re-run gateway effective-policy resolution or re-mint the gateway token.
Authorization resolution.
handle_get_sandbox_configis the single resolver deciding which registrations a sandbox may hold credentials for. A cheaper parallel resolver would duplicate policy composition and backfill side effects on a security-critical path, where drift could fail open.RFC layout. Extension-authentication detail lives in
appendices/extension-authentication.mdunder RFC 0009 for the shared contract and RFC 0010 for interceptor differences, leaving both accepted RFC bodies intact.Follow-ups
Testing
mise run pre-commitpasses, including workspace and E2E strict Clippy checksmise run testpasses, including no-signer compatibility and signer-enabled fail-closed regression coveragemise run go:proto:checkpassesChecklist
Example branch
The runnable authenticated middleware example is intentionally kept on the separate
2623-authenticated-middleware-example/pimlockbranch. Its service-side JWT verifier is a dedicated example module marked for future extraction into an extension-building SDK, keeping inbound verification concerns out ofopenshell-extension-core. After this PR lands, the branch can be rebased and merged separately or folded into later work. Its verifier must requiretyp: openshell-ext+jwt, validate the configured exact audience before dispatch, and may returnexpected_audiencefromDescribeas a post-authentication consistency assertion.