Add ability to start & stop ssh-agent process
parent
95de938511
commit
f9c89f84e5
|
@ -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)
|
||||
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
Loading…
Reference in New Issue