Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optionally use ssh for docker-env instead of tcp #9548

Merged
merged 10 commits into from
Dec 9, 2020
123 changes: 104 additions & 19 deletions cmd/minikube/cmd/docker-env.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ import (
"k8s.io/minikube/pkg/minikube/sysinit"
)

var dockerSetEnvTmpl = fmt.Sprintf(
var dockerEnvTCPTmpl = fmt.Sprintf(
"{{ .Prefix }}%s{{ .Delimiter }}{{ .DockerTLSVerify }}{{ .Suffix }}"+
"{{ .Prefix }}%s{{ .Delimiter }}{{ .DockerHost }}{{ .Suffix }}"+
"{{ .Prefix }}%s{{ .Delimiter }}{{ .DockerCertPath }}{{ .Suffix }}"+
Expand All @@ -70,6 +70,12 @@ var dockerSetEnvTmpl = fmt.Sprintf(
constants.ExistingDockerHostEnv,
constants.ExistingDockerCertPathEnv,
constants.MinikubeActiveDockerdEnv)
var dockerEnvSSHTmpl = fmt.Sprintf(
"{{ .Prefix }}%s{{ .Delimiter }}{{ .DockerHost }}{{ .Suffix }}"+
"{{ .Prefix }}%s{{ .Delimiter }}{{ .MinikubeDockerdProfile }}{{ .Suffix }}"+
"{{ .UsageHint }}",
constants.DockerHostEnv,
constants.MinikubeActiveDockerdEnv)

// DockerShellConfig represents the shell config for Docker
type DockerShellConfig struct {
Expand All @@ -88,6 +94,8 @@ type DockerShellConfig struct {

var (
noProxy bool
sshHost bool
sshAdd bool
dockerUnset bool
defaultNoProxyGetter NoProxyGetter
)
Expand All @@ -105,12 +113,19 @@ func dockerShellCfgSet(ec DockerEnvConfig, envMap map[string]string) *DockerShel
profile := ec.profile
const usgPlz = "To point your shell to minikube's docker-daemon, run:"
usgCmd := fmt.Sprintf("minikube -p %s docker-env", profile)
if ec.ssh {
usgCmd += " --ssh-host"
}
s := &DockerShellConfig{
Config: *shell.CfgSet(ec.EnvConfig, usgPlz, usgCmd),
}
s.DockerCertPath = envMap[constants.DockerCertPathEnv]
if !ec.ssh {
s.DockerCertPath = envMap[constants.DockerCertPathEnv]
}
s.DockerHost = envMap[constants.DockerHostEnv]
s.DockerTLSVerify = envMap[constants.DockerTLSVerifyEnv]
if !ec.ssh {
s.DockerTLSVerify = envMap[constants.DockerTLSVerifyEnv]
}

s.ExistingDockerCertPath = envMap[constants.ExistingDockerCertPathEnv]
s.ExistingDockerHost = envMap[constants.ExistingDockerHostEnv]
Expand Down Expand Up @@ -226,8 +241,10 @@ var dockerEnvCmd = &cobra.Command{
out.V{"runtime": co.Config.KubernetesConfig.ContainerRuntime})
}

ensureDockerd(cname, co.CP.Runner)
r := co.CP.Runner
ensureDockerd(cname, r)

d := co.CP.Host.Driver
port := constants.DockerDaemonPort
if driver.NeedsPortForward(driverName) {
port, err = oci.ForwardedPort(driverName, cname, port)
Expand All @@ -236,14 +253,30 @@ var dockerEnvCmd = &cobra.Command{
}
}

hostname, err := d.GetSSHHostname()
if err != nil {
exit.Error(reason.IfSSHClient, "Error getting ssh client", err)
}

sshport, err := d.GetSSHPort()
if err != nil {
exit.Error(reason.IfSSHClient, "Error getting ssh client", err)
}

hostIP := co.CP.IP.String()
ec := DockerEnvConfig{
EnvConfig: sh,
profile: cname,
driver: driverName,
hostIP: co.CP.IP.String(),
ssh: sshHost,
hostIP: hostIP,
port: port,
certsDir: localpath.MakeMiniPath("certs"),
noProxy: noProxy,
username: d.GetSSHUsername(),
hostname: hostname,
sshport: sshport,
keypath: d.GetSSHKeyPath(),
}

dockerPath, err := exec.LookPath("docker")
Expand All @@ -265,6 +298,22 @@ var dockerEnvCmd = &cobra.Command{
if err := dockerSetScript(ec, os.Stdout); err != nil {
exit.Error(reason.InternalDockerScript, "Error generating set output", err)
}

if sshAdd {
klog.Infof("Adding %v", d.GetSSHKeyPath())

path, err := exec.LookPath("ssh-add")
if err != nil {
exit.Error(reason.IfSSHClient, "Error with ssh-add", err)
}

cmd := exec.Command(path, d.GetSSHKeyPath())
cmd.Stderr = os.Stderr
err = cmd.Run()
if err != nil {
exit.Error(reason.IfSSHClient, "Error with ssh-add", err)
}
}
},
}

Expand All @@ -273,33 +322,32 @@ type DockerEnvConfig struct {
shell.EnvConfig
profile string
driver string
ssh bool
hostIP string
port int
certsDir string
noProxy bool
username string
hostname string
sshport int
keypath string
}

// dockerSetScript writes out a shell-compatible 'docker-env' script
func dockerSetScript(ec DockerEnvConfig, w io.Writer) error {
var dockerSetEnvTmpl string
if ec.ssh {
dockerSetEnvTmpl = dockerEnvSSHTmpl
} else {
dockerSetEnvTmpl = dockerEnvTCPTmpl
}
envVars := dockerEnvVars(ec)
return shell.SetScript(ec.EnvConfig, w, dockerSetEnvTmpl, dockerShellCfgSet(ec, envVars))
}

// dockerSetScript writes out a shell-compatible 'docker-env unset' script
func dockerUnsetScript(ec DockerEnvConfig, w io.Writer) error {
vars := []string{
constants.DockerTLSVerifyEnv,
constants.DockerHostEnv,
constants.DockerCertPathEnv,
constants.MinikubeActiveDockerdEnv,
}

if ec.noProxy {
k, _ := defaultNoProxyGetter.GetNoProxyVar()
if k != "" {
vars = append(vars, k)
}
}
vars := dockerEnvNames(ec)
return shell.UnsetScript(ec.EnvConfig, w, vars)
}

Expand All @@ -308,14 +356,31 @@ func dockerURL(ip string, port int) string {
return fmt.Sprintf("tcp://%s", net.JoinHostPort(ip, strconv.Itoa(port)))
}

// sshURL returns the docker endpoint URL when using socket over ssh.
func sshURL(username string, hostname string, port int) string {
// assumes standard /var/run/docker.sock as the path (not possible to set it at the moment)
return fmt.Sprintf("ssh://%s@%s", username, net.JoinHostPort(hostname, strconv.Itoa(port)))
}

// dockerEnvVars gets the necessary docker env variables to allow the use of minikube's docker daemon
func dockerEnvVars(ec DockerEnvConfig) map[string]string {
rt := map[string]string{
envTCP := map[string]string{
constants.DockerTLSVerifyEnv: "1",
constants.DockerHostEnv: dockerURL(ec.hostIP, ec.port),
constants.DockerCertPathEnv: ec.certsDir,
constants.MinikubeActiveDockerdEnv: ec.profile,
}
envSSH := map[string]string{
constants.DockerHostEnv: sshURL(ec.username, ec.hostname, ec.sshport),
constants.MinikubeActiveDockerdEnv: ec.profile,
}

var rt map[string]string
if ec.ssh {
rt = envSSH
} else {
rt = envTCP
}
if os.Getenv(constants.MinikubeActiveDockerdEnv) == "" {
for _, env := range constants.DockerDaemonEnvs {
if v := oci.InitialEnv(env); v != "" {
Expand All @@ -327,6 +392,24 @@ func dockerEnvVars(ec DockerEnvConfig) map[string]string {
return rt
}

// dockerEnvNames gets the necessary docker env variables to reset after using minikube's docker daemon
func dockerEnvNames(ec DockerEnvConfig) []string {
vars := []string{
constants.DockerTLSVerifyEnv,
constants.DockerHostEnv,
constants.DockerCertPathEnv,
constants.MinikubeActiveDockerdEnv,
}

if ec.noProxy {
k, _ := defaultNoProxyGetter.GetNoProxyVar()
if k != "" {
vars = append(vars, k)
}
}
return vars
}

// dockerEnvVarsList gets the necessary docker env variables to allow the use of minikube's docker daemon to be used in a exec.Command
func dockerEnvVarsList(ec DockerEnvConfig) []string {
return []string{
Expand All @@ -348,6 +431,8 @@ func tryDockerConnectivity(bin string, ec DockerEnvConfig) ([]byte, error) {
func init() {
defaultNoProxyGetter = &EnvNoProxyGetter{}
dockerEnvCmd.Flags().BoolVar(&noProxy, "no-proxy", false, "Add machine IP to NO_PROXY environment variable")
dockerEnvCmd.Flags().BoolVar(&sshHost, "ssh-host", false, "Use SSH connection instead of HTTPS (port 2376)")
dockerEnvCmd.Flags().BoolVar(&sshAdd, "ssh-add", false, "Add SSH identity key to SSH authentication agent")
dockerEnvCmd.Flags().StringVar(&shell.ForceShell, "shell", "", "Force environment to be configured for a specified shell: [fish, cmd, powershell, tcsh, bash, zsh], default is auto-detect")
dockerEnvCmd.Flags().BoolVarP(&dockerUnset, "unset", "u", false, "Unset variables instead of setting them")
}
16 changes: 16 additions & 0 deletions cmd/minikube/cmd/docker-env_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,22 @@ export MINIKUBE_ACTIVE_DOCKERD="dockerdriver"
unset DOCKER_HOST;
unset DOCKER_CERT_PATH;
unset MINIKUBE_ACTIVE_DOCKERD;
`,
},
{
"bash",
DockerEnvConfig{profile: "dockerdriver", driver: "docker", ssh: true, username: "root", hostname: "host", sshport: 22},
nil,
`export DOCKER_HOST="ssh://root@host:22"
export MINIKUBE_ACTIVE_DOCKERD="dockerdriver"

# To point your shell to minikube's docker-daemon, run:
# eval $(minikube -p dockerdriver docker-env --ssh-host)
`,
`unset DOCKER_TLS_VERIFY;
unset DOCKER_HOST;
unset DOCKER_CERT_PATH;
unset MINIKUBE_ACTIVE_DOCKERD;
`,
},
{
Expand Down
2 changes: 2 additions & 0 deletions site/content/en/docs/commands/docker-env.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ minikube docker-env [flags]
```
--no-proxy Add machine IP to NO_PROXY environment variable
--shell string Force environment to be configured for a specified shell: [fish, cmd, powershell, tcsh, bash, zsh], default is auto-detect
--ssh-add Add SSH identity key to SSH authentication agent
--ssh-host Use SSH connection instead of HTTPS (port 2376)
-u, --unset Unset variables instead of setting them
```

Expand Down