-
Notifications
You must be signed in to change notification settings - Fork 2.5k
/
Copy pathconfigssh.go
154 lines (134 loc) · 3.74 KB
/
configssh.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
147
148
149
150
151
152
153
154
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
package configssh // import "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/sshcheckreceiver/internal/configssh"
import (
"errors"
"fmt"
"os"
"time"
"github.com/pkg/sftp"
"go.opentelemetry.io/collector/component"
"golang.org/x/crypto/ssh"
"golang.org/x/crypto/ssh/knownhosts"
)
const (
defaultClientVersion = "SSH-2.0-OTelClient"
)
var errMissingKnownHosts = errors.New(`known_hosts file is missing`)
type SSHClientSettings struct {
// Endpoint is always required
Endpoint string `mapstructure:"endpoint"`
Timeout time.Duration `mapstructure:"timeout"`
// authentication requires a Username and either a Password or KeyFile
Username string `mapstructure:"username"`
Password string `mapstructure:"password"`
KeyFile string `mapstructure:"key_file"`
// file path to the known_hosts
KnownHosts string `mapstructure:"known_hosts"`
// IgnoreHostKey provides an insecure path to quickstarts and testing
IgnoreHostKey bool `mapstructure:"ignore_host_key"`
}
type Client struct {
*ssh.Client
*ssh.ClientConfig
DialFunc func(network, address string, config *ssh.ClientConfig) (*ssh.Client, error)
}
// Dial starts an SSH session.
func (c *Client) Dial(endpoint string) (err error) {
c.Client, err = c.DialFunc("tcp", endpoint, c.ClientConfig)
if err != nil {
return err
}
return nil
}
func (c *Client) SFTPClient() (*SFTPClient, error) {
if c.Client == nil || c.Client.Conn == nil {
return nil, fmt.Errorf("SSH client not initialized")
}
client, err := sftp.NewClient(c.Client)
if err != nil {
return nil, err
}
return &SFTPClient{
Client: client,
ClientConfig: c.ClientConfig,
}, nil
}
type SFTPClient struct {
*sftp.Client
*ssh.ClientConfig
}
// ToClient creates an SSHClient.
func (scs *SSHClientSettings) ToClient(_ component.Host, _ component.TelemetrySettings) (*Client, error) {
var (
auth ssh.AuthMethod
hkc ssh.HostKeyCallback
)
if len(scs.KeyFile) > 0 {
key, err := os.ReadFile(scs.KeyFile)
if err != nil {
return nil, fmt.Errorf("unable to read private key: %w", err)
}
if len(scs.Password) > 0 {
sgn, err := ssh.ParsePrivateKeyWithPassphrase(key, []byte(scs.Password))
if err != nil {
return nil, fmt.Errorf("unable to parse private key with passphrase: %w", err)
}
auth = ssh.PublicKeys(sgn)
} else {
sgn, err := ssh.ParsePrivateKey(key)
if err != nil {
return nil, fmt.Errorf("unable to parse private key with passphrase: %w", err)
}
auth = ssh.PublicKeys(sgn)
}
} else {
auth = ssh.Password(scs.Password)
}
switch {
case scs.IgnoreHostKey:
hkc = ssh.InsecureIgnoreHostKey() //#nosec G106
case scs.KnownHosts != "":
fn, err := knownhosts.New(scs.KnownHosts)
if err != nil {
return nil, err
}
hkc = fn
default:
fn, err := defaultKnownHostsCallback()
if err != nil {
return nil, err
}
hkc = fn
}
return &Client{
ClientConfig: &ssh.ClientConfig{
User: scs.Username,
Auth: []ssh.AuthMethod{auth},
HostKeyCallback: hkc,
ClientVersion: defaultClientVersion,
},
DialFunc: ssh.Dial,
}, nil
}
func defaultKnownHostsPath() (string, error) {
home, err := os.UserHomeDir()
if err != nil {
return "", err
}
path := fmt.Sprintf("%s/.ssh/known_hosts", home)
if _, err := os.Stat(path); err != nil {
return "", errMissingKnownHosts
}
return path, nil
}
func defaultKnownHostsCallback() (hkc ssh.HostKeyCallback, err error) {
var knownHosts []string
if homeKH, err := defaultKnownHostsPath(); err == nil {
knownHosts = append(knownHosts, homeKH)
}
if _, err := os.Stat("/etc/ssh/known_hosts"); err == nil {
knownHosts = append(knownHosts, "/etc/ssh/known_hosts")
}
return knownhosts.New(knownHosts...)
}