Fix/cert material (#91)

* Fixes connection via TLS to docker host with file contents. Closes #86 
* Fixes skip of TLS verification if ca_material is not present. Closes #14
This commit is contained in:
Manuel Vogel 2018-09-26 18:27:04 +02:00 committed by GitHub
parent 79c198a3b4
commit f8a9755fcd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 99 additions and 7 deletions

View file

@ -1,4 +1,9 @@
## 1.0.2 (Unreleased)
BUG FIXES
* Fixes connection via TLS to docker host with file contents [GH-86]
* Skips TLS verification if `ca_material` is not set [GH-14]
## 1.0.1 (August 06, 2018)
BUG FIXES

View file

@ -1,9 +1,16 @@
package docker
import (
"crypto/tls"
"crypto/x509"
"errors"
"fmt"
"net"
"net/http"
"path/filepath"
"runtime"
"strings"
"time"
"github.com/docker/docker/api/types"
"github.com/docker/docker/client"
@ -21,20 +28,80 @@ type Config struct {
CertPath string
}
// buildHTTPClientFromBytes builds the http client from bytes (content of the files)
func buildHTTPClientFromBytes(caPEMCert, certPEMBlock, keyPEMBlock []byte) (*http.Client, error) {
tlsConfig := &tls.Config{}
if certPEMBlock != nil && keyPEMBlock != nil {
tlsCert, err := tls.X509KeyPair(certPEMBlock, keyPEMBlock)
if err != nil {
return nil, err
}
tlsConfig.Certificates = []tls.Certificate{tlsCert}
}
if caPEMCert == nil || len(caPEMCert) == 0 {
tlsConfig.InsecureSkipVerify = true
} else {
caPool := x509.NewCertPool()
if !caPool.AppendCertsFromPEM(caPEMCert) {
return nil, errors.New("Could not add RootCA pem")
}
tlsConfig.RootCAs = caPool
}
tr := defaultTransport()
tr.TLSClientConfig = tlsConfig
return &http.Client{Transport: tr}, nil
}
// defaultTransport returns a new http.Transport with similar default values to
// http.DefaultTransport, but with idle connections and keepalives disabled.
func defaultTransport() *http.Transport {
transport := defaultPooledTransport()
transport.DisableKeepAlives = true
transport.MaxIdleConnsPerHost = -1
return transport
}
// defaultPooledTransport returns a new http.Transport with similar default
// values to http.DefaultTransport.
func defaultPooledTransport() *http.Transport {
transport := &http.Transport{
Proxy: http.ProxyFromEnvironment,
DialContext: (&net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
}).DialContext,
MaxIdleConns: 100,
IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
MaxIdleConnsPerHost: runtime.GOMAXPROCS(0) + 1,
}
return transport
}
// NewClient returns a new Docker client.
func (c *Config) NewClient() (*client.Client, error) {
if c.Ca != "" || c.Cert != "" || c.Key != "" {
if c.Ca == "" || c.Cert == "" || c.Key == "" {
return nil, fmt.Errorf("ca_material, cert_material, and key_material must be specified")
if c.Cert != "" || c.Key != "" {
if c.Cert == "" || c.Key == "" {
return nil, fmt.Errorf("cert_material, and key_material must be specified")
}
if c.CertPath != "" {
return nil, fmt.Errorf("cert_path must not be specified")
}
httpClient, err := buildHTTPClientFromBytes([]byte(c.Ca), []byte(c.Cert), []byte(c.Key))
if err != nil {
return nil, err
}
// Note: don't change the order here, because the custom client
// needs to be set first them we overwrite the other options: host, version
return client.NewClientWithOpts(
client.WithHTTPClient(httpClient),
client.WithHost(c.Host),
client.WithTLSClientConfig(c.Ca, c.Cert, c.Key),
client.WithVersion(apiVersion),
)
}

View file

@ -91,6 +91,25 @@ and paste it into `~/.docker/config.json`:
}
```
## Certificate information
Specify certificate information either with a directory or
directly with the content of the files for connecting to the Docker host via TLS.
```hcl
provider "docker" {
host = "tcp://your-host-ip:2376/"
# -> specify either
cert_path = "${pathexpand("~/.docker")}"
# -> or the following
ca_material = "${file(pathexpand("~/.docker/ca.pem"))}" # this can be omitted
cert_material = "${file(pathexpand("~/.docker/cert.pem"))}"
key_material = "${file(pathexpand("~/.docker/key.pem"))}"
}
```
## Argument Reference
The following arguments are supported:
@ -99,11 +118,12 @@ The following arguments are supported:
blank, the `DOCKER_HOST` environment variable will also be read.
* `cert_path` - (Optional) Path to a directory with certificate information
for connecting to the Docker host via TLS. If this is blank, the
`DOCKER_CERT_PATH` will also be checked.
for connecting to the Docker host via TLS. It is expected that the 3 files `{ca, cert, key}.pem`
are present in the path. If the path is blank, the `DOCKER_CERT_PATH` will also be checked.
* `ca_material`, `cert_material`, `key_material`, - (Optional) Content of `ca.pem`, `cert.pem`, and `key.pem` files
for TLS authentication. Cannot be used together with `cert_path`.
for TLS authentication. Cannot be used together with `cert_path`. If `ca_material` is omitted
the client does not check the servers certificate chain and host name.
* `registry_auth` - (Optional) A block specifying the credentials for a target
v2 Docker registry.