Initializing Enclave...

Fixing CodeBuild AccessDeniedException: Missing sts:TagSession Permission for AssumeRole

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


TL;DR

  • What broke: CodeBuild's service role (or the role it's trying to assume) is missing sts:TagSession, causing AssumeRole to hard-fail with AccessDeniedException.
  • How to fix it: Add sts:TagSession to the IAM policy attached to the principal initiating the AssumeRole call — not the target role's trust policy.
  • Fast path: Use our Client-Side Sandbox below to auto-refactor your failing policy. Paste it in, get the corrected version without sending your ARNs to a third-party server.

The Incident (What Does the Error Mean?)

You hit this in your CodeBuild build logs or CloudTrail:

An error occurred (AccessDeniedException) when calling the AssumeRole operation:
User: arn:aws:sts::123456789012:assumed-role/CodeBuildServiceRole/session
is not authorized to perform: sts:AssumeRole on resource: arn:aws:iam::123456789012:role/TargetDeployRole
with an explicit deny or missing permission: sts:TagSession

Immediate consequence: The build pipeline stops cold. No deployment proceeds. If this role assumption is part of a cross-account deploy, staging or production pushes are completely blocked. The error message is deliberately vague — AWS bundles the sts:TagSession denial into the AssumeRole failure, which is why engineers waste 30+ minutes chasing the wrong permission.

Root cause: When session tags are passed during AssumeRole (either explicitly in your buildspec or implicitly by a service like CodePipeline passing PrincipalTag context), AWS requires the calling principal to have sts:TagSession on the target role resource. Without it, the entire AssumeRole call is rejected — not just the tagging.


The Attack Vector / Blast Radius

This is a least-privilege enforcement gap, not a vulnerability in the traditional sense — but misconfiguring the fix creates real risk.

The wrong fix engineers reach for: Granting sts:* or sts:AssumeRole with Resource: "*" to make the error go away. That's now a critical misconfiguration. Any principal with that policy can assume any role in the account that has a permissive trust policy, including roles with AdministratorAccess.

Blast radius of the lazy fix:

  • Compromised CodeBuild execution environment (supply chain attack via a malicious dependency) can call sts:AssumeRole on high-privilege roles.
  • Lateral movement across accounts if cross-account roles exist with weak trust policies.
  • Privilege escalation path: CodeBuild role → sts:AssumeRole * → AdminRole → full account compromise.

The correct blast radius is zerosts:TagSession scoped to the exact target role ARN adds no new attack surface.


How to Fix It (The Solution)

Basic Fix

Add sts:TagSession to the IAM policy attached to your CodeBuild service role, scoped to the specific target role ARN.

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
-       "sts:AssumeRole"
+       "sts:AssumeRole",
+       "sts:TagSession"
      ],
      "Resource": "arn:aws:iam::123456789012:role/TargetDeployRole"
    }
  ]
}

Critical: Resource must be the exact ARN of the role being assumed. Never "*".


Enterprise Best Practice (Condition Key Locking)

Scope sts:TagSession further using aws:RequestedRegion and sts:TransitiveTagKeys condition keys to prevent tag-based privilege escalation via transitive session tags.

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowAssumeDeployRole",
      "Effect": "Allow",
      "Action": [
-       "sts:AssumeRole"
+       "sts:AssumeRole",
+       "sts:TagSession"
      ],
      "Resource": "arn:aws:iam::123456789012:role/TargetDeployRole",
+     "Condition": {
+       "StringEquals": {
+         "aws:RequestedRegion": "us-east-1"
+       },
+       "ForAllValues:StringEquals": {
+         "sts:TransitiveTagKeys": ["Project", "Environment"]
+       }
+     }
    }
  ]
}

Why sts:TransitiveTagKeys matters: Without this condition, a compromised build could inject arbitrary session tags that propagate to subsequent AssumeRole chains, potentially satisfying ABAC conditions on high-privilege roles downstream.

Also verify the target role's trust policy allows tagged sessions from your CodeBuild role:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::123456789012:role/CodeBuildServiceRole"
      },
-     "Action": "sts:AssumeRole"
+     "Action": [
+       "sts:AssumeRole",
+       "sts:TagSession"
+     ]
    }
  ]
}

💡 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 Client-Side WASM 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 (IaC scanning — catches this pre-merge):

Add a custom Checkov check or use the built-in CKV_AWS_110 / CKV_AWS_111 rules to flag any sts:AssumeRole statement missing a paired sts:TagSession when session tags are in use. Run in your PR pipeline:

checkov -d ./iam --framework cloudformation --check CKV_AWS_110

2. OPA / Conftest Policy (enforce in Terraform plan):

deny[msg] {
  stmt := input.resource.aws_iam_policy.document.statement[_]
  "sts:AssumeRole" == stmt.actions[_]
  not contains_tag_session(stmt.actions)
  msg := sprintf("IAM policy '%v' has sts:AssumeRole without sts:TagSession", [input.resource.aws_iam_policy.document])
}

contains_tag_session(actions) {
  actions[_] == "sts:TagSession"
}

3. AWS Config Rule: Enable iam-policy-no-statements-with-admin-access and write a custom Config rule that flags CodeBuild service roles missing sts:TagSession when cross-role assumptions are detected in CloudTrail.

4. Terraform aws_iam_role_policy module pattern: Enforce a reusable module for all CodeBuild roles that always pairs sts:AssumeRole with sts:TagSession and mandates a non-wildcard Resource. Block direct aws_iam_policy_document usage outside the module via Sentinel (Terraform Cloud) or OPA.

Related Diagnostics

"Part of the Security Utility Matrix."

View all 140 Security Tools →