Skip to content
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

Issue setting credentials provider and region with the new high level configuration #358

Open
NathanEckert opened this issue Aug 30, 2024 · 3 comments

Comments

@NathanEckert
Copy link

Problem:

Latest version 3.2.1 of the encryption client.

With the new high level configuration (see https://github.com/aws/amazon-s3-encryption-client-java/blob/main/src/examples/java/software/amazon/encryption/s3/examples/ClientConfigurationExample.java),

I was expecting to be able to create my client like this:

    return S3EncryptionClient.builder()
        .credentialsProvider(credentialsProvider)
        .region(region)
        .kmsKeyId(keyId)
        .enableLegacyUnauthenticatedModes(true)
        .enableLegacyWrappingAlgorithms(true)
        .build();

However, when reading a KMS enrcypted file, this does not work software.amazon.encryption.s3.S3EncryptionClientException: Missing Authentication Token (Service: Kms, Status Code: 400, Request ID: ***************)

I tried the following variations:

    return S3EncryptionClient.builder()
        .credentialsProvider(credentialsProvider)
        .region(region)
        .kmsKeyId(keyId)
        .enableLegacyUnauthenticatedModes(true)
        .enableLegacyWrappingAlgorithms(true)
        .wrappedClient(S3Client.builder().credentialsProvider(credentialsProvider).region(region).build())
        .wrappedAsyncClient(S3AsyncClient.builder().credentialsProvider(credentialsProvider).region(region).build())
        .build();

This raised the same exception

Finally, the one I got working is the one were I remove the high level provider:

    return S3EncryptionClient.builder()
        .region(region)
        .kmsKeyId(keyId)
        .enableLegacyUnauthenticatedModes(true)
        .enableLegacyWrappingAlgorithms(true)
        .wrappedClient(S3Client.builder().credentialsProvider(credentialsProvider).region(region).build())
        .wrappedAsyncClient(S3AsyncClient.builder().credentialsProvider(credentialsProvider).region(region).build())
        .build();

Also, I was surprised to see the following failing:

    return S3EncryptionClient.builder()
        .kmsKeyId(keyId)
        .enableLegacyUnauthenticatedModes(true)
        .enableLegacyWrappingAlgorithms(true)
        .wrappedClient(S3Client.builder().credentialsProvider(credentialsProvider).region(region).build())
        .wrappedAsyncClient(S3AsyncClient.builder().credentialsProvider(credentialsProvider).region(region).build())
        .build();

With this time a different error: software.amazon.awssdk.core.exception.SdkClientException: Unable to load region from any of the providers in the chain software.amazon.awssdk.regions.providers.DefaultAwsRegionProviderChain

Shouldn't all of those work ?

@kessplas
Copy link
Contributor

kessplas commented Oct 3, 2024

Hey Nathan,

Could you share what credential provider you're using? This is generally unexpected behavior; the KMS client is instantiated using the credentialsProvider that's passed to the S3EC's builder. Based on the code snippets you have provided, the credential provider you're using doesn't work with KMS, but the default one does.

To give more detail:

    return S3EncryptionClient.builder()
        .credentialsProvider(credentialsProvider)
        .region(region)
        .kmsKeyId(keyId)
        .enableLegacyUnauthenticatedModes(true)
        .enableLegacyWrappingAlgorithms(true)
        .build();

This shares the credentialsProvider and region between KMS and S3 clients.

    return S3EncryptionClient.builder()
        .credentialsProvider(credentialsProvider)
        .region(region)
        .kmsKeyId(keyId)
        .enableLegacyUnauthenticatedModes(true)
        .enableLegacyWrappingAlgorithms(true)
        .wrappedClient(S3Client.builder().credentialsProvider(credentialsProvider).region(region).build())
        .wrappedAsyncClient(S3AsyncClient.builder().credentialsProvider(credentialsProvider).region(region).build())
        .build();

This is functionally equivalent to the first snippet; the top-level credentialProvider and region are applied to the KMS client, and the S3 clients are explicitly configured using the same values.

    return S3EncryptionClient.builder()
        .region(region)
        .kmsKeyId(keyId)
        .enableLegacyUnauthenticatedModes(true)
        .enableLegacyWrappingAlgorithms(true)
        .wrappedClient(S3Client.builder().credentialsProvider(credentialsProvider).region(region).build())
        .wrappedAsyncClient(S3AsyncClient.builder().credentialsProvider(credentialsProvider).region(region).build())
        .build();

This configures the region on the KMS client, but the KMS client will use the default credentials provider instead of credentialsProvider since none is given. In other words, the default credential provider works with KMS, and the explicitly provided credentialsProvider instance works with S3.

    return S3EncryptionClient.builder()
        .kmsKeyId(keyId)
        .enableLegacyUnauthenticatedModes(true)
        .enableLegacyWrappingAlgorithms(true)
        .wrappedClient(S3Client.builder().credentialsProvider(credentialsProvider).region(region).build())
        .wrappedAsyncClient(S3AsyncClient.builder().credentialsProvider(credentialsProvider).region(region).build())
        .build();

This uses the default region with the KMS client, which in your case is not set, hence the SdkClientException: Unable to load region... error from KMS. This is different from the above configuration because in the above configuration the region is explicitly configured for KMS via the top-level configuration.

@NathanEckert
Copy link
Author

We are using the following credentials provider:

public class S3CredentialsProviderChain implements AwsCredentialsProvider {

  private static final Log LOG = LogFactory.getLog(S3CredentialsProviderChain.class);

  private boolean tryDefaultChain = true;

  @Override
  public AwsCredentials resolveCredentials() {
    if (this.tryDefaultChain) {
      try {
        return AwsCredentialsProviderChain.builder().build().resolveCredentials();
      } catch (final RuntimeException e) {
        // No provider found in the default chain, we won't try it again.
        this.tryDefaultChain = false;
        LOG.debug("No S3 credentials available; falling back to anonymous access for this session");
      }
    }
    return AnonymousCredentialsProvider.create().resolveCredentials();
  }
}

I checked in debug mode and an exception is raised when resolving the credentials, thus it returns an AnonymousCredentialsProvider

@kessplas
Copy link
Contributor

Hey @NathanEckert,

When you set the credentialProvider() on the S3 Encryption Client and supply a KMS key ID, the S3EC will use that credential provider for the S3 clients AND the KMS client. It seems like the S3CredentialsProviderChain / AnonymousCredentialsProvider you're using don't work with KMS. To resolve this, you can either modify the provider to return credentials that work with KMS as well as S3, or explicitly configure and provide the KMS client separately through the KmsKeyring. This will override the top-level credentialProvider() configuration.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants