Repository navigation
feat(providers): add OCI principal refresh strategies (instance, resource, OKE workload identity) #3961
Description
Problem Statement
An agent in an OpenShell sandbox running on OCI compute, OKE, or Functions should authenticate to OCI services as the workload's principal, not with a long-lived API key someone pasted into a provider. The gateway credential refresh system (#1306, #1349) supports OAuth2, Google service-account JWT, and AWS STS AssumeRole (#1576, #1782), but has no OCI strategy. Instance metadata is hard-blocked in sandboxes, so ambient credentials are unavailable there by design. Users must either store a long-lived OCI API key and private key in the provider (a security liability) or run an external refresh daemon, which moves lifecycle management outside OpenShell, the situation #1306 set out to fix.
This feature adds three gateway-owned refresh strategies that mint short-lived OCI security tokens:
oci_instance_principal: X.509 federation of the gateway host's instance certificate athttps://auth.<region>.oraclecloud.com/v1/x509.oci_resource_principal: exchange of the resource principal session token and key exposed to the gateway (Functions, Data Science, and similar), following theOCI_RESOURCE_PRINCIPAL_RPSTandOCI_RESOURCE_PRINCIPAL_PRIVATE_PEMconventions of the OCI SDKs.oci_oke_workload_identity: exchange of the gateway pod's Kubernetes service-account token at the OKEresourcePrincipalSessionTokensendpoint on port 12250.
Technical Context
Like STS, each OCI strategy produces coupled values from one exchange: a security token, used as keyId="ST$<token>", and the ephemeral RSA session key that signed the exchange, which must be the key that later signs API requests. The additional_outputs mechanism added for STS (aws_sts_assume_role co-mints three values) fits directly: the primary credential is OCI_KEY_ID and the sibling is OCI_PRIVATE_KEY. Session tokens are short-lived, so refresh_before_seconds and max_lifetime_seconds apply as they do for STS.
The gateway generates the session key pair itself, so no long-lived private key ever leaves the gateway, and the minted pair is consumed by proxy-side OCI signing (filed alongside this issue) without any sandbox-side change. Static API-key material needs no refresh strategy: it is ordinary static credentials consumed by the same signing path.
Impact / Why This Matters
Without this, OCI-hosted agents cannot use the identity the platform already gives them, and operators fall back to long-lived keys or out-of-band refreshers. With it, an OKE-hosted gateway can run fleets of agents whose OCI access is scoped by IAM dynamic groups and rotated automatically, matching what aws_sts_assume_role gives AWS users today.
Proposed Design
Profile YAML declares the strategy on the primary credential, with the session key as an additional output:
credentials:
- name: key_id
env_vars: [OCI_KEY_ID]
required: true
refresh:
strategy: oci_instance_principal
refresh_before_seconds: 300
max_lifetime_seconds: 3600
additional_outputs:
- output: private_key
credential: private_key
material:
- name: region
description: Region whose identity endpoint federates the instance
required: true
secret: false
- name: private_key
env_vars: [OCI_PRIVATE_KEY]
required: trueopenshell provider refresh configure accepts the three new strategy names. oci_oke_workload_identity reads the projected service-account token path and the OKE endpoint from material; oci_resource_principal reads the RPST and key paths from material with the SDK environment names as defaults.
Acceptance Criteria
- The three strategies are accepted by profile parsing,
is_gateway_mintable_strategy, andopenshell provider refresh configure. - Each strategy mints
OCI_KEY_ID(ST$...) andOCI_PRIVATE_KEYin one operation and rotates them before expiry without operator action. - A sandbox attached to such a provider can call a signed OCI endpoint with the minted pair and no long-lived key anywhere in the gateway record.
- Failures map to the existing
RefreshFailurerecovery actions (retry vs fix_configuration) rather than retrying configuration errors forever. - Docs list the strategies and their material keys in the refresh strategy tables.
Alternatives Considered
externalstrategy with a sidecar refresher: works but recreates the out-of-band lifecycle problem feat(providers): add gateway-owned credential refresh for short-lived provider tokens #1306 removed.- Long-lived API keys as static credentials: works with proxy-side signing but is the liability this feature exists to remove.
- Emulating OCI instance metadata inside the sandbox, as the GCE emulator does: would hand the sandbox a usable token instead of keeping it at the gateway, and metadata endpoints are deliberately blocked.
Agent Investigation
Checked against main at 5acaaba:
| Component | Key files | Role |
|---|---|---|
| Proto model | proto/openshell.proto:2313-2321 (ProviderCredentialRefreshStrategy, last value AWS_STS_ASSUME_ROLE = 6) |
Add values 7, 8, 9 |
| Profile parsing | crates/openshell-providers/src/profiles.rs:1148 (is_gateway_mintable_strategy), :1164 (strategy_output_spec), :1185 (primary env key), :1418-1419 (strategy names) |
Register names, outputs, primary key |
| Refresh engine | crates/openshell-server/src/provider_refresh.rs:660-670 (strategy names, strategy_secret_material_keys) and the mint dispatch |
Three mint functions and material keys |
| Gateway RPC | crates/openshell-server/src/grpc/provider.rs (handle_configure_provider_refresh) |
Accept the new strategies |
| Docs | docs/how-it-works/providers/profiles.mdx strategy tables |
Names and material keys |
Related: #3879 (umbrella), #3904 (bearer profile, merged), #1576 / #1782 (STS precedent), and the proxy-side OCI signing issue filed alongside this one, which this feature depends on to be useful.
Activity
fede-kamel commentedon Sep 30, 2026
Depends on proxy-side OCI signing: #3960.
purp commentedon Oct 1, 2026
purp commentedon Oct 1, 2026
Appreciate you proposing this, @fede-kamel, as well as #3960. As I mentioned there we like this sort of provider-specific addition to be brought to the community for acceptance, either in CNCF Slack #openshell-dev or in the weekly community meeting (Tue 9a Pacific/noon Eastern). Hope you'll bring it to one of those venues for discussion.
I'm going to move the attached PRs on both issues to draft with a linked note.