Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
6 changes: 6 additions & 0 deletions .changes/next-release/feature-AWSSDKforJavav2-2c52c12.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"type": "feature",
"category": "AWS SDK for Java v2",
"contributor": "",
"description": "Adds setting to disable making EC2 Instance Metadata Service (IMDS) calls for credentials without a token header when prefetching a token does not work. This feature can be configured through environment variables (AWS_EC2_METADATA_V1_DISABLED), system property (aws.disableEc2MetadataV1) or AWS config file (ec2_metadata_v1_disabled). When you configure this setting to true, no calls without token headers will be made to IMDS."
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import software.amazon.awssdk.annotations.SdkPublicApi;
import software.amazon.awssdk.annotations.SdkTestInternalApi;
import software.amazon.awssdk.auth.credentials.internal.Ec2MetadataConfigProvider;
import software.amazon.awssdk.auth.credentials.internal.Ec2MetadataDisableV1Resolver;
import software.amazon.awssdk.auth.credentials.internal.HttpCredentialsLoader;
import software.amazon.awssdk.auth.credentials.internal.HttpCredentialsLoader.LoadedCredentials;
import software.amazon.awssdk.auth.credentials.internal.StaticResourcesEndpointProvider;
Expand All @@ -40,6 +41,7 @@
import software.amazon.awssdk.profiles.ProfileFile;
import software.amazon.awssdk.profiles.ProfileFileSupplier;
import software.amazon.awssdk.profiles.ProfileFileSystemSetting;
import software.amazon.awssdk.profiles.ProfileProperty;
import software.amazon.awssdk.regions.util.HttpResourcesUtils;
import software.amazon.awssdk.regions.util.ResourcesEndpointProvider;
import software.amazon.awssdk.utils.Logger;
Expand All @@ -53,10 +55,13 @@

/**
* Credentials provider implementation that loads credentials from the Amazon EC2 Instance Metadata Service.
*
* <P>
* <p>
* If {@link SdkSystemSetting#AWS_EC2_METADATA_DISABLED} is set to true, it will not try to load
* credentials from EC2 metadata service and will return null.
* <p>
* If {@link SdkSystemSetting#AWS_EC2_METADATA_V1_DISABLED} or {@link ProfileProperty#EC2_METADATA_V1_DISABLED}
* is set to true, credentials will only be loaded from EC2 metadata service if a token is successfully retrieved -
* fallback to load credentials without a token will be disabled.
*/
@SdkPublicApi
public final class InstanceProfileCredentialsProvider
Expand Down Expand Up @@ -225,17 +230,15 @@ private String getToken(String imdsHostname) {
return HttpResourcesUtils.instance().readResource(tokenEndpoint, "PUT");
} catch (SdkServiceException e) {
if (e.statusCode() == 400) {

throw SdkClientException.builder()
.message("Unable to fetch metadata token.")
.cause(e)
.build();
}

log.debug(() -> "Ignoring non-fatal exception while attempting to load metadata token from instance profile.", e);
return null;
return handleTokenErrorResponse(e);
} catch (Exception e) {
log.debug(() -> "Ignoring non-fatal exception while attempting to load metadata token from instance profile.", e);
return null;
return handleTokenErrorResponse(e);
}
}

Expand All @@ -247,6 +250,25 @@ private URI getTokenEndpoint(String imdsHostname) {
return URI.create(finalHost + TOKEN_RESOURCE);
}

private String handleTokenErrorResponse(Exception e) {
if (isInsecureFallbackDisabled()) {
String message = String.format("Failed to retrieve IMDS token, and fallback to IMDS v1 is disabled via the %s "
+ "environment variable and/or %s system property",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and/or config as well?

SdkSystemSetting.AWS_EC2_METADATA_V1_DISABLED.environmentVariable(),
SdkSystemSetting.AWS_EC2_METADATA_V1_DISABLED.property());
throw SdkClientException.builder()
.message(message)
.cause(e)
.build();
}
log.debug(() -> "Ignoring non-fatal exception while attempting to load metadata token from instance profile.", e);
return null;
}

private boolean isInsecureFallbackDisabled() {
return Ec2MetadataDisableV1Resolver.create(profileFile, profileName).resolve();
}

private String[] getSecurityCredentials(String imdsHostname, String metadataToken) {
ResourcesEndpointProvider securityCredentialsEndpoint =
new StaticResourcesEndpointProvider(URI.create(imdsHostname + SECURITY_CREDENTIALS_RESOURCE),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/

package software.amazon.awssdk.auth.credentials.internal;

import java.util.Optional;
import java.util.function.Supplier;
import software.amazon.awssdk.annotations.SdkInternalApi;
import software.amazon.awssdk.core.SdkSystemSetting;
import software.amazon.awssdk.profiles.ProfileFile;
import software.amazon.awssdk.profiles.ProfileFileSystemSetting;
import software.amazon.awssdk.profiles.ProfileProperty;
import software.amazon.awssdk.utils.OptionalUtils;

@SdkInternalApi
public final class Ec2MetadataDisableV1Resolver {
private final Supplier<ProfileFile> profileFile;
private final String profileName;

private Ec2MetadataDisableV1Resolver(Supplier<ProfileFile> profileFile, String profileName) {
this.profileFile = profileFile;
this.profileName = profileName;
}

public static Ec2MetadataDisableV1Resolver create(Supplier<ProfileFile> profileFile, String profileName) {
return new Ec2MetadataDisableV1Resolver(profileFile, profileName);
}

public boolean resolve() {
return OptionalUtils.firstPresent(fromSystemSettings(),
() -> fromProfileFile(profileFile, profileName))
.orElse(false);
}

private static Optional<Boolean> fromSystemSettings() {
return SdkSystemSetting.AWS_EC2_METADATA_V1_DISABLED.getBooleanValue();
}

private static Optional<Boolean> fromProfileFile(Supplier<ProfileFile> profileFile, String profileName) {
profileFile = profileFile != null ? profileFile : ProfileFile::defaultProfileFile;
profileName = profileName != null ? profileName : ProfileFileSystemSetting.AWS_PROFILE.getStringValueOrThrow();
if (profileFile.get() == null) {
return Optional.empty();
}
return profileFile.get()
.profile(profileName)
.flatMap(p -> p.property(ProfileProperty.EC2_METADATA_V1_DISABLED))
.map(Ec2MetadataDisableV1Resolver::safeProfileStringToBoolean);
}

private static boolean safeProfileStringToBoolean(String value) {
if (value.equalsIgnoreCase("true")) {
return true;
}
if (value.equalsIgnoreCase("false")) {
return false;
}

throw new IllegalStateException("Profile property '" + ProfileProperty.EC2_METADATA_V1_DISABLED + "', "
+ "was defined as '" + value + "', but should be 'false' or 'true'");
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -264,13 +264,14 @@ private AwsCredentialsProvider credentialSourceCredentialProvider(CredentialSour
case EC2_INSTANCE_METADATA:
// The IMDS credentials provider should source the endpoint config properties from the currently active profile
Ec2MetadataConfigProvider configProvider = Ec2MetadataConfigProvider.builder()
.profileFile(() -> profileFile)
.profileName(name)
.build();

.profileFile(() -> profileFile)
.profileName(name)
.build();
return InstanceProfileCredentialsProvider.builder()
.endpoint(configProvider.getEndpoint())
.build();
.endpoint(configProvider.getEndpoint())
.profileFile(profileFile)
.profileName(name)
.build();
case ENVIRONMENT:
return AwsCredentialsProviderChain.builder()
.addCredentialsProvider(SystemPropertyCredentialsProvider.create())
Expand Down
Loading