X Tutup
Skip to content

Added SSL support#78

Merged
marcuslinke merged 3 commits intodocker-java:masterfrom
sabre1041:ssl-support
Oct 24, 2014
Merged

Added SSL support#78
marcuslinke merged 3 commits intodocker-java:masterfrom
sabre1041:ssl-support

Conversation

@sabre1041
Copy link
Copy Markdown
Contributor

Added SSL support for defining a keystore/password and truststore/password to Docker client. Latest release of Boot2Docker (OSX) enables TLS by default and SSL support is needed for continued interoperability with Docker

@alexec
Copy link
Copy Markdown
Contributor

alexec commented Oct 20, 2014

+1 please!

@marcuslinke
Copy link
Copy Markdown
Contributor

@sabre1041 I guess after merging this no integration test is running anymore. Could you provide basic documentation how to setup the appropriate certs please?

@sabre1041
Copy link
Copy Markdown
Contributor Author

The following documentation corresponds to the creation of the requisite java certificate stores and usage within the docker-java client. Please let me know if you how you want to handle incorporating into official documentation

Configuring SSL

Follow the steps as documented in the Running Docker with https to create a CA, server and client key.

The following files are produced:

  • ca.pem
  • cert.pem
  • key.pem

Truststore

Create a truststore for the CA key

keytool -import -alias root  -trustcacerts -file ca.pem -keystore docker.ts

Keystore

The keystore will hold the server and client certificates

Create a PKCS12 archive containing the server and client certificates

openssl pkcs12 -export -inkey key.pem -in cert.pem -out docker.p12

Import the PKCS12 archive into a Java keystore

keytool -importkeystore -srckeystore docker.p12 -srcstoretype PKCS12 -destkeystore docker.ks

Create a SSL Enabled Docker Client

DockerClientConfig.DockerClientConfigBuilder configBuilder = new DockerClientConfig.DockerClientConfigBuilder()
.withKeystore("/path/to/file/docker.ks")
.withKeystorePassword("changeit")
.withTruststore("/path/to/file/docker.ts")
.withTruststorePassword("changeit")
.withUri("https://localhost:2375");

DockerClientConfig config = configBuilder.build();
DockerClient client = DockerClientBuilder.getInstance(config).build();

@marcuslinke
Copy link
Copy Markdown
Contributor

@sabre1041 Thanks! But wait, what do you think of examples mentioned in #77? I think this approach would be much easier to setup.

@sabre1041
Copy link
Copy Markdown
Contributor Author

@marcuslinke I like the idea of loading the pem formatted certificates as referenced in the link in #77 but still would like to give the users options to specify their own keystore. I can go ahead and add the following methods

  • withDockerCertPath(certPath)
    • User specifies location containing ca.pem, cert.pem and key.pem files
  • withDefaultCertPath()
    • Uses ca.pem, cert.pem and key.pem files in a location defined by the DOCKER_CERT_PATH environment variable

Thoughts?

@alexec
Copy link
Copy Markdown
Contributor

alexec commented Oct 21, 2014

Could we use the same mechanism as the authentication? I.e. ~/.docker.io.properties, or System.getProperty(...)

@sabre1041
Copy link
Copy Markdown
Contributor Author

@alexec I was planning on adding a "docker.io.dockerCertPath" system property when leveraging system properties instead of explicitly calling these methods as used elsewhere in the DockerClientConfigBuilder

@marcuslinke
Copy link
Copy Markdown
Contributor

@sabre1041 I think as long as we achieve the default behavior to load certs from DOCKER_CERT_PATH everything will be fine. Absent certs configuration should be accepted to support older (non-secured) server versions also.

@sabre1041
Copy link
Copy Markdown
Contributor Author

@marcuslinke that works for me. I would recommend at least having the option for a user to define the DOCKER_CERT_PATH in the event they wish to not set an environment variable. I will go ahead and remove the references to keystore and truststore configurations

@marcuslinke
Copy link
Copy Markdown
Contributor

@sabre1041 Great! Thanks.

…ty to leverage environment variable or explicitly specify location of default certificates
@sabre1041
Copy link
Copy Markdown
Contributor Author

Functionality to utilize default pem files (ca.pem, cert.pem and key.pem) implemented. Will attempt to locate file locations when set in the following order:

  1. Explicitly set by the user
  2. Environment variable
  3. .docker folder in user home directory

@marcuslinke
Copy link
Copy Markdown
Contributor

@sabre1041 Thanks! I will definitely merge it within the next few days.

@toddwest
Copy link
Copy Markdown

