Add ability to start & stop ssh-agent process

pull/16761/head
Steven Powell 2023-06-22 17:05:46 -07:00
parent 95de938511
commit f9c89f84e5
5 changed files with 190 additions and 24 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.StopSSHAgent(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,7 +84,9 @@ 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 }}"+
@ -99,6 +107,9 @@ type DockerShellConfig struct {
ExistingDockerCertPath string
ExistingDockerHost string
ExistingDockerTLSVerify string
SSHAuthSock string
SSHAgentPID string
}
var (
@ -142,6 +153,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 +330,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 +387,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
@ -502,6 +520,8 @@ func dockerEnvVars(ec DockerEnvConfig) map[string]string {
constants.DockerHostEnv: dockerURL(ec.hostIP, ec.port),
constants.DockerCertPathEnv: ec.certsDir,
constants.MinikubeActiveDockerdEnv: ec.profile,
constants.SSHAuthSock: ec.sshAuthSock,
constants.SSHAgentPID: strconv.Itoa(ec.sshAgentPID),
}
envSSH := map[string]string{
constants.DockerHostEnv: sshURL(ec.username, ec.hostname, ec.sshport),

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,136 @@
/*
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
}
// StartSSHAgent starts an ssh-agent process
func StartSSHAgent(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 := isSSHAgentRunning(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")
}
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 := parseSSHAgentOutput(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 parseSSHAgentOutput(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 isSSHAgentRunning(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
}
// StopSSHAgent stops an ssh-agent process
func StopSSHAgent(profile string) error {
cc, err := config.Load(profile)
if err != nil {
return fmt.Errorf("failed loading config: %v", err)
}
running, err := isSSHAgentRunning(cc)
if err != nil {
return fmt.Errorf("failed checking if SSH agent is running: %v", err)
}
if running {
if err := killSSHAgentProcess(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 killSSHAgentProcess(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
}