Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
Next Next commit
Removed options to specify keystore and truststore files. Added abili…
…ty to leverage environment variable or explicitly specify location of default certificates
  • Loading branch information
sabre1041 committed Oct 22, 2014
commit 2ce62f4ab93de79e74b5db8dbba70552bc46f005
7 changes: 7 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
<jsr305.version>1.3.9</jsr305.version>
<jnr.unixsocket.version>0.3</jnr.unixsocket.version>
<guava.version>18.0</guava.version>
<bouncycastle.version>1.51</bouncycastle.version>

<!--test dependencies -->
<version.logback>1.0.1</version.logback>
Expand Down Expand Up @@ -142,6 +143,12 @@
<artifactId>guava</artifactId>
<version>${guava.version}</version>
</dependency>

<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk15on</artifactId>
<version>${bouncycastle.version}</version>
</dependency>

<!-- /// Test /////////////////////////// -->
<dependency>
Expand Down
142 changes: 142 additions & 0 deletions src/main/java/com/github/dockerjava/core/CertificateUtils.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
package com.github.dockerjava.core;

import java.io.BufferedReader;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

import org.apache.commons.io.IOUtils;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.openssl.PEMKeyPair;
import org.bouncycastle.openssl.PEMParser;

public class CertificateUtils {

public static boolean verifyCertificatesExist(String dockerCertPath) {
String[] files = {"ca.pem", "cert.pem", "key.pem"};
for (String file : files) {
Path path = Paths.get(dockerCertPath, file);
boolean exists = Files.exists(path);
if(!exists) {
return false;
}
}

return true;
}

public static KeyStore createKeyStore(final String dockerCertPath) throws NoSuchAlgorithmException, InvalidKeySpecException, IOException, CertificateException, KeyStoreException {
KeyPair keyPair = loadPrivateKey(dockerCertPath);
Certificate privateCertificate = loadCertificate(dockerCertPath);

KeyStore keyStore = KeyStore.getInstance("JKS");
keyStore.load(null);

keyStore.setKeyEntry("docker", keyPair.getPrivate(), "docker".toCharArray(), new Certificate[]{privateCertificate});
return keyStore;
}

public static KeyStore createTrustStore(final String dockerCertPath) throws IOException, CertificateException, KeyStoreException, NoSuchAlgorithmException {
Path caPath = Paths.get(dockerCertPath, "ca.pem");
BufferedReader reader = Files.newBufferedReader(caPath, Charset.defaultCharset());
PEMParser pemParser = null;

try {
pemParser = new PEMParser(reader);
X509CertificateHolder certificateHolder = (X509CertificateHolder) pemParser.readObject();
Certificate caCertificate = new JcaX509CertificateConverter().setProvider("BC").getCertificate(certificateHolder);

KeyStore trustStore = KeyStore.getInstance("JKS");
trustStore.load(null);
trustStore.setCertificateEntry("ca", caCertificate);
return trustStore;

}
finally {
if(pemParser != null) {
IOUtils.closeQuietly(pemParser);
}

if(reader != null) {
IOUtils.closeQuietly(reader);
}
}

}

private static Certificate loadCertificate(final String dockerCertPath) throws IOException, CertificateException {
Path certificate = Paths.get(dockerCertPath, "cert.pem");
BufferedReader reader = Files.newBufferedReader(certificate, Charset.defaultCharset());
PEMParser pemParser = null;

try {
pemParser = new PEMParser(reader);
X509CertificateHolder certificateHolder = (X509CertificateHolder) pemParser.readObject();
return new JcaX509CertificateConverter().setProvider("BC").getCertificate(certificateHolder);
}
finally {
if(pemParser != null) {
IOUtils.closeQuietly(pemParser);
}

if(reader != null) {
IOUtils.closeQuietly(reader);
}
}

}

private static KeyPair loadPrivateKey(final String dockerCertPath) throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
Path certificate = Paths.get(dockerCertPath, "key.pem");
BufferedReader reader = Files.newBufferedReader(certificate, Charset.defaultCharset());

PEMParser pemParser = null;

try {
pemParser = new PEMParser(reader);

PEMKeyPair pemKeyPair = (PEMKeyPair) pemParser.readObject();

byte[] pemPrivateKeyEncoded = pemKeyPair.getPrivateKeyInfo().getEncoded();
byte[] pemPublicKeyEncoded = pemKeyPair.getPublicKeyInfo().getEncoded();

KeyFactory factory = KeyFactory.getInstance("RSA");

X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(pemPublicKeyEncoded);
PublicKey publicKey = factory.generatePublic(publicKeySpec);

PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(pemPrivateKeyEncoded);
PrivateKey privateKey = factory.generatePrivate(privateKeySpec);

return new KeyPair(publicKey, privateKey);

}
finally {
if(pemParser != null) {
IOUtils.closeQuietly(pemParser);
}

if(reader != null) {
IOUtils.closeQuietly(reader);
}
}


}

}
48 changes: 9 additions & 39 deletions src/main/java/com/github/dockerjava/core/DockerClientConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

