-
Notifications
You must be signed in to change notification settings - Fork 7
Expand file tree
/
Copy pathssh.go
More file actions
131 lines (107 loc) · 2.66 KB
/
Copy pathssh.go
File metadata and controls
131 lines (107 loc) · 2.66 KB
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
package xk6ssh
import (
"bytes"
"fmt"
"io"
"os"
"path"
"github.com/spf13/afero"
"golang.org/x/crypto/ssh"
)
// K6SSH is the main export of the k6 extension.
type K6SSH struct {
Session *ssh.Session
Client *ssh.Client
Config *ssh.ClientConfig
Out *bytes.Buffer
Stdin io.WriteCloser
fs afero.Fs
}
// ConnectionOptions provides configuration for the SSH session.
type ConnectionOptions struct {
RsaKey string
PrivateKey string //nolint:gosec // user-supplied private key contents option, not a hardcoded credential
Passphrase string
Host string
Port int
Username string
Password string //nolint:gosec // user-supplied connection password option, not a hardcoded credential
}
// Connect starts and SSH session with the provided options.
func (k6ssh *K6SSH) Connect(options ConnectionOptions) error {
var (
authMethod ssh.AuthMethod
err error
)
if options.Password != "" {
authMethod = ssh.Password(options.Password)
} else {
authMethod, err = k6ssh.rsaKeyAuthMethod(options)
if err != nil {
return err
}
}
k6ssh.Config = &ssh.ClientConfig{
Config: ssh.Config{},
User: options.Username,
Auth: []ssh.AuthMethod{authMethod},
// #nosec G106
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
ClientVersion: "",
Timeout: 0,
}
addr := fmt.Sprintf("%s:%d", options.Host, options.Port)
client, err := ssh.Dial("tcp", addr, k6ssh.Config)
if err != nil {
return err
}
k6ssh.Client = client
return nil
}
// Run executes a remote command over SSH.
func (k6ssh *K6SSH) Run(command string) (string, error) {
session, err := k6ssh.Client.NewSession()
if err != nil {
return "", err
}
defer func() {
_ = session.Close()
}()
var stdoutBuf bytes.Buffer
session.Stdout = &stdoutBuf
err = session.Run(command)
return stdoutBuf.String(), err
}
func (k6ssh *K6SSH) rsaKeyAuthMethod(options ConnectionOptions) (ssh.AuthMethod, error) {
var key []byte
var err error
if options.PrivateKey != "" {
key = []byte(options.PrivateKey)
} else {
pk := options.RsaKey
if pk == "" {
pk = k6ssh.defaultKeyPath()
}
key, err = afero.ReadFile(k6ssh.fs, pk)
if err != nil {
return nil, err
}
}
var signer ssh.Signer
if options.Passphrase != "" {
signer, err = ssh.ParsePrivateKeyWithPassphrase(key, []byte(options.Passphrase))
} else {
signer, err = ssh.ParsePrivateKey(key)
}
if err != nil {
return nil, err
}
return ssh.PublicKeys(signer), nil
}
func (k6ssh *K6SSH) defaultKeyPath() string {
home := os.Getenv("HOME") //nolint:forbidigo // locating the user's default SSH key requires the home dir
if len(home) > 0 {
return path.Join(home, ".ssh/id_rsa")
}
return ""
}