After updating to the latest Boot2Docker we ran into this exact same issue. I've verified that the code changes provided here solve our problem. We're excited to see this get in!

@marcuslinke marcuslinke merged commit 077df9f into docker-java:master Oct 24, 2014
@marcuslinke
Copy link
Copy Markdown
Contributor

@sabre1041 I've done some minor modifications to make it compile and run under java 1.6.
Please compile and test in your environment.

@sabre1041
Copy link
Copy Markdown
Contributor Author

@marcuslinke Successful execution of secured TLS client using java 7. I am receiving errors when running building the project though (connection refused in AbstractDockerClientTest)

Running TestSuite
Configuring TestNG with: TestNGMapConfigurator
23:53:55.658 INFO  c.g.d.c.AbstractDockerClientTest - ======================= BEFORETEST =======================
23:53:55.662 INFO  c.g.d.c.AbstractDockerClientTest - Connecting to Docker server
23:53:55.853 INFO  c.g.d.c.AbstractDockerClientTest - Pulling image 'busybox'
[XmlMethodSelector] CLASSNAME:org.apache.maven.surefire.testng.utils.GroupMatcherMethodSelector
[XmlMethodSelector] SETTING PRIORITY:9999
Tests run: 222, Failures: 1, Errors: 0, Skipped: 221, Time elapsed: 1.803 sec <<< FAILURE! - in TestSuite
beforeTest(com.github.dockerjava.client.DockerClientTest)  Time elapsed: 1.615 sec  <<< FAILURE!
javax.ws.rs.ProcessingException: java.net.ConnectException: Connection refused
    at java.net.PlainSocketImpl.socketConnect(Native Method)
    at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:339)
    at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:200)
    at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:182)
    at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
    at java.net.Socket.connect(Socket.java:579)
    at sun.net.NetworkClient.doConnect(NetworkClient.java:175)
    at sun.net.www.http.HttpClient.openServer(HttpClient.java:432)
    at sun.net.www.http.HttpClient.openServer(HttpClient.java:527)
    at sun.net.www.http.HttpClient.<init>(HttpClient.java:211)
    at sun.net.www.http.HttpClient.New(HttpClient.java:308)
    at sun.net.www.http.HttpClient.New(HttpClient.java:326)
    at sun.net.www.protocol.http.HttpURLConnection.getNewHttpClient(HttpURLConnection.java:996)
    at sun.net.www.protocol.http.HttpURLConnection.plainConnect(HttpURLConnection.java:932)
    at sun.net.www.protocol.http.HttpURLConnection.connect(HttpURLConnection.java:850)
    at sun.net.www.protocol.http.HttpURLConnection.getOutputStream(HttpURLConnection.java:1091)
    at org.glassfish.jersey.client.HttpUrlConnector$3.getOutputStream(HttpUrlConnector.java:312)
    at org.glassfish.jersey.message.internal.CommittingOutputStream.commitStream(CommittingOutputStream.java:200)
    at org.glassfish.jersey.message.internal.CommittingOutputStream.commitStream(CommittingOutputStream.java:194)
    at org.glassfish.jersey.message.internal.CommittingOutputStream.write(CommittingOutputStream.java:243)
    at org.glassfish.jersey.filter.LoggingFilter$LoggingStream.write(LoggingFilter.java:327)
    at java.io.OutputStream.write(OutputStream.java:116)
    at org.glassfish.jersey.message.internal.WriterInterceptorExecutor$UnCloseableOutputStream.write(WriterInterceptorExecutor.java:299)
    at com.fasterxml.jackson.core.json.UTF8JsonGenerator._flushBuffer(UTF8JsonGenerator.java:1862)
    at com.fasterxml.jackson.core.json.UTF8JsonGenerator.flush(UTF8JsonGenerator.java:1056)
    at com.fasterxml.jackson.databind.ObjectWriter.writeValue(ObjectWriter.java:615)
    at com.fasterxml.jackson.jaxrs.base.ProviderBase.writeTo(ProviderBase.java:635)
    at org.glassfish.jersey.message.internal.WriterInterceptorExecutor$TerminalWriterInterceptor.invokeWriteTo(WriterInterceptorExecutor.java:265)
    at org.glassfish.jersey.message.internal.WriterInterceptorExecutor$TerminalWriterInterceptor.aroundWriteTo(WriterInterceptorExecutor.java:250)
    at org.glassfish.jersey.message.internal.WriterInterceptorExecutor.proceed(WriterInterceptorExecutor.java:162)
    at org.glassfish.jersey.filter.LoggingFilter.aroundWriteTo(LoggingFilter.java:293)
    at org.glassfish.jersey.message.internal.WriterInterceptorExecutor.proceed(WriterInterceptorExecutor.java:162)
    at org.glassfish.jersey.message.internal.MessageBodyFactory.writeTo(MessageBodyFactory.java:1154)
    at org.glassfish.jersey.client.ClientRequest.writeEntity(ClientRequest.java:503)
    at org.glassfish.jersey.client.HttpUrlConnector._apply(HttpUrlConnector.java:315)
    at org.glassfish.jersey.client.HttpUrlConnector.apply(HttpUrlConnector.java:227)
    at org.glassfish.jersey.client.ClientRuntime.invoke(ClientRuntime.java:246)
    at org.glassfish.jersey.client.JerseyInvocation$1.call(JerseyInvocation.java:667)
    at org.glassfish.jersey.client.JerseyInvocation$1.call(JerseyInvocation.java:664)
    at org.glassfish.jersey.internal.Errors.process(Errors.java:315)
    at org.glassfish.jersey.internal.Errors.process(Errors.java:297)
    at org.glassfish.jersey.internal.Errors.process(Errors.java:228)
    at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:424)
    at org.glassfish.jersey.client.JerseyInvocation.invoke(JerseyInvocation.java:664)
    at org.glassfish.jersey.client.JerseyInvocation$Builder.method(JerseyInvocation.java:424)
    at org.glassfish.jersey.client.JerseyInvocation$Builder.post(JerseyInvocation.java:333)
    at com.github.dockerjava.jaxrs.PullImageCmdExec.execute(PullImageCmdExec.java:32)
    at com.github.dockerjava.jaxrs.PullImageCmdExec.execute(PullImageCmdExec.java:16)
    at com.github.dockerjava.jaxrs.AbstrDockerCmdExec.exec(AbstrDockerCmdExec.java:47)
    at com.github.dockerjava.core.command.AbstrDockerCmd.exec(AbstrDockerCmd.java:26)
    at com.github.dockerjava.client.AbstractDockerClientTest.beforeTest(AbstractDockerClientTest.java:40)
    at com.github.dockerjava.client.DockerClientTest.beforeTest(DockerClientTest.java:32)


