[docker] upgrade to docker 1.7 and refactor TLS support
Project: http://git-wip-us.apache.org/repos/asf/jclouds/repo Commit: http://git-wip-us.apache.org/repos/asf/jclouds/commit/55a7d8ce Tree: http://git-wip-us.apache.org/repos/asf/jclouds/tree/55a7d8ce Diff: http://git-wip-us.apache.org/repos/asf/jclouds/diff/55a7d8ce Branch: refs/heads/master Commit: 55a7d8ce3cdd293667202515e52c25691ac83759 Parents: 4edaf18 Author: Csaba Palfi <[email protected]> Authored: Mon Jul 13 22:26:26 2015 +0200 Committer: Ignasi Barrera <[email protected]> Committed: Wed Jul 22 10:22:36 2015 +0200 ---------------------------------------------------------------------- apis/docker/README.md | 10 +- apis/docker/pom.xml | 33 +++- .../org/jclouds/docker/DockerApiMetadata.java | 3 + .../docker/config/DockerHttpApiModule.java | 17 +- .../config/DockerOkHttpClientSupplier.java | 40 ++--- .../java/org/jclouds/docker/domain/Config.java | 37 ++-- .../java/org/jclouds/docker/domain/Info.java | 16 +- .../suppliers/DockerSSLContextSupplier.java | 64 +++++++ .../DockerUntrustedSSLContextSupplier.java | 60 +++++++ .../docker/suppliers/SSLContextBuilder.java | 174 +++++++++++++++++++ .../suppliers/SSLContextWithKeysSupplier.java | 166 ------------------ .../docker/compute/BaseDockerApiLiveTest.java | 4 +- .../docker/features/ContainerApiLiveTest.java | 2 +- .../org/jclouds/docker/parse/InfoParseTest.java | 8 +- apis/docker/src/test/resources/info.json | 8 +- 15 files changed, 404 insertions(+), 238 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/jclouds/blob/55a7d8ce/apis/docker/README.md ---------------------------------------------------------------------- diff --git a/apis/docker/README.md b/apis/docker/README.md index b3c51a1..c5080e6 100644 --- a/apis/docker/README.md +++ b/apis/docker/README.md @@ -8,15 +8,11 @@ Please follow these steps to configure your workstation for jclouds-docker: - install the latest Docker release (please visit https://docs.docker.com/installation/) -If you are using `boot2docker`, notice that from version v1.3.0 the Docker daemon is set to use an encrypted TCP -socket (--tls, or --tlsverify), -then you need to import CA certificate into Trusted Certs: - - `keytool -import -trustcacerts -file /Users/andrea/.boot2docker/certs/boot2docker-vm/ca.pem -alias BOOT2DOCKER -keystore $JAVA_HOME/jre/lib/security/cacerts` +If you are using `boot2docker` then it can also manage certificates and help you setup `DOCKER_CERT_PATH` and `DOCKER_HOST` environment variables. (See `boot2docker shellinit`) -by default the passoword is `changeit` +Assuming these environment variables are setup correctly there are no further setups steps are required. -N.B.: From `Docker 1.3.2+` the server doesn't accept sslv3 protocol (https://github.com/docker/docker/pull/8588/files) +Live tests then can now be run: `mvn -Plive integration-test` #How it works http://git-wip-us.apache.org/repos/asf/jclouds/blob/55a7d8ce/apis/docker/pom.xml ---------------------------------------------------------------------- diff --git a/apis/docker/pom.xml b/apis/docker/pom.xml index aebfce3..d93d43b 100644 --- a/apis/docker/pom.xml +++ b/apis/docker/pom.xml @@ -34,10 +34,11 @@ <packaging>bundle</packaging> <properties> - <test.docker.endpoint>https://localhost:4243</test.docker.endpoint> - <test.docker.api-version>1.15</test.docker.api-version> - <test.docker.identity>FIXME</test.docker.identity> - <test.docker.credential>FIXME</test.docker.credential> + <test.docker.api-version>1.19</test.docker.api-version> + <test.docker.identity>${env.DOCKER_CERT_PATH}/cert.pem</test.docker.identity> + <test.docker.credential>${env.DOCKER_CERT_PATH}/key.pem</test.docker.credential> + <test.docker.cacert.path>${env.DOCKER_CERT_PATH}/ca.pem</test.docker.cacert.path> + <test.jclouds.trust-all-certs>false</test.jclouds.trust-all-certs> <jclouds.osgi.export>org.jclouds.docker*;version="${project.version}"</jclouds.osgi.export> <jclouds.osgi.import> org.jclouds.compute.internal;version="${project.version}", @@ -135,6 +136,26 @@ <build> <plugins> <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>build-helper-maven-plugin</artifactId> + <version>1.9.1</version> + <executions> + <execution> + <id>regex-property</id> + <goals> + <goal>regex-property</goal> + </goals> + <configuration> + <name>test.docker.endpoint</name> + <value>${env.DOCKER_HOST}</value> + <regex>tcp</regex> + <replacement>https</replacement> + <failIfNoMatch>false</failIfNoMatch> + </configuration> + </execution> + </executions> + </plugin> + <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <executions> @@ -149,8 +170,10 @@ <systemPropertyVariables> <test.docker.endpoint>${test.docker.endpoint}</test.docker.endpoint> <test.docker.api-version>${test.docker.api-version}</test.docker.api-version> - <test.docker.credential>${test.docker.identity}</test.docker.credential> + <test.docker.identity>${test.docker.identity}</test.docker.identity> <test.docker.credential>${test.docker.credential}</test.docker.credential> + <test.docker.cacert.path>${test.docker.cacert.path}</test.docker.cacert.path> + <test.jclouds.trust-all-certs>${test.jclouds.trust-all-certs}</test.jclouds.trust-all-certs> </systemPropertyVariables> </configuration> </execution> http://git-wip-us.apache.org/repos/asf/jclouds/blob/55a7d8ce/apis/docker/src/main/java/org/jclouds/docker/DockerApiMetadata.java ---------------------------------------------------------------------- diff --git a/apis/docker/src/main/java/org/jclouds/docker/DockerApiMetadata.java b/apis/docker/src/main/java/org/jclouds/docker/DockerApiMetadata.java index ea10c70..6585541 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/DockerApiMetadata.java +++ b/apis/docker/src/main/java/org/jclouds/docker/DockerApiMetadata.java @@ -37,6 +37,8 @@ import static org.jclouds.reflect.Reflection2.typeToken; @AutoService(ApiMetadata.class) public class DockerApiMetadata extends BaseHttpApiMetadata<DockerApi> { + public static final String DOCKER_CA_CERT_PATH = "docker.cacert.path"; + @Override public Builder toBuilder() { return new Builder().fromApiMetadata(this); @@ -55,6 +57,7 @@ public class DockerApiMetadata extends BaseHttpApiMetadata<DockerApi> { properties.setProperty(Constants.PROPERTY_CONNECTION_TIMEOUT, "1200000"); // 15 minutes properties.setProperty(ComputeServiceProperties.IMAGE_LOGIN_USER, "root:password"); properties.setProperty(TEMPLATE, "osFamily=UBUNTU,os64Bit=true"); + properties.setProperty(DOCKER_CA_CERT_PATH, ""); return properties; } http://git-wip-us.apache.org/repos/asf/jclouds/blob/55a7d8ce/apis/docker/src/main/java/org/jclouds/docker/config/DockerHttpApiModule.java ---------------------------------------------------------------------- diff --git a/apis/docker/src/main/java/org/jclouds/docker/config/DockerHttpApiModule.java b/apis/docker/src/main/java/org/jclouds/docker/config/DockerHttpApiModule.java index ed23d79..afd8e36 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/config/DockerHttpApiModule.java +++ b/apis/docker/src/main/java/org/jclouds/docker/config/DockerHttpApiModule.java @@ -16,8 +16,14 @@ */ package org.jclouds.docker.config; +import com.google.common.base.Supplier; +import com.google.inject.AbstractModule; +import com.google.inject.TypeLiteral; +import com.google.inject.name.Names; +import com.google.inject.util.Modules; import org.jclouds.docker.DockerApi; import org.jclouds.docker.handlers.DockerErrorHandler; +import org.jclouds.docker.suppliers.DockerUntrustedSSLContextSupplier; import org.jclouds.http.HttpErrorHandler; import org.jclouds.http.annotation.ClientError; import org.jclouds.http.annotation.Redirection; @@ -28,6 +34,8 @@ import org.jclouds.http.okhttp.config.OkHttpCommandExecutorServiceModule; import org.jclouds.rest.ConfiguresHttpApi; import org.jclouds.rest.config.HttpApiModule; +import javax.net.ssl.SSLContext; + /** * Configures the Docker connection. */ @@ -48,8 +56,13 @@ public class DockerHttpApiModule extends HttpApiModule<DockerApi> { @Override protected void configure() { super.configure(); - install(new OkHttpCommandExecutorServiceModule()); + install(Modules.override(new OkHttpCommandExecutorServiceModule()).with(new AbstractModule() { + @Override + protected void configure() { + bind(new TypeLiteral<Supplier<SSLContext>>() {}).annotatedWith(Names.named("untrusted")).to(DockerUntrustedSSLContextSupplier.class); + } + })); bind(OkHttpClientSupplier.class).to(DockerOkHttpClientSupplier.class); - } + } } http://git-wip-us.apache.org/repos/asf/jclouds/blob/55a7d8ce/apis/docker/src/main/java/org/jclouds/docker/config/DockerOkHttpClientSupplier.java ---------------------------------------------------------------------- diff --git a/apis/docker/src/main/java/org/jclouds/docker/config/DockerOkHttpClientSupplier.java b/apis/docker/src/main/java/org/jclouds/docker/config/DockerOkHttpClientSupplier.java index 3888b98..6bc171f 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/config/DockerOkHttpClientSupplier.java +++ b/apis/docker/src/main/java/org/jclouds/docker/config/DockerOkHttpClientSupplier.java @@ -19,7 +19,7 @@ package org.jclouds.docker.config; import javax.inject.Inject; import javax.inject.Singleton; -import org.jclouds.docker.suppliers.SSLContextWithKeysSupplier; +import org.jclouds.docker.suppliers.DockerSSLContextSupplier; import org.jclouds.http.okhttp.OkHttpClientSupplier; import com.google.common.collect.ImmutableList; @@ -30,24 +30,24 @@ import com.squareup.okhttp.TlsVersion; @Singleton public class DockerOkHttpClientSupplier implements OkHttpClientSupplier { - private final SSLContextWithKeysSupplier sslContextWithKeysSupplier; - - @Inject - DockerOkHttpClientSupplier(SSLContextWithKeysSupplier sslContextWithKeysSupplier) { - this.sslContextWithKeysSupplier = sslContextWithKeysSupplier; - } - - @Override - public OkHttpClient get() { - OkHttpClient client = new OkHttpClient(); - ConnectionSpec tlsSpec = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS) - .tlsVersions(TlsVersion.TLS_1_0, TlsVersion.TLS_1_1, TlsVersion.TLS_1_2) - .build(); - ConnectionSpec cleartextSpec = new ConnectionSpec.Builder(ConnectionSpec.CLEARTEXT) - .build(); - client.setConnectionSpecs(ImmutableList.of(tlsSpec, cleartextSpec)); - client.setSslSocketFactory(sslContextWithKeysSupplier.get().getSocketFactory()); - return client; - } + private final DockerSSLContextSupplier dockerSSLContextSupplier; + + @Inject + DockerOkHttpClientSupplier(DockerSSLContextSupplier dockerSSLContextSupplier) { + this.dockerSSLContextSupplier = dockerSSLContextSupplier; + } + + @Override + public OkHttpClient get() { + OkHttpClient client = new OkHttpClient(); + ConnectionSpec tlsSpec = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS) + .tlsVersions(TlsVersion.TLS_1_0, TlsVersion.TLS_1_1, TlsVersion.TLS_1_2) + .build(); + ConnectionSpec cleartextSpec = new ConnectionSpec.Builder(ConnectionSpec.CLEARTEXT) + .build(); + client.setConnectionSpecs(ImmutableList.of(tlsSpec, cleartextSpec)); + client.setSslSocketFactory(dockerSSLContextSupplier.get().getSocketFactory()); + return client; + } } http://git-wip-us.apache.org/repos/asf/jclouds/blob/55a7d8ce/apis/docker/src/main/java/org/jclouds/docker/domain/Config.java ---------------------------------------------------------------------- diff --git a/apis/docker/src/main/java/org/jclouds/docker/domain/Config.java b/apis/docker/src/main/java/org/jclouds/docker/domain/Config.java index 05dcc48..464f562 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/domain/Config.java +++ b/apis/docker/src/main/java/org/jclouds/docker/domain/Config.java @@ -101,8 +101,6 @@ public abstract class Config { @Nullable public abstract String networkMode(); - public abstract Map<String, String> devices(); - Config() { } @@ -112,7 +110,7 @@ public abstract class Config { "AttachStderr", "Tty", "OpenStdin", "StdinOnce", "Env", "Cmd", "Entrypoint", "Image", "Volumes", "WorkingDir", "NetworkDisabled", "ExposedPorts", "SecurityOpts", "HostConfig", "Binds", "Links", "LxcConf", "PortBindings", "PublishAllPorts", "Privileged", "Dns", "DnsSearch", "VolumesFrom", - "CapAdd", "CapDrop", "RestartPolicy", "NetworkMode", "Devices" + "CapAdd", "CapDrop", "RestartPolicy", "NetworkMode" }) public static Config create(String hostname, String domainname, String user, int memory, int memorySwap, int cpuShares, boolean attachStdin, boolean attachStdout, boolean attachStderr, boolean tty, @@ -121,13 +119,12 @@ public abstract class Config { Map<String, ?> exposedPorts, List<String> securityOpts, HostConfig hostConfig, List<String> binds, List<String> links, List<Map<String, String>> lxcConf, Map<String, List<Map<String, String>>> portBindings, boolean publishAllPorts, boolean privileged, List<String> dns, String dnsSearch, String volumesFrom, - List<String> capAdd, List<String> capDrop, Map<String, String> restartPolicy, String networkMode, Map<String, String> devices) { + List<String> capAdd, List<String> capDrop, Map<String, String> restartPolicy, String networkMode) { return new AutoValue_Config(hostname, domainname, user, memory, memorySwap, cpuShares, attachStdin, attachStdout, attachStderr, tty, openStdin, stdinOnce, copyOf(env), copyOf(cmd), copyOf(entrypoint), image, copyOf(volumes), workingDir, networkDisabled, copyOf(exposedPorts), copyOf(securityOpts), hostConfig, copyOf(binds), copyOf(links), copyOf(lxcConf), copyOf(portBindings), publishAllPorts, privileged, - copyOf(dns), dnsSearch, volumesFrom, copyOf(capAdd), copyOf(capDrop), copyOf(restartPolicy), networkMode, - copyOf(devices)); + copyOf(dns), dnsSearch, volumesFrom, copyOf(capAdd), copyOf(capDrop), copyOf(restartPolicy), networkMode); } public static Builder builder() { @@ -174,7 +171,6 @@ public abstract class Config { private List<String> capDrop = Lists.newArrayList(); private Map<String, String> restartPolicy = Maps.newHashMap(); private String networkMode; - private Map<String, String> devices = Maps.newHashMap(); public Builder hostname(String hostname) { this.hostname = hostname; @@ -191,18 +187,24 @@ public abstract class Config { return this; } - public Builder memory(int memory) { - this.memory = memory; + public Builder memory(Integer memory) { + if (memory != null) { + this.memory = memory; + } return this; } - public Builder memorySwap(int memorySwap) { - this.memorySwap = memorySwap; + public Builder memorySwap(Integer memorySwap) { + if (memorySwap != null) { + this.memorySwap = memorySwap; + } return this; } - public Builder cpuShares(int cpuShares) { - this.cpuShares = cpuShares; + public Builder cpuShares(Integer cpuShares) { + if (cpuShares != null) { + this.cpuShares = cpuShares; + } return this; } @@ -351,17 +353,12 @@ public abstract class Config { return this; } - public Builder devices(Map<String, String> devices) { - this.devices = devices; - return this; - } - public Config build() { return Config.create(hostname, domainname, user, memory, memorySwap, cpuShares, attachStdin, attachStdout, attachStderr, tty, openStdin, stdinOnce, env, cmd, entrypoint, image, volumes, workingDir, networkDisabled, exposedPorts, securityOpts, hostConfig, binds, links, lxcConf, portBindings, publishAllPorts, privileged, dns, dnsSearch, volumesFrom, capAdd, capDrop, restartPolicy, - networkMode, devices); + networkMode); } public Builder fromConfig(Config in) { @@ -373,7 +370,7 @@ public abstract class Config { .hostConfig(in.hostConfig()).binds(in.binds()).links(in.links()).lxcConf(in.lxcConf()) .portBindings(in.portBindings()).publishAllPorts(in.publishAllPorts()).privileged(in.privileged()) .dns(in.dns()).dnsSearch(in.dnsSearch()).volumesFrom(in.volumesFrom()).capAdd(in.capAdd()) - .capDrop(in.capDrop()).restartPolicy(in.restartPolicy()).networkMode(in.networkMode()).devices(in.devices()); + .capDrop(in.capDrop()).restartPolicy(in.restartPolicy()).networkMode(in.networkMode()); } } http://git-wip-us.apache.org/repos/asf/jclouds/blob/55a7d8ce/apis/docker/src/main/java/org/jclouds/docker/domain/Info.java ---------------------------------------------------------------------- diff --git a/apis/docker/src/main/java/org/jclouds/docker/domain/Info.java b/apis/docker/src/main/java/org/jclouds/docker/domain/Info.java index c0e2ac7..e2b51ca 100644 --- a/apis/docker/src/main/java/org/jclouds/docker/domain/Info.java +++ b/apis/docker/src/main/java/org/jclouds/docker/domain/Info.java @@ -28,7 +28,7 @@ public abstract class Info { public abstract int containers(); - public abstract int debug(); + public abstract boolean debug(); public abstract String driver(); @@ -36,7 +36,7 @@ public abstract class Info { public abstract String executionDriver(); - public abstract int iPv4Forwarding(); + public abstract boolean iPv4Forwarding(); public abstract int images(); @@ -48,7 +48,7 @@ public abstract class Info { public abstract String kernelVersion(); - public abstract int memoryLimit(); + public abstract boolean memoryLimit(); public abstract int nEventsListener(); @@ -58,7 +58,7 @@ public abstract class Info { public abstract String operatingSystem(); - public abstract int swapLimit(); + public abstract boolean swapLimit(); public abstract String dockerRootDir(); @@ -96,10 +96,10 @@ public abstract class Info { "NFd", "NGoroutines", "OperatingSystem", "SwapLimit", "DockerRootDir", "Labels", "MemTotal", "NCPU", "ID", "Name" }) - public static Info create(int containers, int debug, String driver, List<List<String>> driverStatus, - String executionDriver, int iPv4Forwarding, int images, String indexServerAddress, - String initPath, String initSha1, String kernelVersion, int memoryLimit, - int nEventsListener, int nFd, int nGoroutines, String operatingSystem, int swapLimit, + public static Info create(int containers, boolean debug, String driver, List<List<String>> driverStatus, + String executionDriver, boolean iPv4Forwarding, int images, String indexServerAddress, + String initPath, String initSha1, String kernelVersion, boolean memoryLimit, + int nEventsListener, int nFd, int nGoroutines, String operatingSystem, boolean swapLimit, String dockerRootDir, List<String> labels, long memTotal, int ncpu, String id, String name) { return new AutoValue_Info(containers, debug, driver, driverStatus, executionDriver, iPv4Forwarding, images, indexServerAddress, initPath, initSha1, kernelVersion, memoryLimit, nEventsListener, nFd, nGoroutines, http://git-wip-us.apache.org/repos/asf/jclouds/blob/55a7d8ce/apis/docker/src/main/java/org/jclouds/docker/suppliers/DockerSSLContextSupplier.java ---------------------------------------------------------------------- diff --git a/apis/docker/src/main/java/org/jclouds/docker/suppliers/DockerSSLContextSupplier.java b/apis/docker/src/main/java/org/jclouds/docker/suppliers/DockerSSLContextSupplier.java new file mode 100644 index 0000000..ed901f3 --- /dev/null +++ b/apis/docker/src/main/java/org/jclouds/docker/suppliers/DockerSSLContextSupplier.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.jclouds.docker.suppliers; + +import com.google.common.base.Strings; +import com.google.common.base.Supplier; +import org.jclouds.docker.DockerApiMetadata; +import org.jclouds.domain.Credentials; +import org.jclouds.location.Provider; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; +import javax.net.ssl.SSLContext; +import java.io.IOException; +import java.security.GeneralSecurityException; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Throwables.propagate; + +@Singleton +public class DockerSSLContextSupplier implements Supplier<SSLContext> { + private final Supplier<Credentials> creds; + private final String caCertPath; + + + @Inject + DockerSSLContextSupplier(@Provider Supplier<Credentials> creds, @Named(DockerApiMetadata.DOCKER_CA_CERT_PATH) String caCertPath) { + this.creds = creds; + this.caCertPath = caCertPath; + } + + @Override + public SSLContext get() { + Credentials currentCreds = checkNotNull(creds.get(), "credential supplier returned null"); + try { + SSLContextBuilder builder = new SSLContextBuilder(); + builder.clientKeyAndCertificate(currentCreds.credential, currentCreds.identity); + if (!Strings.isNullOrEmpty(caCertPath)) { + builder.caCertificate(caCertPath); + } + return builder.build(); + } catch (GeneralSecurityException e) { + throw propagate(e); + } catch (IOException e) { + throw propagate(e); + } + } + +} http://git-wip-us.apache.org/repos/asf/jclouds/blob/55a7d8ce/apis/docker/src/main/java/org/jclouds/docker/suppliers/DockerUntrustedSSLContextSupplier.java ---------------------------------------------------------------------- diff --git a/apis/docker/src/main/java/org/jclouds/docker/suppliers/DockerUntrustedSSLContextSupplier.java b/apis/docker/src/main/java/org/jclouds/docker/suppliers/DockerUntrustedSSLContextSupplier.java new file mode 100644 index 0000000..d0c9077 --- /dev/null +++ b/apis/docker/src/main/java/org/jclouds/docker/suppliers/DockerUntrustedSSLContextSupplier.java @@ -0,0 +1,60 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.jclouds.docker.suppliers; + +import com.google.common.base.Supplier; +import org.jclouds.domain.Credentials; +import org.jclouds.http.config.SSLModule; +import org.jclouds.location.Provider; + +import javax.inject.Inject; +import javax.inject.Singleton; +import javax.net.ssl.SSLContext; +import java.io.IOException; +import java.security.GeneralSecurityException; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Throwables.propagate; + +@Singleton +public class DockerUntrustedSSLContextSupplier implements Supplier<SSLContext> { + private final Supplier<Credentials> creds; + private final SSLModule.TrustAllCerts insecureTrustManager; + + + @Inject + DockerUntrustedSSLContextSupplier(@Provider Supplier<Credentials> creds, SSLModule.TrustAllCerts insecureTrustManager) { + this.creds = creds; + this.insecureTrustManager = insecureTrustManager; + } + + @Override + public SSLContext get() { + Credentials currentCreds = checkNotNull(creds.get(), "credential supplier returned null"); + try { + SSLContextBuilder builder = new SSLContextBuilder(); + builder.clientKeyAndCertificate(currentCreds.credential, currentCreds.identity); + builder.trustManager(insecureTrustManager); + return builder.build(); + } catch (GeneralSecurityException e) { + throw propagate(e); + } catch (IOException e) { + throw propagate(e); + } + } + +} http://git-wip-us.apache.org/repos/asf/jclouds/blob/55a7d8ce/apis/docker/src/main/java/org/jclouds/docker/suppliers/SSLContextBuilder.java ---------------------------------------------------------------------- diff --git a/apis/docker/src/main/java/org/jclouds/docker/suppliers/SSLContextBuilder.java b/apis/docker/src/main/java/org/jclouds/docker/suppliers/SSLContextBuilder.java new file mode 100644 index 0000000..6030def --- /dev/null +++ b/apis/docker/src/main/java/org/jclouds/docker/suppliers/SSLContextBuilder.java @@ -0,0 +1,174 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.jclouds.docker.suppliers; + +import com.google.common.base.Charsets; +import com.google.common.io.Files; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.openssl.PEMKeyPair; +import org.bouncycastle.openssl.PEMParser; +import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter; +import org.jclouds.util.Closeables2; + +import javax.net.ssl.KeyManager; +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManager; +import javax.net.ssl.TrustManagerFactory; +import javax.net.ssl.X509ExtendedKeyManager; +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.IOException; +import java.io.StringReader; +import java.net.Socket; +import java.security.GeneralSecurityException; +import java.security.KeyManagementException; +import java.security.KeyPair; +import java.security.KeyStore; +import java.security.NoSuchAlgorithmException; +import java.security.Principal; +import java.security.PrivateKey; +import java.security.SecureRandom; +import java.security.Security; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; + +import static com.google.common.base.Throwables.propagate; + +public class SSLContextBuilder { + + private KeyManager[] keyManagers; + private TrustManager[] trustManagers; + + public SSLContextBuilder() { } + + public SSLContextBuilder clientKeyAndCertificate(String keyPath, String certPath) throws IOException, CertificateException { + X509Certificate certificate = getCertificate(loadFile(certPath)); + PrivateKey privateKey = getKey(loadFile(keyPath)); + keyManagers = new KeyManager[]{new InMemoryKeyManager(certificate, privateKey)}; + return this; + } + + public SSLContextBuilder caCertificate(String caCertPath){ + trustManagers = getTrustManagerWithCaCert(caCertPath); + return this; + } + + public SSLContextBuilder trustManager(TrustManager trustManager) { + trustManagers = new TrustManager[]{trustManager}; + return this; + } + + public SSLContext build() throws NoSuchAlgorithmException, KeyManagementException { + SSLContext sslContext = SSLContext.getInstance("TLS"); + sslContext.init(keyManagers, trustManagers, new SecureRandom()); + return sslContext; + } + + private TrustManager[] getTrustManagerWithCaCert(String caCertPath) { + try { + X509Certificate caCert = getCertificate(loadFile(caCertPath)); + KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType()); + trustStore.load(null, null); + trustStore.setCertificateEntry("ca", caCert); + TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); + tmf.init(trustStore); + return tmf.getTrustManagers(); + } catch (GeneralSecurityException e) { + throw propagate(e); + } catch (IOException e) { + throw propagate(e); + } + } + + private static X509Certificate getCertificate(String certificate) { + try { + return (X509Certificate) CertificateFactory.getInstance("X.509").generateCertificate( + new ByteArrayInputStream(certificate.getBytes(Charsets.UTF_8))); + } catch (CertificateException ex) { + throw new RuntimeException("Invalid certificate", ex); + } + } + + private static PrivateKey getKey(String privateKey) { + PEMParser pemParser = new PEMParser(new StringReader(privateKey)); + try { + Object object = pemParser.readObject(); + if (Security.getProvider("BC") == null) { + Security.addProvider(new BouncyCastleProvider()); + } + JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider("BC"); + KeyPair keyPair = converter.getKeyPair((PEMKeyPair) object); + return keyPair.getPrivate(); + } catch (IOException ex) { + throw new RuntimeException("Invalid private key", ex); + } finally { + Closeables2.closeQuietly(pemParser); + } + } + + private static String loadFile(final String filePath) throws IOException { + return Files.toString(new File(filePath), Charsets.UTF_8); + } + + private static class InMemoryKeyManager extends X509ExtendedKeyManager { + private static final String DEFAULT_ALIAS = "docker"; + + private final X509Certificate certificate; + + private final PrivateKey privateKey; + + public InMemoryKeyManager(final X509Certificate certificate, final PrivateKey privateKey) + throws IOException, CertificateException { + this.certificate = certificate; + this.privateKey = privateKey; + } + + @Override + public String chooseClientAlias(final String[] keyType, final Principal[] issuers, + final Socket socket) { + return DEFAULT_ALIAS; + } + + @Override + public String chooseServerAlias(final String keyType, final Principal[] issuers, + final Socket socket) { + return DEFAULT_ALIAS; + } + + @Override + public X509Certificate[] getCertificateChain(final String alias) { + return new X509Certificate[]{certificate}; + } + + @Override + public String[] getClientAliases(final String keyType, final Principal[] issuers) { + return new String[]{DEFAULT_ALIAS}; + } + + @Override + public PrivateKey getPrivateKey(final String alias) { + return privateKey; + } + + @Override + public String[] getServerAliases(final String keyType, final Principal[] issuers) { + return new String[]{DEFAULT_ALIAS}; + } + } + +} http://git-wip-us.apache.org/repos/asf/jclouds/blob/55a7d8ce/apis/docker/src/main/java/org/jclouds/docker/suppliers/SSLContextWithKeysSupplier.java ---------------------------------------------------------------------- diff --git a/apis/docker/src/main/java/org/jclouds/docker/suppliers/SSLContextWithKeysSupplier.java b/apis/docker/src/main/java/org/jclouds/docker/suppliers/SSLContextWithKeysSupplier.java deleted file mode 100644 index 83ccacd..0000000 --- a/apis/docker/src/main/java/org/jclouds/docker/suppliers/SSLContextWithKeysSupplier.java +++ /dev/null @@ -1,166 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 org.jclouds.docker.suppliers; - -import static com.google.common.base.Preconditions.checkNotNull; -import static com.google.common.base.Throwables.propagate; - -import java.io.ByteArrayInputStream; -import java.io.File; -import java.io.IOException; -import java.io.StringReader; -import java.net.Socket; -import java.security.KeyManagementException; -import java.security.KeyPair; -import java.security.NoSuchAlgorithmException; -import java.security.Principal; -import java.security.PrivateKey; -import java.security.SecureRandom; -import java.security.Security; -import java.security.cert.CertificateException; -import java.security.cert.CertificateFactory; -import java.security.cert.X509Certificate; - -import javax.inject.Inject; -import javax.inject.Singleton; -import javax.net.ssl.KeyManager; -import javax.net.ssl.SSLContext; -import javax.net.ssl.TrustManager; -import javax.net.ssl.X509ExtendedKeyManager; - -import org.bouncycastle.jce.provider.BouncyCastleProvider; -import org.bouncycastle.openssl.PEMKeyPair; -import org.bouncycastle.openssl.PEMParser; -import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter; -import org.jclouds.domain.Credentials; -import org.jclouds.http.HttpUtils; -import org.jclouds.http.config.SSLModule.TrustAllCerts; -import org.jclouds.location.Provider; -import org.jclouds.util.Closeables2; - -import com.google.common.base.Charsets; -import com.google.common.base.Supplier; -import com.google.common.io.Files; - -@Singleton -public class SSLContextWithKeysSupplier implements Supplier<SSLContext> { - private final TrustManager[] trustManager; - private final Supplier<Credentials> creds; - - @Inject - SSLContextWithKeysSupplier(@Provider Supplier<Credentials> creds, HttpUtils utils, TrustAllCerts trustAllCerts) { - this.trustManager = utils.trustAllCerts() ? new TrustManager[]{trustAllCerts} : null; - this.creds = creds; - } - - @Override - public SSLContext get() { - Credentials currentCreds = checkNotNull(creds.get(), "credential supplier returned null"); - try { - SSLContext sslContext = SSLContext.getInstance("TLS"); - X509Certificate certificate = getCertificate(loadFile(currentCreds.identity)); - PrivateKey privateKey = getKey(loadFile(currentCreds.credential)); - sslContext.init(new KeyManager[]{new InMemoryKeyManager(certificate, privateKey)}, trustManager, new SecureRandom()); - return sslContext; - } catch (NoSuchAlgorithmException e) { - throw propagate(e); - } catch (KeyManagementException e) { - throw propagate(e); - } catch (CertificateException e) { - throw propagate(e); - } catch (IOException e) { - throw propagate(e); - } - } - - private static X509Certificate getCertificate(String certificate) { - try { - return (X509Certificate) CertificateFactory.getInstance("X.509").generateCertificate( - new ByteArrayInputStream(certificate.getBytes(Charsets.UTF_8))); - } catch (CertificateException ex) { - throw new RuntimeException("Invalid certificate", ex); - } - } - - private static PrivateKey getKey(String privateKey) { - PEMParser pemParser = new PEMParser(new StringReader(privateKey)); - try { - Object object = pemParser.readObject(); - if (Security.getProvider("BC") == null) { - Security.addProvider(new BouncyCastleProvider()); - } - JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider("BC"); - KeyPair keyPair = converter.getKeyPair((PEMKeyPair) object); - return keyPair.getPrivate(); - } catch (IOException ex) { - throw new RuntimeException("Invalid private key", ex); - } finally { - Closeables2.closeQuietly(pemParser); - } - } - - private static String loadFile(final String filePath) throws IOException { - return Files.toString(new File(filePath), Charsets.UTF_8); - } - - private static class InMemoryKeyManager extends X509ExtendedKeyManager { - private static final String DEFAULT_ALIAS = "docker"; - - private final X509Certificate certificate; - - private final PrivateKey privateKey; - - public InMemoryKeyManager(final X509Certificate certificate, final PrivateKey privateKey) - throws IOException, CertificateException { - this.certificate = certificate; - this.privateKey = privateKey; - } - - @Override - public String chooseClientAlias(final String[] keyType, final Principal[] issuers, - final Socket socket) { - return DEFAULT_ALIAS; - } - - @Override - public String chooseServerAlias(final String keyType, final Principal[] issuers, - final Socket socket) { - return DEFAULT_ALIAS; - } - - @Override - public X509Certificate[] getCertificateChain(final String alias) { - return new X509Certificate[]{certificate}; - } - - @Override - public String[] getClientAliases(final String keyType, final Principal[] issuers) { - return new String[]{DEFAULT_ALIAS}; - } - - @Override - public PrivateKey getPrivateKey(final String alias) { - return privateKey; - } - - @Override - public String[] getServerAliases(final String keyType, final Principal[] issuers) { - return new String[]{DEFAULT_ALIAS}; - } - } - -} http://git-wip-us.apache.org/repos/asf/jclouds/blob/55a7d8ce/apis/docker/src/test/java/org/jclouds/docker/compute/BaseDockerApiLiveTest.java ---------------------------------------------------------------------- diff --git a/apis/docker/src/test/java/org/jclouds/docker/compute/BaseDockerApiLiveTest.java b/apis/docker/src/test/java/org/jclouds/docker/compute/BaseDockerApiLiveTest.java index 447c9e4..5778d9a 100644 --- a/apis/docker/src/test/java/org/jclouds/docker/compute/BaseDockerApiLiveTest.java +++ b/apis/docker/src/test/java/org/jclouds/docker/compute/BaseDockerApiLiveTest.java @@ -29,6 +29,7 @@ import org.jboss.shrinkwrap.api.GenericArchive; import org.jboss.shrinkwrap.api.ShrinkWrap; import org.jboss.shrinkwrap.api.asset.ClassLoaderAsset; import org.jboss.shrinkwrap.api.exporter.TarExporter; +import org.jclouds.Constants; import org.jclouds.apis.BaseApiLiveTest; import org.jclouds.compute.config.ComputeServiceProperties; import org.jclouds.docker.DockerApi; @@ -56,8 +57,9 @@ public class BaseDockerApiLiveTest extends BaseApiLiveTest<DockerApi> { @Override protected Properties setupProperties() { Properties overrides = super.setupProperties(); - overrides.setProperty("jclouds.trust-all-certs", "false"); overrides.setProperty(ComputeServiceProperties.IMAGE_LOGIN_USER, "root:password"); + setIfTestSystemPropertyPresent(overrides, provider + ".cacert.path"); + setIfTestSystemPropertyPresent(overrides, Constants.PROPERTY_TRUST_ALL_CERTS); return overrides; } http://git-wip-us.apache.org/repos/asf/jclouds/blob/55a7d8ce/apis/docker/src/test/java/org/jclouds/docker/features/ContainerApiLiveTest.java ---------------------------------------------------------------------- diff --git a/apis/docker/src/test/java/org/jclouds/docker/features/ContainerApiLiveTest.java b/apis/docker/src/test/java/org/jclouds/docker/features/ContainerApiLiveTest.java index 684f5de..23e7e7b 100644 --- a/apis/docker/src/test/java/org/jclouds/docker/features/ContainerApiLiveTest.java +++ b/apis/docker/src/test/java/org/jclouds/docker/features/ContainerApiLiveTest.java @@ -128,7 +128,7 @@ public class ContainerApiLiveTest extends BaseDockerApiLiveTest { @Test(dependsOnMethods = "testRestartContainer") public void testWaitContainer() { api().stopContainer(container.id(), 1); - assertEquals(api().wait(container.id()).statusCode(), -1); + assertEquals(api().wait(container.id()).statusCode(), 137); } @Test(dependsOnMethods = "testWaitContainer") http://git-wip-us.apache.org/repos/asf/jclouds/blob/55a7d8ce/apis/docker/src/test/java/org/jclouds/docker/parse/InfoParseTest.java ---------------------------------------------------------------------- diff --git a/apis/docker/src/test/java/org/jclouds/docker/parse/InfoParseTest.java b/apis/docker/src/test/java/org/jclouds/docker/parse/InfoParseTest.java index fa31c8b..87d149c 100644 --- a/apis/docker/src/test/java/org/jclouds/docker/parse/InfoParseTest.java +++ b/apis/docker/src/test/java/org/jclouds/docker/parse/InfoParseTest.java @@ -40,25 +40,25 @@ public class InfoParseTest extends BaseDockerParseTest<Info> { public Info expected() { return Info.create( 0, // containers - 1, // debug + true, // debug "aufs", // driver ImmutableList.<List<String>>of( ImmutableList.of("Root Dir", "/mnt/sda1/var/lib/docker/aufs"), ImmutableList.of("Dirs", "46") ), // driverStatus "native-0.2", // ExecutionDriver - 1, // IPv4Forwarding + true, // IPv4Forwarding 46, // Images "https://index.docker.io/v1/", // IndexServerAddress "/usr/local/bin/docker", // InitPath "", // InitSha1 "3.16.7-tinycore64", // KernelVersion - 1, // MemoryLimit + true, // MemoryLimit 0, // NEventsListener 10, // NFd 11, // NGoroutines "Boot2Docker 1.4.1 (TCL 5.4); master : 86f7ec8 - Tue Dec 16 23:11:29 UTC 2014", // OperatingSystem - 1, // SwapLimit + true, // SwapLimit "/mnt/sda1/var/lib/docker", // DockerRootDir null, // Labels 2105585664, // MemTotal http://git-wip-us.apache.org/repos/asf/jclouds/blob/55a7d8ce/apis/docker/src/test/resources/info.json ---------------------------------------------------------------------- diff --git a/apis/docker/src/test/resources/info.json b/apis/docker/src/test/resources/info.json index e869382..4c67e6a 100644 --- a/apis/docker/src/test/resources/info.json +++ b/apis/docker/src/test/resources/info.json @@ -1,6 +1,6 @@ { "Containers": 0, - "Debug": 1, + "Debug": true, "DockerRootDir": "/mnt/sda1/var/lib/docker", "Driver": "aufs", "DriverStatus": [ @@ -15,7 +15,7 @@ ], "ExecutionDriver": "native-0.2", "ID": "7V5Y:IQ2M:HWIL:AZJV:HKRD:Q7OZ:3EQA:ZHMO:4LAD:OSIY:YBAA:BSX6", - "IPv4Forwarding": 1, + "IPv4Forwarding": true, "Images": 46, "IndexServerAddress": "https://index.docker.io/v1/", "InitPath": "/usr/local/bin/docker", @@ -23,12 +23,12 @@ "KernelVersion": "3.16.7-tinycore64", "Labels": null, "MemTotal": 2105585664, - "MemoryLimit": 1, + "MemoryLimit": true, "NCPU": 8, "NEventsListener": 0, "NFd": 10, "NGoroutines": 11, "Name": "boot2docker", "OperatingSystem": "Boot2Docker 1.4.1 (TCL 5.4); master : 86f7ec8 - Tue Dec 16 23:11:29 UTC 2014", - "SwapLimit": 1 + "SwapLimit": true } \ No newline at end of file
