fix(security): authenticate extension services - #2638
Draft
pimlock wants to merge 6 commits into
Draft
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>
pimlock
force-pushed
the
feat/2623-extension-auth/pimlock
branch
from
August 7, 2026 21:19
6763e56 to
4dc58b4
Compare
Collaborator
Author
|
/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>
Collaborator
Author
|
/ok to test 32f93e1 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Authenticate remote supervisor middleware and gateway-interceptor RPCs with short-lived, exact-audience gateway-signed JWTs. Add a shared extension client foundation so both mechanisms use the same bearer rotation and TLS/custom-CA implementation.
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 caller identity and policy-constrained token distribution while preserving the existing Ed25519 signing authority and sandbox refresh API.
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.RefreshSandboxToken, held in a supervisor-owned store that rotates slots in place./.well-known/jwks.jsonand/.well-known/openid-configuration.Describemanifest at startup rather than relying on configuration matching on both sides of the boundary.allow_insecure_transportper registration for deployments that keep a plaintext endpoint.Review notes
Compatibility. With
gateway_jwtconfigured, extension endpoints usehttps://; interceptors may also useunix://, middleware may not, because a middleware endpoint has to be reachable from every sandbox supervisor. Deployments that keep a plaintext endpoint setallow_insecure_transport = trueon that registration. OpenShell then attaches no credential, refuses to mint one if a supervisor asks, and names the registration in a warning at every gateway startup.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 ~120 lines of policy composition and backfill side effects on a security-critical path, where drift fails open.RFC layout. Extension-authentication detail lives in
appendices/extension-authentication.mdunder RFC 0009 (the shared contract) and RFC 0010 (interceptor differences), leaving both accepted RFC bodies intact. RFC 0010's appendix is currently reachable only by path, since its body has no appendix reference to link from.Out of scope: the service-side verification SDK (tracked separately; Rust and Python first), a separate signing key per trust domain, and mTLS. Residual risks are recorded in the RFC 0009 appendix.
Testing
mise run pre-commitpassesChecklist
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+jwtand returnexpected_audiencefromDescribe.Follow-up: JWKS bootstrap
Verification keys are served once the gateway is up, so a service's first contact still depends on out-of-band provisioning.
/.well-known/openid-configurationcovers discovery for services that can reach the gateway. The open question is whether to bind the primary listener early and serve only the well-known routes until ready, or to treat operator provisioning of the public key that certgen already writes to disk as the supported bootstrap and leave both endpoints as convenience. The latter matches how Kubernetes handles the same problem and avoids the listener-swap machinery.