Fixing AWS IAM Error: Assume Role Session Duration Exceeds Maximum Limit
Threat/Impact Level: HIGH | Exploitability/Downtime Risk: HIGH | Time to Fix: 10 mins
TL;DR
- What broke:
AssumeRolecall specifies aDurationSecondsvalue higher than the role'sMaxSessionDuration(default: 3600s, hard ceiling: 43200s), causing an immediateValidationErrorand broken automation pipelines or federated login flows. - How to fix it: Either lower
DurationSecondsin your SDK/CLI call to match the role's currentMaxSessionDuration, or raiseMaxSessionDurationon the role itself viaaws iam update-role. - Fast path: Use our Client-Side Sandbox below to auto-refactor this — paste your trust policy or CLI invocation and get corrected code without leaking your ARNs.
The Incident (What does the error mean?)
Raw error from AWS CLI or SDK:
An error occurred (ValidationError) when calling the AssumeRole operation:
The requested DurationSeconds exceeds the MaxSessionDuration set for this role.
Or from STS directly:
An error occurred (ValidationError) when calling the AssumeRole operation:
Role session duration 43201 exceeds the maximum allowed duration of 43200 seconds.
Immediate consequence: The AssumeRole call returns a non-retryable ValidationError. Any Lambda, ECS task, CI/CD runner, or federated SSO flow that depends on this credential vend is dead. No temporary credentials are issued. Downstream services get AccessDenied on every subsequent API call.
The Attack Vector / Blast Radius
This is a broken availability control, not just a config typo. Here's the blast radius:
- CI/CD pipelines: A GitHub Actions or Jenkins job calling
aws sts assume-role --duration-seconds 86400against a role capped at 3600s will fail at credential bootstrap — every job in that pipeline dies before running a single deployment step. - Federated SSO sessions: SAML/OIDC federation that requests long-lived console sessions (e.g., 8-hour workday sessions) silently fails if the role
MaxSessionDurationwas never updated from the default 1-hour. Users get kicked out mid-session. - Privilege escalation surface: If an attacker has
iam:UpdateRolepermission, they can raiseMaxSessionDurationto 12 hours on a high-privilege role, then assume it with a near-maximum duration — dramatically extending the window of a compromised credential before it expires. This is an active lateral movement technique. - Chained role assumptions: When Role A assumes Role B, the session duration of the chained session is bounded by the remaining duration of Role A's session. Misconfigured durations in a chain cause unpredictable mid-pipeline expiry.
The default MaxSessionDuration of 3600 seconds exists for a reason. Blindly raising it to 43200 on all roles is a security regression.
How to Fix It (The Solution)
Basic Fix — Lower DurationSeconds in your call
If you don't control the role definition, match your request to the existing cap:
# AWS CLI
- aws sts assume-role \
- --role-arn arn:aws:iam::123456789012:role/MyRole \
- --role-session-name mysession \
- --duration-seconds 43200
+ aws sts assume-role \
+ --role-arn arn:aws:iam::123456789012:role/MyRole \
+ --role-session-name mysession \
+ --duration-seconds 3600
# Python boto3
- response = sts.assume_role(
- RoleArn='arn:aws:iam::123456789012:role/MyRole',
- RoleSessionName='mysession',
- DurationSeconds=43200
- )
+ response = sts.assume_role(
+ RoleArn='arn:aws:iam::123456789012:role/MyRole',
+ RoleSessionName='mysession',
+ DurationSeconds=3600 # Must be <= role's MaxSessionDuration
+ )
Enterprise Best Practice — Set MaxSessionDuration explicitly and enforce it
Step 1: Update the role's MaxSessionDuration to an intentional value (not default, not maximum).
# Terraform aws_iam_role resource
resource "aws_iam_role" "ci_deploy_role" {
name = "ci-deploy-role"
- # max_session_duration not set — defaults to 3600
+ max_session_duration = 7200 # 2 hours — scoped to CI job window
assume_role_policy = data.aws_iam_policy_document.trust.json
}
Step 2: Lock down who can call iam:UpdateRole to prevent attackers from silently raising this ceiling.
# SCP or permission boundary — deny MaxSessionDuration tampering
{
"Version": "2012-10-17",
"Statement": [
+ {
+ "Sid": "DenyMaxSessionDurationEscalation",
+ "Effect": "Deny",
+ "Action": "iam:UpdateRole",
+ "Resource": "arn:aws:iam::*:role/high-privilege-*",
+ "Condition": {
+ "NumericGreaterThan": {
+ "iam:MaxSessionDuration": "3600"
+ }
+ }
+ }
]
}
Step 3: For federated/SAML sessions, the DurationSeconds in AssumeRoleWithSAML is additionally bounded by the IdP's SessionDuration attribute. Both must align.
# SAML assertion attribute (e.g., Okta, ADFS)
- <Attribute Name="https://aws.amazon.com/SAML/Attributes/SessionDuration">
- <AttributeValue>43200</AttributeValue>
- </Attribute>
+ <Attribute Name="https://aws.amazon.com/SAML/Attributes/SessionDuration">
+ <AttributeValue>7200</AttributeValue> <!-- Match role MaxSessionDuration -->
+ </Attribute>
💡 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 — catch uncapped MaxSessionDuration in Terraform before merge:
# .checkov.yml
checks:
- CKV_AWS_9 # Ensures IAM password policy
# Custom check — enforce MaxSessionDuration ceiling
custom_checks:
- name: ENFORCE_MAX_SESSION_DURATION
resource: aws_iam_role
attribute: max_session_duration
operator: less_than_or_equal
value: 14400 # Your org's ceiling — 4 hours
2. OPA/Conftest policy — block Terraform plans that set duration too high:
package terraform.iam
deny[msg] {
resource := input.resource_changes[_]
resource.type == "aws_iam_role"
resource.change.after.max_session_duration > 14400
msg := sprintf(
"Role '%v' MaxSessionDuration %v exceeds org ceiling of 14400s",
[resource.name, resource.change.after.max_session_duration]
)
}
3. AWS Config Rule — detect drift in production:
aws configservice put-config-rule --config-rule '{
"ConfigRuleName": "iam-role-max-session-duration-check",
"Source": {
"Owner": "CUSTOM_LAMBDA",
"SourceIdentifier": "arn:aws:lambda:us-east-1:123456789012:function:check-session-duration"
},
"Scope": {
"ComplianceResourceTypes": ["AWS::IAM::Role"]
}
}'
4. GitHub Actions — validate duration before assuming role:
- name: Validate session duration before assume-role
run: |
MAX=$(aws iam get-role --role-name $ROLE_NAME \
--query 'Role.MaxSessionDuration' --output text)
REQUESTED=7200
if [ "$REQUESTED" -gt "$MAX" ]; then
echo "ERROR: Requested duration $REQUESTED exceeds role max $MAX"
exit 1
fi
Build this check into your bootstrap step. A failed credential vend at pipeline start is infinitely cheaper than a mid-deployment expiry.