From 774f7f341eee1de4dd7cf719e88bfee9bcea9570 Mon Sep 17 00:00:00 2001 From: Shulhan Date: Mon, 5 Apr 2021 00:54:35 +0700 Subject: ssh: ask for passphrase if private key is encrypted on generateSigners In case the private key defined in IdentityFile is encrypted, prompt for the passphrase on the screen and read it from stdin using term.ReadPassword(). This changes also remove call to generateSigners on postConfig(), instead invoke it from NewClient() to minimize multiple calls to generateSigners(). --- CHANGELOG.adoc | 11 ++++++++++ go.mod | 2 +- lib/ssh/client.go | 5 +++++ lib/ssh/config_section.go | 56 +++++++++++++++++++++++++++++++++++++---------- 4 files changed, 61 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index ff2007db..e49cd77b 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -68,6 +68,16 @@ This library is released every month, usually at the first week of month. * If requestType is RequestTypeJSON and params is not nil, the params will be encoded as JSON in body. +* ssh: ask for passphrase if private key is encrypted on generateSigners + + In case the private key defined in IdentityFile is encrypted, prompt + for the passphrase on the screen and read it from stdin using + terminal.ReadPassword(). + + This changes also remove call to generateSigners on postConfig(), + instead invoke it from NewClient() to minimize multiple calls to + generateSigners(). + === Enhancements * xmlrpc: add debug statements to print request and response @@ -75,6 +85,7 @@ This library is released every month, usually at the first week of month. The debug level is set minimum to 3. If its set it will print the request and response to standard output. + == share v0.24.0 (2021-03-06) === Breaking changes diff --git a/go.mod b/go.mod index b0f528c4..993971e2 100644 --- a/go.mod +++ b/go.mod @@ -6,5 +6,5 @@ require ( golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 golang.org/x/net v0.0.0-20210331212208-0fccb6fa2b5c golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57 - golang.org/x/term v0.0.0-20210317153231-de623e64d2a6 // indirect + golang.org/x/term v0.0.0-20210317153231-de623e64d2a6 ) diff --git a/lib/ssh/client.go b/lib/ssh/client.go index d3ae9af6..5e6871c0 100644 --- a/lib/ssh/client.go +++ b/lib/ssh/client.go @@ -31,6 +31,11 @@ func NewClient(cfg *ConfigSection) (cl *Client, err error) { cfg.postConfig("") + err = cfg.generateSigners() + if err != nil { + return nil, err + } + sshConfig := &ssh.ClientConfig{ User: cfg.User, Auth: []ssh.AuthMethod{ diff --git a/lib/ssh/config_section.go b/lib/ssh/config_section.go index ea7132ac..0b98419a 100644 --- a/lib/ssh/config_section.go +++ b/lib/ssh/config_section.go @@ -14,6 +14,7 @@ import ( "strings" "golang.org/x/crypto/ssh" + "golang.org/x/term" ) const ( @@ -135,24 +136,36 @@ func newConfigSection() *ConfigSection { // generateSigners convert the IdentityFile to ssh.Signer for authentication // using PublicKey. // -func (section *ConfigSection) generateSigners() { +func (section *ConfigSection) generateSigners() (err error) { + var ( + logp = "generateSigners" + pkey string + pkeyRaw []byte + signer ssh.Signer + ) + section.signers = make([]ssh.Signer, 0, len(section.IdentityFile)) - for _, pkey := range section.IdentityFile { - pkeyRaw, err := ioutil.ReadFile(pkey) + for _, pkey = range section.IdentityFile { + pkeyRaw, err = ioutil.ReadFile(pkey) if err != nil { - if !errors.Is(err, os.ErrNotExist) { - log.Printf("generateSigners %s: %s", pkey, - err.Error()) + if errors.Is(err, os.ErrNotExist) { + continue } - continue + return fmt.Errorf("%s: %w", logp, err) } - signer, err := ssh.ParsePrivateKey(pkeyRaw) + signer, err = ssh.ParsePrivateKey(pkeyRaw) if err != nil { - log.Printf("generateSigners %s: ParsePrivateKey: %s", - pkey, err.Error()) - continue + _, isMissingPass := err.(*ssh.PassphraseMissingError) + if !isMissingPass { + return fmt.Errorf("%s: %w", logp, err) + } + + signer, err = section.generateSignerWithPassphrase(pkey, pkeyRaw) + if err != nil { + return fmt.Errorf("%s: %w", logp, err) + } } if len(section.privateKeyFile) == 0 { @@ -160,6 +173,7 @@ func (section *ConfigSection) generateSigners() { } section.signers = append(section.signers, signer) } + return nil } // @@ -210,8 +224,26 @@ func (section *ConfigSection) postConfig(homeDir string) { "~", homeDir, 1) } } +} + +func (section *ConfigSection) generateSignerWithPassphrase( + pkey string, pkeyRaw []byte, +) (signer ssh.Signer, err error) { + var pass []byte + + fmt.Printf("Enter passphrase for %s:", pkey) + + pass, err = term.ReadPassword(0) + if err != nil { + return nil, err + } + + signer, err = ssh.ParsePrivateKeyWithPassphrase(pkeyRaw, pass) + if err != nil { + return nil, err + } - section.generateSigners() + return signer, nil } func (section *ConfigSection) setAddKeysToAgent(val string) (err error) { -- cgit v1.3