Skip to content

Commit b426037

Browse files
committed
fix: credentials search precedence
1 parent 68dd646 commit b426037

File tree

3 files changed

+224
-35
lines changed

3 files changed

+224
-35
lines changed

aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/DefaultChainCredentialsProvider.kt

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,12 @@ import aws.smithy.kotlin.runtime.util.PlatformProvider
2323
*
2424
* Resolution order:
2525
*
26-
* 1. Environment variables ([EnvironmentCredentialsProvider])
27-
* 2. Profile ([ProfileCredentialsProvider])
26+
* 1. System properties ([SystemPropertyCredentialsProvider])
27+
* 2. Environment variables ([EnvironmentCredentialsProvider])
2828
* 3. Web Identity Tokens ([StsWebIdentityCredentialsProvider]]
29-
* 4. ECS (IAM roles for tasks) ([EcsCredentialsProvider])
30-
* 5. EC2 Instance Metadata (IMDSv2) ([ImdsCredentialsProvider])
29+
* 4. Profile ([ProfileCredentialsProvider])
30+
* 5. ECS (IAM roles for tasks) ([EcsCredentialsProvider])
31+
* 6. EC2 Instance Metadata (IMDSv2) ([ImdsCredentialsProvider])
3132
*
3233
* The chain is decorated with a [CachedCredentialsProvider].
3334
*
@@ -54,9 +55,9 @@ public class DefaultChainCredentialsProvider constructor(
5455
private val chain = CredentialsProviderChain(
5556
SystemPropertyCredentialsProvider(platformProvider::getProperty),
5657
EnvironmentCredentialsProvider(platformProvider::getenv),
57-
ProfileCredentialsProvider(profileName = profileName, platformProvider = platformProvider, httpClient = engine, region = region),
5858
// STS web identity provider can be constructed from either the profile OR 100% from the environment
5959
StsWebIdentityProvider(platformProvider = platformProvider, httpClient = engine, region = region),
60+
ProfileCredentialsProvider(profileName = profileName, platformProvider = platformProvider, httpClient = engine, region = region),
6061
EcsCredentialsProvider(platformProvider, engine),
6162
ImdsCredentialsProvider(
6263
client = lazy {

aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/profile/ProfileChain.kt

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,16 @@ internal data class ProfileChain(
3333
val roles: List<RoleArn>,
3434
) {
3535
companion object {
36+
/**
37+
* Resolves profile chain with the following precedence:
38+
*
39+
* 1. Static credentials
40+
* 2. Assume role with source profile OR assume role with named provider (mutually exclusive)
41+
* 3. Web ID token file & role arn
42+
* 4. SSO session
43+
* 5. Legacy SSO
44+
* 6. Process
45+
*/
3646
internal fun resolve(config: AwsSharedConfig): ProfileChain {
3747
val visited = mutableSetOf<String>()
3848
val chain = mutableListOf<RoleArn>()
@@ -53,11 +63,9 @@ internal data class ProfileChain(
5363
throw ProviderConfigurationException("profile formed an infinite loop: ${visited.joinToString(separator = " -> ")} -> $sourceProfileName")
5464
}
5565

56-
// when chaining assume role profiles, SDKs MUST terminate the chain as soon as they hit a profile with static credentials
57-
if (visited.size > 1) {
58-
leaf = profile.staticCredsOrNull()
59-
if (leaf != null) break@loop
60-
}
66+
// static credentials have the highest precedence
67+
leaf = profile.staticCredsOrNull()
68+
if (leaf != null) break@loop
6169

6270
// the existence of `role_arn` is the only signal that multiple profiles will be chained
6371
val roleArn = profile.roleArnOrNull()
@@ -132,8 +140,11 @@ internal const val SSO_SESSION = "sso_session"
132140
internal const val CREDENTIAL_PROCESS = "credential_process"
133141

134142
private fun AwsProfile.roleArnOrNull(): RoleArn? {
143+
val validSource = contains(CREDENTIAL_SOURCE) || contains(SOURCE_PROFILE)
144+
145+
// chained roles have higher precedence than web id token file
135146
// web identity tokens are leaf providers, not chained roles
136-
if (contains(WEB_IDENTITY_TOKEN_FILE)) return null
147+
if (!validSource && contains(WEB_IDENTITY_TOKEN_FILE)) return null
137148

138149
val roleArn = getOrNull(ROLE_ARN) ?: return null
139150

@@ -237,11 +248,12 @@ private fun AwsProfile.ssoSessionCreds(config: AwsSharedConfig): LeafProviderRes
237248
}
238249

239250
/**
240-
* Attempt to load [LeafProvider.Process] from the current profile or `null` if the current profile does not contain
251+
* Attempt to load [LeafProvider.Process] from the current profile or exception if the current profile does not contain
241252
* a credentials process command to execute
242253
*/
243-
private fun AwsProfile.processCreds(): LeafProviderResult? {
244-
if (!contains(CREDENTIAL_PROCESS)) return null
254+
private fun AwsProfile.processCreds(): LeafProviderResult {
255+
// Process is last in precedence - credentials not found means no credentials in profile
256+
if (!contains(CREDENTIAL_PROCESS)) return LeafProviderResult.Err("profile ($name) did not contain credential information")
245257

246258
val credentialProcess = getOrNull(CREDENTIAL_PROCESS) ?: return LeafProviderResult.Err("profile ($name) missing `$CREDENTIAL_PROCESS`")
247259

@@ -257,7 +269,7 @@ private fun AwsProfile.staticCreds(): LeafProviderResult {
257269
val secretKey = getOrNull(AWS_SECRET_ACCESS_KEY)
258270
val accountId = getOrNull(AWS_ACCOUNT_ID)
259271
return when {
260-
accessKeyId == null && secretKey == null -> LeafProviderResult.Err("profile ($name) did not contain credential information")
272+
accessKeyId == null && secretKey == null -> LeafProviderResult.Err("profile ($name) missing `aws_access_key_id` & `aws_secret_access_key`")
261273
accessKeyId == null -> LeafProviderResult.Err("profile ($name) missing `aws_access_key_id`")
262274
secretKey == null -> LeafProviderResult.Err("profile ($name) missing `aws_secret_access_key`")
263275
else -> {
@@ -315,7 +327,6 @@ private fun AwsProfile.leafProvider(config: AwsSharedConfig): LeafProvider {
315327
return webIdentityTokenCreds()
316328
.orElse { ssoSessionCreds(config) }
317329
.orElse(::legacySsoCreds)
318-
.orElse(::processCreds)
319-
.unwrapOrElse(::staticCreds)
330+
.unwrapOrElse(::processCreds)
320331
.unwrap()
321332
}

0 commit comments

Comments
 (0)