public class DockerClientConfig {
private final URI uri;
private final String version, username, password, email, keystore, keystorePassword, truststore, truststorePassword;
private final String version, username, password, email, dockerCertPath;
private final Integer readTimeout;
private final boolean loggingFilterEnabled;

Expand All @@ -22,10 +22,7 @@ private DockerClientConfig(DockerClientConfigBuilder builder) {
this.email = builder.email;
this.readTimeout = builder.readTimeout;
this.loggingFilterEnabled = builder.loggingFilterEnabled;
this.keystore = builder.keystore;
this.keystorePassword = builder.keystorePassword;
this.truststore = builder.truststore;
this.truststorePassword = builder.truststorePassword;
this.dockerCertPath = builder.dockerCertPath;
}

public URI getUri() {
Expand Down Expand Up @@ -56,20 +53,8 @@ public boolean isLoggingFilterEnabled() {
return loggingFilterEnabled;
}

public String getKeystore() {
return keystore;
}

public String getKeystorePassword() {
return keystorePassword;
}

public String getTruststore() {
return truststore;
}

public String getTruststorePassword() {
return truststorePassword;
public String getDockerCertPath() {
return dockerCertPath;
}

public static Properties loadIncludedDockerProperties() {
Expand Down Expand Up @@ -117,7 +102,7 @@ public static Properties overrideDockerPropertiesWithSystemProperties(Properties
overriddenProperties.putAll(p);

// TODO Add all values from system properties that begin with docker.io.*
for (String s : new String[]{ "url", "version", "username", "password", "email", "readTimeout", "enableLoggingFilter", "keystore", "keystorePassword", "truststore", "truststorePassword"}) {
for (String s : new String[]{ "url", "version", "username", "password", "email", "readTimeout", "enableLoggingFilter", "dockerCertPath"}) {
final String key = "docker.io." + s;
if (System.getProperties().containsKey(key)) {
overriddenProperties.setProperty(key, System.getProperty(key));
Expand All @@ -135,7 +120,7 @@ public static DockerClientConfigBuilder createDefaultConfigBuilder() {

public static class DockerClientConfigBuilder {
private URI uri;
private String version, username, password, email, keystore, keystorePassword, truststore, truststorePassword;
private String version, username, password, email, dockerCertPath;
private Integer readTimeout;
private boolean loggingFilterEnabled;

Expand All @@ -159,10 +144,7 @@ public DockerClientConfigBuilder withProperties(Properties p) {
.withEmail(p.getProperty("docker.io.email"))
.withReadTimeout(Integer.valueOf(p.getProperty("docker.io.readTimeout", "0")))
.withLoggingFilter(Boolean.valueOf(p.getProperty("docker.io.enableLoggingFilter", "true")))
.withKeystore(p.getProperty("docker.io.keystore"))
.withKeystorePassword(p.getProperty("docker.io.keystorePassword"))
.withTruststore(p.getProperty("docker.io.truststore"))
.withTruststorePassword(p.getProperty("docker.io.truststorePassword"));
.withDockerCertPath(p.getProperty("docker.io.dockerCertPath"));
}

public final DockerClientConfigBuilder withUri(String uri) {
Expand Down Expand Up @@ -194,20 +176,8 @@ public final DockerClientConfigBuilder withLoggingFilter(boolean loggingFilterEn
this.loggingFilterEnabled = loggingFilterEnabled;
return this;
}
public final DockerClientConfigBuilder withKeystore(String keystore) {
this.keystore = keystore;
return this;
}
public final DockerClientConfigBuilder withKeystorePassword(String keystorePassword) {
this.keystorePassword = keystorePassword;
return this;
}
public final DockerClientConfigBuilder withTruststore(String truststore) {
this.truststore = truststore;
return this;
}
public final DockerClientConfigBuilder withTruststorePassword(String truststorePassword) {
this.truststorePassword = truststorePassword;
public final DockerClientConfigBuilder withDockerCertPath(String dockerCertPath) {
this.dockerCertPath = dockerCertPath;
return this;
}
public DockerClientConfig build() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.github.dockerjava.jaxrs;

import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider;
import com.github.dockerjava.api.DockerClientException;
import com.github.dockerjava.api.command.AttachContainerCmd;
import com.github.dockerjava.api.command.AuthCmd;
import com.github.dockerjava.api.command.BuildImageCmd;
Expand Down Expand Up @@ -33,20 +34,25 @@
import com.github.dockerjava.api.command.UnpauseContainerCmd;
import com.github.dockerjava.api.command.VersionCmd;
import com.github.dockerjava.api.command.WaitContainerCmd;
import com.github.dockerjava.core.CertificateUtils;
import com.github.dockerjava.core.DockerClientConfig;
import com.github.dockerjava.jaxrs.util.JsonClientFilter;
import com.github.dockerjava.jaxrs.util.ResponseStatusExceptionFilter;
import com.github.dockerjava.jaxrs.util.SelectiveLoggingFilter;
import com.google.common.base.Preconditions;

import java.io.File;
import java.io.IOException;
import java.security.KeyStore;
import java.security.Security;
import java.util.logging.Logger;

import javax.net.ssl.SSLContext;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.WebTarget;

import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.glassfish.jersey.CommonProperties;
import org.glassfish.jersey.SslConfigurator;
import org.glassfish.jersey.client.ClientConfig;
Expand Down Expand Up @@ -83,22 +89,49 @@ public void init(DockerClientConfig dockerClientConfig) {

ClientBuilder clientBuilder = ClientBuilder.newBuilder().withConfig(clientConfig);


if((dockerClientConfig.getKeystore() != null && dockerClientConfig.getKeystorePassword() != null) || (dockerClientConfig.getTruststore() != null && dockerClientConfig.getTruststorePassword() != null)) {
SslConfigurator sslConfig = SslConfigurator.newInstance();
// Attempt to load Docker SSL certificates from location in following order:
// 1. User Defined
// 2. Environment Variable
// 3. User Home Directory
String dockerCertPath = dockerClientConfig.getDockerCertPath();

if(dockerClientConfig.getKeystore() != null && dockerClientConfig.getKeystorePassword() != null) {
sslConfig.keyStoreFile(dockerClientConfig.getKeystore());
sslConfig.keyStorePassword(dockerClientConfig.getKeystorePassword());
}
if(dockerCertPath == null) {
dockerCertPath = System.getenv("DOCKER_CERT_PATH");
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is cool. Do we want to use a common set of env properties for configuration for consistency? E.g. DOCKER_USERNAME etc.?

}

if(dockerClientConfig.getTruststore() != null && dockerClientConfig.getTruststorePassword() != null) {
sslConfig.trustStoreFile(dockerClientConfig.getTruststore());
sslConfig.trustStorePassword(dockerClientConfig.getTruststorePassword());
}
if(dockerCertPath == null) {
dockerCertPath = System.getProperty("USER_HOME") + File.separator + ".docker";
}

if(dockerCertPath != null) {
boolean certificatesExist = CertificateUtils.verifyCertificatesExist(dockerCertPath);

SSLContext sslContext = sslConfig.createSSLContext();
clientBuilder.sslContext(sslContext);
if(certificatesExist) {

try {

Security.addProvider(new BouncyCastleProvider());

SslConfigurator sslConfig = SslConfigurator.newInstance();

KeyStore keyStore = CertificateUtils.createKeyStore(dockerCertPath);
KeyStore trustStore = CertificateUtils.createTrustStore(dockerCertPath);

sslConfig.keyStore(keyStore);
sslConfig.keyStorePassword("docker");

sslConfig.trustStore(trustStore);

SSLContext sslContext = sslConfig.createSSLContext();
clientBuilder.sslContext(sslContext);

}
catch(Exception e) {
throw new DockerClientException(e.getMessage(), e);
}

}

}

client = clientBuilder.build();
Expand Down