Skip to content

feat: update to IMDS credentials provider SEP v2.1.1 #1625

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import aws.smithy.kotlin.runtime.util.asyncLazy
import kotlin.coroutines.coroutineContext

private const val CODE_ASSUME_ROLE_UNAUTHORIZED_ACCESS: String = "AssumeRoleUnauthorizedAccess"
private const val MAX_ATTEMPTS = 10
private const val PROVIDER_NAME = "IMDSv2"

/**
Expand Down Expand Up @@ -105,11 +106,15 @@ public class ImdsCredentialsProvider(

override suspend fun resolve(attributes: Attributes): Credentials = sfg.singleFlight(::resolveSingleFlight)

private suspend fun resolveSingleFlight(): Credentials {
private suspend fun resolveSingleFlight(attempts: Int = 0): Credentials {
if (providerDisabled.get()) {
throw CredentialsNotLoadedException("AWS EC2 metadata is explicitly disabled; credentials not loaded")
}

if (attempts >= MAX_ATTEMPTS) {
throw CredentialsNotLoadedException("Failed to retrieve profile credentials after $attempts attempts")
}

val profileName = instanceProfileName.get() ?: resolvedProfileName ?: try {
actualClient.get(urlBase).also {
if (apiVersion == null) {
Expand All @@ -122,7 +127,7 @@ public class ImdsCredentialsProvider(
apiVersion == null && ex.status == HttpStatusCode.NotFound -> {
// Tried EXTENDED and that didn't work; fallback to LEGACY
apiVersion = ApiVersion.LEGACY
return resolveSingleFlight()
return resolveSingleFlight(attempts) // one-time condition, do not increase `attempts`
}

ex.status == HttpStatusCode.NotFound -> {
Expand All @@ -148,13 +153,13 @@ public class ImdsCredentialsProvider(
apiVersion == null && ex.status == HttpStatusCode.NotFound -> {
// Tried EXTENDED and that didn't work; fallback to LEGACY
apiVersion = ApiVersion.LEGACY
return resolveSingleFlight()
return resolveSingleFlight() // one-time condition, do not increase `attempts`
}

instanceProfileName.get() == null && ex.status == HttpStatusCode.NotFound -> {
// A previously-resolved profile is now invalid; forget the resolved name and re-resolve
resolvedProfileName = null
return resolveSingleFlight()
return resolveSingleFlight(attempts + 1) // potentially infinite recursion, increase `attempts`
}

else -> return usePreviousCredentials()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
package aws.sdk.kotlin.runtime.config

import aws.sdk.kotlin.runtime.InternalSdkApi
import aws.sdk.kotlin.runtime.config.AwsSdkSetting.AwsAccessKeyId
import aws.sdk.kotlin.runtime.config.AwsSdkSetting.AwsContainerCredentialsRelativeUri
import aws.sdk.kotlin.runtime.config.AwsSdkSetting.AwsSecretAccessKey
import aws.sdk.kotlin.runtime.config.endpoints.AccountIdEndpointMode
import aws.sdk.kotlin.runtime.http.AWS_APP_ID_ENV
import aws.sdk.kotlin.runtime.http.AWS_APP_ID_PROP
Expand Down Expand Up @@ -88,7 +91,7 @@ public object AwsSdkSetting {
* Whether to load information such as credentials, regions from EC2 Metadata instance service.
*/
public val AwsEc2MetadataDisabled: EnvironmentSetting<Boolean> =
boolEnvSetting("aws.disableEc2Metadata", "AWS_EC2_METADATA_DISABLED").orElse(false)
boolEnvSetting("aws.ec2MetadataDisabled", "AWS_EC2_METADATA_DISABLED").orElse(false)

/**
* The EC2 instance metadata service endpoint.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ public val AwsProfile.ec2InstanceProfileName: String?
*/
@InternalSdkApi
public val AwsProfile.ec2MetadataDisabled: Boolean?
get() = getBooleanOrNull("disable_ec2_metadata")
get() = getBooleanOrNull("ec2_metadata_disabled")

/**
* Parse a config value as a boolean, ignoring case.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -653,6 +653,149 @@ val imdsCredentialsTestSpec = """
"result": "invalid profile"
}
]
},
{
"summary": "Test IMDS credentials provider when profile never stabilizes returns no credentials",
"config": {
"ec2InstanceProfileName": null
},
"expectations": [
{
"get": "/latest/meta-data/iam/security-credentials-extended",
"response": {
"status": 200,
"body": "my-profile-0013"
}
},
{
"get": "/latest/meta-data/iam/security-credentials-extended/my-profile-0013",
"response": {
"status": 404
}
},
{
"get": "/latest/meta-data/iam/security-credentials-extended",
"response": {
"status": 200,
"body": "my-profile-0013"
}
},
{
"get": "/latest/meta-data/iam/security-credentials-extended/my-profile-0013",
"response": {
"status": 404
}
},
{
"get": "/latest/meta-data/iam/security-credentials-extended",
"response": {
"status": 200,
"body": "my-profile-0013"
}
},
{
"get": "/latest/meta-data/iam/security-credentials-extended/my-profile-0013",
"response": {
"status": 404
}
},
{
"get": "/latest/meta-data/iam/security-credentials-extended",
"response": {
"status": 200,
"body": "my-profile-0013"
}
},
{
"get": "/latest/meta-data/iam/security-credentials-extended/my-profile-0013",
"response": {
"status": 404
}
},
{
"get": "/latest/meta-data/iam/security-credentials-extended",
"response": {
"status": 200,
"body": "my-profile-0013"
}
},
{
"get": "/latest/meta-data/iam/security-credentials-extended/my-profile-0013",
"response": {
"status": 404
}
},
{
"get": "/latest/meta-data/iam/security-credentials-extended",
"response": {
"status": 200,
"body": "my-profile-0013"
}
},
{
"get": "/latest/meta-data/iam/security-credentials-extended/my-profile-0013",
"response": {
"status": 404
}
},
{
"get": "/latest/meta-data/iam/security-credentials-extended",
"response": {
"status": 200,
"body": "my-profile-0013"
}
},
{
"get": "/latest/meta-data/iam/security-credentials-extended/my-profile-0013",
"response": {
"status": 404
}
},
{
"get": "/latest/meta-data/iam/security-credentials-extended",
"response": {
"status": 200,
"body": "my-profile-0013"
}
},
{
"get": "/latest/meta-data/iam/security-credentials-extended/my-profile-0013",
"response": {
"status": 404
}
},
{
"get": "/latest/meta-data/iam/security-credentials-extended",
"response": {
"status": 200,
"body": "my-profile-0013"
}
},
{
"get": "/latest/meta-data/iam/security-credentials-extended/my-profile-0013",
"response": {
"status": 404
}
},
{
"get": "/latest/meta-data/iam/security-credentials-extended",
"response": {
"status": 200,
"body": "my-profile-0013"
}
},
{
"get": "/latest/meta-data/iam/security-credentials-extended/my-profile-0013",
"response": {
"status": 404
}
}
],
"outcomes": [
{
"result": "no credentials"
}
]
}
]
""".trimIndent()
Loading