SE
STACKENGINE
Zero-Trust Client IDE/ Security

EKS IRSA OIDC Provider Thumbprint Auditor

EKS IRSA pods throw AccessDenied because the OIDC provider thumbprint is missing, stale, or mismatched, breaking the STS AssumeRoleWithWebIdentity trust chain entirely.

SYSTEM STATUS
CREDITS: 0 / 3
source.yaml(1L)
AI Audit Console
> Waiting for pre-flight audit...
AirLock HYBRID KERNEL: Active
TECHNICAL WHITEPAPER & ARCHITECTURE SPECUpdated 2026

Fixing EKS IRSA AccessDenied: Missing or Invalid OIDC Provider Thumbprint

Threat/Impact Level: HIGH | Downtime Risk: HIGH | Time to Fix: 5–15 mins


TL;DR

  • What broke: Your EKS pod's service account cannot assume its IAM role because AWS STS rejected the OIDC token — the OIDC provider in IAM is either missing its TLS thumbprint, has a stale one after an AWS-side certificate rotation, or the issuer URL has a trailing slash mismatch.
  • How to fix it: Re-derive the OIDC provider thumbprint from the live endpoint, update the IAM OIDC provider resource, and verify the trust policy StringEquals condition matches the exact service account ARN format.
  • Fast path: Use our Client-Side Sandbox above to auto-refactor your trust policy and OIDC provider Terraform block without leaking your ARNs.

The Incident (What does the error mean?)

Your pod logs or kubectl describe pod surfaces this:

An error occurred (AccessDenied) when calling the AssumeRoleWithWebIdentity operation:
Not authorized to perform sts:AssumeRoleWithWebIdentity

Or from the AWS SDK inside the container:

botocore.exceptions.ClientError: An error occurred (AccessDenied) when calling the 
AssumeRoleWithWebIdentity operation: Not authorized to perform sts:AssumeRoleWithWebIdentity

Immediate consequence: Every pod using this service account loses all AWS API access. If this is your logging, secrets, or storage workload — it is down. STS is not retrying. The token is being presented, but the OIDC provider record in IAM cannot be validated because the thumbprint chain is broken or absent.


The Attack Vector / Blast Radius

This is not just an ops inconvenience. The OIDC thumbprint is the cryptographic anchor that tells AWS IAM: "trust JWTs signed by this OIDC issuer." When it's wrong or missing:

  1. All pods bound to any service account using this OIDC provider lose IAM access simultaneously — not just one pod, the entire provider is untrusted.
  2. A stale thumbprint (AWS rotated the EKS OIDC endpoint certificate — this has happened in production) causes a silent, total IRSA failure across your cluster with no pre-warning.
  3. If you "fix" this by broadening the trust policy to remove the StringEquals condition on sub (the service account), you've created a privilege escalation vector: any pod in the cluster that can obtain an OIDC token can now assume the role.
  4. Misconfigured thumbprints are frequently the result of copy-pasting an OIDC provider ARN from a different cluster/region — the issuer URL looks identical but the thumbprint is environment-specific.

How to Fix It (The Solution)

Step 1: Re-derive the live thumbprint

Do not guess or copy from docs. Derive it from the live endpoint:

# Get your cluster's OIDC issuer URL
aws eks describe-cluster --name <cluster-name> --query "cluster.identity.oidc.issuer" --output text
# Example output: https://oidc.eks.us-east-1.amazonaws.com/id/EXAMPLED539D4633E53DE1B71EXAMPLE

# Extract the thumbprint (last cert in chain, SHA-1 fingerprint)
OIDC_URL=$(aws eks describe-cluster --name <cluster-name> \
  --query "cluster.identity.oidc.issuer" --output text | sed 's|https://||')

openssl s_client -servername $OIDC_URL -showcerts \
  -connect ${OIDC_URL}:443 </dev/null 2>/dev/null \
  | awk '/-----BEGIN CERTIFICATE-----/{c++} c==2{print}' \
  | openssl x509 -fingerprint -noout -sha1 \
  | sed 's/://g' | awk -F= '{print tolower($2)}'

