Secretless OCI Authentication with SPIFFE-based workload identity

Written by
Sebastian Tder
Published on
January 12, 2026

On-the-wire credential injection: Secretless OCI access example

In our view, every workload must have a verifiable identity, and only workloads with a trusted, cryptographically provable identityshould be allowed to access protected resources, without relying on secrets.

This approach is essential to defend against modern threats, where increasingly sophisticated attacks routinely target static credentials, configuration files, and long-lived keys. Secrets eventually leak; identities can be continuously verified.

Riptides is built around this principle: SPIFFE-based workload identities, enforced at runtime, with access decisions tied to who the workload is, not what secrets it happens to possess.

For a deeper exploration of the problem space and why the industry must move beyond credentials, see the following posts:

You can also learn how Riptides assigns trusted identities to processes at the kernel level here:
Workload Attestation and Metadata Gathering: Building Trust from the Ground Up

Overview

In this post, we show how to securely access OCI resources without creating or managing secrets, using the OCI CLI as client/example.
This pattern applies to any client application accessing OCI.

Riptides integrates transparently with the OCI SDK, enabling this capability without requiring any application code changes.

If you’re interested in how Riptides supports other cloud providers, see:

In this walkthrough, we configure secretless access for the OCI CLI using Riptides. Under the hood, this leverages:

  • Riptides’ open‑source oci-req-signer-c library
  • OCI Workload Identity Federation

For background, see:

The standard way: Authenticating and authorizing the client

To access OCI resources, the OCI CLI traditionally authenticates as an IAM user with appropriate permissions. This is the conventional model used by most cloud SDKs and CLIs today.

For this demo:

  • Create an IAM group called IAMUserViewers
  • Add a test IAM user to the group
  • Attach the following policy:
Allow group IAMUserViewers to inspect users in tenancy

The OCI CLI requires a configuration file that specifies which user it authenticates as. This file typically contains sensitive material such as:

  • key_file (private signing key)
  • pass_phrase protecting the private key
  • security_token_file for session-based auth

In this model, the client application is directly responsible for handling credentials. Even when carefully secured, these files become high-value targets: they must be stored somewhere, protected at rest, rotated regularly, and kept out of logs, backups, and build artifacts. This credential-centric approach is functional, but it tightly couples application execution with secret management, increasing both operational overhead and security risk.

Even if all best practices are followed, this model does not protect against supply‑chain attacks, as discussed in the posts linked earlier.

Listing users using this approach:

oci iam user list | jq '.data[].name'

"testuser1"
"testuser2"

On‑the‑wire Credential injection with Riptides

Now let’s look at the same operation in a Riptides managed environment.

Riptides eliminates stored secrets by injecting credentials dynamically at runtime. The client never stores, reads, or manages OCI credentials directly.

Prerequisites

Register Riptides Control Plane as an external IDP

To enable secretless access, OCI must be able to trust identities issued by Riptides and map them to an OCI IAM principal. This is done in two steps:

  1. Create an OCI service user that will be impersonated
  2. Configure OCI to trust Riptides as an OIDC identity provider and map workload identities to that user

Step 1: Create an OCI service user for impersonation

OCI requires a concrete IAM principal to authorize API calls. Instead of authenticating as a human user, we create a service user that will be impersonated using short-lived credentials.

The following service user definition creates a service user in OCI Identity Domains:

{
  "schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],
  "urn:ietf:params:scim:schemas:oracle:idcs:extension:user:User": {
    "serviceUser": true
  },
  "userName": "testserviceuser1"
}

What this does:

  • Declares a standard user object
  • Marks the user as a service user, not a human
  • Creates an identity that can be safely impersonated by workloads

Add this service user to the IAMUserViewers group so it inherits the required permissions.

At this point, we have an OCI principal that is allowed to access the Identity API, but no credentials have been issued or stored yet.

Step 2: Trust Riptides as an OIDC Identity Provider

Next, register the Riptides Control Plane as an OIDC identity provider in OCI. This enables OCI to trust tokens issued by Riptides and exchange them for User Principal Session Tokens (UPSTs).

{
  "active": true,
  "allowImpersonation": true,
  "issuer": "https://<riptides-control-plane>/oidc",
  "name": "Token Trust JWT to UPST",
  "oauthClients": ["1d92..."],
  "publicKeyEndpoint": "https://<riptides-control-plane>/oidc/keys",
  "impersonationServiceUsers": [
    {
      "rule": "sub eq spiffe://acme.org/oci-cli",
      "value": "2a86..."
    }
  ],
  "subjectType": "User",
  "type": "JWT",
  "schemas": [
    "urn:ietf:params:scim:schemas:oracle:idcs:IdentityPropagationTrust"
  ]
}

What this configuration enables:

  • issuer: Identifies the Riptides Control Plane as the trusted OIDC issuer
  • oauthClients: Restricts trust to a specific OAuth 2.0 client registered for the Riptides Control Plane
  • publicKeyEndpoint: Allows OCI to fetch the public keys used to verify ID tokens signed by Riptides Control Plane
  • impersonationServiceUsers: Defines how a workload identity maps to an OCI IAM service user