Results :

Failed tests: 
  DockerClientTest.beforeTest:32->AbstractDockerClientTest.beforeTest:40 » Processing

Tests run: 222, Failures: 1, Errors: 0, Skipped: 221

@marcuslinke
Copy link
Copy Markdown
Contributor

@sabre1041 This is because of wrong configuration probably. I've just pushed a fix for the default configuration. Please test again.

@sabre1041
Copy link
Copy Markdown
Contributor Author

@marcuslinke same result on freshly pulled code

@marcuslinke
Copy link
Copy Markdown
Contributor

@sabre1041 Whats your test environment? Do you test against local running docker? For me all integration tests passes with jdk1.7.0_67 on MacOS and local running boot2docker v1.3.0.

@marcuslinke
Copy link
Copy Markdown
Contributor

@sabre1041 Integration tests are now enabled by default: See #79

@gesellix
Copy link
Copy Markdown
Contributor

I can confirm that the build runs successfully on a Linux machine (no boot2docker, no TLS). But I can confirm that the error message above occurs when the docker.io.url is missing or wrong. It probably needs to be changed to another port (2375 -> 2376)?

@sabre1041
Copy link
Copy Markdown
Contributor Author

After confirming and configuring values in properties file, integration tests pass successfully on MacOS jdk1.7.0.67.

I do question whether integration tests should be run as part of the default build. There is up front work that is required to be able to build the project without specifying maven options to disable tests. This breaks the "fail fast" principles Continuous Integration/Continuous Delivery where only unit tests are validated during an initial build and integration/finer grained testing occurs in subsequent builds.

I agree that integration tests which do take longer than straight unit tests should belong to the own testing group as brought up in #79, but should also be separated out potentially into their own maven profile that focuses on integration testing specifically. thoughts?

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.?

@gesellix
Copy link
Copy Markdown
Contributor

+1 for @alexec's suggestion. It would be quite convenient to support well known default env variables like DOCKER_HOST.

@gesellix
Copy link
Copy Markdown
Contributor

P.S. that would be another pull request/issue though ;-)

@alexec
Copy link
Copy Markdown
Contributor

alexec commented Oct 26, 2014

I've created a new pull for the updated config. #81

@marcuslinke
Copy link
Copy Markdown
Contributor

@alexec Thanks! Just merged it. In my opinion it great to use the default docker env variables (DOCKER_HOST, DOCKER_CERT_PATH ...) but we should NOT introduce additional ones.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

X Tutup