Skip to main content
>_ supraj.dev
OCI Workload Identity on OKE: Pod-Level Access to OCI Resources Without Instance Principals
// PUBLISHED ON JUNE 22, 2026 UPDATED JUNE 22, 2026 5 min read

OCI Workload Identity on OKE: Pod-Level Access to OCI Resources Without Instance Principals

Stop pinning pods to node pools for IAM. OKE Workload Identity grants Kubernetes pods fine-grained, policy-driven access to OCI resources scoped to a service account. Real policies, manifests, and SDK config.

#containers #oci #oke #workload-identity #kubernetes #security

OCI Workload Identity on OKE: Pod-Level Access to OCI Resources Without Instance Principals

TL;DR: OKE Workload Identity lets you grant a Kubernetes pod fine-grained access to OCI resources through an IAM policy scoped to its cluster + namespace + service account — no instance principals, no API keys, no pinning pods to special node pools. It’s GA, requires an enhanced cluster, and every call is tracked in OCI Audit. This is the right way to do pod-level IAM on OKE.


What is OKE Workload Identity?

On Oracle Container Engine for Kubernetes (OKE), a workload identity treats a running workload as a first-class OCI principal. The identity is defined by a unique combination of three things: the cluster, the namespace, and the Kubernetes service account the pods run as.

You then write a normal OCI IAM policy that grants that identity access to other OCI resources — Object Storage, Vault secrets, Autonomous Database, and so on. The pods authenticate using the OCI SDK’s workload-identity provider; OCI IAM issues short-lived tokens; OCI Audit records every call.

Think of it as OCI’s answer to EKS IRSA or GKE Workload Identity Federation: identity follows the pod, not the node it happens to land on.


Why Does It Matter?

The alternative on OKE is instance principals. An instance principal makes the worker node the principal, via a dynamic group. Every process on that node inherits the same OCI permissions.

That has two painful consequences:

  • Coarse, node-level blast radius. Any pod scheduled on the node can use the node’s permissions. To enforce least privilege you have to isolate workloads onto dedicated node pools with different dynamic groups, then constrain scheduling so the sensitive pods only land on the “right” nodes.
  • Higher TCO. Splitting workloads across node pools for the sake of IAM means more nodes than your compute actually needs.

Workload Identity removes both. Permissions attach to the service account, so a pod gets exactly its scoped access regardless of which node it runs on. You can pack workloads densely, run fewer nodes, and still keep each pod least-privileged. And because the principal is the workload, OCI Audit attributes every API call to a specific cluster/namespace/service-account — clean compliance trails instead of “some pod on node X did this.”


How It Works

 Pod (serviceAccountName: finance-app-sa)
   │  projected SA token mounted into the pod

 OCI SDK workload-identity provider
   │  hands the token to the OKE proxymux

 OKE proxymux  ──►  OCI IAM
                     │  matches policy on:
                     │    request.principal.type = 'workload'
                     │    namespace + service_account + cluster_id

              short-lived token  ──►  call Object Storage / Vault / ADB
                                         (every call logged in OCI Audit)

The pod never holds a long-lived credential. The SDK provider reads the projected service-account token, the OKE proxymux performs the token exchange with IAM, and IAM authorizes the call against your policy. You write one policy; the platform does the rest.


Hands-On: Step-by-Step

Requires an enhanced OKE cluster. Workload Identity is not available on basic clusters. If your cluster is basic, upgrade it to enhanced first (one-way, in the Console or via API). GA feature, current as of 2026.

1. Confirm the cluster is enhanced and grab its OCID.

oci ce cluster get --cluster-id <cluster-ocid> \
  --query 'data."type"'        # expect "ENHANCED_CLUSTER"

# keep the cluster OCID handy — the policy needs it
echo "<cluster-ocid>"

2. Create a namespace and a service account for the application:

kubectl create namespace finance
kubectl create serviceaccount finance-app-sa --namespace finance

3. Write the IAM policy that maps this exact workload identity to scoped permissions. The format is rigid — all four request.principal.* conditions, joined with all { }:

