Initializing Enclave...

How to Fix IAM Access Analyzer 'Publicly Accessible' Finding on Roles with AWS:* Principal

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


TL;DR

  • What broke: An IAM role trust policy contains "Principal": {"AWS": "*"}, making the role assumable by any AWS principal on the internet — Access Analyzer correctly flags this as a public finding.
  • How to fix it: Replace the wildcard principal with a specific account ID, role ARN, or service principal, and add a Condition block scoped to your AWS Organization ID (aws:PrincipalOrgID) or specific account.
  • Fast path: Use our Client-Side Sandbox above to auto-refactor this — paste your trust policy and get a hardened replacement without sending your ARNs to a third-party server.

The Incident (What Does the Error Mean?)

AWS IAM Access Analyzer performs cross-account reachability analysis using automated reasoning. When it sees this in a role's trust policy:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "*"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}

It generates a PUBLIC finding with status ACTIVE and resource type AWS::IAM::Role. The finding detail reads:

Finding type: Public
Condition: none
Principal: *
Action: sts:AssumeRole
Resource: arn:aws:iam::123456789012:role/YourRoleName

Immediate consequence: Any authenticated AWS principal — including accounts you have zero relationship with — can call sts:AssumeRole against this role. If sts:AssumeRole succeeds, they inherit every permission attached to this role's permission policies.


The Attack Vector / Blast Radius

This is not theoretical. The attack chain is four API calls:

  1. Attacker enumerates your account ID (leaked in a public S3 bucket policy, CloudTrail log export, or error message — account IDs are not secret).
  2. Attacker calls aws sts assume-role --role-arn arn:aws:iam::YOUR_ACCOUNT:role/YourRoleName --role-session-name pwned from any AWS account they control.
  3. If no Condition block restricts the call, AWS STS returns a valid AccessKeyId, SecretAccessKey, and SessionToken.
  4. Attacker uses those credentials to execute whatever the role's attached policies permit — S3 data exfil, EC2 instance launch, Secrets Manager read, RDS snapshot export.

Blast radius scales directly with the role's attached policies. An AdministratorAccess-attached role with this trust policy is full account takeover. Even a read-only role leaks your entire infrastructure topology, data lake contents, and secret names.

Why Condition blocks alone are insufficient without fixing the Principal: A Condition on aws:PrincipalOrgID is only evaluated after the principal match. With Principal: *, AWS still processes the request — a misconfigured or missing condition is a single point of failure. The correct fix is both: a scoped principal AND a condition.


How to Fix It (The Solution)

Basic Fix — Scope the Principal to a Specific Account

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

This alone removes the Access Analyzer public finding. Replace 123456789012 with the specific trusted account ID.


Enterprise Best Practice — Org-Scoped Principal with Condition Defense-in-Depth

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
-     "Principal": {
-       "AWS": "*"
-     },
-     "Action": "sts:AssumeRole"
+     "Principal": {
+       "AWS": "arn:aws:iam::123456789012:role/SpecificCallerRole"
+     },
+     "Action": "sts:AssumeRole",
+     "Condition": {
+       "StringEquals": {
+         "aws:PrincipalOrgID": "o-exampleorgid11"
+       },
+       "Bool": {
+         "aws:MultiFactorAuthPresent": "true"
+       }
+     }
    }
  ]
}

Key hardening decisions:

  • aws:PrincipalOrgID — even if the specific role ARN is somehow spoofed or the account is moved, this condition ensures the caller must be inside your AWS Organization.
  • aws:MultiFactorAuthPresent — for human-assumed roles, require MFA session. Remove this for service-to-service automation roles.
  • Specific role ARN, not :root:root trusts all principals in that account subject to their own IAM policies. A specific role ARN is the minimum required surface.
  • If this is a cross-service role (e.g., Lambda execution), use a service principal instead: "Service": "lambda.amazonaws.com" — not an AWS principal at all.

💡 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

This misconfiguration should never reach production. Gate it at three layers:

1. Checkov (Terraform / CloudFormation static analysis)

Checkov rule CKV_AWS_110 flags IAM trust policies with wildcard principals. Add to your pipeline:

checkov -d ./terraform --check CKV_AWS_110 --hard-fail-on HIGH

2. OPA / Conftest policy for Terraform plan JSON

package iam.trust

deny[msg] {
  resource := input.resource_changes[_]
  resource.type == "aws_iam_role"
  statement := resource.change.after.assume_role_policy
  policy := json.unmarshal(statement)
  principal := policy.Statement[_].Principal
  principal.AWS == "*"
  msg := sprintf("Role '%v' has wildcard AWS principal in trust policy — Access Analyzer will flag PUBLIC.", [resource.name])
}

Run in CI:

terraform show -json tfplan.binary | conftest test -p policies/ -

3. AWS Config Rule (runtime continuous compliance)

Deploy the managed rule IAM_NO_INLINE_POLICY_CHECK and a custom Config rule backed by a Lambda that calls access-analyzer:list-findings filtered by findingType: Public and resourceType: AWS::IAM::Role. Alert to Security Hub and auto-remediate via SSM Automation.

4. Access Analyzer as a deployment gate (aws CLI)

# After terraform apply, fail the pipeline if any public IAM findings exist
aws accessanalyzer list-findings \
  --analyzer-arn arn:aws:access-analyzer:us-east-1:123456789012:analyzer/ConsoleAnalyzer \
  --filter '{"findingType":{"eq":["Public"]},"resourceType":{"eq":["AWS::IAM::Role"]},"status":{"eq":["ACTIVE"]}}' \
  --query 'findings[*].id' \
  --output text | grep -q . && echo "FAIL: Public IAM role findings detected" && exit 1

Insert this as a post-deploy step in your GitHub Actions, GitLab CI, or Jenkins pipeline.

Related Diagnostics

"Part of the Security Utility Matrix."

View all 140 Security Tools →