In this example:

  • If the ID token contains a sub claim equal to spiffe://acme.org/oci-cli
  • OCI will issue temporary credentials for the service user with ID 2a86...
  • type: JWT indicates that the trust relationship is based on JWT-formatted ID tokens

How this fits into the secretless flow:

  1. Riptides issues an ID token (JWT SVID) to a workload, containing a SPIFFE-based subject
  2. OCI verifies the token using the configured trust
  3. OCI exchanges the token for a short-lived UPST
  4. The workload impersonates the service user without ever handling secrets

At no point does the application receive, store, or manage credentials. Secrets exist, but they are managed by the platform, not the application.

Setting up the client application with Riptides

From Riptides’ perspective, the OCI IAM service API is an external dependency. It is not discovered automatically. To enable credential injection, Riptides must first be told which external service the workload will communicate with.

This is done by registering the OCI Identity API as an external service in the Riptides Control Plane using a Kubernetes custom resource:

apiVersion: core.riptides.io/v1alpha1
kind: Service
metadata:
  name: oci-identity-api
  namespace: riptides-system
spec:
  addresses:
    - address: identity.eu-frankfurt-1.oci.oraclecloud.com
     # OCI Identity REST API service endpoint
      port: 443 # Port the client connects to
  labels:
    app: oci-identity-api # Label for matching this service
  external: true # Indicates this is an external service, not managed by Riptides

What this configuration does:

  • Declares the OCI Identity REST API endpoint as a known external service
  • Allows Riptides to match outbound connections from workloads to this destination

Defining how workloads obtain OCI credentials

At this point, Riptides knows where the workload will connect (OCI Identity API). Next, we define how Riptides should obtain credentialsfor that workload.

This is done in two steps:

  1. Define how to obtain temporary OCI credentials
  2. Bind those credentials to a specific workload identity

Step 1: Define a credential source

A CredentialSource describes how Riptides exchanges identity for temporary OCI credentials. In this case, the source is OCI itself, using OCI IAM Workload Identity Federation.

apiVersion: core.riptides.io/v1alpha1
kind: CredentialSource
metadata:
  name: oci-cred-1
  namespace: riptides-system
spec:
  oci: # Temporary credentials sourced from OCI
    region: eu-frankfurt-1
    clientId: 1d92...         # the OAUth 2.0 client id defined in OCI for the Riptides Control Plane
    clientSecret: idcscs-.... # the client secret defined in OCI for the Riptides Control Plane
    identityDomainUrl: https://idcs-......identity.oraclecloud.com # the URL of the identity domain where the impersonated OCI service user is defined 
    tenancyOcid: ocid1.tenancy.oc1........ 

What this configuration defines:

  • region: The OCI region where credentials will be issued.
  • clientId / clientSecret: OAuth 2.0 credentials used by the Riptides Control Plane, not the application, to interact with OCI IAM.
  • identityDomainUrl: The OCI Identity Domain where the impersonated service user is defined.
  • tenancyOcid: Identifies the OCI tenancy where authentication and authorization occur.

This resource does not issue credentials on its own. It simply defines how credentials can be obtained when needed.

Step 2: Bind credentials to a workload identity

Next, we associate the credential source with a specific workload identity using a WorkloadCredential:

apiVersion: core.riptides.io/v1alpha1
kind: WorkloadCredential
metadata:
  name: oci-cli-cred-1
  namespace: riptides-system
spec:
  credentialSource: oci-cred-1 # Source of temporary credentials
  workloadID: oci-cli          # Workload ID to get OCI temporary credentials for

What this does:

  • Links the oci-cli workload identity to the OCI credential source
  • Ensures that only workloads with this identity can obtain these credentials

At this point, no credentials are issued yet; the configuration merely defines the relationship.

How it works at runtime

When a workload with the oci-cli identity needs to call OCI:

  1. The Riptides Control Plane issues an ID token for the workload.
  2. The token’s sub claim follows the SPIFFE format: spiffe://<TRUST_DOMAIN>/<WORKLOAD_ID>
    In this demo: spiffe://acme.org/oci-cli
  3. OCI validates this token using the previously configured trust relationship.
  4. OCI exchanges the token for short-lived credentials impersonating the service user.
  5. Riptides injects these credentials transparently into outgoing requests.
  6. Credentials are automatically refreshed before expiration.

At no point does the application:

  • See credentials
  • Store credentials
  • Handle rotation or expiration logic

Secrets exist, but they are **entirely managed by the platform, not the workload.

Assigning Workload IDs to processes

We define which processes can be assigned the oci-cli workload ID, and under what conditions:

apiVersion: core.riptides.io/v1alpha1
kind: WorkloadIdentity
metadata:
  name: oci-cli-wid
  namespace: riptides-system
spec:
  scope:
    agent: 
      id: <AGENT_WORKLOAD_ID> # Scope: node(s) where this workload identity can be assigned
  workloadID: oci-cli         # The workload ID assigned to matching processes
  selectors:
    - process:name: [oci]     # Runtime process attribute that must match to get this identity
  egress: # Egress rules for credential injection
    - selectors:
        - app: oci-identity-api # Service endpoint(s) targeted by this rule
      credentialName: oci-cli-cred-1 # Credentials to inject, referenced from WorkloadCredential
      connection:
        tls:
          intercept: true # Intercept traffic and inject credentials into HTTP requests

