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

Some minor cosmetics from #663 #674

Merged
merged 1 commit into from
Jan 2, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion doc/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

* **0.18.2**
- Better log message when waiting for URL (#640)
- Extended authentication for AWS ECR
- Extended authentication for AWS ECR (#663)
- Add two new goals: "volume-create" and "volume-remove" for volume handling independent of images.


Expand Down
12 changes: 6 additions & 6 deletions src/main/java/io/fabric8/maven/docker/access/AuthConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@ public class AuthConfig {

public AuthConfig(Map<String,String> params) {
this(params.get("username"),
params.get("password"),
params.get("email"),
params.get("auth"));
params.get("password"),
params.get("email"),
params.get("auth"));
}

public AuthConfig(String username, String password, String email, String auth) {
Expand All @@ -42,11 +42,11 @@ public AuthConfig(String username, String password, String email, String auth) {
/**
* Constructor which takes an base64 encoded credentials in the form 'user:password'
*
* @param credentialsDockerEncoded the docker encoded user and password
* @param credentialsEncoded the docker encoded user and password
* @param email the email to use for authentication
*/
public AuthConfig(String credentialsDockerEncoded, String email) {
String credentials = new String(Base64.decodeBase64(credentialsDockerEncoded));
public AuthConfig(String credentialsEncoded, String email) {
String credentials = new String(Base64.decodeBase64(credentialsEncoded));
String[] parsedCreds = credentials.split(":",2);
username = parsedCreds[0];
password = parsedCreds[1];
Expand Down
35 changes: 16 additions & 19 deletions src/main/java/io/fabric8/maven/docker/access/ecr/AwsSigner4.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,19 +20,12 @@
import io.fabric8.maven.docker.access.AuthConfig;

/**
* AwsSigner4 implementation that signs requests with the AWS4 signing protocol.
* AwsSigner4 implementation that signs requests with the AWS4 signing protocol. Refer to the AWS docs for mor details.
*
* @author chas
* @since 2016-12-9
*/
public class AwsSigner4 {

private static final Comparator<NameValuePair> PAIR_NAME_COMPARATOR = new Comparator<NameValuePair>() {
@Override
public int compare(NameValuePair l, NameValuePair r) {
return l.getName().compareToIgnoreCase(r.getName());
}
};
class AwsSigner4 {

// a-f must be lower case
final private static char[] HEXITS = "0123456789abcdef".toCharArray();
Expand All @@ -46,7 +39,7 @@ public int compare(NameValuePair l, NameValuePair r) {
* @param region The aws region.
* @param service The aws service.
*/
public AwsSigner4(String region, String service) {
AwsSigner4(String region, String service) {
this.region = region;
this.service = service;
}
Expand All @@ -58,7 +51,7 @@ public AwsSigner4(String region, String service) {
* @param credentials The credentials to use when signing.
* @param signingTime The invocation time to use;
*/
public void sign(HttpRequest request, AuthConfig credentials, Date signingTime) {
void sign(HttpRequest request, AuthConfig credentials, Date signingTime) {
AwsSigner4Request sr = new AwsSigner4Request(region, service, request, signingTime);
if(!request.containsHeader("X-Amz-Date")) {
request.addHeader("X-Amz-Date", sr.getSigningDateTime());
Expand Down Expand Up @@ -101,13 +94,12 @@ final byte[] task3(AwsSigner4Request sr, AuthConfig credentials) {
return hmacSha256(getSigningKey(sr, credentials), task2(sr));
}

static byte[] getSigningKey(AwsSigner4Request sr, AuthConfig credentials) {
private static byte[] getSigningKey(AwsSigner4Request sr, AuthConfig credentials) {
byte[] kSecret = ("AWS4" + credentials.getPassword()).getBytes(StandardCharsets.UTF_8);
byte[] kDate = hmacSha256(kSecret, sr.getSigningDate());
byte[] kRegion = hmacSha256(kDate, sr.getRegion());
byte[] kService = hmacSha256(kRegion, sr.getService());
byte[] signingKey = hmacSha256(kService, "aws4_request");
return signingKey;
return hmacSha256(kService, "aws4_request");
}

/**
Expand All @@ -129,19 +121,24 @@ private String getCanonicalQuery(URI uri) {
return "";
}
List<NameValuePair> params = URLEncodedUtils.parse(query, StandardCharsets.UTF_8);
Collections.sort(params, PAIR_NAME_COMPARATOR);
Collections.sort(params, new Comparator<NameValuePair>() {
@Override
public int compare(NameValuePair l, NameValuePair r) {
return l.getName().compareToIgnoreCase(r.getName());
}
});
return URLEncodedUtils.format(params, StandardCharsets.UTF_8);
}

static void hexEncode(StringBuilder dst, byte[] src) {
for ( int i = 0; i < src.length; ++i ) {
int v = src[i] & 0xFF;
for (byte aSrc : src) {
int v = aSrc & 0xFF;
dst.append(HEXITS[v >>> 4]);
dst.append(HEXITS[v & 0x0F]);
}
}

static byte[] hmacSha256(byte[] key, String value) {
private static byte[] hmacSha256(byte[] key, String value) {
try {
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(key, "HmacSHA256"));
Expand All @@ -161,7 +158,7 @@ private static byte[] sha256(byte[] bytes) {
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update(bytes);
return md.digest();
}
}
catch (NoSuchAlgorithmException e) {
throw new UnsupportedOperationException(e.getMessage(), e);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,9 @@ public class EcrExtendedAuth {
Pattern.compile("^(\\d{12})\\.dkr\\.ecr\\.([a-z\\-0-9]+)\\.amazonaws\\.com$");

private final Logger logger;
private final Matcher matcher;
private final boolean isValid;
private final boolean isAwsRegistry;
private final String accountId;
private final String region;

/**
* Initialize an extended authentication for ecr registry.
Expand All @@ -45,17 +46,24 @@ public class EcrExtendedAuth {
*/
public EcrExtendedAuth(Logger logger, String registry) {
this.logger = logger;
matcher = AWS_REGISTRY.matcher(registry);
isValid = matcher.matches();
logger.debug("registry = %s, isValid= %b", registry, isValid);
Matcher matcher = AWS_REGISTRY.matcher(registry);
isAwsRegistry = matcher.matches();
if (isAwsRegistry) {
accountId = matcher.group(1);
region = matcher.group(2);
} else {
accountId = null;
region = null;
}
logger.debug("registry = %s, isValid= %b", registry, isAwsRegistry);
}

/**
* Is the registry an ecr registry?
* @return true, if the registry matches the ecr pattern
*/
public boolean isValidRegistry() {
return isValid;
public boolean isAwsRegistry() {
return isAwsRegistry;
}

/**
Expand Down Expand Up @@ -86,12 +94,12 @@ CloseableHttpClient createClient() {
return HttpClients.createDefault();
}

JSONObject executeRequest(CloseableHttpClient client, HttpPost request) throws IOException, MojoExecutionException {
private JSONObject executeRequest(CloseableHttpClient client, HttpPost request) throws IOException, MojoExecutionException {
try {
CloseableHttpResponse response = client.execute(request);
int statusCode = response.getStatusLine().getStatusCode();
logger.debug("Response status %d", statusCode);
if(statusCode != HttpStatus.SC_OK) {
if (statusCode != HttpStatus.SC_OK) {
throw new MojoExecutionException("AWS authentication failure");
}

Expand All @@ -105,11 +113,9 @@ JSONObject executeRequest(CloseableHttpClient client, HttpPost request) throws I
}

HttpPost createSignedRequest(AuthConfig localCredentials, Date time) {
String accountId = matcher.group(1);
String region = matcher.group(2);
String host = "ecr." + region + ".amazonaws.com";

logger.debug("GetAuthorizationToken from %s", host);
logger.debug("Get ECR AuthorizationToken from %s", host);

HttpPost request = new HttpPost("https://" + host + '/');
request.setHeader("host", host);
Expand Down
33 changes: 13 additions & 20 deletions src/main/java/io/fabric8/maven/docker/util/AuthConfigFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,6 @@ public void setLog(Logger log) {
* credentials are not from docker settings, they will be interpreted as iam credentials
* and exchanged for ecr credentials.
*
* @param logger The logger for tracing
* @param isPush if true this AuthConfig is created for a push, if false it's for a pull
* @param skipExtendedAuth if false, do not execute extended authentication methods
* @param authConfig String-String Map holding configuration info from the plugin's configuration. Can be <code>null</code> in
Expand All @@ -107,16 +106,11 @@ public AuthConfig createAuthConfig(boolean isPush, boolean skipExtendedAuth, Map

AuthConfig ret = createStandardAuthConfig(isPush, authConfig, settings, user, registry);
if (ret != null) {
if (registry == null ) {
log.debug("default registry; no extended auth");
return ret;
}
if (skipExtendedAuth) {
log.debug("skipping extended auth");
if (registry == null || skipExtendedAuth) {
return ret;
}
try {
return extendedAuthentication(registry, ret);
return extendedAuthentication(ret, registry);
} catch (IOException e) {
throw new MojoExecutionException(e.getMessage(), e);
}
Expand All @@ -125,31 +119,30 @@ public AuthConfig createAuthConfig(boolean isPush, boolean skipExtendedAuth, Map
// Finally check ~/.docker/config.json
ret = getAuthConfigFromDockerConfig(registry);
if(ret != null) {
log.debug("found credentials in ~.docker/config.json");
log.debug("AuthConfig: credentials from ~.docker/config.json");
return ret;
}

log.debug("no credentials found");
log.debug("AuthConfig: no credentials found");
return null;
}

/**
* Try various extended authentication method. Currently only supports amazon ECR
*
* @param logger The logger for tracing
* @param standardAuthConfig The locally stored credentials.
* @param registry The registry to authenticated against.
* @param localCredentials The locally stored credentials.
* @return The given credentials, if registry does not need extended authentication;
* else, the credentials after authentication.
* @throws IOException
* @throws MojoExecutionException
*/
private AuthConfig extendedAuthentication(String registry, AuthConfig localCredentials) throws IOException, MojoExecutionException {
private AuthConfig extendedAuthentication(AuthConfig standardAuthConfig, String registry) throws IOException, MojoExecutionException {
EcrExtendedAuth ecr = new EcrExtendedAuth(log, registry);
if (ecr.isValidRegistry()) {
return ecr.extendedAuth(localCredentials);
if (ecr.isAwsRegistry()) {
return ecr.extendedAuth(standardAuthConfig);
}
return localCredentials;
return standardAuthConfig;
}

/**
Expand Down Expand Up @@ -192,21 +185,21 @@ private AuthConfig createStandardAuthConfig(boolean isPush, Map authConfigMap, S
// System properties docker.username and docker.password always take precedence
ret = getAuthConfigFromSystemProperties(lookupMode);
if (ret != null) {
log.debug("found credentials in system properties");
log.debug("AuthConfig: credentials from system properties");
return ret;
}

// Check for openshift authentication either from the plugin config or from system props
ret = getAuthConfigFromOpenShiftConfig(lookupMode,authConfigMap);
if (ret != null) {
log.debug("found openshift credentials");
log.debug("AuthConfig: OpenShift credentials");
return ret;
}

// Get configuration from global plugin config
ret = getAuthConfigFromPluginConfiguration(lookupMode,authConfigMap);
if (ret != null) {
log.debug("found credentials in plugin config");
log.debug("AuthConfig: credentials from plugin config");
return ret;
}
}
Expand All @@ -217,7 +210,7 @@ private AuthConfig createStandardAuthConfig(boolean isPush, Map authConfigMap, S
// Now lets lookup the registry & user from ~/.m2/setting.xml
ret = getAuthConfigFromSettings(settings, user, registry);
if (ret != null) {
log.debug("found credentials in ~/.m2/setting.xml");
log.debug("AuthConfig: credentials from ~/.m2/setting.xml");
return ret;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package io.fabric8.maven.docker.access.ecr;

import static org.junit.Assert.*;

import org.junit.Test;

import io.fabric8.maven.docker.access.AuthConfig;
Expand Down Expand Up @@ -39,12 +37,12 @@ public class EcrExtendedAuthTest {

@Test
public void testIsNotAws() {
assertFalse(new EcrExtendedAuth(logger, "jolokia").isValidRegistry());
assertFalse(new EcrExtendedAuth(logger, "jolokia").isAwsRegistry());
}

@Test
public void testIsAws() {
assertTrue(new EcrExtendedAuth(logger, "123456789012.dkr.ecr.eu-west-1.amazonaws.com").isValidRegistry());
assertTrue(new EcrExtendedAuth(logger, "123456789012.dkr.ecr.eu-west-1.amazonaws.com").isAwsRegistry());
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
* @since 29.07.14
*/
@RunWith(JMockit.class)
public class AuthConfigFatoryTest {
public class AuthConfigFactoryTest {

public static final String ECR_NAME = "123456789012.dkr.ecr.bla.amazonaws.com";

Expand Down