diff --git a/.changes/next-release/feature-AWSSDKforJavav2-2f79ee6.json b/.changes/next-release/feature-AWSSDKforJavav2-2f79ee6.json new file mode 100644 index 000000000000..6a39e27bff87 --- /dev/null +++ b/.changes/next-release/feature-AWSSDKforJavav2-2f79ee6.json @@ -0,0 +1,6 @@ +{ + "type": "feature", + "category": "AWS SDK for Java v2", + "contributor": "", + "description": "Adding 'disable_ec2_metadata' in the profile property, this can be used to disable IMDS credential fetching." +} diff --git a/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/InstanceProfileCredentialsProvider.java b/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/InstanceProfileCredentialsProvider.java index b1ddc5d7faef..32169627bf01 100644 --- a/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/InstanceProfileCredentialsProvider.java +++ b/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/InstanceProfileCredentialsProvider.java @@ -55,7 +55,8 @@ /** * Credentials provider implementation that loads credentials from the Amazon EC2 Instance Metadata Service. *

- * If {@link SdkSystemSetting#AWS_EC2_METADATA_DISABLED} is set to true, it will not try to load + * If {@link SdkSystemSetting#AWS_EC2_METADATA_DISABLED} is set to true, or if the configuration file parameter + * {@link ProfileProperty#EC2_METADATA_DISABLED} is set to true, it will not try to load * credentials from EC2 metadata service and will return null. *

* If {@link SdkSystemSetting#AWS_EC2_METADATA_V1_DISABLED} or {@link ProfileProperty#EC2_METADATA_V1_DISABLED} @@ -152,7 +153,8 @@ public AwsCredentials resolveCredentials() { private RefreshResult refreshCredentials() { if (isLocalCredentialLoadingDisabled()) { - throw SdkClientException.create("IMDS credentials have been disabled by environment variable or system property."); + throw SdkClientException.create("IMDS credentials have been disabled by environment variable, system property, " + + "or configuration file profile setting."); } try { @@ -170,7 +172,7 @@ private RefreshResult refreshCredentials() { } private boolean isLocalCredentialLoadingDisabled() { - return SdkSystemSetting.AWS_EC2_METADATA_DISABLED.getBooleanValueOrThrow(); + return SdkSystemSetting.AWS_EC2_METADATA_DISABLED.getBooleanValueOrThrow() || configProvider.isMetadataDisabled(); } private Instant staleTime(Instant expiration) { diff --git a/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/internal/Ec2MetadataConfigProvider.java b/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/internal/Ec2MetadataConfigProvider.java index fb73540e75b4..fd9a040f8cb8 100644 --- a/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/internal/Ec2MetadataConfigProvider.java +++ b/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/internal/Ec2MetadataConfigProvider.java @@ -45,12 +45,14 @@ public final class Ec2MetadataConfigProvider { private final String profileName; private final Lazy metadataV1Disabled; + private final Lazy metadataDisabled; private final Lazy serviceTimeout; private Ec2MetadataConfigProvider(Builder builder) { this.profileFile = builder.profileFile; this.profileName = builder.profileName; this.metadataV1Disabled = new Lazy<>(this::resolveMetadataV1Disabled); + this.metadataDisabled = new Lazy<>(this::resolveMetadataDisabled); this.serviceTimeout = new Lazy<>(this::resolveServiceTimeout); } @@ -120,6 +122,14 @@ public boolean isMetadataV1Disabled() { return metadataV1Disabled.getValue(); } + /** + * Resolves whether EC2 Metadata is disabled. + * @return true if EC2 Metadata is disabled, false otherwise. + */ + public boolean isMetadataDisabled() { + return metadataDisabled.getValue(); + } + /** * Resolves the EC2 Metadata Service Timeout in milliseconds. * @return the timeout value in milliseconds. @@ -137,6 +147,12 @@ private boolean resolveMetadataV1Disabled() { .orElse(false); } + // Internal resolution logic for Metadata disabled + private boolean resolveMetadataDisabled() { + return fromProfileFileMetadataDisabled(profileFile, profileName) + .orElse(false); + } + // Internal resolution logic for Service Timeout private long resolveServiceTimeout() { return OptionalUtils.firstPresent( @@ -158,6 +174,15 @@ private static Optional fromProfileFileMetadataV1Disabled(Supplier p.booleanProperty(ProfileProperty.EC2_METADATA_V1_DISABLED)); } + // Profile file resolution for Metadata disabled + private static Optional fromProfileFileMetadataDisabled(Supplier profileFile, String profileName) { + Optional profile = profileFile.get().profile(profileName); + if (profile.isPresent()) { + return profile.get().booleanProperty(ProfileProperty.EC2_METADATA_DISABLED); + } + return Optional.empty(); + } + // System settings resolution for Service Timeout private static Optional fromSystemSettingsServiceTimeout() { return SdkSystemSetting.AWS_METADATA_SERVICE_TIMEOUT.getNonDefaultStringValue() diff --git a/core/auth/src/test/java/software/amazon/awssdk/auth/credentials/InstanceProfileCredentialsProviderTest.java b/core/auth/src/test/java/software/amazon/awssdk/auth/credentials/InstanceProfileCredentialsProviderTest.java index 671e591b17b5..a6b338de69a3 100644 --- a/core/auth/src/test/java/software/amazon/awssdk/auth/credentials/InstanceProfileCredentialsProviderTest.java +++ b/core/auth/src/test/java/software/amazon/awssdk/auth/credentials/InstanceProfileCredentialsProviderTest.java @@ -319,12 +319,49 @@ void resolveCredentials_metadataLookupDisabled_throws() { try { assertThatThrownBy(() -> InstanceProfileCredentialsProvider.builder().build().resolveCredentials()) .isInstanceOf(SdkClientException.class) - .hasMessage("IMDS credentials have been disabled by environment variable or system property."); + .hasMessage("IMDS credentials have been disabled by environment variable, system property, or configuration file profile setting."); } finally { System.clearProperty(SdkSystemSetting.AWS_EC2_METADATA_DISABLED.property()); } } + @Test + void resolveCredentials_metadataDisabledThroughConfig_throwsException() { + stubSecureCredentialsResponse(aResponse().withBody(STUB_CREDENTIALS)); + try { + InstanceProfileCredentialsProvider.builder() + .profileFile(configFile("profile test", Pair.of("disable_ec2_metadata", "true"))) + .profileName("test") + .build() + .resolveCredentials(); + ProfileFile config = configFile("profile test", Pair.of("disable_ec2_metadata", "true")); + System.out.println("Test config file: " + config.toString()); + } catch (Exception e) { + assertThat(e).isInstanceOf(SdkClientException.class); + assertThat(e).hasMessage("IMDS credentials have been disabled by environment variable, system property, or configuration file profile setting."); + } + + // Verify that no calls were made to the IMDS endpoints + WireMock.verify(0, putRequestedFor(urlPathEqualTo(TOKEN_RESOURCE_PATH))); + WireMock.verify(0, getRequestedFor(urlPathEqualTo(CREDENTIALS_RESOURCE_PATH))); + } + + @Test + void resolveCredentials_metadataEnabledThroughConfig_returnsCredentials() { + stubSecureCredentialsResponse(aResponse().withBody(STUB_CREDENTIALS)); + + InstanceProfileCredentialsProvider provider = InstanceProfileCredentialsProvider.builder() + .profileFile(configFile("profile test", Pair.of("disable_ec2_metadata", "false"))) + .profileName("test") + .build(); + + AwsCredentials credentials = provider.resolveCredentials(); + assertThat(credentials.accessKeyId()).isEqualTo("ACCESS_KEY_ID"); + assertThat(credentials.secretAccessKey()).isEqualTo("SECRET_ACCESS_KEY"); + + verifyImdsCallWithToken(); + } + @Test void resolveCredentials_customProfileFileAndName_usesCorrectEndpoint() { WireMockServer mockMetadataEndpoint_2 = new WireMockServer(WireMockConfiguration.options().dynamicPort()); diff --git a/core/profiles/src/main/java/software/amazon/awssdk/profiles/ProfileProperty.java b/core/profiles/src/main/java/software/amazon/awssdk/profiles/ProfileProperty.java index cd97c6047a55..fb34cd665847 100644 --- a/core/profiles/src/main/java/software/amazon/awssdk/profiles/ProfileProperty.java +++ b/core/profiles/src/main/java/software/amazon/awssdk/profiles/ProfileProperty.java @@ -161,6 +161,12 @@ public final class ProfileProperty { public static final String EC2_METADATA_V1_DISABLED = "ec2_metadata_v1_disabled"; + /** + * Disables the use of the Amazon EC2 instance metadata service (IMDS). When set to "true", SDK will not try to load + * credentials from EC2 metadata service and will return null. + */ + public static final String EC2_METADATA_DISABLED = "disable_ec2_metadata"; + public static final String METADATA_SERVICE_TIMEOUT = "metadata_service_timeout"; /**