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