⚠️ You must target the second-to-last (root CA) certificate in the chain, not the leaf. The c==2 awk filter handles this. AWS docs confirm SHA-1 of the root CA is what IAM validates.


Basic Fix: Update the OIDC Provider thumbprint via CLI

aws iam update-open-id-connect-provider-thumbprint \
  --open-id-connect-provider-arn arn:aws:iam::<account-id>:oidc-provider/oidc.eks.<region>.amazonaws.com/id/<oidc-id> \
  --thumbprint-list <new-thumbprint-hex>

Enterprise Best Practice: Terraform with locked thumbprint + least-privilege trust policy

- resource "aws_iam_openid_connect_provider" "eks" {
-   url = data.aws_eks_cluster.main.identity[0].oidc[0].issuer
-   client_id_list  = ["sts.amazonaws.com"]
-   # thumbprint_list omitted — THIS IS THE BUG
- }

+ resource "aws_iam_openid_connect_provider" "eks" {
+   url             = data.aws_eks_cluster.main.identity[0].oidc[0].issuer
+   client_id_list  = ["sts.amazonaws.com"]
+   thumbprint_list = [data.tls_certificate.eks_oidc.certificates[0].sha1_fingerprint]
+ }
+
+ data "tls_certificate" "eks_oidc" {
+   url = data.aws_eks_cluster.main.identity[0].oidc[0].issuer
+ }

And the trust policy — never omit the sub condition:

  {
    "Effect": "Allow",
    "Principal": {
      "Federated": "arn:aws:iam::<account>:oidc-provider/oidc.eks.<region>.amazonaws.com/id/<id>"
    },
    "Action": "sts:AssumeRoleWithWebIdentity",
    "Condition": {
      "StringEquals": {
-       "oidc.eks.<region>.amazonaws.com/id/<id>:aud": "sts.amazonaws.com"
-       // Missing sub condition — ANY pod can assume this role
+       "oidc.eks.<region>.amazonaws.com/id/<id>:aud": "sts.amazonaws.com",
+       "oidc.eks.<region>.amazonaws.com/id/<id>:sub": "system:serviceaccount:<namespace>:<serviceaccount-name>"
      }
    }
  }

💡 Tired of pasting proprietary configs into ChatGPT? Generic AI tools log your company's ARNs, DB strings, and private keys. StackEngine is a zero-backend, pure browser-native utility. Drop your failing config into the sandbox above. We redact your secrets locally in the browser and auto-generate the refactored code using your own API key.


Prevention in CI/CD

1. Checkov — catch missing thumbprint at plan time

Checkov rule CKV_AWS_79 flags OIDC providers with empty thumbprint_list. Add to your pipeline:

checkov -d . --check CKV_AWS_79 --framework terraform

2. OPA/Conftest policy — enforce `sub` condition in trust policies

# policy/irsa_trust.rego
package irsa

deny[msg] {
  doc := input.resource_changes[_]
  doc.type == "aws_iam_role"
  assume := doc.change.after.assume_role_policy
  policy := json.unmarshal(assume)
  stmt := policy.Statement[_]
  stmt.Action == "sts:AssumeRoleWithWebIdentity"
  not _has_sub_condition(stmt)
  msg := sprintf("Role %v is missing sub condition on IRSA trust policy", [doc.address])
}

_has_sub_condition(stmt) {
  keys := object.keys(stmt.Condition.StringEquals)
  endswith(keys[_], ":sub")
}

3. Renovate / scheduled Terraform drift detection

The EKS OIDC endpoint certificate can rotate without notice. Run terraform plan on a schedule (daily) in your CI. A thumbprint drift will surface as a planned change before it causes an outage:

# .github/workflows/drift-detect.yml
schedule:
  - cron: '0 6 * * *'
jobs:
  drift:
    steps:
      - run: terraform plan -detailed-exitcode

Exitcode 2 = drift detected. Alert on it.

Further Exploration / Matrix Routing