-
Notifications
You must be signed in to change notification settings - Fork 119
/
scp.go
159 lines (130 loc) · 3.52 KB
/
scp.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
155
156
157
158
159
/*
* Copyright 2019 New Relic Corporation. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package inputs
import (
"encoding/hex"
"encoding/json"
"fmt"
"io/ioutil"
"strings"
"time"
"github.com/newrelic/nri-flex/internal/load"
"github.com/newrelic/nri-flex/internal/utils"
"github.com/pkg/sftp"
"golang.org/x/crypto/ssh"
)
// RunScpWithTimeout performs scp with timeout to gather data from a remote file.
func RunScpWithTimeout(dataStore *[]interface{}, cfg *load.Config, api load.API) error {
load.Logrus.Debugf("%v - running scp requests", cfg.Name)
remoteFile := api.Scp.RemoteFile
client, err := getSSHConnection(cfg, api)
if err != nil {
return err
}
srcFile, err := client.Open(remoteFile)
if err != nil {
return fmt.Errorf("ssh: failed to open source file: %s, error: %v", remoteFile, err)
}
fileContent, err := ioutil.ReadAll(srcFile)
if err != nil {
return fmt.Errorf("ssh: failed to read file: %s, error: %v", remoteFile, err)
}
return handleScpJSON(dataStore, fileContent)
}
func getSSHConnection(yml *load.Config, api load.API) (*sftp.Client, error) {
var user string
var timeout time.Duration
host := api.Scp.Host
port := api.Scp.Port
if yml.Global.User != "" {
user = yml.Global.User
}
if api.Scp.User != "" {
user = api.Scp.User
}
if yml.Global.Timeout > 0 {
timeout = time.Duration(yml.Global.Timeout) * time.Millisecond
} else {
timeout = load.DefaultPingTimeout
}
authMethod, err := getAuthMethod(yml, api)
if err != nil {
return nil, err
}
sshConfig := &ssh.ClientConfig{
User: user,
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
Timeout: timeout,
Auth: []ssh.AuthMethod{
authMethod,
},
} // #nosec
sshConfig.SetDefaults()
conn, err := ssh.Dial("tcp", host+":"+port, sshConfig)
if err != nil {
return nil, fmt.Errorf("ssh: failed to connect to sftp host: %s, with user %s, error: %v",
host, user, err)
}
client, err := sftp.NewClient(conn)
if err != nil {
return nil, fmt.Errorf("ssh: failed to init sftp client, error: %v", err)
}
return client, nil
}
func getAuthMethod(yml *load.Config, api load.API) (ssh.AuthMethod, error) {
var sshPemFile, pass, passphrase string
if yml.Global.SSHPEMFile != "" {
sshPemFile = yml.Global.SSHPEMFile
}
if api.Scp.SSHPEMFile != "" {
sshPemFile = api.Scp.SSHPEMFile
}
if sshPemFile != "" {
return publicKeyFile(sshPemFile)
}
if yml.Global.Pass != "" {
pass = yml.Global.Pass
}
if yml.Global.Passphrase != "" {
passphrase = yml.Global.Passphrase
}
if api.Scp.Pass != "" {
pass = api.Scp.Pass
}
if api.Scp.Passphrase != "" {
passphrase = api.Scp.Passphrase
}
if passphrase != "" {
encryptedPass, err := hex.DecodeString(pass)
if err == nil {
realPass, err := utils.Decrypt(encryptedPass, passphrase)
if err == nil {
pass = string(realPass)
}
}
}
return ssh.Password(pass), nil
}
func publicKeyFile(file string) (ssh.AuthMethod, error) {
buffer, err := ioutil.ReadFile(file)
if err != nil {
return nil, fmt.Errorf("failed to read ssh pem file: %v", err)
}
key, err := ssh.ParsePrivateKey(buffer)
if err != nil {
return nil, fmt.Errorf("failed to parse private key: %v", err)
}
return ssh.PublicKeys(key), nil
}
func handleScpJSON(dataStore *[]interface{}, body []byte) error {
newBody := strings.Replace(string(body), " ", "", -1)
var data interface{}
err := json.Unmarshal([]byte(newBody), &data)
if err != nil {
return fmt.Errorf("ssh: failed to unmarshal JSON error: %v", err)
}
*dataStore = append(*dataStore, data)
return nil
}