Skip to content

fix(awsutil): only set AssumeRole ExternalID when provided#186

Open
nomeelnoj wants to merge 1 commit into
hashicorp:mainfrom
nomeelnoj:fix/awsutil-empty-external-id
Open

fix(awsutil): only set AssumeRole ExternalID when provided#186
nomeelnoj wants to merge 1 commit into
hashicorp:mainfrom
nomeelnoj:fix/awsutil-empty-external-id

Conversation

@nomeelnoj

@nomeelnoj nomeelnoj commented Jun 18, 2026

Copy link
Copy Markdown

Summary

The non-web-identity AssumeRole path in awsutil/generate_credentials.go set options.ExternalID unconditionally:

options.ExternalID = aws.String(c.RoleExternalId)

When no external ID is configured, aws.String("") is a non-nil pointer to an empty string, which the SDK serializes as ExternalId="" on the sts:AssumeRole call. STS rejects it with:

ValidationError: ... Value '' at 'externalId' failed to satisfy constraint:
Member must have length greater than or equal to 2

This breaks role assumption for any caller that does not use an external ID.

Fix

Only assign ExternalID when a non-empty value was provided. This matches the existing empty-value handling for RoleExternalId elsewhere in the file (line 149) and leaves AssumeRoleOptions.ExternalID as nil when unset, so no ExternalId parameter is sent to STS.

Only the plain (non-web-identity) AssumeRole path is affected; the web-identity branches never set ExternalID.

Fixes: #185

Context

Surfaced downstream in terraform-provider-vault (hashicorp/terraform-provider-vault#2922, fix in hashicorp/terraform-provider-vault#2923). The provider change stops routing non-web-identity assumption through WithRoleArn, which incidentally avoids this code path; this PR fixes the underlying library defect so other consumers are protected.

Notes for maintainers

  • The awsutil module has no CHANGELOG file, so no changelog entry was added. Happy to add one if you keep release notes elsewhere.
  • Per CONTRIBUTING, flagging that this touches credential/AssumeRole behavior used across multiple products; reviewers from affected teams welcome. The change is intentionally minimal and behavior-preserving when an external ID is provided.

PCI review checklist

  • If applicable, I've documented a plan to revert these changes if they require more than reverting the pull request.

  • If applicable, I've worked with GRC to document the impact of any changes to security controls.

    Examples of changes to controls include access controls, encryption, logging, etc.

  • If applicable, I've worked with GRC to ensure compliance due to a significant change to the in-scope PCI environment.

    Examples include changes to operating systems, ports, protocols, services, cryptography-related components, PII processing code, etc.

The non-web-identity AssumeRole path set options.ExternalID unconditionally
via aws.String(c.RoleExternalId). When no external ID was configured this
produced a non-nil pointer to an empty string, which the SDK serializes as
ExternalId="" on the STS AssumeRole call. STS then rejects the request with
a ValidationError ("Member must have length greater than or equal to 2"),
breaking role assumption for any caller that does not use an external ID.

Guard the assignment so ExternalID is only set when a non-empty value is
provided, matching the existing empty-value handling for this field. Adds a
regression test covering AssumeRole without an external ID.

Signed-off-by: nomeelnoj <4316746+nomeelnoj@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

awsutil: AssumeRole sends ExternalId="" when no external ID is configured, STS rejects with ValidationError

1 participant