How it works

  1. Riptides Agent runs on nodes and acts as the bridge between the Control Plane and the Linux kernel module.
  2. Workload IDs and credentials issued by the Control Plane are restricted to processes on the node where the agent runs — this is controlled by the scope field in the WorkloadIdentity CR. Multiple agents can also be targeted if needed.
  3. The Linux kernel module monitors running processes and checks their runtime attributes against the spec.selectors values. Only matching processes are assigned the workload ID.
  4. When a process with an assigned workload ID sends a request to a service referenced in the egress rules, the temporary credentials from the specified WorkloadCredential are injected directly into the request.

In this example, any process named oci will receive OCI temporary credentials automatically when sending requests to the OCI Identity service endpoint.

The original HTTP request sent by OCI CLI to OCI Identity service endpoint is modified by Riptides' Linux kernel module as it injects the temporary credentials on the fly. The modified HTTP request requires resigning with the correct OCI signature. Our Linux kernel module accomplishes this using our oci-req-signer-c library, implemented in portable C with Linux kernel compatibility. This enables credentials to be injected and signed at the kernel level just before the request is sent, ensuring full OCI authentication without exposing keys to the client application.

Why is this matters: Unlike sidecars, environment variables, or SDK hooks:

  • Identity is bound to process execution, not deployment metadata
  • Credentials are scoped to specific destinations
  • Enforcement happens at the kernel boundary
  • Compromising the application does not automatically expose credentials

From the application’s point of view, authentication “just works.” From a security perspective, access is tightly constrained, observable, and auditable.

Running the Client Application

The OCI SDK still expects a configuration file, so we provide dummy credentials that satisfy the SDK’s syntax requirements but are never used for authentication. Riptides intercepts the request, injects valid temporary credentials, and re-signs the request transparently at runtime.

[DEFAULT]
user=ocid1.user.oc1..dummy
fingerprint=00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00
key_file=/var/tmp/dummy_priv_key.pem
tenancy=ocid1.tenancy.oc1......
region=eu-frankfurt-1

The private key referenced here must be syntactically valid, but it has no permissions and no security value. At runtime, Riptides replaces it with workload-bound private key derived from the workload identity.

oci --cert-bundle /sys/module/riptides/certs/ca-certificates.crt iam user list | jq '.data.[].name'

"testuser1"
"testuser2"
"testserviceuser1

What actually happened:

  • The OCI CLI believed it was using local credentials
  • The real credentials were never present in files, environment variables, or process memory
  • Authentication and request signing occurred inside the kernel, just before the request was sent
  • Credentials were scoped, temporary, and automatically refreshed

Key points

  • No credentials are ever stored on the client machine or in configuration files.
  • Credentials are provided just-in-time for each request, minimizing the risk of leaks or misuse.
  • The application workflow remains unchanged, from the client’s perspective, authentication happens automatically and transparently.

When the On-the-wire credential injection is not an option

There may be situations where you cannot or do not want to use on-the-wire credential injection. In these cases, Riptides still provides credentials to the OCI SDK without exposing them on the filesystem:

  • The configuration file, private key file, and session token file are made available via sysfs.
  • Only processes with the appropriate workload identity can access these files. To disable on-the-wire injection, set connection.tls.intercept: false in the WorkloadIdentity custom resource.

Running OCI CLI with sysfs-based credentials

You can point the OCI CLI to the Riptides managed configuration files as follows:

oci oci --auth security_token --cert-bundle /sys/module/riptides/certs/ca-certificates.crt  --config-file /sys/module/riptides/credentials/4e1ef9dd-fa21-513d-8505-7e9ef13b9be0/oci-cli-cred-1/oci_config iam user list | jq '.data.[].name'

"testuser1"
"testuser2"
"testserviceuser1

The exact path of oci_config can be retrieved from the WorkloadCredential custom resource status fields.

Why is this approach safe

  • Credentials never appear on disk or in environment variables
  • Only processes with the matching workload identity can read the configuration
  • The OCI CLI behaves normally, but secrets are still tightly scoped and ephemeral

This provides a fallback option when on-the-wire injection is not feasible, without compromising the security guarantees of the platform.

Final Thoughts

At Riptides, we are strong advocates of SPIFFE-based workload identities as the foundation for secure, scalable non‑human authentication.

The benefits are concrete and measurable:

  • Elimination of static keys — no long-lived secrets to steal or rotate
  • Short-lived, impersonated credentials — automatically issued and refreshed
  • Full auditability — every action is traceable to a workload

This is more than an implementation detail; it’s a security philosophy: no static secrets, no blind trust, only verifiable workload identities at runtime.

If you enjoyed this post, follow us on LinkedIn and X for more updates. If you’d like to see Riptides in action, get in touch with us for a demo.

Share this post
federation
non-human identity
oci

Ready to replace secrets
with trusted identities?

Build with trust at the core.