-
Notifications
You must be signed in to change notification settings - Fork 39
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #284 from carolynvs/docker-tls
Support connecting to TLS secured docker host
- Loading branch information
Showing
3 changed files
with
156 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
package docker | ||
|
||
import ( | ||
"fmt" | ||
"os" | ||
"path/filepath" | ||
"strconv" | ||
|
||
"github.com/docker/cli/cli/command" | ||
cliconfig "github.com/docker/cli/cli/config" | ||
cliflags "github.com/docker/cli/cli/flags" | ||
"github.com/docker/go-connections/tlsconfig" | ||
) | ||
|
||
const ( | ||
// DockerTLSVerifyEnvVar is the Docker environment variable that indicates that | ||
// Docker socket is protected with TLS. | ||
DockerTLSVerifyEnvVar = "DOCKER_TLS_VERIFY" | ||
|
||
// DockerCertPathEnvVar is the Docker environment variable that specifies a | ||
// custom path to the TLS certificates for the Docker socket. | ||
DockerCertPathEnvVar = "DOCKER_CERT_PATH" | ||
) | ||
|
||
// GetDockerClient creates a Docker CLI client that uses the user's Docker configuration | ||
// such as environment variables and the Docker home directory to initialize the client. | ||
func GetDockerClient() (*command.DockerCli, error) { | ||
cli, err := command.NewDockerCli() | ||
if err != nil { | ||
return nil, fmt.Errorf("could not create new docker client: %w", err) | ||
} | ||
opts := buildDockerClientOptions() | ||
if err = cli.Initialize(opts); err != nil { | ||
return nil, fmt.Errorf("error initializing docker client: %w", err) | ||
} | ||
return cli, nil | ||
} | ||
|
||
// manually handle DOCKER_TLS_VERIFY and DOCKER_CERT_PATH because the docker cli | ||
// library only binds these values when initializing its cli flags. There isn't | ||
// other parts of the library that we can take advantage of to get these values | ||
// for "free". | ||
// | ||
// DOCKER_HOST however is retrieved dynamically later so that doesn't | ||
// require additional configuration. | ||
func buildDockerClientOptions() *cliflags.ClientOptions { | ||
cliOpts := cliflags.NewClientOptions() | ||
cliOpts.ConfigDir = cliconfig.Dir() | ||
|
||
// Check if TLS is enabled Docker configures TLS settings if DOCKER_TLS_VERIFY is | ||
// set to anything, so it could be false and that still means we should use TLS | ||
// (but don't check the certs). | ||
tlsVerify, tlsConfigured := os.LookupEnv(DockerTLSVerifyEnvVar) | ||
if tlsConfigured && tlsVerify != "" { | ||
cliOpts.Common.TLS = true | ||
|
||
// Check if we should verify certs or allow self-signed certs (insecure) | ||
verify, _ := strconv.ParseBool(tlsVerify) | ||
cliOpts.Common.TLSVerify = verify | ||
|
||
// Check if the TLS certs have been overridden | ||
var certPath string | ||
if certPathOverride, ok := os.LookupEnv(DockerCertPathEnvVar); ok && certPathOverride != "" { | ||
certPath = certPathOverride | ||
} else { | ||
certPath = cliOpts.ConfigDir | ||
} | ||
|
||
cliOpts.Common.TLSOptions = &tlsconfig.Options{ | ||
CAFile: filepath.Join(certPath, cliflags.DefaultCaFile), | ||
CertFile: filepath.Join(certPath, cliflags.DefaultCertFile), | ||
KeyFile: filepath.Join(certPath, cliflags.DefaultKeyFile), | ||
} | ||
} | ||
|
||
return cliOpts | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
package docker | ||
|
||
import ( | ||
"os" | ||
"testing" | ||
|
||
"github.com/docker/go-connections/tlsconfig" | ||
"github.com/stretchr/testify/assert" | ||
) | ||
|
||
func Test_buildDockerClientOptions(t *testing.T) { | ||
// Tell Docker where its config is located, so that we have repeatable paths in the tests | ||
os.Setenv("DOCKER_CONFIG", "/home/me/.docker") | ||
defer os.Unsetenv("DOCKER_CONFIG") | ||
|
||
defaultTLSOptions := &tlsconfig.Options{ | ||
CAFile: "/home/me/.docker/ca.pem", | ||
CertFile: "/home/me/.docker/cert.pem", | ||
KeyFile: "/home/me/.docker/key.pem", | ||
} | ||
|
||
customTLSOptions := &tlsconfig.Options{ | ||
CAFile: "/mycerts/ca.pem", | ||
CertFile: "/mycerts/cert.pem", | ||
KeyFile: "/mycerts/key.pem", | ||
} | ||
|
||
t.Run("tls disabled", func(t *testing.T) { | ||
os.Unsetenv(DockerTLSVerifyEnvVar) | ||
opts := buildDockerClientOptions() | ||
assert.False(t, opts.Common.TLS, "expected TLS to be disabled") | ||
assert.False(t, opts.Common.TLSVerify, "expected TLSVerify to be disabled") | ||
assert.Nil(t, opts.Common.TLSOptions, "expected TLSOptions to be unset") | ||
}) | ||
|
||
t.Run("tls enabled without certs", func(t *testing.T) { | ||
os.Setenv(DockerTLSVerifyEnvVar, "true") | ||
os.Unsetenv(DockerCertPathEnvVar) | ||
defer func() { | ||
os.Unsetenv(DockerTLSVerifyEnvVar) | ||
}() | ||
|
||
opts := buildDockerClientOptions() | ||
assert.True(t, opts.Common.TLS, "expected TLS to be enabled") | ||
assert.True(t, opts.Common.TLSVerify, "expected the certs to be verified") | ||
assert.Equal(t, defaultTLSOptions, opts.Common.TLSOptions, "expected TLSOptions to be initialized to the default TLS settings") | ||
}) | ||
|
||
t.Run("tls enabled with custom certs", func(t *testing.T) { | ||
os.Setenv(DockerTLSVerifyEnvVar, "true") | ||
os.Setenv(DockerCertPathEnvVar, "/mycerts") | ||
defer func() { | ||
os.Unsetenv(DockerTLSVerifyEnvVar) | ||
os.Unsetenv(DockerCertPathEnvVar) | ||
}() | ||
|
||
opts := buildDockerClientOptions() | ||
assert.True(t, opts.Common.TLS, "expected TLS to be enabled") | ||
assert.True(t, opts.Common.TLSVerify, "expected the certs to be verified") | ||
assert.Equal(t, customTLSOptions, opts.Common.TLSOptions, "expected TLSOptions to use the custom DOCKER_CERT_PATH set") | ||
}) | ||
|
||
t.Run("tls enabled with self-signed certs", func(t *testing.T) { | ||
os.Setenv(DockerTLSVerifyEnvVar, "false") | ||
os.Setenv(DockerCertPathEnvVar, "/mycerts") | ||
defer func() { | ||
os.Unsetenv(DockerTLSVerifyEnvVar) | ||
os.Unsetenv(DockerCertPathEnvVar) | ||
}() | ||
|
||
opts := buildDockerClientOptions() | ||
assert.True(t, opts.Common.TLS, "expected TLS to be enabled") | ||
assert.False(t, opts.Common.TLSVerify, "expected TLSVerify to be false") | ||
assert.Equal(t, customTLSOptions, opts.Common.TLSOptions, "expected TLSOptions to use the custom DOCKER_CERT_PATH set") | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters