github.com/openshift/installer@v1.4.17/pkg/gather/ssh/ssh.go (about) 1 // Package ssh contains utilities that help gather logs, etc. on failures using ssh. 2 package ssh 3 4 import ( 5 "os" 6 "path/filepath" 7 "strings" 8 9 "github.com/pkg/errors" 10 "github.com/pkg/sftp" 11 "github.com/sirupsen/logrus" 12 "golang.org/x/crypto/ssh" 13 "golang.org/x/crypto/ssh/agent" 14 utilerrors "k8s.io/apimachinery/pkg/util/errors" 15 16 "github.com/openshift/installer/pkg/lineprinter" 17 ) 18 19 // NewClient creates a new SSH client which can be used to SSH to address using user and the keys. 20 // 21 // if keys list is empty, it tries to load the keys from the user's environment. 22 func NewClient(user, address string, keys []string) (*ssh.Client, error) { 23 ag, agentType, err := getAgent(keys) 24 if err != nil { 25 return nil, errors.Wrap(err, "failed to initialize the SSH agent") 26 } 27 28 client, err := ssh.Dial("tcp", address, &ssh.ClientConfig{ 29 User: user, 30 Auth: []ssh.AuthMethod{ 31 // Use a callback rather than PublicKeys 32 // so we only consult the agent once the remote server 33 // wants it. 34 ssh.PublicKeysCallback(ag.Signers), 35 }, 36 HostKeyCallback: ssh.InsecureIgnoreHostKey(), 37 }) 38 if err != nil { 39 if strings.Contains(err.Error(), "ssh: handshake failed: ssh: unable to authenticate") { 40 if agentType == "agent" { 41 return nil, errors.Wrap(err, "failed to use pre-existing agent, make sure the appropriate keys exist in the agent for authentication") 42 } 43 return nil, errors.Wrap(err, "failed to use the provided keys for authentication") 44 } 45 return nil, err 46 } 47 if err := agent.ForwardToAgent(client, ag); err != nil { 48 return nil, errors.Wrap(err, "failed to forward agent") 49 } 50 return client, nil 51 } 52 53 // Run uses an SSH client to execute commands. 54 func Run(client *ssh.Client, command string) error { 55 sess, err := client.NewSession() 56 if err != nil { 57 return err 58 } 59 defer sess.Close() 60 if err := agent.RequestAgentForwarding(sess); err != nil { 61 return errors.Wrap(err, "failed to setup request agent forwarding") 62 } 63 64 debugW := &lineprinter.LinePrinter{Print: (&lineprinter.Trimmer{WrappedPrint: logrus.Debug}).Print} 65 defer debugW.Close() 66 sess.Stdout = debugW 67 sess.Stderr = debugW 68 return sess.Run(command) 69 } 70 71 // PullFileTo downloads the file from remote server using SSH connection and writes to localPath. 72 func PullFileTo(client *ssh.Client, remotePath, localPath string) error { 73 sc, err := sftp.NewClient(client) 74 if err != nil { 75 return errors.Wrap(err, "failed to initialize the sftp client") 76 } 77 defer sc.Close() 78 79 // Open the source file 80 rFile, err := sc.Open(remotePath) 81 if err != nil { 82 return errors.Wrap(err, "failed to open remote file") 83 } 84 defer rFile.Close() 85 86 lFile, err := os.Create(localPath) 87 if err != nil { 88 return errors.Wrap(err, "failed to create file") 89 } 90 defer lFile.Close() 91 92 if _, err := rFile.WriteTo(lFile); err != nil { 93 return err 94 } 95 return nil 96 } 97 98 // defaultPrivateSSHKeys returns a list of all the PRIVATE SSH keys from user's home directory. 99 // It does not return any intermediate errors if at least one private key was loaded. 100 func defaultPrivateSSHKeys() (map[string]interface{}, error) { 101 d := filepath.Join(os.Getenv("HOME"), ".ssh") 102 paths, err := os.ReadDir(d) 103 if err != nil { 104 return nil, errors.Wrapf(err, "failed to read directory %q", d) 105 } 106 107 var files []string 108 for _, path := range paths { 109 if path.IsDir() { 110 continue 111 } 112 files = append(files, filepath.Join(d, path.Name())) 113 } 114 keys, err := LoadPrivateSSHKeys(files) 115 if len(keys) > 0 { 116 return keys, nil 117 } 118 return nil, err 119 } 120 121 // LoadPrivateSSHKeys try to optimistically load PRIVATE SSH keys from the all paths. 122 func LoadPrivateSSHKeys(paths []string) (map[string]interface{}, error) { 123 var errs []error 124 keys := make(map[string]interface{}) 125 for _, path := range paths { 126 data, err := os.ReadFile(path) 127 if err != nil { 128 errs = append(errs, errors.Wrapf(err, "failed to read %q", path)) 129 continue 130 } 131 key, err := ssh.ParseRawPrivateKey(data) 132 if err != nil { 133 logrus.Debugf("failed to parse SSH private key from %q", path) 134 errs = append(errs, errors.Wrapf(err, "failed to parse SSH private key from %q", path)) 135 continue 136 } 137 keys[path] = key 138 } 139 if err := utilerrors.NewAggregate(errs); err != nil { 140 return keys, err 141 } 142 return keys, nil 143 }