Allow any-user to manage objects in compartment finance-cmp where all {
  request.principal.type = 'workload',
  request.principal.namespace = 'finance',
  request.principal.service_account = 'finance-app-sa',
  request.principal.cluster_id = 'ocid1.cluster.oc1.iad.aaaaaaaaaf______jrd'
}

Keep the verb and resource as narrow as the app needs. manage objects in compartment finance-cmp is far better than manage object-family in tenancy. The whole point of Workload Identity is least privilege — don’t widen it back out in the policy.

4. Deploy the workload referencing that service account. Run non-root, image pulled from OCIR:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: finance-app
  namespace: finance
spec:
  replicas: 2
  selector:
    matchLabels: { app: finance-app }
  template:
    metadata:
      labels: { app: finance-app }
    spec:
      serviceAccountName: finance-app-sa          # binds the workload identity
      containers:
      - name: finance-app
        image: iad.ocir.io/<tenancy-namespace>/finance-app:1.0.0
        resources:
          requests: { cpu: "250m", memory: 256Mi }
          limits:   { cpu: "500m", memory: 512Mi }
        securityContext:
          runAsNonRoot: true
          allowPrivilegeEscalation: false
          readOnlyRootFilesystem: true
          capabilities:
            drop: ["ALL"]

5. Authenticate from the app. No API keys, no ~/.oci/config. The SDK’s workload-identity provider handles the token exchange.

Go SDK (v65+):

import (
    "github.com/oracle/oci-go-sdk/v65/common/auth"
    "github.com/oracle/oci-go-sdk/v65/objectstorage"
)

// Reads the projected SA token; the OKE proxymux exchanges it with IAM.
provider, err := auth.OkeWorkloadIdentityConfigurationProvider()
if err != nil { panic(err) }

client, err := objectstorage.NewObjectStorageClientWithConfigurationProvider(provider)
if err != nil { panic(err) }

For Java, add the oci-java-sdk-addons-oke-workload-identity artifact and use its provider. For quick checks, the OCI CLI takes a flag:

oci os ns get --auth oke_workload_identity

6. Verify end-to-end from inside a pod:

kubectl exec -n finance deploy/finance-app-it -- \
  oci os object list -bn finance-bucket --auth oke_workload_identity

# Cross-check it's actually the workload principal in OCI Audit:
#   Identity & Security > Audit  →  filter by the bucket / time window;
#   the principal should resolve to your cluster/namespace/service-account.

If the call is denied, it’s almost always the policy (step 3) — a mismatched namespace, service account, or cluster OCID.


Common Pitfalls

  1. Cluster is basic, not enhanced. Workload Identity silently isn’t an option on basic clusters. Upgrade to enhanced before anything else — it’s the most common reason setups “don’t work.”
  2. Policy conditions don’t match the pod exactly. namespace, service_account, and cluster_id must match the deployed pod precisely. A typo, the wrong namespace, or a stale cluster OCID = NotAuthorized. This is the #1 debugging target.
  3. App uses the instance-principal provider by mistake. If your SDK call uses InstancePrincipalConfigurationProvider() (which hits the 169.254.169.254 IMDS endpoint), it’ll fall back to node permissions or fail. Use the workload-identity provider explicitly.
  4. Forgetting serviceAccountName on the pod spec. Create the service account and reference it in the deployment. Without serviceAccountName, pods run as default and your policy never matches.
  5. Over-broad policy. Granting manage all-resources in tenancy defeats the purpose. Scope the verb, resource type, and compartment to the minimum — Workload Identity exists to make least privilege easy.

When to Use / When NOT to Use


Key Takeaways

  • A workload identity = cluster + namespace + service account; that’s what your IAM policy targets.
  • Requires an enhanced OKE cluster — basic clusters can’t use it.
  • Pod-level permissions mean no node-pool pinning for IAM, denser packing, lower TCO.
  • Policy uses request.principal.type = 'workload' plus the namespace, service_account, and cluster_id conditions, all joined with all { }.
  • Apps authenticate with the SDK workload-identity provider (OkeWorkloadIdentityConfigurationProvider in Go) or --auth oke_workload_identity in the CLI — no long-lived keys.
  • Every call is short-lived and tracked in OCI Audit against the workload principal.
  • Keep policies narrow: scope verb, resource, and compartment to the minimum the app needs.

Further Reading