Skip to content

Commit

Permalink
#147: Lookup in ~/.docker/config.json for registry credentials.
Browse files Browse the repository at this point in the history
  • Loading branch information
rhuss committed Dec 7, 2015
1 parent bdf1301 commit 9e65c75
Show file tree
Hide file tree
Showing 6 changed files with 132 additions and 12 deletions.
1 change: 1 addition & 0 deletions doc/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
- Allow multiple configurations with different Docker hosts again (#320)
- `docker:start` blocks now only when system property docker.follow is given (#249)
- `docker:stop` only stops containers started by this plugin by default (#87)
- Lookup `~/.docker/config.json` for registry credentials as fallback (#147)

* **0.13.6**
- Don't use user from image when pulling base images ([#147](https://github.com/rhuss/docker-maven-plugin/issues/147))
Expand Down
4 changes: 4 additions & 0 deletions doc/manual/authentication.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ There are three different ways for providing credentials:
from the outside
* Using a `<server>` configuration in the the `~/.m2/settings.xml`
settings
* Login into a registry with `docker login`

Using the username and password directly in the `pom.xml` is not
recommended since this is widely visible. This is most easiest and
Expand Down Expand Up @@ -57,6 +58,9 @@ have a second accorunt 'rhuss' then use an `<id>docker.io/rhuss</id>` for this s
username with a slash to the id name. The default without username is only taken if no server entry with
a username appended id is chosen.

As a final fallback, this plugin consults `~/.docker/config.json` for getting to the credentials. Within this
file credentials are stored when connecting to a registry with the command `docker login` from the command line.

#### Password encryption

Regardless which mode you choose you can encrypt password as described
Expand Down
24 changes: 18 additions & 6 deletions src/main/java/org/jolokia/docker/maven/access/AuthConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,20 @@ public AuthConfig(String user, String password, String email, String auth) {
this.authEncoded = createAuthEncoded(params);
}

public AuthConfig(String authEncoded) {
this.authEncoded = authEncoded;
/**
* Constructor which takes an base64 encoded credentials in the form 'user:password'
*
* @param credentialsDockerEncoded 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));
String[] parsedCreds = credentials.split(":",2);
Map<String,String> params = new HashMap<>();
putNonNull(params,"username",parsedCreds[0]);
putNonNull(params,"password",parsedCreds[1]);
putNonNull(params,"email",email);
this.authEncoded = createAuthEncoded(params);
}

public String toHeaderValue() {
Expand All @@ -46,10 +58,10 @@ public String toHeaderValue() {

private String createAuthEncoded(Map<String,String> params) {
JSONObject ret = new JSONObject();
add(params,ret,"username");
add(params,ret,"password");
add(params,ret,"email");
add(params,ret,"auth");
add(params, ret, "username");
add(params, ret, "password");
add(params, ret, "email");
add(params, ret, "auth");
try {
return Base64.encodeBase64String(ret.toString().getBytes("UTF-8"));
} catch (UnsupportedEncodingException e) {
Expand Down
45 changes: 43 additions & 2 deletions src/main/java/org/jolokia/docker/maven/util/AuthConfigFactory.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.jolokia.docker.maven.util;

import java.io.*;
import java.lang.reflect.Method;
import java.util.*;

Expand All @@ -11,6 +12,8 @@
import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
import org.codehaus.plexus.util.xml.Xpp3Dom;
import org.jolokia.docker.maven.access.AuthConfig;
import org.json.JSONObject;
import org.json.JSONTokener;
import org.sonatype.plexus.components.sec.dispatcher.SecDispatcher;

/**
Expand All @@ -27,6 +30,8 @@ public class AuthConfigFactory {
private static final String DOCKER_EMAIL = "docker.email";
private static final String DOCKER_AUTH = "docker.authToken";

static final String DOCKER_LOGIN_DEFAULT_REGISTRY = "https://index.docker.io/v1/";

private final PlexusContainer container;
public static final String[] DEFAULT_REGISTRIES = new String[]{
"docker.io", "index.docker.io", "registry.hub.docker.com"
Expand Down Expand Up @@ -79,7 +84,9 @@ public AuthConfig createAuthConfig(Map authConfig, Settings settings, String use
}
AuthConfig ret = getAuthConfigFromSettings(settings,user,registry);
if (ret == null) {
return getAuthConfigFromDockerLogin()
return getAuthConfigFromDockerConfig(registry);
} else {
return ret;
}
}

Expand Down Expand Up @@ -109,10 +116,44 @@ private AuthConfig getAuthConfigFromPluginConfiguration(Map authConfig) throws M
return new AuthConfig(cloneConfig);
}

private AuthConfig getAuthConfigFromDockerLogin() throws MojoExecutionException {
private AuthConfig getAuthConfigFromDockerConfig(String registry) throws MojoExecutionException {
JSONObject dockerConfig = readDockerConfig();
if (dockerConfig != null && dockerConfig.has("auths")) {
JSONObject auths = dockerConfig.getJSONObject("auths");
String registryToLookup = registry != null ? registry : DOCKER_LOGIN_DEFAULT_REGISTRY;
if (auths.has(registryToLookup)) {
JSONObject entry = auths.getJSONObject(registryToLookup);
if (entry.has("auth")) {
String auth = entry.getString("auth");
String email = entry.has("email") ? entry.getString("email") : null;
return new AuthConfig(auth,email);
}
}
}
return null;
}

private JSONObject readDockerConfig() {
String homeDir = System.getProperty("user.home");
if (homeDir == null) {
homeDir = System.getenv("HOME");
}
if (homeDir != null) {
File config = new File(homeDir + "/.docker/config.json");
if (config.exists()) {
try {
JSONTokener tokener =new JSONTokener(new FileReader(config));
return new JSONObject(tokener);
} catch (FileNotFoundException e) {
// Shouldnt happen. Nevertheless ...
throw new IllegalStateException("Cannot find " + config,e);
}
}
}
return null;
}


private AuthConfig getAuthConfigFromSettings(Settings settings, String user, String registry) throws MojoExecutionException {
Server defaultServer = null;
Server found;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package org.jolokia.docker.maven.util;

import java.io.*;
import java.nio.file.Files;
import java.util.*;

import mockit.*;
Expand All @@ -11,12 +13,12 @@
import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
import org.codehaus.plexus.util.xml.Xpp3Dom;
import org.jolokia.docker.maven.access.AuthConfig;
import org.json.JSONObject;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.sonatype.plexus.components.sec.dispatcher.SecDispatcher;

import static mockit.Deencapsulation.getField;
import static org.junit.Assert.*;

/**
Expand Down Expand Up @@ -53,7 +55,7 @@ public void containerSetup() throws ComponentLookupException {

@Test
public void testEmpty() throws Exception {
assertNull(factory.createAuthConfig(null, settings, null, null));
assertNull(factory.createAuthConfig(null, settings, null, "blubberbla:1611"));
}

@Test
Expand All @@ -71,6 +73,60 @@ public void testSystemProperty() throws Exception {
}
}


@Test
public void testDockerLogin() throws Exception {
String userHome = System.getProperty("user.home");
try {
File tempDir = Files.createTempDirectory("d-m-p").toFile();
System.setProperty("user.home", tempDir.getAbsolutePath());
checkDockerLogin(tempDir,AuthConfigFactory.DOCKER_LOGIN_DEFAULT_REGISTRY,null);
checkDockerLogin(tempDir,"localhost:5000","localhost:5000");
} finally {
System.setProperty("user.home",userHome);
}
}

@Test
public void testDockerLoginNoConfig() throws MojoExecutionException {
String userHome = System.getProperty("user.home");
try {
System.setProperty("user.home","never/have/this/home");
AuthConfig config = factory.createAuthConfig(null, settings, "roland", null);
assertNull(config);
} finally {
System.setProperty("user.home",userHome);
}
}
private void checkDockerLogin(File homeDir,String configRegistry, String lookupRegistry) throws IOException,
MojoExecutionException {
createDockerConfig(homeDir, "roland", "secret", "roland@jolokia.org", configRegistry);
AuthConfig config = factory.createAuthConfig(null, settings, "roland", lookupRegistry);
verifyAuthConfig(config,"roland","secret","roland@jolokia.org");
}

private void createDockerConfig(File homeDir, String user, String password, String email, String registry)
throws IOException {
File dockerDir = new File(homeDir,".docker");
dockerDir.mkdirs();
writeDockerConfigJson(dockerDir, user, password, email, registry);
}

private void writeDockerConfigJson(File dockerDir, String user, String password,
String email, String registry) throws IOException {
File configFile = new File(dockerDir,"config.json");
JSONObject config = new JSONObject();
JSONObject auths = new JSONObject();
JSONObject value = new JSONObject();
value.put("auth", Base64.getEncoder().encodeToString(new String(user + ":" + password).getBytes()));
value.put("email",email);
auths.put(registry,value);
config.put("auths",auths);
Writer writer = new FileWriter(configFile);
config.write(writer);
writer.close();
}

@Test(expected = MojoExecutionException.class)
public void testSystemPropertyNoUser() throws Exception {
checkException("docker.password");
Expand Down Expand Up @@ -126,7 +182,7 @@ public void testFromSettingsDefault() throws MojoExecutionException {
}

@Test
public void testFormSettingsDefault() throws MojoExecutionException {
public void testFromSettingsDefault2() throws MojoExecutionException {
setupServers();
AuthConfig config = factory.createAuthConfig(null,settings,"tanja",null);
assertNotNull(config);
Expand Down Expand Up @@ -167,7 +223,7 @@ private void setupServers() {
}

private void verifyAuthConfig(AuthConfig config, String username, String password, String email) {
Map params = getField(config,"params");
JSONObject params = new JSONObject(new String(Base64.getDecoder().decode(config.toHeaderValue())));
assertEquals(username,params.get("username"));
assertEquals(password,params.get("password"));
assertEquals(email, params.get("email"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@ public void mapConstructor() {
check(config);
}

@Test
public void dockerLoginConstructor() {
AuthConfig config = new AuthConfig(Base64.encodeBase64String("roland:secret".getBytes()),"roland@jolokia.org");
check(config);
}

private void check(AuthConfig config) {
String header = new String(Base64.decodeBase64(config.toHeaderValue()));
JSONObject data = new JSONObject(header);
Expand Down

0 comments on commit 9e65c75

Please sign in to comment.