Skip to content

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

Description

@fede-kamel

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 at https://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 the OCI_RESOURCE_PRINCIPAL_RPST and OCI_RESOURCE_PRINCIPAL_PRIVATE_PEM conventions of the OCI SDKs.
  • oci_oke_workload_identity: exchange of the gateway pod's Kubernetes service-account token at the OKE resourcePrincipalSessionTokens endpoint 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: true

openshell 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, and openshell provider refresh configure.
  • Each strategy mints OCI_KEY_ID (ST$...) and OCI_PRIVATE_KEY in 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 RefreshFailure recovery 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

  • external strategy 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

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    state:triage-neededOpened without agent diagnostics and needs triage

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions