From bafd5107d0e62c1df26519358bba722b797868b3 Mon Sep 17 00:00:00 2001 From: Jeff Yemin Date: Tue, 30 Aug 2022 17:43:11 -0400 Subject: [PATCH 1/5] Use AWS SDK v2 if available for AWS credential fetching To fetch AWS credentials from the environment: * Use AWS SDK v2 if available on the classpath * Otherwise, use the existing driver implementation JAVA-4718 --- .evergreen/.evg.yml | 25 ++++++- .evergreen/run-mongodb-aws-test.sh | 11 +-- driver-core/build.gradle | 3 + .../authentication/AwsCredentialHelper.java | 68 +++++++---------- .../AwsSdkV2CredentialSupplier.java | 42 +++++++++++ .../BuiltInAwsCredentialSupplier.java | 73 +++++++++++++++++++ .../AwsAuthenticationSpecification.groovy | 7 ++ 7 files changed, 180 insertions(+), 49 deletions(-) create mode 100644 driver-core/src/main/com/mongodb/internal/authentication/AwsSdkV2CredentialSupplier.java create mode 100644 driver-core/src/main/com/mongodb/internal/authentication/BuiltInAwsCredentialSupplier.java diff --git a/.evergreen/.evg.yml b/.evergreen/.evg.yml index 2a6274e18d2..8d90aad18f9 100644 --- a/.evergreen/.evg.yml +++ b/.evergreen/.evg.yml @@ -371,7 +371,9 @@ functions: PASS=$(urlencode ${iam_auth_ecs_secret_access_key}) MONGODB_URI="mongodb://$USER:$PASS@localhost" EOF - JAVA_VERSION=${JAVA_VERSION} PROJECT_DIRECTORY=${PROJECT_DIRECTORY} .evergreen/run-mongodb-aws-test.sh + JAVA_VERSION=${JAVA_VERSION} PROJECT_DIRECTORY=${PROJECT_DIRECTORY} \ + USE_BUILT_IN_AWS_CREDENTIAL_PROVIDER=${USE_BUILT_IN_AWS_CREDENTIAL_PROVIDER} \ + .evergreen/run-mongodb-aws-test.sh "run aws auth test with assume role credentials": - command: shell.exec @@ -400,7 +402,9 @@ functions: SESSION_TOKEN=$(urlencode $SESSION_TOKEN) MONGODB_URI="mongodb://$USER:$PASS@localhost" EOF - JAVA_VERSION=${JAVA_VERSION} PROJECT_DIRECTORY=${PROJECT_DIRECTORY} DRIVERS_TOOLS=${DRIVERS_TOOLS} .evergreen/run-mongodb-aws-test.sh + JAVA_VERSION=${JAVA_VERSION} PROJECT_DIRECTORY=${PROJECT_DIRECTORY} DRIVERS_TOOLS=${DRIVERS_TOOLS} \ + USE_BUILT_IN_AWS_CREDENTIAL_PROVIDER=${USE_BUILT_IN_AWS_CREDENTIAL_PROVIDER} \ + .evergreen/run-mongodb-aws-test.sh "run aws auth test with aws EC2 credentials": - command: shell.exec @@ -1637,6 +1641,18 @@ axes: variables: LOGIN_CONTEXT_NAME: "com.sun.security.jgss.initiate" + - id: aws-credential-provider + display_name: AWS Credential Provider + values: + - id: aws_sdk_v2 + display_name: AWS SDK V2 + variables: + USE_BUILT_IN_AWS_CREDENTIAL_PROVIDER: "false" + - id: built_in + display_name: Built-In + variables: + USE_BUILT_IN_AWS_CREDENTIAL_PROVIDER: "true" + task_groups: - name: testgcpkms_task_group setup_group_can_fail_task: true @@ -1813,8 +1829,9 @@ buildvariants: - name: "plain-auth-test" - matrix_name: "aws-auth-test" - matrix_spec: { ssl: "nossl", jdk: ["jdk8", "jdk17"], version: ["4.4", "5.0", "6.0", "latest"], os: "ubuntu" } - display_name: "MONGODB-AWS Auth test ${version} ${jdk}" + matrix_spec: { ssl: "nossl", jdk: ["jdk8", "jdk17"], version: ["4.4", "5.0", "6.0", "latest"], os: "ubuntu", + aws-credential-provider: "*" } + display_name: "MONGODB-AWS Auth test ${version} ${jdk} ${aws-credential-provider}" run_on: ubuntu1804-test tasks: - name: "aws-auth-test-with-regular-aws-credentials" diff --git a/.evergreen/run-mongodb-aws-test.sh b/.evergreen/run-mongodb-aws-test.sh index 8a421e6cc60..09cc934ba6d 100755 --- a/.evergreen/run-mongodb-aws-test.sh +++ b/.evergreen/run-mongodb-aws-test.sh @@ -4,9 +4,9 @@ set -o xtrace set -o errexit # Exit the script with error if any of the commands fail # Supported/used environment variables: -# JDK Set the version of java to be used. Java versions can be set from the java toolchain /opt/java -# "jdk5", "jdk6", "jdk7", "jdk8", "jdk9", "jdk11" - +# JDK Set the version of java to be used. Java versions can be set from the java toolchain /opt/java +# "jdk5", "jdk6", "jdk7", "jdk8", "jdk9", "jdk11" +# USE_BUILT_IN_AWS_CREDENTIAL_PROVIDER "true" or "false" ############################################ # Main Program # ############################################ @@ -37,5 +37,6 @@ echo "Running tests with Java ${JAVA_VERSION}" # As this script may be executed multiple times in a single task, with different values for MONGODB_URI, it's necessary # to run cleanTest to ensure that the test actually executes each run -./gradlew -PjavaVersion=${JAVA_VERSION} -Dorg.mongodb.test.uri=${MONGODB_URI} --stacktrace --debug --info --no-build-cache \ -driver-core:cleanTest driver-core:test --tests AwsAuthenticationSpecification +./gradlew -PjavaVersion="${JAVA_VERSION}" -Dorg.mongodb.test.uri="${MONGODB_URI}" \ +-Dorg.mongodb.test.use.built.in.aws.credential.provider="${USE_BUILT_IN_AWS_CREDENTIAL_PROVIDER}" \ +--stacktrace --debug --info --no-build-cache driver-core:cleanTest driver-core:test --tests AwsAuthenticationSpecification diff --git a/driver-core/build.gradle b/driver-core/build.gradle index bf4aa8fd9bc..d1f181f5204 100644 --- a/driver-core/build.gradle +++ b/driver-core/build.gradle @@ -47,6 +47,9 @@ dependencies { api "io.netty:netty-buffer", optional api "io.netty:netty-transport", optional api "io.netty:netty-handler", optional + + implementation 'software.amazon.awssdk:auth:2.17.261', optional + implementation "org.xerial.snappy:snappy-java:$snappyVersion", optional implementation "com.github.luben:zstd-jni:$zstdVersion", optional implementation "org.mongodb:mongodb-crypt:$mongoCryptVersion", optional diff --git a/driver-core/src/main/com/mongodb/internal/authentication/AwsCredentialHelper.java b/driver-core/src/main/com/mongodb/internal/authentication/AwsCredentialHelper.java index 28ff12ae09c..57685ddf849 100644 --- a/driver-core/src/main/com/mongodb/internal/authentication/AwsCredentialHelper.java +++ b/driver-core/src/main/com/mongodb/internal/authentication/AwsCredentialHelper.java @@ -17,12 +17,13 @@ package com.mongodb.internal.authentication; import com.mongodb.AwsCredential; -import org.bson.BsonDocument; +import com.mongodb.diagnostics.logging.Logger; +import com.mongodb.diagnostics.logging.Loggers; +import com.mongodb.internal.VisibleForTesting; -import java.util.HashMap; -import java.util.Map; +import java.util.function.Supplier; -import static com.mongodb.internal.authentication.HttpHelper.getHttpContents; +import static com.mongodb.internal.VisibleForTesting.AccessModifier.PRIVATE; /** * Utility class for working with AWS authentication. @@ -30,48 +31,35 @@ *

