Categories
AWS Java

Credentials

What I want to achieve

In my past experiments the AWS credentials were 'magically' set in the background. To learn more about AWS credentials I will remove step by step the 'magic' and set credentials explicit in my code.

Cleanup

In my first experiment I set up the credentials on my Windows machine.
To ensure, that they are provided I test with my SNS-Test Program from my last post:

package aws;

import software.amazon.awssdk.services.sns.SnsClient;
import software.amazon.awssdk.services.sns.model.ListTopicsRequest;
import software.amazon.awssdk.services.sns.model.ListTopicsResponse;

public class CredentialsTest {

	public static void main(String[] args) {		
		SnsClient snsClient = SnsClient.builder().build();
		ListTopicsRequest request = ListTopicsRequest.builder().build();
		ListTopicsResponse result = snsClient.listTopics(request);
		System.out.println("Status was " + result.sdkHttpResponse().statusCode() + "\n\nTopics\n\n" + result.topics());
	}
}

Result: A list of my SNS topics

To remove the 'magic' I rename the files credentials and config in C:\Users\USERNAME\.aws folder to credentials_backup and config_backup.

Start CredentialsTest and the result: A list of my SNS topics.
So the credentials are provided by another mechanism.

Next try to remove the 'magic' I remove environment variables AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY and AWS_DEFAULT_REGION.
As I have started my IDE with this environment variables set, I need to restart IDE first.

Start CredentialsTest and the result:

Exception in thread "main" software.amazon.awssdk.core.exception.SdkClientException: Unable to load region from any of the providers in the chain software.amazon.awssdk.regions.providers.DefaultAwsRegionProviderChain@4372b9b6: [software.amazon.awssdk.regions.providers.SystemSettingsRegionProvider@3e6f3f28: Unable to load region from system settings. Region must be specified either via environment variable (AWS_REGION) or  system property (aws.region)., software.amazon.awssdk.regions.providers.AwsProfileRegionProvider@4816278d: No region provided in profile: default, software.amazon.awssdk.regions.providers.InstanceProfileRegionProvider@1ecee32c: Unable to contact EC2 metadata service.]
	at software.amazon.awssdk.core.exception.SdkClientException$BuilderImpl.build(SdkClientException.java:98)

Provide region:

SnsClient snsClient = SnsClient.builder().region(Region.EU_CENTRAL_1).build();

Result:

Exception in thread "main" software.amazon.awssdk.core.exception.SdkClientException: Unable to load credentials from any of the providers in the chain AwsCredentialsProviderChain(credentialsProviders=[SystemPropertyCredentialsProvider(), EnvironmentVariableCredentialsProvider(), WebIdentityTokenCredentialsProvider(), ProfileCredentialsProvider(), ContainerCredentialsProvider(), InstanceProfileCredentialsProvider()]) : [SystemPropertyCredentialsProvider(): Unable to load credentials from system settings. Access key must be specified either via environment variable (AWS_ACCESS_KEY_ID) or system property (aws.accessKeyId)., EnvironmentVariableCredentialsProvider(): Unable to load credentials from system settings. Access key must be specified either via environment variable (AWS_ACCESS_KEY_ID) or system property (aws.accessKeyId)., WebIdentityTokenCredentialsProvider(): Either the environment variable AWS_WEB_IDENTITY_TOKEN_FILE or the javaproperty aws.webIdentityTokenFile must be set., ProfileCredentialsProvider(): Profile file contained no credentials for profile 'default': ProfileFile(profiles=[]), ContainerCredentialsProvider(): Cannot fetch credentials from container - neither AWS_CONTAINER_CREDENTIALS_FULL_URI or AWS_CONTAINER_CREDENTIALS_RELATIVE_URI environment variables are set., InstanceProfileCredentialsProvider(): Unable to load credentials from service endpoint.]
	at software.amazon.awssdk.core.exception.SdkClientException$BuilderImpl.build(SdkClientException.java:98)

OK, looks good so far.
Remove region from code and restore files credentials and config in C:\Users\USERNAME\.aws folder.
Run CredentialsTest, Result: A list of my SNS topics.

Rename the files credentials and config in C:\Users\USERNAME\.aws folder to credentials_backup and config_backup again.
Run CredentialsTest, Result: Unable to load region error again.

The 'magic' has been removed,

ProfileCredentialsProvider

Restore files credentials and config in C:\Users\USERNAME\.aws folder.
Empty [default] block and create a new [CredentialsTest] block:

[default]

[CredentialsTest]
aws_access_key_id = My_AWS_Access_Key_Id
aws_secret_access_key = My_AWS_Secret_Access_Key
[default]

[CredentialsTest]
region = eu-central-1

Run CredentialsTest, Result:

2020-09-09 21:13:17 [main] WARN  software.amazon.awssdk.profiles.internal.ProfileFileReader:105 - Ignoring profile 'CredentialsTest' on line 3 because it did not start with 'profile ' and it was not 'default'.
Exception in thread "main" software.amazon.awssdk.core.exception.SdkClientException: Unable to load region from any of the providers in the chain software.amazon.awssdk.regions.providers.DefaultAwsRegionProviderChain@260e86a1: [software.amazon.awssdk.regions.providers.SystemSettingsRegionProvider@59e505b2: Unable to load region from system settings. Region must be specified either via environment variable (AWS_REGION) or  system property (aws.region)., software.amazon.awssdk.regions.providers.AwsProfileRegionProvider@8e50104: No region provided in profile: default, software.amazon.awssdk.regions.providers.InstanceProfileRegionProvider@43b6123e: Unable to contact EC2 metadata service.]

So I used to try to work with the ProfileCredentialsProvider this way:

import com.amazonaws.auth.profile.ProfileCredentialsProvider;
SnsClient snsClient = SnsClient.builder().credentialsProvider(new ProfileCredentialsProvider("CredentialsTest")).build();

Unfortunatly this won't compile because:

The method credentialsProvider(AwsCredentialsProvider) in the type AwsClientBuilder<SnsClientBuilder,
 SnsClient> is not applicable for the arguments (ProfileCredentialsProvider)

Refactor to:

import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
import software.amazon.awssdk.auth.credentials.ProfileCredentialsProvider;
AwsCredentialsProvider credentialsProvider = ProfileCredentialsProvider.builder().profileName("CredentialsTest").build();
SnsClient snsClient = SnsClient.builder().credentialsProvider(credentialsProvider).build();

Result:

2020-09-09 21:22:40 [main] WARN  software.amazon.awssdk.profiles.internal.ProfileFileReader:105 - Ignoring profile 'CredentialsTest' on line 3 because it did not start with 'profile ' and it was not 'default'.
Exception in thread "main" software.amazon.awssdk.core.exception.SdkClientException: Unable to load region from any of the providers in the chain software.amazon.awssdk.regions.providers.DefaultAwsRegionProviderChain@78f5c518: [software.amazon.awssdk.regions.providers.SystemSettingsRegionProvider@f107c50: Unable to load region from system settings. Region must be specified either via environment variable (AWS_REGION) or  system property (aws.region)., software.amazon.awssdk.regions.providers.AwsProfileRegionProvider@4ebff610: No region provided in profile: default, software.amazon.awssdk.regions.providers.InstanceProfileRegionProvider@8692d67: Unable to contact EC2 metadata service.]

Hmkay, enhance the code with a region; need to set this explicit, could not find any way to read this from the config file.

SnsClient snsClient = SnsClient.builder().credentialsProvider(credentialsProvider).region(Region.EU_CENTRAL_1).build();

Result: A list of my SNS topics.

Rename the files credentials and config in C:\Users\USERNAME\.aws folder to credentials_backup and config_backup again.
Run CredentialsTest, Result: Profile file contained no credentials for profile 'CredentialsTest' error.

Remove ProfileCredentialsProvider and Region from code.
Run CredentialsTest, Result: Unable to load region error again.

Own AwsCredentialsProvider implementation

Write an own credential provider, the simplest way:

package aws;

import software.amazon.awssdk.auth.credentials.AwsCredentials;
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;

public class IngosCredentialProvider implements AwsCredentialsProvider {

	public AwsCredentials resolveCredentials() {
		System.out.println("IngosCredentialProvider::resolveCredentials called");
		AwsCredentials credentials = new AwsCredentials() {
			
			public String secretAccessKey() {
				return "My_AWS_Secret_Access_Key";
			}
			
			public String accessKeyId() {
				return "My_AWS_Access_Key_Id";
			}
		};
		return credentials;
	}
}

Use your own credentials provider in code, don't forget the region:

package aws;

import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.sns.SnsClient;
import software.amazon.awssdk.services.sns.model.ListTopicsRequest;
import software.amazon.awssdk.services.sns.model.ListTopicsResponse;

public class CredentialsTest {

	public static void main(String[] args) {
		SnsClient snsClient = SnsClient.builder().credentialsProvider(new IngosCredentialProvider()).region(Region.EU_CENTRAL_1).build();
		ListTopicsRequest request = ListTopicsRequest.builder().build();
		ListTopicsResponse result = snsClient.listTopics(request);
		System.out.println("Status was " + result.sdkHttpResponse().statusCode() + "\n\nTopics\n\n" + result.topics());
	}
}

Run CredentialsTest, Result: A list of my SNS topics.

Leave a Reply

Your email address will not be published. Required fields are marked *