Skip to content

feat(providers): gateway-minted OCI principal refresh strategies - #3975

Draft
fede-kamel wants to merge 4 commits into
NVIDIA:mainfrom
fede-kamel:feat/oci-principal-refresh
Draft

fede-kamel wants to merge 4 commits into
NVIDIA:mainfrom
fede-kamel:feat/oci-principal-refresh

Conversation

@fede-kamel

Copy link
Copy Markdown
Contributor

Stacked on #3962: the first three commits are that PR unchanged; review fda2e9466 for this change. Draft until #3962 lands, then it rebases to a single commit.

Summary

Add three gateway-minted credential refresh strategies for Oracle Cloud Infrastructure principals: oci_instance_principal, oci_resource_principal, and oci_oke_workload_identity. Each one mints OCI_KEY_ID as an ST$<security token> and co-mints OCI_PRIVATE_KEY as the paired session key through additional_outputs, so a sandbox bound to the provider signs OCI requests through credential_signing: oci without any long-lived key existing anywhere. This is the OCI counterpart of aws_sts_assume_role (#1782): the gateway host's own platform identity becomes the credential source.

Related Issue

Closes #3961. Phase 3 of the OCI integration; depends on #3962 (credential_signing: oci), which contributes the signer this branch is stacked on. The bearer-only oci-genai profile landed in #3904.

Changes

  • crates/openshell-core/src/oci_signature.rs (new): the OCI signing primitives shared by the proxy and the gateway. OciSigningKey, sign_headers over a SigningInput (with host optional so the gateway can sign federation requests the way the OCI SDKs do), SessionKeyPair::generate (RSA-2048 PKCS#8), sanitize_pem, security_token_expiry_ms (JWT exp), and imf_fixdate. crates/openshell-supervisor-network/src/oci_signature.rs becomes the HTTP framing layer over it; the proxy behaviour and tests from feat(sandbox): proxy-side OCI request signing for CONNECT tunnels #3962 are unchanged.
  • proto/openshell.proto: enum values 7–9 on ProviderCredentialRefreshStrategy.
  • crates/openshell-server/src/provider_refresh.rs: the three mint functions.
    • Instance principal: reads the leaf certificate, key, and intermediate from the instance metadata service (Authorization: Bearer Oracle), computes the SHA-1 fingerprint, and posts a request signed under <tenancy>/fed-x509/<fingerprint> to https://auth.<region>.<realm_domain>/v1/x509 carrying the sanitized certificate, the ephemeral session public key, and an optional purpose.
    • Resource principal: re-reads the platform-rotated session token and key (rpst_file/private_key_file, rpst/private_key, or the OCI_RESOURCE_PRINCIPAL_* environment variables) on every refresh and republishes them in the single-line form the proxy accepts. Passphrase-protected keys are rejected with a configuration error.
    • OKE workload identity: exchanges the projected service-account token at https://<KUBERNETES_SERVICE_HOST>:12250/resourcePrincipalSessionTokens (TLS pinned to the projected ca.crt) for a token bound to the session public key; a 403 names the OKE enhanced-cluster requirement.
    • Expiry is the token's exp capped by max_lifetime_seconds. Endpoint failures map to fix_configuration for 401/403/4xx and to retryable for everything else. Identity endpoint overrides exist only under #[cfg(test)] and only for loopback, mirroring sts_endpoint_url; the configure API rejects metadata_base_url, federation_endpoint_url, and proxymux_endpoint_url material (CWE-918).
  • crates/openshell-providers/src/profiles.rs: YAML names, mintable-strategy set, is_oci_principal_strategy, output spec (private_key required), and the pinned env keys OCI_KEY_ID/OCI_PRIVATE_KEY.
  • CLI (--strategy oci-instance-principal|oci-resource-principal|oci-oke-workload-identity), TUI display names, Go SDK constants and converters, regenerated Go protobuf bindings with the pinned protoc-gen-go.
  • providers/oci-instance-principal.yaml, providers/oci-resource-principal.yaml, providers/oci-oke-workload-identity.yaml: endpointless example profiles that pair with credential_binding and credential_signing: oci, with setup notes in the header.
  • Docs: strategy table, CLI accepted values, material keys, and kebab-case mapping in docs/how-it-works/providers/profiles.mdx.

Testing

  • Core unit tests (openshell_core::oci_signature): signing string with and without host, body header set for POST/PUT/PATCH, PEM helpers, session key generation, JWT exp extraction, IMF-fixdate. The supervisor oci_signature and l7::rest tests from feat(sandbox): proxy-side OCI request signing for CONNECT tunnels #3962 pass unchanged over the shared module.
  • Gateway mint tests (provider_refresh, wiremock): instance principal against a mock metadata service and federation endpoint with an rcgen RSA certificate, asserting the <tenancy>/fed-x509/<fingerprint> key id, the host-less header list, the sanitized certificate and intermediate, the optional purpose, an ephemeral session key distinct from the instance key, and expiry at the token exp; federation 401 maps to PERMISSION_DENIED. OKE workload identity against a mock proxymux, asserting the Bearer service-account token, opc-request-id, a podKey equal to the session key's SPKI, the base64 envelope decode, and expiry capped at max_lifetime_seconds; 403 names the enhanced-cluster requirement; an unreadable token file is INVALID_ARGUMENT. Resource principal from temp files republishes the token and the key in the single-line form the proxy accepts; passphrase keys and a missing token are rejected. A refresh state without the pinned private_key output fails with FAILED_PRECONDITION. Every minted pair loads in OciSigningKey::from_credentials.
  • Configure API tests: oci-instance-principal configures with empty material and pins private_key to OCI_PRIVATE_KEY; metadata_base_url, federation_endpoint_url, and proxymux_endpoint_url material are rejected for the instance and OKE strategies.
  • Provider profile tests: YAML round trip, mintable set, pinned env keys, output spec, and the three example profiles parse and allow --runtime-credentials. Provider listing and example-catalog validation include the new ids. The public/durable schema inventory test is re-pinned for the additive enum values with the review note in storage_proto.rs.
  • Full suites: openshell-server (1874), openshell-supervisor-network (1432), openshell-core (515), openshell-providers (137), CLI and TUI. Go SDK go build ./... && go test ./... with bindings regenerated by the pinned protoc-gen-go 1.36.11.
  • cargo fmt --all --check; cargo clippy --all-targets -D warnings on core, supervisor-network, providers, server, CLI, and TUI; license headers; markdownlint; fern check.
  • openshell provider profile lint on the three new profiles (and the three from feat(sandbox): proxy-side OCI request signing for CONNECT tunnels #3962) against a gateway and CLI built from this branch: all pass. openshell provider refresh configure --help lists the three strategies.
  • Live mint on an OCI Compute instance, OKE enhanced cluster, or Functions host: not run; the development gateway runs on a laptop. The request and response shapes follow the OCI SDK reference signers and are pinned by the mock tests above. The ST$ token plus session key transport these strategies produce was verified live in feat(sandbox): proxy-side OCI request signing for CONNECT tunnels #3962 with a security-token principal.
  • E2E tests added to the repository: not in this PR.

Checklist

Add credential_signing: oci, a sibling of the SigV4 modes. When an endpoint
sets it, the sandbox proxy strips the client's signing headers, resolves
OCI_KEY_ID and OCI_PRIVATE_KEY from the endpoint-bound provider, and signs
the request with Oracle Cloud Infrastructure's RSA-SHA256 HTTP Signature
scheme before forwarding it. The sandbox never holds the key. OCI_KEY_ID is
either an API-key triple or an ST$ security token, so one signing path
serves every OCI principal type.

The signer covers the SDK header set: date (request-target) host, plus
content-length content-type x-content-sha256 for POST, PUT, and PATCH,
whose bodies are buffered and hashed under the same 10 MiB ceiling as SigV4
body signing. Other methods stream their bodies through unsigned, as the
OCI SDKs do. Provider credential values cannot contain newlines, so the
private key is accepted as single-line base64 of the PEM, base64 DER, or
\\n-escaped PEM; passphrase-protected keys are rejected with a clear
message.

Policy validation accepts oci and requires signing_service for the SigV4
modes only. The server's signing credential-source check picks the required
keys by scheme. Three example profiles (oci, oci-object-storage,
oci-genai-native) follow the aws.yaml and aws-s3.yaml shape with setup notes
inlined per NVIDIA#3906.

Verified against real OCI endpoints: a proxy-shaped signed GET on Object
Storage and a signed POST on the native Generative AI chat API both return
200 (tests/oci_signature_live.rs, ignored by default).

Closes NVIDIA#3960

Signed-off-by: Federico Kamelhar <federico.kamelhar@oracle.com>
…ule in the OCI examples

The read-write access preset excludes DELETE, so the Object Storage example
says how to grant it. Two attached profiles may not both provide OCI_KEY_ID,
so both signing examples point multi-service sandboxes at the endpointless
oci profile bound from a sandbox policy. Both facts surfaced in the
end-to-end run against real OCI.

Signed-off-by: Federico Kamelhar <federico.kamelhar@oracle.com>
Send a GENERIC chat with a base64 PNG image part through the OCI signer so
the live suite covers body hashing on a multimodal payload, not only text.

Signed-off-by: Federico Kamelhar <federico.kamelhar@oracle.com>
Add oci_instance_principal, oci_resource_principal, and
oci_oke_workload_identity to ProviderCredentialRefreshStrategy. Each mints
OCI_KEY_ID as an ST$ security token and co-mints OCI_PRIVATE_KEY as the
paired session key through additional_outputs, so sandboxes bound to the
provider sign through credential_signing: oci without a long-lived key.

- Instance principal federates the host's identity certificate from the
  instance metadata service at auth.<region>.<realm_domain>/v1/x509,
  signing under <tenancy>/fed-x509/<sha1 fingerprint> with an ephemeral
  RSA session key in the request.
- Resource principal re-reads the platform-rotated RPST and private key
  on every refresh from files, literal material, or the
  OCI_RESOURCE_PRINCIPAL_* environment.
- OKE workload identity exchanges the projected service-account token at
  the cluster proxymux (port 12250, TLS pinned to the projected CA) for a
  token bound to the session public key; 403 names the enhanced-cluster
  requirement.
- Expiry is the token exp capped by max_lifetime_seconds; 401/403 and
  other 4xx map to fix_configuration, the rest retry. Identity endpoint
  overrides exist only under cfg(test) for loopback, and the configure API
  rejects metadata_base_url, federation_endpoint_url, and
  proxymux_endpoint_url material.

Move the OCI signing primitives into openshell-core so the gateway and the
sandbox proxy share one signer; the supervisor module keeps the HTTP
framing. Add the profile plumbing (YAML names, mintable set, pinned
OCI_KEY_ID/OCI_PRIVATE_KEY outputs), CLI and TUI names, Go SDK constants
and converters with regenerated bindings, three endpointless example
profiles, and the strategy, material, and mapping rows in the profiles
docs. Re-pin the public and durable schema fingerprints for the additive
enum values.

Closes NVIDIA#3961

Signed-off-by: Federico Kamelhar <federico.kamelhar@oracle.com>
@copy-pr-bot

copy-pr-bot Bot commented Sep 30, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@fede-kamel

Copy link
Copy Markdown
Contributor Author

I have read the DCO document and I hereby sign the DCO.

@purp

purp commented Oct 1, 2026

Copy link
Copy Markdown
Collaborator

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(providers): add OCI principal refresh strategies (instance, resource, OKE workload identity)

2 participants