Merge pull request #16761 from spowelljr/sshAgent

Add ability to start & stop ssh-agent process
pull/16800/head
Steven Powell 2023-06-30 13:40:16 -07:00 committed by GitHub
commit 7ba7f7b868
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 252 additions and 30 deletions

View File

@ -51,6 +51,7 @@ import (
"k8s.io/minikube/pkg/minikube/out"
"k8s.io/minikube/pkg/minikube/out/register"
"k8s.io/minikube/pkg/minikube/reason"
"k8s.io/minikube/pkg/minikube/sshagent"
"k8s.io/minikube/pkg/minikube/style"
)
@ -93,6 +94,9 @@ var hostAndDirsDeleter = func(api libmachine.API, cc *config.ClusterConfig, prof
if err := killMountProcess(); err != nil {
out.FailureT("Failed to kill mount process: {{.error}}", out.V{"error": err})
}
if err := sshagent.Stop(profileName); err != nil {
out.FailureT("Failed to stop ssh-agent process: {{.error}}", out.V{"error": err})
}
deleteHosts(api, cc)

View File

@ -71,6 +71,12 @@ var dockerEnvTCPTmpl = fmt.Sprintf(
"{{ if .NoProxyVar }}"+
"{{ .Prefix }}{{ .NoProxyVar }}{{ .Delimiter }}{{ .NoProxyValue }}{{ .Suffix }}"+
"{{ end }}"+
"{{ if .SSHAuthSock }}"+
"{{ .Prefix }}%s{{ .Delimiter }}{{ .SSHAuthSock }}{{ .Suffix }}"+
"{{ end }}"+
"{{ if .SSHAgentPID }}"+
"{{ .Prefix }}%s{{ .Delimiter }}{{ .SSHAgentPID }}{{ .Suffix }}"+
"{{ end }}"+
"{{ .UsageHint }}",
constants.DockerTLSVerifyEnv,
constants.DockerHostEnv,
@ -78,13 +84,23 @@ var dockerEnvTCPTmpl = fmt.Sprintf(
constants.ExistingDockerTLSVerifyEnv,
constants.ExistingDockerHostEnv,
constants.ExistingDockerCertPathEnv,
constants.MinikubeActiveDockerdEnv)
constants.MinikubeActiveDockerdEnv,
constants.SSHAuthSock,
constants.SSHAgentPID)
var dockerEnvSSHTmpl = fmt.Sprintf(
"{{ .Prefix }}%s{{ .Delimiter }}{{ .DockerHost }}{{ .Suffix }}"+
"{{ .Prefix }}%s{{ .Delimiter }}{{ .MinikubeDockerdProfile }}{{ .Suffix }}"+
"{{ if .SSHAuthSock }}"+
"{{ .Prefix }}%s{{ .Delimiter }}{{ .SSHAuthSock }}{{ .Suffix }}"+
"{{ end }}"+
"{{ if .SSHAgentPID }}"+
"{{ .Prefix }}%s{{ .Delimiter }}{{ .SSHAgentPID }}{{ .Suffix }}"+
"{{ end }}"+
"{{ .UsageHint }}",
constants.DockerHostEnv,
constants.MinikubeActiveDockerdEnv)
constants.MinikubeActiveDockerdEnv,
constants.SSHAuthSock,
constants.SSHAgentPID)
// DockerShellConfig represents the shell config for Docker
type DockerShellConfig struct {
@ -99,6 +115,9 @@ type DockerShellConfig struct {
ExistingDockerCertPath string
ExistingDockerHost string
ExistingDockerTLSVerify string
SSHAuthSock string
SSHAgentPID string
}
var (
@ -142,6 +161,9 @@ func dockerShellCfgSet(ec DockerEnvConfig, envMap map[string]string) *DockerShel
s.MinikubeDockerdProfile = envMap[constants.MinikubeActiveDockerdEnv]
s.SSHAuthSock = envMap[constants.SSHAuthSock]
s.SSHAgentPID = envMap[constants.SSHAgentPID]
if ec.noProxy {
noProxyVar, noProxyValue := defaultNoProxyGetter.GetNoProxyVar()
@ -316,18 +338,20 @@ docker-cli install instructions: https://minikube.sigs.k8s.io/docs/tutorials/doc
hostIP := co.CP.IP.String()
ec := DockerEnvConfig{
EnvConfig: sh,
profile: cname,
driver: driverName,
ssh: sshHost,
hostIP: hostIP,
port: port,
certsDir: localpath.MakeMiniPath("certs"),
noProxy: noProxy,
username: d.GetSSHUsername(),
hostname: hostname,
sshport: sshport,
keypath: d.GetSSHKeyPath(),
EnvConfig: sh,
profile: cname,
driver: driverName,
ssh: sshHost,
hostIP: hostIP,
port: port,
certsDir: localpath.MakeMiniPath("certs"),
noProxy: noProxy,
username: d.GetSSHUsername(),
hostname: hostname,
sshport: sshport,
keypath: d.GetSSHKeyPath(),
sshAuthSock: co.Config.SSHAuthSock,
sshAgentPID: co.Config.SSHAgentPID,
}
dockerPath, err := exec.LookPath("docker")
@ -371,17 +395,19 @@ docker-cli install instructions: https://minikube.sigs.k8s.io/docs/tutorials/doc
// DockerEnvConfig encapsulates all external inputs into shell generation for Docker
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
profile string
driver string
ssh bool
hostIP string
port int
certsDir string
noProxy bool
username string
hostname string
sshport int
keypath string
sshAuthSock string
sshAgentPID int
}
// dockerSetScript writes out a shell-compatible 'docker-env' script
@ -497,11 +523,18 @@ func sshURL(username string, hostname string, port int) string {
// dockerEnvVars gets the necessary docker env variables to allow the use of minikube's docker daemon
func dockerEnvVars(ec DockerEnvConfig) map[string]string {
agentPID := strconv.Itoa(ec.sshAgentPID)
// set agentPID to nil value if not set
if agentPID == "0" {
agentPID = ""
}
envTCP := map[string]string{
constants.DockerTLSVerifyEnv: "1",
constants.DockerHostEnv: dockerURL(ec.hostIP, ec.port),
constants.DockerCertPathEnv: ec.certsDir,
constants.MinikubeActiveDockerdEnv: ec.profile,
constants.SSHAuthSock: ec.sshAuthSock,
constants.SSHAgentPID: agentPID,
}
envSSH := map[string]string{
constants.DockerHostEnv: sshURL(ec.username, ec.hostname, ec.sshport),
@ -532,6 +565,8 @@ func dockerEnvNames(ec DockerEnvConfig) []string {
constants.DockerHostEnv,
constants.DockerCertPathEnv,
constants.MinikubeActiveDockerdEnv,
constants.SSHAuthSock,
constants.SSHAgentPID,
}
if ec.noProxy {
@ -550,6 +585,8 @@ func dockerEnvVarsList(ec DockerEnvConfig) []string {
fmt.Sprintf("%s=%s", constants.DockerHostEnv, dockerURL(ec.hostIP, ec.port)),
fmt.Sprintf("%s=%s", constants.DockerCertPathEnv, ec.certsDir),
fmt.Sprintf("%s=%s", constants.MinikubeActiveDockerdEnv, ec.profile),
fmt.Sprintf("%s=%s", constants.SSHAuthSock, ec.sshAuthSock),
fmt.Sprintf("%s=%d", constants.SSHAgentPID, ec.sshAgentPID),
}
}

View File

@ -63,6 +63,8 @@ export MINIKUBE_ACTIVE_DOCKERD="dockerdriver"
unset DOCKER_HOST;
unset DOCKER_CERT_PATH;
unset MINIKUBE_ACTIVE_DOCKERD;
unset SSH_AUTH_SOCK;
unset SSH_AGENT_PID;
`,
nil,
},
@ -81,6 +83,8 @@ export MINIKUBE_ACTIVE_DOCKERD="dockerdriver"
unset DOCKER_HOST;
unset DOCKER_CERT_PATH;
unset MINIKUBE_ACTIVE_DOCKERD;
unset SSH_AUTH_SOCK;
unset SSH_AGENT_PID;
`,
nil,
},
@ -101,6 +105,8 @@ export MINIKUBE_ACTIVE_DOCKERD="bash"
unset DOCKER_HOST;
unset DOCKER_CERT_PATH;
unset MINIKUBE_ACTIVE_DOCKERD;
unset SSH_AUTH_SOCK;
unset SSH_AGENT_PID;
`,
nil,
},
@ -121,6 +127,8 @@ export MINIKUBE_ACTIVE_DOCKERD="ipv6"
unset DOCKER_HOST;
unset DOCKER_CERT_PATH;
unset MINIKUBE_ACTIVE_DOCKERD;
unset SSH_AUTH_SOCK;
unset SSH_AGENT_PID;
`,
nil,
},
@ -141,6 +149,8 @@ set -gx MINIKUBE_ACTIVE_DOCKERD "fish";
set -e DOCKER_HOST;
set -e DOCKER_CERT_PATH;
set -e MINIKUBE_ACTIVE_DOCKERD;
set -e SSH_AUTH_SOCK;
set -e SSH_AGENT_PID;
`,
nil,
},
@ -161,6 +171,8 @@ $Env:MINIKUBE_ACTIVE_DOCKERD = "powershell"
Remove-Item Env:\\DOCKER_HOST
Remove-Item Env:\\DOCKER_CERT_PATH
Remove-Item Env:\\MINIKUBE_ACTIVE_DOCKERD
Remove-Item Env:\\SSH_AUTH_SOCK
Remove-Item Env:\\SSH_AGENT_PID
`,
nil,
},
@ -181,6 +193,8 @@ REM @FOR /f "tokens=*" %i IN ('minikube -p cmd docker-env --shell cmd') DO @%i
SET DOCKER_HOST=
SET DOCKER_CERT_PATH=
SET MINIKUBE_ACTIVE_DOCKERD=
SET SSH_AUTH_SOCK=
SET SSH_AGENT_PID=
`,
nil,
},
@ -200,6 +214,8 @@ SET MINIKUBE_ACTIVE_DOCKERD=
(setenv "DOCKER_HOST" nil)
(setenv "DOCKER_CERT_PATH" nil)
(setenv "MINIKUBE_ACTIVE_DOCKERD" nil)
(setenv "SSH_AUTH_SOCK" nil)
(setenv "SSH_AGENT_PID" nil)
`,
nil,
},
@ -222,6 +238,8 @@ export NO_PROXY="127.0.0.1"
unset DOCKER_HOST;
unset DOCKER_CERT_PATH;
unset MINIKUBE_ACTIVE_DOCKERD;
unset SSH_AUTH_SOCK;
unset SSH_AGENT_PID;
unset NO_PROXY;
`,
nil,
@ -245,6 +263,8 @@ export no_proxy="127.0.0.1"
unset DOCKER_HOST;
unset DOCKER_CERT_PATH;
unset MINIKUBE_ACTIVE_DOCKERD;
unset SSH_AUTH_SOCK;
unset SSH_AGENT_PID;
unset no_proxy;
`,
nil,
@ -267,6 +287,8 @@ $Env:no_proxy = "192.168.0.1"
Remove-Item Env:\\DOCKER_HOST
Remove-Item Env:\\DOCKER_CERT_PATH
Remove-Item Env:\\MINIKUBE_ACTIVE_DOCKERD
Remove-Item Env:\\SSH_AUTH_SOCK
Remove-Item Env:\\SSH_AGENT_PID
Remove-Item Env:\\no_proxy
`,
nil,
@ -290,6 +312,8 @@ export NO_PROXY="192.168.0.1,10.0.0.4,127.0.0.1"
unset DOCKER_HOST;
unset DOCKER_CERT_PATH;
unset MINIKUBE_ACTIVE_DOCKERD;
unset SSH_AUTH_SOCK;
unset SSH_AGENT_PID;
unset NO_PROXY;
`,
nil,
@ -308,23 +332,29 @@ MINIKUBE_ACTIVE_DOCKERD=noneshell
DOCKER_HOST
DOCKER_CERT_PATH
MINIKUBE_ACTIVE_DOCKERD
SSH_AUTH_SOCK
SSH_AGENT_PID
`,
nil,
},
{
"none",
"text",
DockerEnvConfig{profile: "nonetext", driver: "docker", hostIP: "127.0.0.1", port: 32842, certsDir: "/certs"},
DockerEnvConfig{profile: "nonetext", driver: "docker", hostIP: "127.0.0.1", port: 32842, certsDir: "/certs", sshAuthSock: "/var/folders/9l/6wpxv6wd1b901m1146r579wc00rqw3/T//ssh-KCQt1sNqrCPI/agent.29227", sshAgentPID: 29228},
nil,
`DOCKER_TLS_VERIFY=1
DOCKER_HOST=tcp://127.0.0.1:32842
DOCKER_CERT_PATH=/certs
MINIKUBE_ACTIVE_DOCKERD=nonetext
SSH_AUTH_SOCK=/var/folders/9l/6wpxv6wd1b901m1146r579wc00rqw3/T//ssh-KCQt1sNqrCPI/agent.29227
SSH_AGENT_PID=29228
`,
`DOCKER_TLS_VERIFY
DOCKER_HOST
DOCKER_CERT_PATH
MINIKUBE_ACTIVE_DOCKERD
SSH_AUTH_SOCK
SSH_AGENT_PID
`,
[]cmp.Option{
cmpopts.AcyclicTransformer("SplitLines", func(s string) []string {
@ -338,19 +368,23 @@ MINIKUBE_ACTIVE_DOCKERD
{
"none",
"json",
DockerEnvConfig{profile: "nonejson", driver: "docker", hostIP: "127.0.0.1", port: 32842, certsDir: "/certs"},
DockerEnvConfig{profile: "nonejson", driver: "docker", hostIP: "127.0.0.1", port: 32842, certsDir: "/certs", sshAuthSock: "/var/folders/9l/6wpxv6wd1b901m1146r579wc00rqw3/T//ssh-KCQt1sNqrCPI/agent.29227", sshAgentPID: 29228},
nil,
`{
"DOCKER_TLS_VERIFY": "1",
"DOCKER_HOST": "tcp://127.0.0.1:32842",
"DOCKER_CERT_PATH": "/certs",
"MINIKUBE_ACTIVE_DOCKERD": "nonejson"
"MINIKUBE_ACTIVE_DOCKERD": "nonejson",
"SSH_AUTH_SOCK": "/var/folders/9l/6wpxv6wd1b901m1146r579wc00rqw3/T//ssh-KCQt1sNqrCPI/agent.29227",
"SSH_AGENT_PID": "29228"
}`,
`[
"DOCKER_TLS_VERIFY",
"DOCKER_HOST",
"DOCKER_CERT_PATH",
"MINIKUBE_ACTIVE_DOCKERD"
"MINIKUBE_ACTIVE_DOCKERD",
"SSH_AUTH_SOCK",
"SSH_AGENT_PID"
]`,
[]cmp.Option{
cmp.FilterValues(func(x, y string) bool {
@ -367,17 +401,21 @@ MINIKUBE_ACTIVE_DOCKERD
{
"none",
"yaml",
DockerEnvConfig{profile: "noneyaml", driver: "docker", hostIP: "127.0.0.1", port: 32842, certsDir: "/certs"},
DockerEnvConfig{profile: "noneyaml", driver: "docker", hostIP: "127.0.0.1", port: 32842, certsDir: "/certs", sshAuthSock: "/var/folders/9l/6wpxv6wd1b901m1146r579wc00rqw3/T//ssh-KCQt1sNqrCPI/agent.29227", sshAgentPID: 29228},
nil,
`DOCKER_TLS_VERIFY: "1"
DOCKER_HOST: tcp://127.0.0.1:32842
DOCKER_CERT_PATH: /certs
MINIKUBE_ACTIVE_DOCKERD: noneyaml
SSH_AUTH_SOCK: /var/folders/9l/6wpxv6wd1b901m1146r579wc00rqw3/T//ssh-KCQt1sNqrCPI/agent.29227
SSH_AGENT_PID: "29228"
`,
`- DOCKER_TLS_VERIFY
- DOCKER_HOST
- DOCKER_CERT_PATH
- MINIKUBE_ACTIVE_DOCKERD
- SSH_AUTH_SOCK
- SSH_AGENT_PID
`,
[]cmp.Option{
cmpopts.AcyclicTransformer("ParseYAML", func(in string) (out interface{}) {

View File

@ -105,6 +105,8 @@ type ClusterConfig struct {
SocketVMnetClientPath string
SocketVMnetPath string
StaticIP string
SSHAuthSock string
SSHAgentPID int
}
// KubernetesConfig contains the parameters used to configure the VM Kubernetes.

View File

@ -92,6 +92,10 @@ const (
// MinikubeActiveDockerdEnv holds the docker daemon which user's shell is pointing at
// value would be profile or empty if pointing to the user's host daemon.
MinikubeActiveDockerdEnv = "MINIKUBE_ACTIVE_DOCKERD"
// SSHAuthSock is used for docker-env
SSHAuthSock = "SSH_AUTH_SOCK"
// SSHAgentPID is used for docker-env
SSHAgentPID = "SSH_AGENT_PID"
// PodmanVarlinkBridgeEnv is used for podman settings
PodmanVarlinkBridgeEnv = "PODMAN_VARLINK_BRIDGE"
// PodmanContainerHostEnv is used for podman settings

View File

@ -0,0 +1,137 @@
/*
Copyright 2023 The Kubernetes Authors All rights reserved.
Licensed 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 sshagent
import (
"context"
"fmt"
"os"
"os/exec"
"regexp"
"runtime"
"strconv"
"strings"
"time"
"github.com/mitchellh/go-ps"
"k8s.io/klog/v2"
"k8s.io/minikube/pkg/minikube/config"
)
type sshAgent struct {
authSock string
agentPID int
}
// Start an ssh-agent process
func Start(profile string) error {
if runtime.GOOS == "windows" {
return fmt.Errorf("starting an SSH agent on Windows is not yet supported")
}
cc, err := config.Load(profile)
if err != nil {
return fmt.Errorf("failed loading config: %v", err)
}
running, err := isRunning(cc)
if err != nil {
return fmt.Errorf("failed checking if SSH agent is running: %v", err)
}
if running {
klog.Info("SSH agent is already running, aborting start")
return nil
}
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
out, err := exec.CommandContext(ctx, "ssh-agent").CombinedOutput()
if err != nil {
return fmt.Errorf("failed starting ssh-agent: %s: %v", string(out), err)
}
parsed, err := parseOutput(string(out))
if err != nil {
return fmt.Errorf("failed to parse ssh-agent output: %v", err)
}
cc.SSHAuthSock = parsed.authSock
cc.SSHAgentPID = parsed.agentPID
if err := config.Write(profile, cc); err != nil {
return fmt.Errorf("failed writing config: %v", err)
}
return nil
}
func parseOutput(out string) (*sshAgent, error) {
sockSubmatches := regexp.MustCompile(`SSH_AUTH_SOCK=(.*?);`).FindStringSubmatch(out)
if len(sockSubmatches) < 2 {
return nil, fmt.Errorf("SSH_AUTH_SOCK not found in output: %s", out)
}
pidSubmatches := regexp.MustCompile(`SSH_AGENT_PID=(.*?);`).FindStringSubmatch(out)
if len(pidSubmatches) < 2 {
return nil, fmt.Errorf("SSH_AGENT_PID not found in output: %s", out)
}
pid, err := strconv.Atoi(pidSubmatches[1])
if err != nil {
return nil, fmt.Errorf("failed to convert pid to int: %v", err)
}
return &sshAgent{sockSubmatches[1], pid}, nil
}
func isRunning(cc *config.ClusterConfig) (bool, error) {
if cc.SSHAgentPID == 0 {
return false, nil
}
entry, err := ps.FindProcess(cc.SSHAgentPID)
if err != nil {
return false, fmt.Errorf("failed finding process: %v", err)
}
if entry == nil {
return false, nil
}
return strings.Contains(entry.Executable(), "ssh-agent"), nil
}
// Stop an ssh-agent process
func Stop(profile string) error {
cc, err := config.Load(profile)
if err != nil {
return fmt.Errorf("failed loading config: %v", err)
}
running, err := isRunning(cc)
if err != nil {
return fmt.Errorf("failed checking if SSH agent is running: %v", err)
}
if running {
if err := killProcess(cc.SSHAgentPID); err != nil {
return fmt.Errorf("failed killing SSH agent process: %v", err)
}
}
cc.SSHAuthSock = ""
cc.SSHAgentPID = 0
if err := config.Write(profile, cc); err != nil {
return fmt.Errorf("failed writing config: %v", err)
}
return nil
}
func killProcess(pid int) error {
proc, err := os.FindProcess(pid)
if err != nil {
return fmt.Errorf("failed finding process: %v", err)
}
if err := proc.Kill(); err != nil {
return fmt.Errorf("failed killing process: %v", err)
}
return nil
}