docs(sdk): clarify extension and E2E setup

Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
This commit is contained in:
Piotr Mlocek
2026-08-21 09:27:39 -07:00
parent bd7016ff84
commit 2ece12f1ae
5 changed files with 27 additions and 17 deletions
-4
View File
@@ -87,10 +87,6 @@ rotates their in-memory slots without rebuilding clients, and publishes the
public verification key at `/.well-known/jwks.json` alongside OIDC-shaped
discovery metadata at `/.well-known/openid-configuration`. HTTPS extensions can
pin an operator-provided CA while retaining endpoint-hostname verification.
Rust extension services use the opt-in `openshell-sdk::extension` surface to
verify these credentials from operator-provisioned Ed25519 public keys or JWKS
documents and normalize gateway and supervisor caller identities. Key refresh
remains a service deployment concern.
Extension credentials reuse the sandbox signing key and are separated from
sandbox-to-gateway admission tokens by exact audience and by an explicit
+6 -9
View File
@@ -49,19 +49,16 @@ mTLS (client certificates) is not supported.
SDK-shaped enums rather than raw proto integers. Failures map to a typed
`SdkError` with a discriminable kind.
Enable the `extension` Cargo feature when implementing an operator-run
OpenShell extension service:
Enable the `extension` Cargo feature when building an OpenShell extension
service:
```toml
openshell-sdk = { version = "...", features = ["extension"] }
```
The `extension` module verifies gateway-minted EdDSA extension JWTs from a
trusted public key or JWKS document. `GatewayJwtAuthenticator` pins the token
type, algorithm, issuer, audience, lifetime, and caller identity shape, then
returns an `AuthenticatedCaller` that service handlers can authorize by caller
kind. This feature is opt-in so gateway-only SDK consumers do not compile the
extension verification dependencies.
The `extension` module provides common SDK building blocks for extension
services. It currently handles caller verification; shared service interfaces
and observability support will follow.
## Modules
@@ -74,7 +71,7 @@ extension verification dependencies.
| `oidc` | OIDC token handling at the transport layer. |
| `refresh` | `Refresh` trait and single-flight refresh coalescing. |
| `edge_tunnel` | Cloudflare Access tunnel dialer. |
| `extension` | Opt-in service-side authentication and identity types for operator-run extensions. |
| `extension` | Opt-in SDK building blocks for extension services. |
| `error` | `SdkError` taxonomy. |
| `types` | Curated request/response types and proto conversions. |
| `raw` | Escape hatch re-exporting the generated tonic clients. |
+3 -3
View File
@@ -1,10 +1,10 @@
// SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
//! APIs for implementing operator-run `OpenShell` extension services.
//! APIs for building `OpenShell` extension services.
//!
//! Enable the `extension` Cargo feature to verify gateway-minted extension
//! credentials and authorize callers without depending on gateway internals.
//! This feature is the home for shared extension interfaces, verification, and
//! observability. It currently provides gateway-minted credential verification.
mod verification;
+1 -1
View File
@@ -90,7 +90,7 @@ Return your expected audience in the `expected_audience` field of your `Describe
Provision the trusted gateway URL, expected gateway ID, and public key or JWKS through the deployment. This operator-provisioned key material is the authoritative cold-start trust anchor. The expected issuer is exactly `openshell-gateway:<gateway_id>`; fetching JWKS does not establish that identity by itself. After initial trust is established, `GET /.well-known/openid-configuration` and its `jwks_uri` provide steady-state key refresh and operational convenience. The document is OIDC-shaped rather than OIDC-compliant: `issuer` is the gateway identity, not the URL serving the document, so compare `iss` against the configured value and fetch updates only over authenticated TLS at the trusted gateway URL.
Rust services can enable the `extension` Cargo feature on `openshell-sdk` and use `openshell_sdk::extension::GatewayJwtAuthenticator`. Construct it from a trusted Ed25519 public key and `kid`, or from a trusted JWKS document, then call `authenticate_request` on each tonic request. The returned `AuthenticatedCaller` exposes the normalized caller kind, subject, and optional sandbox ID for RPC-specific authorization. Key refresh remains deployment-owned; rebuild the authenticator after accepting a refreshed key set from the trusted gateway URL.
Rust services can enable the `extension` Cargo feature on `openshell-sdk` for common extension interfaces, verification, and observability. Caller verification is available today through `openshell_sdk::extension::GatewayJwtAuthenticator`; additional SDK support will follow.
Cache keys by `kid`. Validate, at minimum:
+17
View File
@@ -279,6 +279,9 @@ connect_current_container_to_docker_network() {
GATEWAY_HOST_ALIAS_IP="${container_ip}"
}
# Build and launch an authenticated TLS extension service that is reachable
# from the host gateway and sandbox containers. The focused middleware E2E lane
# opts into this fixture; other Docker E2E callers retain the existing setup.
start_middleware_fixture() {
local manifest="${ROOT}/e2e/supervisor-middleware-fixture/Cargo.toml"
local binary="${TARGET_DIR}/debug/openshell-e2e-middleware-fixture"
@@ -290,8 +293,12 @@ start_middleware_fixture() {
CARGO_TARGET_DIR="${TARGET_DIR}" cargo build --locked --manifest-path "${manifest}"
if [ -n "${GATEWAY_HOST_ALIAS_IP}" ]; then
# In GitHub Actions, the fixture and gateway run in the job container. The
# wrapper has already attached that container to the sandbox network.
fixture_host="${GATEWAY_HOST_ALIAS_IP}"
elif [ "$(uname -s)" = "Linux" ]; then
# In a native Linux run, the fixture and gateway run on the host. The
# bridge gateway address is reachable from both the host and containers.
fixture_host="$(docker network inspect \
--format '{{(index .IPAM.Config 0).Gateway}}' \
"${DOCKER_NETWORK_NAME}")"
@@ -306,6 +313,8 @@ start_middleware_fixture() {
fi
mkdir -p "${fixture_dir}"
# Generate a one-run CA and a CA-signed server certificate whose IP SAN
# matches the shared address used by the gateway and sandbox supervisor.
openssl req -x509 -newkey rsa:2048 -nodes \
-keyout "${fixture_dir}/ca.key" \
-out "${fixture_dir}/ca.crt" \
@@ -335,6 +344,8 @@ start_middleware_fixture() {
fixture_port="$(e2e_pick_port)"
echo "Starting supervisor middleware E2E fixture on ${fixture_host}:${fixture_port}..."
# Give the fixture both sides of the trust relationship: its TLS identity and
# the gateway identity/audience required to verify extension bearer tokens.
OPENSHELL_E2E_MIDDLEWARE_LISTEN="0.0.0.0:${fixture_port}" \
OPENSHELL_E2E_MIDDLEWARE_TLS_CERT="${fixture_dir}/tls.crt" \
OPENSHELL_E2E_MIDDLEWARE_TLS_KEY="${fixture_dir}/tls.key" \
@@ -345,6 +356,8 @@ start_middleware_fixture() {
"${binary}" >"${MIDDLEWARE_FIXTURE_LOG}" 2>&1 &
MIDDLEWARE_FIXTURE_PID=$!
# A TCP readiness probe is sufficient here: the gateway performs the
# authenticated gRPC Describe call when it starts immediately afterward.
local attempt
for attempt in $(seq 1 100); do
if ! kill -0 "${MIDDLEWARE_FIXTURE_PID}" 2>/dev/null; then
@@ -587,6 +600,8 @@ echo "Starting openshell-gateway on port ${HOST_PORT} (namespace: ${E2E_NAMESPAC
echo "Using sandbox image: ${SANDBOX_IMAGE} (pull policy: ${SANDBOX_IMAGE_PULL_POLICY})"
e2e_generate_gateway_jwt "${JWT_DIR}"
if [ "${OPENSHELL_E2E_MIDDLEWARE_FIXTURE:-0}" = "1" ]; then
# The gateway validates middleware with Describe during startup, so the
# extension service and its trust material must exist first.
start_middleware_fixture
fi
@@ -615,6 +630,8 @@ GATEWAY_CONFIG="${STATE_DIR}/gateway.toml"
fi
fi
if [ -n "${MIDDLEWARE_FIXTURE_ENDPOINT}" ]; then
# Register only the opt-in fixture, pinning its ephemeral CA and the exact
# audience that both the gateway token issuer and fixture verifier expect.
printf '[[openshell.supervisor.middleware]]\n'
printf 'name = "e2e-scripted"\n'
printf 'grpc_endpoint = %s\n' "$(toml_string "${MIDDLEWARE_FIXTURE_ENDPOINT}")"