summaryrefslogtreecommitdiff
path: root/pkg/terminal
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/terminal')
-rw-r--r--pkg/terminal/util.go133
1 files changed, 133 insertions, 0 deletions
diff --git a/pkg/terminal/util.go b/pkg/terminal/util.go
new file mode 100644
index 000000000..ab3dc54e4
--- /dev/null
+++ b/pkg/terminal/util.go
@@ -0,0 +1,133 @@
+package terminal
+
+import (
+ "bufio"
+ "errors"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "sync"
+
+ "github.com/sirupsen/logrus"
+ "golang.org/x/crypto/ssh"
+ "golang.org/x/crypto/ssh/terminal"
+ "k8s.io/client-go/util/homedir"
+)
+
+var (
+ passPhrase []byte
+ phraseSync sync.Once
+ password []byte
+ passwordSync sync.Once
+)
+
+// ReadPassword prompts for a secret and returns value input by user from stdin
+// Unlike terminal.ReadPassword(), $(echo $SECRET | podman...) is supported.
+// Additionally, all input after `<secret>/n` is queued to podman command.
+func ReadPassword(prompt string) (pw []byte, err error) {
+ fd := int(os.Stdin.Fd())
+ if terminal.IsTerminal(fd) {
+ fmt.Fprint(os.Stderr, prompt)
+ pw, err = terminal.ReadPassword(fd)
+ fmt.Fprintln(os.Stderr)
+ return
+ }
+
+ var b [1]byte
+ for {
+ n, err := os.Stdin.Read(b[:])
+ // terminal.ReadPassword discards any '\r', so we do the same
+ if n > 0 && b[0] != '\r' {
+ if b[0] == '\n' {
+ return pw, nil
+ }
+ pw = append(pw, b[0])
+ // limit size, so that a wrong input won't fill up the memory
+ if len(pw) > 1024 {
+ err = errors.New("password too long, 1024 byte limit")
+ }
+ }
+ if err != nil {
+ // terminal.ReadPassword accepts EOF-terminated passwords
+ // if non-empty, so we do the same
+ if err == io.EOF && len(pw) > 0 {
+ err = nil
+ }
+ return pw, err
+ }
+ }
+}
+
+func PublicKey(path string, passphrase []byte) (ssh.AuthMethod, error) {
+ key, err := ioutil.ReadFile(path)
+ if err != nil {
+ return nil, err
+ }
+
+ signer, err := ssh.ParsePrivateKey(key)
+ if err != nil {
+ if _, ok := err.(*ssh.PassphraseMissingError); !ok {
+ return nil, err
+ }
+ if len(passphrase) == 0 {
+ passphrase = ReadPassphrase()
+ }
+ signer, err = ssh.ParsePrivateKeyWithPassphrase(key, passphrase)
+ if err != nil {
+ return nil, err
+ }
+ }
+ return ssh.PublicKeys(signer), nil
+}
+
+func ReadPassphrase() []byte {
+ phraseSync.Do(func() {
+ secret, err := ReadPassword("Key Passphrase: ")
+ if err != nil {
+ secret = []byte{}
+ }
+ passPhrase = secret
+ })
+ return passPhrase
+}
+
+func ReadLogin() []byte {
+ passwordSync.Do(func() {
+ secret, err := ReadPassword("Login password: ")
+ if err != nil {
+ secret = []byte{}
+ }
+ password = secret
+ })
+ return password
+}
+
+func HostKey(host string) ssh.PublicKey {
+ // parse OpenSSH known_hosts file
+ // ssh or use ssh-keyscan to get initial key
+ knownHosts := filepath.Join(homedir.HomeDir(), ".ssh", "known_hosts")
+ fd, err := os.Open(knownHosts)
+ if err != nil {
+ logrus.Error(err)
+ return nil
+ }
+
+ scanner := bufio.NewScanner(fd)
+ for scanner.Scan() {
+ _, hosts, key, _, _, err := ssh.ParseKnownHosts(scanner.Bytes())
+ if err != nil {
+ logrus.Errorf("Failed to parse known_hosts: %s", scanner.Text())
+ continue
+ }
+
+ for _, h := range hosts {
+ if h == host {
+ return key
+ }
+ }
+ }
+
+ return nil
+}