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.
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
- 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.”
- Policy conditions don’t match the pod exactly.
namespace,service_account, andcluster_idmust match the deployed pod precisely. A typo, the wrong namespace, or a stale cluster OCID =NotAuthorized. This is the #1 debugging target. - App uses the instance-principal provider by mistake. If your SDK call uses
InstancePrincipalConfigurationProvider()(which hits the169.254.169.254IMDS endpoint), it’ll fall back to node permissions or fail. Use the workload-identity provider explicitly. - Forgetting
serviceAccountNameon the pod spec. Create the service account and reference it in the deployment. WithoutserviceAccountName, pods run asdefaultand your policy never matches. - Over-broad policy. Granting
manage all-resources in tenancydefeats 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 withall { }. - Apps authenticate with the SDK workload-identity provider (
OkeWorkloadIdentityConfigurationProviderin Go) or--auth oke_workload_identityin 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
- Granting Workloads Access to OCI Resources (OKE) — https://docs.oracle.com/en-us/iaas/Content/ContEng/Tasks/contenggrantingworkloadaccesstoresources.htm
- Enhanced Clusters and Basic Clusters — https://docs.oracle.com/en-us/iaas/Content/ContEng/Tasks/contengworkingwithenhancedclusters.htm
- OCI SDK for Go (auth providers) — https://docs.oracle.com/en-us/iaas/Content/API/SDKDocs/gosdk.htm