github.com/Jeffail/benthos/v3@v3.65.0/internal/impl/sftp/sftp.go (about) 1 package sftp 2 3 import ( 4 "fmt" 5 "net" 6 "os" 7 8 "github.com/Jeffail/benthos/v3/internal/docs" 9 "github.com/pkg/sftp" 10 "golang.org/x/crypto/ssh" 11 ) 12 13 // CredentialsDocs returns a documentation field spec for SFTP credentials 14 // fields within a Config. 15 func CredentialsDocs() docs.FieldSpecs { 16 return docs.FieldSpecs{ 17 docs.FieldCommon("username", "The username to connect to the SFTP server."), 18 docs.FieldCommon("password", "The password for the username to connect to the SFTP server."), 19 docs.FieldCommon("private_key_file", "The private key for the username to connect to the SFTP server."), 20 docs.FieldCommon("private_key_pass", "Optional passphrase for private key."), 21 } 22 } 23 24 // Credentials contains the credentials for connecting to the SFTP server 25 type Credentials struct { 26 Username string `json:"username" yaml:"username"` 27 Password string `json:"password" yaml:"password"` 28 PrivateKeyFile string `json:"private_key_file" yaml:"private_key_file"` 29 PrivateKeyPass string `json:"private_key_pass" yaml:"private_key_pass"` 30 } 31 32 // GetClient establishes a fresh sftp client from a set of credentials and an 33 // address. 34 func (c Credentials) GetClient(address string) (*sftp.Client, error) { 35 host, port, err := net.SplitHostPort(address) 36 if err != nil { 37 return nil, fmt.Errorf("failed to parse address: %v", err) 38 } 39 40 // create sftp client and establish connection 41 server := &Server{ 42 Host: host, 43 Port: port, 44 } 45 46 certCheck := &ssh.CertChecker{ 47 IsHostAuthority: HostAuthCallback(), 48 IsRevoked: CertCallback(server), 49 HostKeyFallback: HostCallback(server), 50 } 51 52 config := &ssh.ClientConfig{ 53 User: c.Username, 54 Auth: []ssh.AuthMethod{}, 55 HostKeyCallback: certCheck.CheckHostKey, 56 } 57 58 // set password auth when provided 59 if c.Password != "" { 60 // append to config.Auth 61 config.Auth = append(config.Auth, ssh.Password(c.Password)) 62 } 63 64 // set private key auth when provided 65 if c.PrivateKeyFile != "" { 66 // read private key file 67 var privateKey []byte 68 privateKey, err = os.ReadFile(c.PrivateKeyFile) 69 if err != nil { 70 return nil, fmt.Errorf("failed to read private key: %v", err) 71 } 72 // check if passphrase is provided and parse private key 73 var signer ssh.Signer 74 if c.PrivateKeyPass == "" { 75 signer, err = ssh.ParsePrivateKey(privateKey) 76 } else { 77 signer, err = ssh.ParsePrivateKeyWithPassphrase(privateKey, []byte(c.PrivateKeyPass)) 78 } 79 if err != nil { 80 return nil, fmt.Errorf("failed to parse private key: %v", err) 81 } 82 // append to config.Auth 83 config.Auth = append(config.Auth, ssh.PublicKeys(signer)) 84 } 85 86 conn, err := ssh.Dial("tcp", address, config) 87 if err != nil { 88 return nil, err 89 } 90 91 client, err := sftp.NewClient(conn) 92 if err != nil { 93 conn.Close() 94 return nil, err 95 } 96 97 return client, nil 98 } 99 100 // Server contains connection data for connecting to an SFTP server 101 type Server struct { 102 Address string // host:port 103 Host string // IP address 104 Port string // port 105 IsSSH bool // true if server is running SSH on address:port 106 Banner string // banner text, if any 107 Cert ssh.Certificate // server's certificate 108 Hostname string // hostname 109 PublicKey ssh.PublicKey // server's public key 110 } 111 112 // HostAuthorityCallback used when setting up the connection to the SFTP server 113 type HostAuthorityCallback func(ssh.PublicKey, string) bool 114 115 // IsRevokedCallback used when setting up the connection to the SFTP server 116 type IsRevokedCallback func(cert *ssh.Certificate) bool 117 118 // HostAuthCallback is called when setting up the connection to the SFTP server 119 func HostAuthCallback() HostAuthorityCallback { 120 return func(p ssh.PublicKey, addr string) bool { 121 return true 122 } 123 } 124 125 // CertCallback is called when setting up the connection to the SFTP server 126 func CertCallback(s *Server) IsRevokedCallback { 127 return func(cert *ssh.Certificate) bool { 128 s.Cert = *cert 129 s.IsSSH = true 130 131 return false 132 } 133 } 134 135 // HostCallback is called when setting up the connection to the SFTP server 136 func HostCallback(s *Server) ssh.HostKeyCallback { 137 return func(hostname string, remote net.Addr, key ssh.PublicKey) error { 138 s.Hostname = hostname 139 s.PublicKey = key 140 return nil 141 } 142 }