From 0313acf80ddd31d3f6dad701d75f64ad9e0749ec Mon Sep 17 00:00:00 2001 From: NewGr8Player <273221594@qq.com> Date: Thu, 27 Aug 2020 23:16:32 +0800 Subject: [PATCH] feat(autok3s): support ssh password login and ssh-key passphrase --- cmd/create.go | 6 ++-- cmd/join.go | 6 ++-- pkg/hosts/dialer.go | 27 ++++++++++++++--- pkg/types/autok3s.go | 13 ++++++-- pkg/utils/ssh.go | 72 ++++++++++++++++++++++++++++++++++++++++---- 5 files changed, 104 insertions(+), 20 deletions(-) diff --git a/cmd/create.go b/cmd/create.go index d11d8b7c..eef9e562 100644 --- a/cmd/create.go +++ b/cmd/create.go @@ -21,9 +21,9 @@ var ( cp providers.Provider cSSH = &types.SSH{ - SSHKey: "~/.ssh/id_rsa", - User: "root", - Port: "22", + SSHKeyPath: "~/.ssh/id_rsa", + User: "root", + Port: "22", } ) diff --git a/cmd/join.go b/cmd/join.go index 41b46941..b0c1e544 100644 --- a/cmd/join.go +++ b/cmd/join.go @@ -21,9 +21,9 @@ var ( jp providers.Provider jSSH = &types.SSH{ - SSHKey: "~/.ssh/id_rsa", - User: "root", - Port: "22", + SSHKeyPath: "~/.ssh/id_rsa", + User: "root", + Port: "22", } ) diff --git a/pkg/hosts/dialer.go b/pkg/hosts/dialer.go index 610719fc..a462454a 100644 --- a/pkg/hosts/dialer.go +++ b/pkg/hosts/dialer.go @@ -25,9 +25,14 @@ type Host struct { type dialer struct { signer ssh.Signer sshKey string + sshCert string sshAddress string username string + password string + passphrase string netConn string + + useSSHAgentAuth bool } type DialersOptions struct { @@ -65,16 +70,28 @@ func newDialer(h *Host, kind string) (*dialer, error) { } d = &dialer{ - sshAddress: fmt.Sprintf("%s:%s", h.PublicIPAddress[0], h.Port), - username: h.User, + sshAddress: fmt.Sprintf("%s:%s", h.PublicIPAddress[0], h.Port), + username: h.User, + password: h.Password, + passphrase: h.SSHKeyPassphrase, + useSSHAgentAuth: h.SSHAgentAuth, + sshKey: h.SSHKey, + sshCert: h.SSHCert, } - if d.sshKey == "" { + if d.password == "" && d.sshKey == "" && !d.useSSHAgentAuth { var err error - d.sshKey, err = utils.SSHPrivateKeyPath(h.SSHKey) + d.sshKey, err = utils.SSHPrivateKeyPath(h.SSHKeyPath) if err != nil { return nil, err } + + if d.sshCert == "" && len(h.SSHCertPath) > 0 { + d.sshCert, err = utils.SSHCertificatePath(h.SSHCertPath) + if err != nil { + return nil, err + } + } } switch kind { @@ -86,7 +103,7 @@ func newDialer(h *Host, kind string) (*dialer, error) { } func (d *dialer) getSSHTunnelConnection() (*ssh.Client, error) { - cfg, err := utils.GetSSHConfig(d.username, d.sshKey) + cfg, err := utils.GetSSHConfig(d.username, d.sshKey, d.passphrase, d.sshCert, d.password, d.useSSHAgentAuth) if err != nil { return nil, err } diff --git a/pkg/types/autok3s.go b/pkg/types/autok3s.go index 10c3e7ce..e9f0d2df 100644 --- a/pkg/types/autok3s.go +++ b/pkg/types/autok3s.go @@ -40,9 +40,16 @@ type Node struct { } type SSH struct { - Port string `json:"ssh-port,omitempty" yaml:"ssh-port,omitempty"` - User string `json:"user,omitempty" yaml:"user,omitempty"` - SSHKey string `json:"ssh-key,omitempty" yaml:"ssh-key,omitempty"` + Port string `json:"ssh-port,omitempty" yaml:"ssh-port,omitempty"` + User string `json:"user,omitempty" yaml:"user,omitempty"` + Password string `json:"password,omitempty" yaml:"password,omitempty"` + SSHKey string `json:"ssh-key,omitempty" yaml:"ssh-key,omitempty"` + SSHKeyPath string `json:"ssh-key-path,omitempty" yaml:"ssh-key-path,omitempty"` + SSHCert string `json:"ssh-cert,omitempty" yaml:"ssh-cert,omitempty"` + SSHCertPath string `json:"ssh-cert-path,omitempty" yaml:"ssh-cert-path,omitempty"` + SSHKeyPassphrase string `json:"ssh-key-passphrase,omitempty" yaml:"ssh-key-passphrase,omitempty"` + + SSHAgentAuth bool `json:"ssh-agent-auth,omitempty" yaml:"ssh-agent-auth,omitempty" ` } type Flag struct { diff --git a/pkg/utils/ssh.go b/pkg/utils/ssh.go index e70870e2..a629715b 100644 --- a/pkg/utils/ssh.go +++ b/pkg/utils/ssh.go @@ -3,11 +3,17 @@ package utils import ( "fmt" "io/ioutil" + "net" + "os" "path/filepath" + "github.com/sirupsen/logrus" "golang.org/x/crypto/ssh" + "golang.org/x/crypto/ssh/agent" ) +const SSHAuthSock = "SSH_AUTH_SOCK" + func SSHPrivateKeyPath(sshKey string) (string, error) { if sshKey[:2] == "~/" { sshKey = filepath.Join(UserHome(), sshKey[2:]) @@ -19,18 +25,68 @@ func SSHPrivateKeyPath(sshKey string) (string, error) { return string(buff), nil } -func GetSSHConfig(username, sshPrivateKeyString string) (*ssh.ClientConfig, error) { +func SSHCertificatePath(sshCertPath string) (string, error) { + if sshCertPath[:2] == "~/" { + sshCertPath = filepath.Join(UserHome(), sshCertPath[2:]) + } + buff, err := ioutil.ReadFile(sshCertPath) + if err != nil { + return "", fmt.Errorf("error while reading SSH certificate file: %v", err) + } + return string(buff), nil +} + +func GetSSHConfig(username, sshPrivateKeyString, passphrase, sshCert string, password string, useAgentAuth bool) (*ssh.ClientConfig, error) { config := &ssh.ClientConfig{ User: username, HostKeyCallback: ssh.InsecureIgnoreHostKey(), } - signer, err := parsePrivateKey(sshPrivateKeyString) - if err != nil { - return config, err - } + if useAgentAuth { + if sshAgentSock := os.Getenv(SSHAuthSock); sshAgentSock != "" { + sshAgent, err := net.Dial("unix", sshAgentSock) + if err != nil { + return config, fmt.Errorf("cannot connect to SSH Auth socket %q: %s", sshAgentSock, err) + } + + config.Auth = append(config.Auth, ssh.PublicKeysCallback(agent.NewClient(sshAgent).Signers)) + + logrus.Debugf("using %q SSH_AUTH_SOCK", sshAgentSock) + return config, nil + } + } else if sshPrivateKeyString != "" { + var ( + signer ssh.Signer + err error + ) + if passphrase != "" { + signer, err = parsePrivateKeyWithPassphrase(sshPrivateKeyString, passphrase) + } else { + signer, err = parsePrivateKey(sshPrivateKeyString) + } + if err != nil { + return config, err + } + + if len(sshCert) > 0 { + key, _, _, _, err := ssh.ParseAuthorizedKey([]byte(sshCert)) + if err != nil { + return config, fmt.Errorf("unable to parse SSH certificate: %v", err) + } - config.Auth = append(config.Auth, ssh.PublicKeys(signer)) + if _, ok := key.(*ssh.Certificate); !ok { + return config, fmt.Errorf("unable to cast public key to SSH certificate") + } + signer, err = ssh.NewCertSigner(key.(*ssh.Certificate), signer) + if err != nil { + return config, err + } + } + + config.Auth = append(config.Auth, ssh.PublicKeys(signer)) + } else if password != "" { + config.Auth = append(config.Auth, ssh.Password(password)) + } return config, nil } @@ -38,3 +94,7 @@ func GetSSHConfig(username, sshPrivateKeyString string) (*ssh.ClientConfig, erro func parsePrivateKey(keyBuff string) (ssh.Signer, error) { return ssh.ParsePrivateKey([]byte(keyBuff)) } + +func parsePrivateKeyWithPassphrase(keyBuff, passphrase string) (ssh.Signer, error) { + return ssh.ParsePrivateKeyWithPassphrase([]byte(keyBuff), []byte(passphrase)) +}