This class should not be considered a part of the public API.

*/ public final class AwsCredentialHelper { + public static final Logger LOGGER = Loggers.getLogger("authenticator"); - public static AwsCredential obtainFromEnvironment() { - if (System.getenv("AWS_ACCESS_KEY_ID") != null) { - return obtainFromEnvironmentVariables(); - } else { - return obtainFromEc2OrEcsResponse(); - } - } - - private static AwsCredential obtainFromEnvironmentVariables() { - return new AwsCredential( - System.getenv("AWS_ACCESS_KEY_ID"), - System.getenv("AWS_SECRET_ACCESS_KEY"), - System.getenv("AWS_SESSION_TOKEN")); - } + private static volatile Supplier awsCredentialSupplier; - private static AwsCredential obtainFromEc2OrEcsResponse() { - String path = System.getenv("AWS_CONTAINER_CREDENTIALS_RELATIVE_URI"); - BsonDocument ec2OrEcsResponse = path == null ? BsonDocument.parse(getEc2Response()) : BsonDocument.parse(getEcsResponse(path)); - - return new AwsCredential( - ec2OrEcsResponse.getString("AccessKeyId").getValue(), - ec2OrEcsResponse.getString("SecretAccessKey").getValue(), - ec2OrEcsResponse.getString("Token").getValue()); + static { + try { + Class.forName("software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider"); + awsCredentialSupplier = new AwsSdkV2CredentialSupplier(); + LOGGER.info("Using software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider from AWS SDK v2 to retrieve AWS " + + "credentials"); + } catch (ClassNotFoundException e) { + awsCredentialSupplier = new BuiltInAwsCredentialSupplier(); + LOGGER.info("Using built-in driver implementation to retrieve AWS credentials. Consider adding a dependency to " + + "software.amazon.awssdk:auth to get access to additional AWS authentication functionality"); + } } - private static String getEcsResponse(final String path) { - return getHttpContents("GET", "http://169.254.170.2" + path, null); + /** + * This method is visible to allow tests to require the built-in provider rather than rely on the fixed checks for classes on the + * classpath. It allows us to easily write tests of both implementations without resorting to runtime classpath shenanigans. + */ + @VisibleForTesting(otherwise = PRIVATE) + public static void requireBuiltInProvider() { + LOGGER.info("Using built-in driver implementation to retrieve AWS credentials"); + awsCredentialSupplier = new BuiltInAwsCredentialSupplier(); } - private static String getEc2Response() { - final String endpoint = "http://169.254.169.254"; - final String path = "/latest/meta-data/iam/security-credentials/"; - - Map header = new HashMap<>(); - header.put("X-aws-ec2-metadata-token-ttl-seconds", "30"); - String token = getHttpContents("PUT", endpoint + "/latest/api/token", header); - - header.clear(); - header.put("X-aws-ec2-metadata-token", token); - String role = getHttpContents("GET", endpoint + path, header); - return getHttpContents("GET", endpoint + path + role, header); + public static AwsCredential obtainFromEnvironment() { + return awsCredentialSupplier.get(); } private AwsCredentialHelper() { diff --git a/driver-core/src/main/com/mongodb/internal/authentication/AwsSdkV2CredentialSupplier.java b/driver-core/src/main/com/mongodb/internal/authentication/AwsSdkV2CredentialSupplier.java new file mode 100644 index 00000000000..df1de41bcbc --- /dev/null +++ b/driver-core/src/main/com/mongodb/internal/authentication/AwsSdkV2CredentialSupplier.java @@ -0,0 +1,42 @@ +/* + * Copyright 2008-present MongoDB, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License 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 com.mongodb.internal.authentication; + +import com.mongodb.AwsCredential; +import software.amazon.awssdk.auth.credentials.AwsCredentials; +import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider; +import software.amazon.awssdk.auth.credentials.AwsSessionCredentials; +import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider; + +import java.util.function.Supplier; + +public final class AwsSdkV2CredentialSupplier implements Supplier { + + private final AwsCredentialsProvider provider = DefaultCredentialsProvider.create(); + + @Override + public AwsCredential get() { + AwsCredentials credentials = provider.resolveCredentials(); + if (credentials instanceof AwsSessionCredentials) { + AwsSessionCredentials sessionCredentials = (AwsSessionCredentials) credentials; + return new AwsCredential(sessionCredentials.accessKeyId(), sessionCredentials.secretAccessKey(), + sessionCredentials.sessionToken()); + } else { + return new AwsCredential(credentials.accessKeyId(), credentials.secretAccessKey(), null); + } + } +} diff --git a/driver-core/src/main/com/mongodb/internal/authentication/BuiltInAwsCredentialSupplier.java b/driver-core/src/main/com/mongodb/internal/authentication/BuiltInAwsCredentialSupplier.java new file mode 100644 index 00000000000..b767f8c2067 --- /dev/null +++ b/driver-core/src/main/com/mongodb/internal/authentication/BuiltInAwsCredentialSupplier.java @@ -0,0 +1,73 @@ +/* + * Copyright 2008-present MongoDB, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License 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 com.mongodb.internal.authentication; + +import com.mongodb.AwsCredential; +import org.bson.BsonDocument; + +import java.util.HashMap; +import java.util.Map; +import java.util.function.Supplier; + +import static com.mongodb.internal.authentication.HttpHelper.getHttpContents; + +public class BuiltInAwsCredentialSupplier implements Supplier { + + @Override + public AwsCredential get() { + if (System.getenv("AWS_ACCESS_KEY_ID") != null) { + return obtainFromEnvironmentVariables(); + } else { + return obtainFromEc2OrEcsResponse(); + } + } + + private static AwsCredential obtainFromEnvironmentVariables() { + return new AwsCredential( + System.getenv("AWS_ACCESS_KEY_ID"), + System.getenv("AWS_SECRET_ACCESS_KEY"), + System.getenv("AWS_SESSION_TOKEN")); + } + + private static AwsCredential obtainFromEc2OrEcsResponse() { + String path = System.getenv("AWS_CONTAINER_CREDENTIALS_RELATIVE_URI"); + BsonDocument ec2OrEcsResponse = path == null ? BsonDocument.parse(getEc2Response()) : BsonDocument.parse(getEcsResponse(path)); + + return new AwsCredential( + ec2OrEcsResponse.getString("AccessKeyId").getValue(), + ec2OrEcsResponse.getString("SecretAccessKey").getValue(), + ec2OrEcsResponse.getString("Token").getValue()); + } + + private static String getEcsResponse(final String path) { + return getHttpContents("GET", "http://169.254.170.2" + path, null); + } + + private static String getEc2Response() { + final String endpoint = "http://169.254.169.254"; + final String path = "/latest/meta-data/iam/security-credentials/"; + + Map header = new HashMap<>(); + header.put("X-aws-ec2-metadata-token-ttl-seconds", "30"); + String token = getHttpContents("PUT", endpoint + "/latest/api/token", header); + + header.clear(); + header.put("X-aws-ec2-metadata-token", token); + String role = getHttpContents("GET", endpoint + path, header); + return getHttpContents("GET", endpoint + path + role, header); + } +} diff --git a/driver-core/src/test/functional/com/mongodb/internal/connection/AwsAuthenticationSpecification.groovy b/driver-core/src/test/functional/com/mongodb/internal/connection/AwsAuthenticationSpecification.groovy index 8334ca1c071..0cb5e492944 100644 --- a/driver-core/src/test/functional/com/mongodb/internal/connection/AwsAuthenticationSpecification.groovy +++ b/driver-core/src/test/functional/com/mongodb/internal/connection/AwsAuthenticationSpecification.groovy @@ -12,6 +12,7 @@ import com.mongodb.connection.ClusterId import com.mongodb.connection.ServerId import com.mongodb.connection.SocketSettings import com.mongodb.connection.SocketStreamFactory +import com.mongodb.internal.authentication.AwsCredentialHelper import org.bson.BsonDocument import org.bson.BsonString import spock.lang.IgnoreIf @@ -31,6 +32,12 @@ import static java.util.concurrent.TimeUnit.SECONDS @IgnoreIf({ getCredential() == null || getCredential().getAuthenticationMechanism() != MONGODB_AWS }) class AwsAuthenticationSpecification extends Specification { + static { + if (Boolean.valueOf(System.getProperty('org.mongodb.test.use.built.in.aws.credential.provider', 'false'))) { + AwsCredentialHelper.requireBuiltInProvider() + } + } + def 'should not authorize when not authenticated'() { given: def connection = createConnection(async, null) From c23563e625e28d379ec023d8a5893a8c453aea45 Mon Sep 17 00:00:00 2001 From: Jeff Yemin Date: Tue, 18 Oct 2022 12:11:36 -0400 Subject: [PATCH 2/5] Pull out ASW SDK version string into a property --- build.gradle | 1 + driver-core/build.gradle | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index bd319fcf6c2..a2a64e32d6e 100644 --- a/build.gradle +++ b/build.gradle @@ -47,6 +47,7 @@ ext { nettyVersion = '4.1.79.Final' snappyVersion = '1.1.8.4' zstdVersion = '1.5.2-3' + awsSdkV2Version = '2.17.293' mongoCryptVersion = '1.6.0-alpha0' projectReactorVersion = '2020.0.22' junitBomVersion = '5.8.1' diff --git a/driver-core/build.gradle b/driver-core/build.gradle index d1f181f5204..39768a43cbc 100644 --- a/driver-core/build.gradle +++ b/driver-core/build.gradle @@ -48,7 +48,7 @@ dependencies { api "io.netty:netty-transport", optional api "io.netty:netty-handler", optional - implementation 'software.amazon.awssdk:auth:2.17.261', optional + implementation "software.amazon.awssdk:auth:$awsSdkV2Version", optional implementation "org.xerial.snappy:snappy-java:$snappyVersion", optional implementation "com.github.luben:zstd-jni:$zstdVersion", optional From 93d70698ff0442f87f78d56aeb72e4e64ec97bf2 Mon Sep 17 00:00:00 2001 From: Jeff Yemin Date: Tue, 18 Oct 2022 13:43:26 -0400 Subject: [PATCH 3/5] Support AWS SDK v1 --- .evergreen/.evg.yml | 18 ++++---- .evergreen/run-mongodb-aws-test.sh | 4 +- build.gradle | 1 + driver-core/build.gradle | 3 ++ .../authentication/AwsCredentialHelper.java | 38 ++++++++++++++--- .../AwsSdkV1CredentialSupplier.java | 42 +++++++++++++++++++ .../AwsAuthenticationSpecification.groovy | 10 ++++- 7 files changed, 101 insertions(+), 15 deletions(-) create mode 100644 driver-core/src/main/com/mongodb/internal/authentication/AwsSdkV1CredentialSupplier.java diff --git a/.evergreen/.evg.yml b/.evergreen/.evg.yml index 8d90aad18f9..6861e1d121d 100644 --- a/.evergreen/.evg.yml +++ b/.evergreen/.evg.yml @@ -372,7 +372,7 @@ functions: MONGODB_URI="mongodb://$USER:$PASS@localhost" EOF JAVA_VERSION=${JAVA_VERSION} PROJECT_DIRECTORY=${PROJECT_DIRECTORY} \ - USE_BUILT_IN_AWS_CREDENTIAL_PROVIDER=${USE_BUILT_IN_AWS_CREDENTIAL_PROVIDER} \ + AWS_CREDENTIAL_PROVIDER=${AWS_CREDENTIAL_PROVIDER} \ .evergreen/run-mongodb-aws-test.sh "run aws auth test with assume role credentials": @@ -403,7 +403,7 @@ functions: MONGODB_URI="mongodb://$USER:$PASS@localhost" EOF JAVA_VERSION=${JAVA_VERSION} PROJECT_DIRECTORY=${PROJECT_DIRECTORY} DRIVERS_TOOLS=${DRIVERS_TOOLS} \ - USE_BUILT_IN_AWS_CREDENTIAL_PROVIDER=${USE_BUILT_IN_AWS_CREDENTIAL_PROVIDER} \ + AWS_CREDENTIAL_PROVIDER=${AWS_CREDENTIAL_PROVIDER} \ .evergreen/run-mongodb-aws-test.sh "run aws auth test with aws EC2 credentials": @@ -424,7 +424,7 @@ functions: ${PREPARE_SHELL} # Write an empty prepare_mongodb_aws so no auth environment variables are set. echo "" > "${PROJECT_DIRECTORY}/prepare_mongodb_aws.sh" - JAVA_VERSION=${JAVA_VERSION} .evergreen/run-mongodb-aws-test.sh + JAVA_VERSION=${JAVA_VERSION} AWS_CREDENTIAL_PROVIDER=${AWS_CREDENTIAL_PROVIDER} .evergreen/run-mongodb-aws-test.sh "run aws auth test with aws credentials as environment variables": - command: shell.exec @@ -453,7 +453,7 @@ functions: working_dir: "src" script: | ${PREPARE_SHELL} - JAVA_VERSION=${JAVA_VERSION} .evergreen/run-mongodb-aws-test.sh + JAVA_VERSION=${JAVA_VERSION} AWS_CREDENTIAL_PROVIDER=${AWS_CREDENTIAL_PROVIDER} .evergreen/run-mongodb-aws-test.sh "run aws auth test with aws credentials and session token as environment variables": - command: shell.exec @@ -483,7 +483,7 @@ functions: working_dir: "src" script: | ${PREPARE_SHELL} - JAVA_VERSION=${JAVA_VERSION} .evergreen/run-mongodb-aws-test.sh + JAVA_VERSION=${JAVA_VERSION} AWS_CREDENTIAL_PROVIDER=${AWS_CREDENTIAL_PROVIDER} .evergreen/run-mongodb-aws-test.sh "run aws ECS auth test": - command: shell.exec @@ -1647,11 +1647,15 @@ axes: - id: aws_sdk_v2 display_name: AWS SDK V2 variables: - USE_BUILT_IN_AWS_CREDENTIAL_PROVIDER: "false" + AWS_CREDENTIAL_PROVIDER: "awsSdkV2" + - id: aws_sdk_v1 + display_name: AWS SDK V1 + variables: + AWS_CREDENTIAL_PROVIDER: "awsSdkV1" - id: built_in display_name: Built-In variables: - USE_BUILT_IN_AWS_CREDENTIAL_PROVIDER: "true" + AWS_CREDENTIAL_PROVIDER: "builtIn" task_groups: - name: testgcpkms_task_group diff --git a/.evergreen/run-mongodb-aws-test.sh b/.evergreen/run-mongodb-aws-test.sh index 09cc934ba6d..ff20ded9936 100755 --- a/.evergreen/run-mongodb-aws-test.sh +++ b/.evergreen/run-mongodb-aws-test.sh @@ -6,7 +6,7 @@ set -o errexit # Exit the script with error if any of the commands fail # Supported/used environment variables: # JDK Set the version of java to be used. Java versions can be set from the java toolchain /opt/java # "jdk5", "jdk6", "jdk7", "jdk8", "jdk9", "jdk11" -# USE_BUILT_IN_AWS_CREDENTIAL_PROVIDER "true" or "false" +# AWS_CREDENTIAL_PROVIDER "builtIn", 'awsSdkV1', 'awsSdkV2' ############################################ # Main Program # ############################################ @@ -38,5 +38,5 @@ echo "Running tests with Java ${JAVA_VERSION}" # As this script may be executed multiple times in a single task, with different values for MONGODB_URI, it's necessary # to run cleanTest to ensure that the test actually executes each run ./gradlew -PjavaVersion="${JAVA_VERSION}" -Dorg.mongodb.test.uri="${MONGODB_URI}" \ --Dorg.mongodb.test.use.built.in.aws.credential.provider="${USE_BUILT_IN_AWS_CREDENTIAL_PROVIDER}" \ +-Dorg.mongodb.test.aws.credential.provider="${AWS_CREDENTIAL_PROVIDER}" \ --stacktrace --debug --info --no-build-cache driver-core:cleanTest driver-core:test --tests AwsAuthenticationSpecification diff --git a/build.gradle b/build.gradle index a2a64e32d6e..6c5ab5d8ed3 100644 --- a/build.gradle +++ b/build.gradle @@ -48,6 +48,7 @@ ext { snappyVersion = '1.1.8.4' zstdVersion = '1.5.2-3' awsSdkV2Version = '2.17.293' + awsSdkV1Version = '1.12.291' mongoCryptVersion = '1.6.0-alpha0' projectReactorVersion = '2020.0.22' junitBomVersion = '5.8.1' diff --git a/driver-core/build.gradle b/driver-core/build.gradle index 39768a43cbc..bc2739b8541 100644 --- a/driver-core/build.gradle +++ b/driver-core/build.gradle @@ -48,7 +48,10 @@ dependencies { api "io.netty:netty-transport", optional api "io.netty:netty-handler", optional + // Optionally depend on both AWS SDK v2 and v1. The driver will use v2 is present, v1 if present, or built-in functionality if + // neither are present implementation "software.amazon.awssdk:auth:$awsSdkV2Version", optional + implementation "com.amazonaws:aws-java-sdk-core:$awsSdkV1Version", optional implementation "org.xerial.snappy:snappy-java:$snappyVersion", optional implementation "com.github.luben:zstd-jni:$zstdVersion", optional diff --git a/driver-core/src/main/com/mongodb/internal/authentication/AwsCredentialHelper.java b/driver-core/src/main/com/mongodb/internal/authentication/AwsCredentialHelper.java index 57685ddf849..c85abba569e 100644 --- a/driver-core/src/main/com/mongodb/internal/authentication/AwsCredentialHelper.java +++ b/driver-core/src/main/com/mongodb/internal/authentication/AwsCredentialHelper.java @@ -40,17 +40,25 @@ public final class AwsCredentialHelper { Class.forName("software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider"); awsCredentialSupplier = new AwsSdkV2CredentialSupplier(); LOGGER.info("Using software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider from AWS SDK v2 to retrieve AWS " - + "credentials"); + + "credentials. This is the recommended configuration"); } catch (ClassNotFoundException e) { - awsCredentialSupplier = new BuiltInAwsCredentialSupplier(); - LOGGER.info("Using built-in driver implementation to retrieve AWS credentials. Consider adding a dependency to " - + "software.amazon.awssdk:auth to get access to additional AWS authentication functionality"); + try { + Class.forName("com.amazonaws.auth.DefaultAWSCredentialsProviderChain"); + awsCredentialSupplier = new AwsSdkV1CredentialSupplier(); + LOGGER.info("Using com.amazonaws.auth.DefaultAWSCredentialsProviderChain from AWS SDK v1 to retrieve AWS " + + "credentials. Consider adding a dependency to AWS SDK v2's software.amazon.awssdk:auth artifact to get access " + + "to additional AWS authentication functionality."); + } catch (ClassNotFoundException e1) { + awsCredentialSupplier = new BuiltInAwsCredentialSupplier(); + LOGGER.info("Using built-in driver implementation to retrieve AWS credentials. Consider adding a dependency to AWS " + + "SDK v2's software.amazon.awssdk:auth artifact to get access to additional AWS authentication functionality."); + } } } /** * This method is visible to allow tests to require the built-in provider rather than rely on the fixed checks for classes on the - * classpath. It allows us to easily write tests of both implementations without resorting to runtime classpath shenanigans. + * classpath. It allows us to easily write tests of the built-in implementation without resorting to runtime classpath shenanigans. */ @VisibleForTesting(otherwise = PRIVATE) public static void requireBuiltInProvider() { @@ -58,6 +66,26 @@ public static void requireBuiltInProvider() { awsCredentialSupplier = new BuiltInAwsCredentialSupplier(); } + /** + * This method is visible to allow tests to require the AWS SDK v1 provider rather than rely on the fixed checks for classes on the + * classpath. It allows us to easily write tests of the AWS SDK v1 implementation without resorting to runtime classpath shenanigans. + */ + @VisibleForTesting(otherwise = PRIVATE) + public static void requireAwsSdkV1Provider() { + LOGGER.info("Using AWS SDK v1 to retrieve AWS credentials"); + awsCredentialSupplier = new AwsSdkV1CredentialSupplier(); + } + + /** + * This method is visible to allow tests to require the AWS SDK v2 provider rather than rely on the fixed checks for classes on the + * classpath. It allows us to easily write tests of the AWS SDK v2 implementation without resorting to runtime classpath shenanigans. + */ + @VisibleForTesting(otherwise = PRIVATE) + public static void requireAwsSdkV2Provider() { + LOGGER.info("Using AWS SDK v2 to retrieve AWS credentials"); + awsCredentialSupplier = new AwsSdkV2CredentialSupplier(); + } + public static AwsCredential obtainFromEnvironment() { return awsCredentialSupplier.get(); } diff --git a/driver-core/src/main/com/mongodb/internal/authentication/AwsSdkV1CredentialSupplier.java b/driver-core/src/main/com/mongodb/internal/authentication/AwsSdkV1CredentialSupplier.java new file mode 100644 index 00000000000..bdb3b69a1e2 --- /dev/null +++ b/driver-core/src/main/com/mongodb/internal/authentication/AwsSdkV1CredentialSupplier.java @@ -0,0 +1,42 @@ +/* + * Copyright 2008-present MongoDB, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License 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 com.mongodb.internal.authentication; + +import com.amazonaws.auth.AWSCredentials; +import com.amazonaws.auth.AWSCredentialsProvider; +import com.amazonaws.auth.AWSSessionCredentials; +import com.amazonaws.auth.DefaultAWSCredentialsProviderChain; +import com.mongodb.AwsCredential; + +import java.util.function.Supplier; + +public final class AwsSdkV1CredentialSupplier implements Supplier { + + private final AWSCredentialsProvider provider = DefaultAWSCredentialsProviderChain.getInstance(); + + @Override + public AwsCredential get() { + AWSCredentials credentials = provider.getCredentials(); + if (credentials instanceof AWSSessionCredentials) { + AWSSessionCredentials sessionCredentials = (AWSSessionCredentials) credentials; + return new AwsCredential(sessionCredentials.getAWSAccessKeyId(), sessionCredentials.getAWSSecretKey(), + sessionCredentials.getSessionToken()); + } else { + return new AwsCredential(credentials.getAWSAccessKeyId(), credentials.getAWSSecretKey(), null); + } + } +} diff --git a/driver-core/src/test/functional/com/mongodb/internal/connection/AwsAuthenticationSpecification.groovy b/driver-core/src/test/functional/com/mongodb/internal/connection/AwsAuthenticationSpecification.groovy index 0cb5e492944..821831d3a56 100644 --- a/driver-core/src/test/functional/com/mongodb/internal/connection/AwsAuthenticationSpecification.groovy +++ b/driver-core/src/test/functional/com/mongodb/internal/connection/AwsAuthenticationSpecification.groovy @@ -33,8 +33,16 @@ import static java.util.concurrent.TimeUnit.SECONDS class AwsAuthenticationSpecification extends Specification { static { - if (Boolean.valueOf(System.getProperty('org.mongodb.test.use.built.in.aws.credential.provider', 'false'))) { + def providerProperty = System.getProperty('org.mongodb.test.aws.credential.provider', 'awsSdkV2') + + if (providerProperty == 'builtIn') { AwsCredentialHelper.requireBuiltInProvider() + } else if (providerProperty == 'awsSdkV1') { + AwsCredentialHelper.requireAwsSdkV1Provider() + } else if (providerProperty == 'awsSdkV2') { + AwsCredentialHelper.requireAwsSdkV2Provider() + } else { + throw new IllegalArgumentException("Unrecognized AWS credential provider: $providerProperty") } } From f225a6bf4174e56f156ba1e9cffa1d5aeaf89c40 Mon Sep 17 00:00:00 2001 From: Jeff Yemin Date: Tue, 18 Oct 2022 15:36:13 -0400 Subject: [PATCH 4/5] Support testing all AWS credential providers for ECS tests --- .evergreen/.evg.yml | 6 ++++++ .evergreen/run-mongodb-aws-ecs-test.sh | 27 +++++++++++++++++++++++++- 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/.evergreen/.evg.yml b/.evergreen/.evg.yml index 6861e1d121d..fda5ab2a3fb 100644 --- a/.evergreen/.evg.yml +++ b/.evergreen/.evg.yml @@ -1843,6 +1843,12 @@ buildvariants: - name: "aws-auth-test-with-aws-credentials-as-environment-variables" - name: "aws-auth-test-with-aws-credentials-and-session-token-as-environment-variables" - name: "aws-auth-test-with-aws-EC2-credentials" + +- matrix_name: "aws-ecs-auth-test" + matrix_spec: { ssl: "nossl", jdk: ["jdk8", "jdk17"], version: ["4.4", "5.0", "6.0", "latest"], os: "ubuntu" } + display_name: "MONGODB-AWS ECS Auth test ${version} ${jdk}" + run_on: ubuntu1804-test + tasks: - name: "aws-ECS-auth-test" - matrix_name: "accept-api-version-2-test" diff --git a/.evergreen/run-mongodb-aws-ecs-test.sh b/.evergreen/run-mongodb-aws-ecs-test.sh index ab80b0856f8..7d10b80f127 100755 --- a/.evergreen/run-mongodb-aws-ecs-test.sh +++ b/.evergreen/run-mongodb-aws-ecs-test.sh @@ -44,5 +44,30 @@ RELATIVE_DIR_PATH="$(dirname "${BASH_SOURCE:-$0}")" ./gradlew -version echo "Running tests..." -./gradlew -Dorg.mongodb.test.uri=${MONGODB_URI} --stacktrace --debug --info driver-core:test --tests AwsAuthenticationSpecification +./gradlew -Dorg.mongodb.test.uri=${MONGODB_URI} -Dorg.mongodb.test.aws.credential.provider=awsSdkV2 --stacktrace --debug --info \ + driver-core:test --tests AwsAuthenticationSpecification +first=$? +echo $first + +./gradlew -Dorg.mongodb.test.uri=${MONGODB_URI} -Dorg.mongodb.test.aws.credential.provider=awsSdkV1 --stacktrace --debug --info \ + driver-core:test --tests AwsAuthenticationSpecification +second=$? +echo $second + +./gradlew -Dorg.mongodb.test.uri=${MONGODB_URI} -Dorg.mongodb.test.aws.credential.provider=builtIn --stacktrace --debug --info \ + driver-core:test --tests AwsAuthenticationSpecification +third=$? +echo $third + +if [ $first -ne 0 ]; then + exit $first +elif [ $second -ne 0 ]; then + exit $second +elif [ $third -ne 0 ]; then + exit $third +else + exit 0 +fi + + cd - From 3e7e3c2bf6442501b01027c6d6b34e259cfda36c Mon Sep 17 00:00:00 2001 From: jyemin Date: Tue, 18 Oct 2022 20:26:34 -0400 Subject: [PATCH 5/5] Cleanup --- .../authentication/AwsCredentialHelper.java | 35 +++++++++++-------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/driver-core/src/main/com/mongodb/internal/authentication/AwsCredentialHelper.java b/driver-core/src/main/com/mongodb/internal/authentication/AwsCredentialHelper.java index c85abba569e..bd4cbb866c6 100644 --- a/driver-core/src/main/com/mongodb/internal/authentication/AwsCredentialHelper.java +++ b/driver-core/src/main/com/mongodb/internal/authentication/AwsCredentialHelper.java @@ -36,23 +36,28 @@ public final class AwsCredentialHelper { private static volatile Supplier awsCredentialSupplier; static { - try { - Class.forName("software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider"); + if (isClassAvailable("software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider")) { awsCredentialSupplier = new AwsSdkV2CredentialSupplier(); - LOGGER.info("Using software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider from AWS SDK v2 to retrieve AWS " - + "credentials. This is the recommended configuration"); + LOGGER.info("Using DefaultCredentialsProvider from AWS SDK v2 to retrieve AWS credentials. This is the recommended " + + "configuration"); + } else if (isClassAvailable("com.amazonaws.auth.DefaultAWSCredentialsProviderChain")) { + awsCredentialSupplier = new AwsSdkV1CredentialSupplier(); + LOGGER.info("Using DefaultAWSCredentialsProviderChain from AWS SDK v1 to retrieve AWS credentials. Consider adding a " + + "dependency to AWS SDK v2's software.amazon.awssdk:auth artifact to get access to additional AWS authentication " + + "functionality."); + } else { + awsCredentialSupplier = new BuiltInAwsCredentialSupplier(); + LOGGER.info("Using built-in driver implementation to retrieve AWS credentials. Consider adding a dependency to AWS SDK " + + "v2's software.amazon.awssdk:auth artifact to get access to additional AWS authentication functionality."); + } + } + + private static boolean isClassAvailable(final String className) { + try { + Class.forName(className); + return true; } catch (ClassNotFoundException e) { - try { - Class.forName("com.amazonaws.auth.DefaultAWSCredentialsProviderChain"); - awsCredentialSupplier = new AwsSdkV1CredentialSupplier(); - LOGGER.info("Using com.amazonaws.auth.DefaultAWSCredentialsProviderChain from AWS SDK v1 to retrieve AWS " - + "credentials. Consider adding a dependency to AWS SDK v2's software.amazon.awssdk:auth artifact to get access " - + "to additional AWS authentication functionality."); - } catch (ClassNotFoundException e1) { - awsCredentialSupplier = new BuiltInAwsCredentialSupplier(); - LOGGER.info("Using built-in driver implementation to retrieve AWS credentials. Consider adding a dependency to AWS " - + "SDK v2's software.amazon.awssdk:auth artifact to get access to additional AWS authentication functionality."); - } + return false; } }