From 1dad81328c565ad5e2ad008fc526fa4f70bcbc7a Mon Sep 17 00:00:00 2001 From: sebastiankirsch Date: Fri, 20 Dec 2019 14:32:41 +0100 Subject: [PATCH 01/15] Add AWS SDK core --- pom.xml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/pom.xml b/pom.xml index 9202889e8..44d0299bd 100644 --- a/pom.xml +++ b/pom.xml @@ -181,6 +181,15 @@ 1.24 + + + + com.amazonaws + aws-java-sdk-core + 1.11.696 + true + + From 2f07c038f456bc150aa2665288627837736e3925 Mon Sep 17 00:00:00 2001 From: sebastiankirsch Date: Fri, 20 Dec 2019 14:36:38 +0100 Subject: [PATCH 02/15] Exclude CBOR & ION serialization --- pom.xml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/pom.xml b/pom.xml index 44d0299bd..e08f798e8 100644 --- a/pom.xml +++ b/pom.xml @@ -188,6 +188,16 @@ aws-java-sdk-core 1.11.696 true + + + com.fasterxml.jackson.dataformat + jackson-dataformat-cbor + + + software.amazon.ion + * + + From f3491a284303936dca448ea2657be44a6afa416b Mon Sep 17 00:00:00 2001 From: sebastiankirsch Date: Fri, 20 Dec 2019 15:32:16 +0100 Subject: [PATCH 03/15] Use AWS SDK to fetch AWS credentials --- .../maven/docker/util/AuthConfigFactory.java | 58 ++++++++++++------- .../util/aws/AwsSdkAuthConfigFactory.java | 40 +++++++++++++ 2 files changed, 77 insertions(+), 21 deletions(-) create mode 100644 src/main/java/io/fabric8/maven/docker/util/aws/AwsSdkAuthConfigFactory.java diff --git a/src/main/java/io/fabric8/maven/docker/util/AuthConfigFactory.java b/src/main/java/io/fabric8/maven/docker/util/AuthConfigFactory.java index 92987b77c..971b91521 100644 --- a/src/main/java/io/fabric8/maven/docker/util/AuthConfigFactory.java +++ b/src/main/java/io/fabric8/maven/docker/util/AuthConfigFactory.java @@ -1,22 +1,11 @@ package io.fabric8.maven.docker.util; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.Reader; +import com.google.common.net.UrlEscapers; +import com.google.gson.Gson; import com.google.gson.JsonObject; - -import java.lang.reflect.Method; -import java.net.URI; -import java.net.URISyntaxException; -import java.nio.charset.StandardCharsets; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Properties; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - +import io.fabric8.maven.docker.access.AuthConfig; +import io.fabric8.maven.docker.access.ecr.EcrExtendedAuth; +import io.fabric8.maven.docker.util.aws.AwsSdkAuthConfigFactory; import org.apache.commons.io.IOUtils; import org.apache.http.HttpStatus; import org.apache.http.client.config.RequestConfig; @@ -34,11 +23,20 @@ import org.codehaus.plexus.util.xml.Xpp3Dom; import org.sonatype.plexus.components.sec.dispatcher.SecDispatcher; -import com.google.common.net.UrlEscapers; -import com.google.gson.Gson; - -import io.fabric8.maven.docker.access.AuthConfig; -import io.fabric8.maven.docker.access.ecr.EcrExtendedAuth; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.lang.reflect.Method; +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.regex.Matcher; +import java.util.regex.Pattern; /** * Factory for creating docker specific authentication configuration @@ -227,6 +225,12 @@ private AuthConfig createStandardAuthConfig(boolean isPush, Map authConfigMap, S // check EC2 instance role if registry is ECR if (EcrExtendedAuth.isAwsRegistry(registry)) { + ret = getAuthConfigViaAwsSdk(); + if (ret != null) { + log.debug("AuthConfig: AWS credentials from AWS SDK"); + return ret; + } + ret = getAuthConfigFromAwsEnvironmentVariables(); if (ret != null) { log.debug("AuthConfig: AWS credentials from ENV variables"); @@ -265,6 +269,18 @@ private AuthConfig createStandardAuthConfig(boolean isPush, Map authConfigMap, S return null; } + private AuthConfig getAuthConfigViaAwsSdk() { + try { + Class.forName("com.amazonaws.auth.DefaultAWSCredentialsProviderChain"); + } catch (ClassNotFoundException e) { + log.info("It appears that you're using AWS ECR." + + " Consider integrating the AWS SDK in order to make use of common AWS authentication mechanisms," + + " see TODO"); + return null; + } + return new AwsSdkAuthConfigFactory(log).createAuthConfig(); + } + /** * Try using the AWS credentials provided via ENV variables. * See https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-envvars.html diff --git a/src/main/java/io/fabric8/maven/docker/util/aws/AwsSdkAuthConfigFactory.java b/src/main/java/io/fabric8/maven/docker/util/aws/AwsSdkAuthConfigFactory.java new file mode 100644 index 000000000..c97653b7b --- /dev/null +++ b/src/main/java/io/fabric8/maven/docker/util/aws/AwsSdkAuthConfigFactory.java @@ -0,0 +1,40 @@ +package io.fabric8.maven.docker.util.aws; + +import com.amazonaws.SdkClientException; +import com.amazonaws.auth.AWSCredentials; +import com.amazonaws.auth.AWSSessionCredentials; +import com.amazonaws.auth.DefaultAWSCredentialsProviderChain; +import io.fabric8.maven.docker.access.AuthConfig; +import io.fabric8.maven.docker.util.Logger; + +public class AwsSdkAuthConfigFactory { + private final Logger log; + + public AwsSdkAuthConfigFactory(Logger log) { + this.log = log; + } + + public AuthConfig createAuthConfig() { + AWSCredentials credentials; + try { + credentials = new DefaultAWSCredentialsProviderChain().getCredentials(); + } catch (SdkClientException e) { + log.debug("Failed to fetch AWS credentials: %s", e); + return null; + } + if (credentials == null) { + return null; + } + if (credentials instanceof AWSSessionCredentials) { + AWSSessionCredentials sessionCredentials = (AWSSessionCredentials) credentials; + return new AuthConfig( + sessionCredentials.getAWSAccessKeyId(), + sessionCredentials.getAWSSecretKey(), + "none", + sessionCredentials.getSessionToken() + ); + } + return new AuthConfig(credentials.getAWSAccessKeyId(), credentials.getAWSSecretKey(), "none", null); + } + +} \ No newline at end of file From 084e1bf5373aea2735fc78a9ab97dff325d088ad Mon Sep 17 00:00:00 2001 From: sebastiankirsch Date: Fri, 20 Dec 2019 16:19:47 +0100 Subject: [PATCH 04/15] Fix imports --- .../maven/docker/util/AuthConfigFactory.java | 41 ++++++++++--------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/src/main/java/io/fabric8/maven/docker/util/AuthConfigFactory.java b/src/main/java/io/fabric8/maven/docker/util/AuthConfigFactory.java index 971b91521..78308d8d8 100644 --- a/src/main/java/io/fabric8/maven/docker/util/AuthConfigFactory.java +++ b/src/main/java/io/fabric8/maven/docker/util/AuthConfigFactory.java @@ -1,11 +1,22 @@ package io.fabric8.maven.docker.util; -import com.google.common.net.UrlEscapers; -import com.google.gson.Gson; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; import com.google.gson.JsonObject; -import io.fabric8.maven.docker.access.AuthConfig; -import io.fabric8.maven.docker.access.ecr.EcrExtendedAuth; -import io.fabric8.maven.docker.util.aws.AwsSdkAuthConfigFactory; + +import java.lang.reflect.Method; +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + import org.apache.commons.io.IOUtils; import org.apache.http.HttpStatus; import org.apache.http.client.config.RequestConfig; @@ -23,20 +34,12 @@ import org.codehaus.plexus.util.xml.Xpp3Dom; import org.sonatype.plexus.components.sec.dispatcher.SecDispatcher; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.Reader; -import java.lang.reflect.Method; -import java.net.URI; -import java.net.URISyntaxException; -import java.nio.charset.StandardCharsets; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Properties; -import java.util.regex.Matcher; -import java.util.regex.Pattern; +import com.google.common.net.UrlEscapers; +import com.google.gson.Gson; + +import io.fabric8.maven.docker.access.AuthConfig; +import io.fabric8.maven.docker.access.ecr.EcrExtendedAuth; +import io.fabric8.maven.docker.util.aws.AwsSdkAuthConfigFactory; /** * Factory for creating docker specific authentication configuration From 7cc78bf35125cc67ed3ea2f9f7f3a503a67ddbab Mon Sep 17 00:00:00 2001 From: sebastiankirsch Date: Fri, 20 Dec 2019 16:22:35 +0100 Subject: [PATCH 05/15] Simlify AuthConfig creation --- .../docker/util/aws/AwsSdkAuthConfigFactory.java | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/src/main/java/io/fabric8/maven/docker/util/aws/AwsSdkAuthConfigFactory.java b/src/main/java/io/fabric8/maven/docker/util/aws/AwsSdkAuthConfigFactory.java index c97653b7b..0c93665f1 100644 --- a/src/main/java/io/fabric8/maven/docker/util/aws/AwsSdkAuthConfigFactory.java +++ b/src/main/java/io/fabric8/maven/docker/util/aws/AwsSdkAuthConfigFactory.java @@ -25,16 +25,9 @@ public AuthConfig createAuthConfig() { if (credentials == null) { return null; } - if (credentials instanceof AWSSessionCredentials) { - AWSSessionCredentials sessionCredentials = (AWSSessionCredentials) credentials; - return new AuthConfig( - sessionCredentials.getAWSAccessKeyId(), - sessionCredentials.getAWSSecretKey(), - "none", - sessionCredentials.getSessionToken() - ); - } - return new AuthConfig(credentials.getAWSAccessKeyId(), credentials.getAWSSecretKey(), "none", null); + String sessionToken = (credentials instanceof AWSSessionCredentials) + ? ((AWSSessionCredentials) credentials).getSessionToken() : null; + return new AuthConfig(credentials.getAWSAccessKeyId(), credentials.getAWSSecretKey(), "none", sessionToken); } } \ No newline at end of file From 6fa4b7faddc1984a1d6a00163d61144c374b0107 Mon Sep 17 00:00:00 2001 From: sebastiankirsch Date: Fri, 3 Jan 2020 09:57:39 +0100 Subject: [PATCH 06/15] Test AwsSdkAuthConfigFactory --- .../util/aws/AwsSdkAuthConfigFactory.java | 10 +- .../util/aws/AwsSdkAuthConfigFactoryTest.java | 99 +++++++++++++++++++ 2 files changed, 108 insertions(+), 1 deletion(-) create mode 100644 src/test/java/io/fabric8/maven/docker/util/aws/AwsSdkAuthConfigFactoryTest.java diff --git a/src/main/java/io/fabric8/maven/docker/util/aws/AwsSdkAuthConfigFactory.java b/src/main/java/io/fabric8/maven/docker/util/aws/AwsSdkAuthConfigFactory.java index 0c93665f1..8acf6108a 100644 --- a/src/main/java/io/fabric8/maven/docker/util/aws/AwsSdkAuthConfigFactory.java +++ b/src/main/java/io/fabric8/maven/docker/util/aws/AwsSdkAuthConfigFactory.java @@ -2,22 +2,30 @@ import com.amazonaws.SdkClientException; import com.amazonaws.auth.AWSCredentials; +import com.amazonaws.auth.AWSCredentialsProvider; import com.amazonaws.auth.AWSSessionCredentials; import com.amazonaws.auth.DefaultAWSCredentialsProviderChain; import io.fabric8.maven.docker.access.AuthConfig; import io.fabric8.maven.docker.util.Logger; public class AwsSdkAuthConfigFactory { + + private final AWSCredentialsProvider credentialsProvider; private final Logger log; public AwsSdkAuthConfigFactory(Logger log) { + this(new DefaultAWSCredentialsProviderChain(), log); + } + + AwsSdkAuthConfigFactory(AWSCredentialsProvider credentialsProvider, Logger log) { + this.credentialsProvider = credentialsProvider; this.log = log; } public AuthConfig createAuthConfig() { AWSCredentials credentials; try { - credentials = new DefaultAWSCredentialsProviderChain().getCredentials(); + credentials = credentialsProvider.getCredentials(); } catch (SdkClientException e) { log.debug("Failed to fetch AWS credentials: %s", e); return null; diff --git a/src/test/java/io/fabric8/maven/docker/util/aws/AwsSdkAuthConfigFactoryTest.java b/src/test/java/io/fabric8/maven/docker/util/aws/AwsSdkAuthConfigFactoryTest.java new file mode 100644 index 000000000..8b12067a5 --- /dev/null +++ b/src/test/java/io/fabric8/maven/docker/util/aws/AwsSdkAuthConfigFactoryTest.java @@ -0,0 +1,99 @@ +package io.fabric8.maven.docker.util.aws; + +import com.amazonaws.SdkClientException; +import com.amazonaws.auth.AWSCredentialsProvider; +import com.amazonaws.auth.BasicAWSCredentials; +import com.amazonaws.auth.BasicSessionCredentials; +import io.fabric8.maven.docker.access.AuthConfig; +import io.fabric8.maven.docker.util.Logger; +import mockit.Expectations; +import mockit.Mocked; +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; + +public class AwsSdkAuthConfigFactoryTest { + + @Mocked + private AWSCredentialsProvider credentialsProvider; + + @Mocked + private Logger log; + + private AwsSdkAuthConfigFactory objectUnderTest; + + + @Before + public void setup() { + objectUnderTest = new AwsSdkAuthConfigFactory(credentialsProvider, log); + } + + @Test + public void exceptionsAreHandledGracefully() { + new Expectations() {{ + credentialsProvider.getCredentials(); + minTimes = 1; + result = new SdkClientException("Unauthorized"); + }}; + + AuthConfig authConfig = objectUnderTest.createAuthConfig(); + + assertNull(authConfig); + } + + @Test + public void nullValueIsPassedOn() { + new Expectations() {{ + credentialsProvider.getCredentials(); + minTimes = 1; + result = null; + }}; + + AuthConfig authConfig = objectUnderTest.createAuthConfig(); + + assertNull(authConfig); + } + + @Test + public void basicCredentialsAreTransformedIntoAuthConfig() { + String accessKey = "accessKey"; + String secretKey = "secretKey"; + new Expectations() {{ + credentialsProvider.getCredentials(); + minTimes = 1; + this.result = new BasicAWSCredentials(accessKey, secretKey); + }}; + + AuthConfig authConfig = objectUnderTest.createAuthConfig(); + + assertNotNull(authConfig); + assertEquals(accessKey, authConfig.getUsername()); + assertEquals(secretKey, authConfig.getPassword()); + assertNull(authConfig.getAuth()); + assertNull(authConfig.getIdentityToken()); + } + + @Test + public void sessionCredentialsAreTransformedIntoAuthConfig() { + String accessKey = "accessKey"; + String secretKey = "secretKey"; + String sessionToken = "sessionToken"; + new Expectations() {{ + credentialsProvider.getCredentials(); + minTimes = 1; + this.result = new BasicSessionCredentials(accessKey, secretKey, sessionToken); + }}; + + AuthConfig authConfig = objectUnderTest.createAuthConfig(); + + assertNotNull(authConfig); + assertEquals(accessKey, authConfig.getUsername()); + assertEquals(secretKey, authConfig.getPassword()); + assertEquals(sessionToken, authConfig.getAuth()); + assertNull(authConfig.getIdentityToken()); + } + +} \ No newline at end of file From ef09dc2e0b80106a34edabcefd5b2a56fcd25ec0 Mon Sep 17 00:00:00 2001 From: sebastiankirsch Date: Fri, 3 Jan 2020 10:17:11 +0100 Subject: [PATCH 07/15] Test AuthConfigFactory --- .../docker/util/AuthConfigFactoryTest.java | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/test/java/io/fabric8/maven/docker/util/AuthConfigFactoryTest.java b/src/test/java/io/fabric8/maven/docker/util/AuthConfigFactoryTest.java index 1b4e5276f..00f9e35d6 100644 --- a/src/test/java/io/fabric8/maven/docker/util/AuthConfigFactoryTest.java +++ b/src/test/java/io/fabric8/maven/docker/util/AuthConfigFactoryTest.java @@ -15,8 +15,10 @@ import com.google.gson.JsonNull; import com.google.gson.JsonObject; import io.fabric8.maven.docker.access.AuthConfig; +import io.fabric8.maven.docker.util.aws.AwsSdkAuthConfigFactory; import mockit.Expectations; import mockit.Mock; +import mockit.MockUp; import mockit.Mocked; import org.apache.http.entity.StringEntity; import org.apache.http.impl.bootstrap.HttpServer; @@ -522,6 +524,17 @@ public void exec(File homeDir) throws IOException, MojoExecutionException { }); } + @Test + public void getAuthConfigViaAwsSdk() throws MojoExecutionException { + String accessKeyId = randomUUID().toString(); + String secretAccessKey = randomUUID().toString(); + new MockedAwsSdkAuthConfigFactory(accessKeyId, secretAccessKey); + + AuthConfig authConfig = factory.createAuthConfig(false, true, null, settings, "user", ECR_NAME); + + verifyAuthConfig(authConfig, accessKeyId, secretAccessKey, null, null); + } + @Test public void ecsTaskRole() throws IOException, MojoExecutionException { String containerCredentialsUri = "/v2/credentials/" + randomUUID().toString(); @@ -654,4 +667,24 @@ private void verifyAuthConfig(AuthConfig config, String username, String passwor verifyAuthConfig(config, username, password, email, null); } + public static class MockedAwsSdkAuthConfigFactory extends MockUp { + private final String accessKeyId; + private final String secretAccessKey; + + public MockedAwsSdkAuthConfigFactory(String accessKeyId, String secretAccessKey) { + this.accessKeyId = accessKeyId; + this.secretAccessKey = secretAccessKey; + } + + @Mock + public void $init(Logger log) { + } + + @Mock + public AuthConfig createAuthConfig() { + return new AuthConfig(accessKeyId, secretAccessKey, null,null); + } + + } + } From 70ec0f2c97cb43db983605b9ea5c655e7bb36fc0 Mon Sep 17 00:00:00 2001 From: sebastiankirsch Date: Fri, 3 Jan 2020 10:25:34 +0100 Subject: [PATCH 08/15] Make sure custom AWS code is tested --- .../docker/util/AuthConfigFactoryTest.java | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/src/test/java/io/fabric8/maven/docker/util/AuthConfigFactoryTest.java b/src/test/java/io/fabric8/maven/docker/util/AuthConfigFactoryTest.java index 00f9e35d6..38bfc3426 100644 --- a/src/test/java/io/fabric8/maven/docker/util/AuthConfigFactoryTest.java +++ b/src/test/java/io/fabric8/maven/docker/util/AuthConfigFactoryTest.java @@ -537,6 +537,7 @@ public void getAuthConfigViaAwsSdk() throws MojoExecutionException { @Test public void ecsTaskRole() throws IOException, MojoExecutionException { + givenAwsSdkIsDisabled(); String containerCredentialsUri = "/v2/credentials/" + randomUUID().toString(); String accessKeyId = randomUUID().toString(); String secretAccessKey = randomUUID().toString(); @@ -551,6 +552,7 @@ public void ecsTaskRole() throws IOException, MojoExecutionException { @Test public void fargateTaskRole() throws IOException, MojoExecutionException { + givenAwsSdkIsDisabled(); String containerCredentialsUri = "v2/credentials/" + randomUUID().toString(); String accessKeyId = randomUUID().toString(); String secretAccessKey = randomUUID().toString(); @@ -565,6 +567,7 @@ public void fargateTaskRole() throws IOException, MojoExecutionException { @Test public void awsTemporaryCredentialsArePickedUpFromEnvironment() throws MojoExecutionException { + givenAwsSdkIsDisabled(); String accessKeyId = randomUUID().toString(); String secretAccessKey = randomUUID().toString(); String sessionToken = randomUUID().toString(); @@ -579,6 +582,7 @@ public void awsTemporaryCredentialsArePickedUpFromEnvironment() throws MojoExecu @Test public void awsStaticCredentialsArePickedUpFromEnvironment() throws MojoExecutionException { + givenAwsSdkIsDisabled(); String accessKeyId = randomUUID().toString(); String secretAccessKey = randomUUID().toString(); environmentVariables.set("AWS_ACCESS_KEY_ID", accessKeyId); @@ -591,6 +595,7 @@ public void awsStaticCredentialsArePickedUpFromEnvironment() throws MojoExecutio @Test public void incompleteAwsCredentialsAreIgnored() throws MojoExecutionException { + givenAwsSdkIsDisabled(); environmentVariables.set("AWS_ACCESS_KEY_ID", randomUUID().toString()); AuthConfig authConfig = factory.createAuthConfig(false, true, null, settings, "user", ECR_NAME); @@ -667,7 +672,11 @@ private void verifyAuthConfig(AuthConfig config, String username, String passwor verifyAuthConfig(config, username, password, email, null); } - public static class MockedAwsSdkAuthConfigFactory extends MockUp { + private static void givenAwsSdkIsDisabled() { + new DisableAwsSdkAuthConfigFactory(); + } + + private static class MockedAwsSdkAuthConfigFactory extends MockUp { private final String accessKeyId; private final String secretAccessKey; @@ -687,4 +696,17 @@ public AuthConfig createAuthConfig() { } + private static class DisableAwsSdkAuthConfigFactory extends MockUp { + + @Mock + public void $init(Logger log) { + } + + @Mock + public AuthConfig createAuthConfig() { + return null; + } + + } + } From 990f285ae6881de3ade80610729bd7a2c1c2c41d Mon Sep 17 00:00:00 2001 From: sebastiankirsch Date: Fri, 17 Jan 2020 08:33:45 +0100 Subject: [PATCH 09/15] Go full reflection --- pom.xml | 20 ------- .../util/aws/AwsSdkAuthConfigFactory.java | 41 +++++++------ .../com/amazonaws/auth/AWSCredentials.java | 23 +++++++ .../amazonaws/auth/AWSSessionCredentials.java | 15 +++++ .../DefaultAWSCredentialsProviderChain.java | 22 +++++++ .../util/aws/AwsSdkAuthConfigFactoryTest.java | 60 ++++++++++++++++--- 6 files changed, 131 insertions(+), 50 deletions(-) create mode 100644 src/test/java/com/amazonaws/auth/AWSCredentials.java create mode 100644 src/test/java/com/amazonaws/auth/AWSSessionCredentials.java create mode 100644 src/test/java/com/amazonaws/auth/DefaultAWSCredentialsProviderChain.java diff --git a/pom.xml b/pom.xml index 71eda6685..5c2f4fc7a 100644 --- a/pom.xml +++ b/pom.xml @@ -181,26 +181,6 @@ 1.24 - - - - com.amazonaws - aws-java-sdk-core - 1.11.696 - true - - - com.fasterxml.jackson.dataformat - jackson-dataformat-cbor - - - software.amazon.ion - * - - - - - diff --git a/src/main/java/io/fabric8/maven/docker/util/aws/AwsSdkAuthConfigFactory.java b/src/main/java/io/fabric8/maven/docker/util/aws/AwsSdkAuthConfigFactory.java index 8acf6108a..d5504341b 100644 --- a/src/main/java/io/fabric8/maven/docker/util/aws/AwsSdkAuthConfigFactory.java +++ b/src/main/java/io/fabric8/maven/docker/util/aws/AwsSdkAuthConfigFactory.java @@ -1,41 +1,40 @@ package io.fabric8.maven.docker.util.aws; -import com.amazonaws.SdkClientException; -import com.amazonaws.auth.AWSCredentials; -import com.amazonaws.auth.AWSCredentialsProvider; -import com.amazonaws.auth.AWSSessionCredentials; -import com.amazonaws.auth.DefaultAWSCredentialsProviderChain; import io.fabric8.maven.docker.access.AuthConfig; import io.fabric8.maven.docker.util.Logger; public class AwsSdkAuthConfigFactory { - private final AWSCredentialsProvider credentialsProvider; private final Logger log; public AwsSdkAuthConfigFactory(Logger log) { - this(new DefaultAWSCredentialsProviderChain(), log); - } - - AwsSdkAuthConfigFactory(AWSCredentialsProvider credentialsProvider, Logger log) { - this.credentialsProvider = credentialsProvider; this.log = log; } public AuthConfig createAuthConfig() { - AWSCredentials credentials; try { - credentials = credentialsProvider.getCredentials(); - } catch (SdkClientException e) { - log.debug("Failed to fetch AWS credentials: %s", e); - return null; - } - if (credentials == null) { + Class credentialsProviderChainClass = Class.forName("com.amazonaws.auth.DefaultAWSCredentialsProviderChain"); + Object credentialsProviderChain = credentialsProviderChainClass.getDeclaredConstructor().newInstance(); + Object credentials = credentialsProviderChainClass.getMethod("getCredentials").invoke(credentialsProviderChain); + if (credentials == null) { + return null; + } + + Class sessionCredentialsClass = Class.forName("com.amazonaws.auth.AWSSessionCredentials"); + String sessionToken = sessionCredentialsClass.isInstance(credentials) + ? (String) sessionCredentialsClass.getMethod("getSessionToken").invoke(credentials) : null; + + Class credentialsClass = Class.forName("com.amazonaws.auth.AWSCredentials"); + return new AuthConfig( + (String) credentialsClass.getMethod("getAWSAccessKeyId").invoke(credentials), + (String) credentialsClass.getMethod("getAWSSecretKey").invoke(credentials), + "none", + sessionToken + ); + } catch (Throwable t) { + log.debug("Failed to fetch AWS credentials: %s", t); return null; } - String sessionToken = (credentials instanceof AWSSessionCredentials) - ? ((AWSSessionCredentials) credentials).getSessionToken() : null; - return new AuthConfig(credentials.getAWSAccessKeyId(), credentials.getAWSSecretKey(), "none", sessionToken); } } \ No newline at end of file diff --git a/src/test/java/com/amazonaws/auth/AWSCredentials.java b/src/test/java/com/amazonaws/auth/AWSCredentials.java new file mode 100644 index 000000000..2890e8a7d --- /dev/null +++ b/src/test/java/com/amazonaws/auth/AWSCredentials.java @@ -0,0 +1,23 @@ +package com.amazonaws.auth; + +/** + * Shameless copy of the original for testing {@link io.fabric8.maven.docker.util.aws.AwsSdkAuthConfigFactory} + */ +public class AWSCredentials { + private final String accessKeyId; + private final String secretKey; + + public AWSCredentials(String accessKeyId, String secretKey) { + this.accessKeyId = accessKeyId; + this.secretKey = secretKey; + } + + public String getAWSAccessKeyId() { + return accessKeyId; + } + + public String getAWSSecretKey() { + return secretKey; + } + +} diff --git a/src/test/java/com/amazonaws/auth/AWSSessionCredentials.java b/src/test/java/com/amazonaws/auth/AWSSessionCredentials.java new file mode 100644 index 000000000..beb78043d --- /dev/null +++ b/src/test/java/com/amazonaws/auth/AWSSessionCredentials.java @@ -0,0 +1,15 @@ +package com.amazonaws.auth; + +/** Shameless copy of the original for testing {@link io.fabric8.maven.docker.util.aws.AwsSdkAuthConfigFactory} */ +public class AWSSessionCredentials extends AWSCredentials { + + private final String sessionKey; + + public AWSSessionCredentials(String accessKeyId, String secretKey, String sessionKey) { + super(accessKeyId, secretKey); + this.sessionKey = sessionKey; + } + + public String getSessionToken() {return sessionKey;} + +} diff --git a/src/test/java/com/amazonaws/auth/DefaultAWSCredentialsProviderChain.java b/src/test/java/com/amazonaws/auth/DefaultAWSCredentialsProviderChain.java new file mode 100644 index 000000000..d9dd34cbc --- /dev/null +++ b/src/test/java/com/amazonaws/auth/DefaultAWSCredentialsProviderChain.java @@ -0,0 +1,22 @@ +package com.amazonaws.auth; + +import io.fabric8.maven.docker.access.AuthConfig; + +import static java.lang.System.getenv; + +/** Shameless copy of the original for testing {@link io.fabric8.maven.docker.util.aws.AwsSdkAuthConfigFactory} */ +public final class DefaultAWSCredentialsProviderChain { + + public AWSCredentials getCredentials() { + String accessKeyId = getenv("AWSCredentials.AWSAccessKeyId"); + if (accessKeyId == null) { + return null; + } + String secretKey = getenv("AWSCredentials.AWSSecretKey"); + String sessionToken = getenv("AWSSessionCredentials.SessionToken"); + return sessionToken == null + ? new AWSCredentials(accessKeyId, secretKey) + : new AWSSessionCredentials(accessKeyId,secretKey,sessionToken); + } + +} \ No newline at end of file diff --git a/src/test/java/io/fabric8/maven/docker/util/aws/AwsSdkAuthConfigFactoryTest.java b/src/test/java/io/fabric8/maven/docker/util/aws/AwsSdkAuthConfigFactoryTest.java index 8b12067a5..5ace9c3bf 100644 --- a/src/test/java/io/fabric8/maven/docker/util/aws/AwsSdkAuthConfigFactoryTest.java +++ b/src/test/java/io/fabric8/maven/docker/util/aws/AwsSdkAuthConfigFactoryTest.java @@ -1,36 +1,78 @@ package io.fabric8.maven.docker.util.aws; -import com.amazonaws.SdkClientException; -import com.amazonaws.auth.AWSCredentialsProvider; -import com.amazonaws.auth.BasicAWSCredentials; -import com.amazonaws.auth.BasicSessionCredentials; import io.fabric8.maven.docker.access.AuthConfig; import io.fabric8.maven.docker.util.Logger; import mockit.Expectations; import mockit.Mocked; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; +import org.junit.contrib.java.lang.system.EnvironmentVariables; +import java.util.UUID; + +import static java.util.UUID.randomUUID; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; public class AwsSdkAuthConfigFactoryTest { - @Mocked - private AWSCredentialsProvider credentialsProvider; + @Rule + public final EnvironmentVariables environmentVariables = new EnvironmentVariables(); @Mocked private Logger log; - private AwsSdkAuthConfigFactory objectUnderTest; @Before public void setup() { - objectUnderTest = new AwsSdkAuthConfigFactory(credentialsProvider, log); + objectUnderTest = new AwsSdkAuthConfigFactory(log); } + @Test + public void nullValueIsPassedOn() { + AuthConfig authConfig = objectUnderTest.createAuthConfig(); + + assertNull(authConfig); + } + + @Test + public void reflectionWorksForBasicCredentials() { + String accessKey = randomUUID().toString(); + String secretKey = randomUUID().toString(); + environmentVariables.set("AWSCredentials.AWSAccessKeyId", accessKey); + environmentVariables.set("AWSCredentials.AWSSecretKey", secretKey); + + AuthConfig authConfig = objectUnderTest.createAuthConfig(); + + assertNotNull(authConfig); + assertEquals(accessKey, authConfig.getUsername()); + assertEquals(secretKey, authConfig.getPassword()); + assertNull(authConfig.getAuth()); + assertNull(authConfig.getIdentityToken()); + } + + @Test + public void reflectionWorksForSessionCredentials() { + String accessKey = randomUUID().toString(); + String secretKey = randomUUID().toString(); + String sessionToken = randomUUID().toString(); + environmentVariables.set("AWSCredentials.AWSAccessKeyId", accessKey); + environmentVariables.set("AWSCredentials.AWSSecretKey", secretKey); + environmentVariables.set("AWSSessionCredentials.SessionToken", sessionToken); + + AuthConfig authConfig = objectUnderTest.createAuthConfig(); + + assertNotNull(authConfig); + assertEquals(accessKey, authConfig.getUsername()); + assertEquals(secretKey, authConfig.getPassword()); + assertEquals(sessionToken, authConfig.getAuth()); + assertNull(authConfig.getIdentityToken()); + } + + /* @Test public void exceptionsAreHandledGracefully() { new Expectations() {{ @@ -95,5 +137,5 @@ public void sessionCredentialsAreTransformedIntoAuthConfig() { assertEquals(sessionToken, authConfig.getAuth()); assertNull(authConfig.getIdentityToken()); } - +*/ } \ No newline at end of file From 18c48ab985eeed49aac3b463deb89239d7425586 Mon Sep 17 00:00:00 2001 From: sebastiankirsch Date: Fri, 17 Jan 2020 08:37:37 +0100 Subject: [PATCH 10/15] Improve comments + clean imports --- src/test/java/com/amazonaws/auth/AWSCredentials.java | 3 ++- .../java/com/amazonaws/auth/AWSSessionCredentials.java | 5 ++++- .../amazonaws/auth/DefaultAWSCredentialsProviderChain.java | 7 ++++--- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/test/java/com/amazonaws/auth/AWSCredentials.java b/src/test/java/com/amazonaws/auth/AWSCredentials.java index 2890e8a7d..411aacbf0 100644 --- a/src/test/java/com/amazonaws/auth/AWSCredentials.java +++ b/src/test/java/com/amazonaws/auth/AWSCredentials.java @@ -1,7 +1,8 @@ package com.amazonaws.auth; /** - * Shameless copy of the original for testing {@link io.fabric8.maven.docker.util.aws.AwsSdkAuthConfigFactory} + * Shameless copy of the original for testing {@link io.fabric8.maven.docker.util.aws.AwsSdkAuthConfigFactory}. + * Based on com.amazonaws:aws-java-sdk-core:1.11.707. */ public class AWSCredentials { private final String accessKeyId; diff --git a/src/test/java/com/amazonaws/auth/AWSSessionCredentials.java b/src/test/java/com/amazonaws/auth/AWSSessionCredentials.java index beb78043d..5c7b19f6c 100644 --- a/src/test/java/com/amazonaws/auth/AWSSessionCredentials.java +++ b/src/test/java/com/amazonaws/auth/AWSSessionCredentials.java @@ -1,6 +1,9 @@ package com.amazonaws.auth; -/** Shameless copy of the original for testing {@link io.fabric8.maven.docker.util.aws.AwsSdkAuthConfigFactory} */ +/** + * Shameless copy of the original for testing {@link io.fabric8.maven.docker.util.aws.AwsSdkAuthConfigFactory}. + * Based on com.amazonaws:aws-java-sdk-core:1.11.707. + */ public class AWSSessionCredentials extends AWSCredentials { private final String sessionKey; diff --git a/src/test/java/com/amazonaws/auth/DefaultAWSCredentialsProviderChain.java b/src/test/java/com/amazonaws/auth/DefaultAWSCredentialsProviderChain.java index d9dd34cbc..ff8bae0ec 100644 --- a/src/test/java/com/amazonaws/auth/DefaultAWSCredentialsProviderChain.java +++ b/src/test/java/com/amazonaws/auth/DefaultAWSCredentialsProviderChain.java @@ -1,10 +1,11 @@ package com.amazonaws.auth; -import io.fabric8.maven.docker.access.AuthConfig; - import static java.lang.System.getenv; -/** Shameless copy of the original for testing {@link io.fabric8.maven.docker.util.aws.AwsSdkAuthConfigFactory} */ +/** + * Shameless copy of the original for testing {@link io.fabric8.maven.docker.util.aws.AwsSdkAuthConfigFactory}. + * Based on com.amazonaws:aws-java-sdk-core:1.11.707. + */ public final class DefaultAWSCredentialsProviderChain { public AWSCredentials getCredentials() { From 69e5e7b1e3fafc0375f8742a41d199d8cf7c7ec9 Mon Sep 17 00:00:00 2001 From: sebastiankirsch Date: Fri, 17 Jan 2020 08:55:34 +0100 Subject: [PATCH 11/15] Mention #1311 in changelog --- doc/changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/changelog.md b/doc/changelog.md index ab3eceac8..81af70bb9 100644 --- a/doc/changelog.md +++ b/doc/changelog.md @@ -2,6 +2,7 @@ * **0.32-SNAPSHOT** - Update to jnr-unixsocket 0.25 to solve concurrency issues (hopefully fixing #552) + - Allow including `com.amazonaws:aws-java-sdk-core` to pick up various forms of AWS credentials with which to authenticate at AWS ECR ([#1311](https://github.com/fabric8io/docker-maven-plugin/issues/1311)) * **0.32.0** (2020-01-08) - Support building dockerFile without pushing it to docker server ([#1197](https://github.com/fabric8io/docker-maven-plugin/issues/1197)) From d74b2e36b61e4578cd1c959b20672551b374eb03 Mon Sep 17 00:00:00 2001 From: sebastiankirsch Date: Fri, 17 Jan 2020 09:01:58 +0100 Subject: [PATCH 12/15] Cleanup --- pom.xml | 1 + .../util/aws/AwsSdkAuthConfigFactoryTest.java | 69 ------------------- 2 files changed, 1 insertion(+), 69 deletions(-) diff --git a/pom.xml b/pom.xml index 5c2f4fc7a..790e6fa74 100644 --- a/pom.xml +++ b/pom.xml @@ -181,6 +181,7 @@ 1.24 + diff --git a/src/test/java/io/fabric8/maven/docker/util/aws/AwsSdkAuthConfigFactoryTest.java b/src/test/java/io/fabric8/maven/docker/util/aws/AwsSdkAuthConfigFactoryTest.java index 5ace9c3bf..99800715d 100644 --- a/src/test/java/io/fabric8/maven/docker/util/aws/AwsSdkAuthConfigFactoryTest.java +++ b/src/test/java/io/fabric8/maven/docker/util/aws/AwsSdkAuthConfigFactoryTest.java @@ -2,15 +2,12 @@ import io.fabric8.maven.docker.access.AuthConfig; import io.fabric8.maven.docker.util.Logger; -import mockit.Expectations; import mockit.Mocked; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.contrib.java.lang.system.EnvironmentVariables; -import java.util.UUID; - import static java.util.UUID.randomUUID; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; @@ -72,70 +69,4 @@ public void reflectionWorksForSessionCredentials() { assertNull(authConfig.getIdentityToken()); } - /* - @Test - public void exceptionsAreHandledGracefully() { - new Expectations() {{ - credentialsProvider.getCredentials(); - minTimes = 1; - result = new SdkClientException("Unauthorized"); - }}; - - AuthConfig authConfig = objectUnderTest.createAuthConfig(); - - assertNull(authConfig); - } - - @Test - public void nullValueIsPassedOn() { - new Expectations() {{ - credentialsProvider.getCredentials(); - minTimes = 1; - result = null; - }}; - - AuthConfig authConfig = objectUnderTest.createAuthConfig(); - - assertNull(authConfig); - } - - @Test - public void basicCredentialsAreTransformedIntoAuthConfig() { - String accessKey = "accessKey"; - String secretKey = "secretKey"; - new Expectations() {{ - credentialsProvider.getCredentials(); - minTimes = 1; - this.result = new BasicAWSCredentials(accessKey, secretKey); - }}; - - AuthConfig authConfig = objectUnderTest.createAuthConfig(); - - assertNotNull(authConfig); - assertEquals(accessKey, authConfig.getUsername()); - assertEquals(secretKey, authConfig.getPassword()); - assertNull(authConfig.getAuth()); - assertNull(authConfig.getIdentityToken()); - } - - @Test - public void sessionCredentialsAreTransformedIntoAuthConfig() { - String accessKey = "accessKey"; - String secretKey = "secretKey"; - String sessionToken = "sessionToken"; - new Expectations() {{ - credentialsProvider.getCredentials(); - minTimes = 1; - this.result = new BasicSessionCredentials(accessKey, secretKey, sessionToken); - }}; - - AuthConfig authConfig = objectUnderTest.createAuthConfig(); - - assertNotNull(authConfig); - assertEquals(accessKey, authConfig.getUsername()); - assertEquals(secretKey, authConfig.getPassword()); - assertEquals(sessionToken, authConfig.getAuth()); - assertNull(authConfig.getIdentityToken()); - } -*/ } \ No newline at end of file From db28e5e4e47f89bc4229c5fb4f0087380db6378e Mon Sep 17 00:00:00 2001 From: sebastiankirsch Date: Mon, 20 Jan 2020 11:02:12 +0100 Subject: [PATCH 13/15] Document usage of AWS SDK for Extended Authentication --- src/main/asciidoc/inc/_authentication.adoc | 30 ++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/main/asciidoc/inc/_authentication.adoc b/src/main/asciidoc/inc/_authentication.adoc index bc17daf49..a2d7834f5 100644 --- a/src/main/asciidoc/inc/_authentication.adoc +++ b/src/main/asciidoc/inc/_authentication.adoc @@ -187,7 +187,37 @@ In case you're using temporary security credentials provided by the AWS Security To do so, either specify the `docker.auth` system property or provide an `` element alongside username & password in the `authConfig`. d-m-p will attempt to read AWS credentials from some well-known spots in case there is no explicit configuration: + * it will pick up ENV variables link:https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-envvars.html[as documented for the AWS CLI] + * it will pick up temporary credentials of link:https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/iam-roles-for-amazon-ec2.html[the IAM role of an EC2 instance] + * it will pick up temporary credentials of link:https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task-iam-roles.html[the IAM role of a fargate task (OR ECS with EC2 with ECS_AWSVPC_BLOCK_IMDS as "true")] + If any of these authentication information is accessible, it will be used. + +[NOTE] +==== +For a more complete, robust and reliable authentication experience, you can add the AWS SDK for Java as a dependency. + +[source,xml] +---- + + + io.fabric8 + docker-maven-plugin + + + com.amazonaws + aws-java-sdk-core + 1.11.707 + + + + +---- + +This allows you to make use of all link:https://docs.aws.amazon.com/sdk-for-java/v1/developer-guide/credentials.html[the options the default credential provider chain provides]. + +If the AWS SDK is found in the classpath, it takes precedence over the custom AWS credentials lookup mechanisms listed above. +==== \ No newline at end of file From 2681c8740f6267738d0da4ed001332fd60e32444 Mon Sep 17 00:00:00 2001 From: sebastiankirsch Date: Mon, 20 Jan 2020 11:03:53 +0100 Subject: [PATCH 14/15] Link to documentation when encountering AWS ECR --- .../java/io/fabric8/maven/docker/util/AuthConfigFactory.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/fabric8/maven/docker/util/AuthConfigFactory.java b/src/main/java/io/fabric8/maven/docker/util/AuthConfigFactory.java index 78308d8d8..4552ac050 100644 --- a/src/main/java/io/fabric8/maven/docker/util/AuthConfigFactory.java +++ b/src/main/java/io/fabric8/maven/docker/util/AuthConfigFactory.java @@ -278,7 +278,7 @@ private AuthConfig getAuthConfigViaAwsSdk() { } catch (ClassNotFoundException e) { log.info("It appears that you're using AWS ECR." + " Consider integrating the AWS SDK in order to make use of common AWS authentication mechanisms," + - " see TODO"); + " see https://dmp.fabric8.io/#extended-authentication"); return null; } return new AwsSdkAuthConfigFactory(log).createAuthConfig(); From c7b16e0b586405086ef429df0a4679b2fc8024d9 Mon Sep 17 00:00:00 2001 From: "Sebastian Kirsch (@skirsch79)" Date: Tue, 21 Jan 2020 08:52:54 +0100 Subject: [PATCH 15/15] Be more precise in AWS SDK usage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Roland Huß --- src/main/asciidoc/inc/_authentication.adoc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/asciidoc/inc/_authentication.adoc b/src/main/asciidoc/inc/_authentication.adoc index a2d7834f5..dd6f30faa 100644 --- a/src/main/asciidoc/inc/_authentication.adoc +++ b/src/main/asciidoc/inc/_authentication.adoc @@ -217,7 +217,7 @@ For a more complete, robust and reliable authentication experience, you can add ---- -This allows you to make use of all link:https://docs.aws.amazon.com/sdk-for-java/v1/developer-guide/credentials.html[the options the default credential provider chain provides]. +This extra dependency allows the usage of all link:https://docs.aws.amazon.com/sdk-for-java/v1/developer-guide/credentials.html[options] that the AWS default credential provider chain provides. If the AWS SDK is found in the classpath, it takes precedence over the custom AWS credentials lookup mechanisms listed above. -==== \ No newline at end of file +====