forked from hypersleep/easyssh
-
Notifications
You must be signed in to change notification settings - Fork 0
/
easyssh.go
156 lines (125 loc) · 3.5 KB
/
easyssh.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
// Package easyssh provides a simple implementation of some SSH protocol features in Go.
// You can simply run command on remote server or get a file even simple than native console SSH client.
// Do not need to think about Dials, sessions, defers and public keys...Let easyssh will be think about it!
package easyssh
import (
"bytes"
"fmt"
"io"
"io/ioutil"
"net"
"os"
"os/user"
"path/filepath"
"golang.org/x/crypto/ssh"
"golang.org/x/crypto/ssh/agent"
)
// Contains main authority information.
// User field should be a name of user on remote server (ex. john in ssh [email protected]).
// Server field should be a remote machine address (ex. example.com in ssh [email protected])
// Key is a path to private key on your local machine.
// Port is SSH server port on remote machine.
// Note: easyssh looking for private key in user's home directory (ex. /home/john + Key).
// Then ensure your Key begins from '/' (ex. /.ssh/id_rsa)
type MakeConfig struct {
User string
Server string
Key string
Port string
Password string
}
// returns ssh.Signer from user you running app home path + cutted key path.
// (ex. pubkey,err := getKeyFile("/.ssh/id_rsa") )
func getKeyFile(keypath string) (ssh.Signer, error) {
usr, err := user.Current()
if err != nil {
return nil, err
}
file := usr.HomeDir + keypath
buf, err := ioutil.ReadFile(file)
if err != nil {
return nil, err
}
pubkey, err := ssh.ParsePrivateKey(buf)
if err != nil {
return nil, err
}
return pubkey, nil
}
// connects to remote server using MakeConfig struct and returns *ssh.Session
func (ssh_conf *MakeConfig) connect() (*ssh.Session, error) {
// auths holds the detected ssh auth methods
auths := []ssh.AuthMethod{}
// figure out what auths are requested, what is supported
if ssh_conf.Password != "" {
auths = append(auths, ssh.Password(ssh_conf.Password))
}
if sshAgent, err := net.Dial("unix", os.Getenv("SSH_AUTH_SOCK")); err == nil {
auths = append(auths, ssh.PublicKeysCallback(agent.NewClient(sshAgent).Signers))
defer sshAgent.Close()
}
if pubkey, err := getKeyFile(ssh_conf.Key); err == nil {
auths = append(auths, ssh.PublicKeys(pubkey))
}
config := &ssh.ClientConfig{
User: ssh_conf.User,
Auth: auths,
}
client, err := ssh.Dial("tcp", ssh_conf.Server+":"+ssh_conf.Port, config)
if err != nil {
return nil, err
}
session, err := client.NewSession()
if err != nil {
return nil, err
}
return session, nil
}
// Runs command on remote machine and returns STDOUT
func (ssh_conf *MakeConfig) Run(command string) (string, error) {
session, err := ssh_conf.connect()
if err != nil {
return "", err
}
defer session.Close()
var b bytes.Buffer
session.Stdout = &b
err = session.Run(command)
if err != nil {
return "", err
}
return b.String(), nil
}
// Scp uploads sourceFile to remote machine like native scp console app.
func (ssh_conf *MakeConfig) Scp(sourceFile string) error {
session, err := ssh_conf.connect()
if err != nil {
return err
}
defer session.Close()
targetFile := filepath.Base(sourceFile)
src, srcErr := os.Open(sourceFile)
if srcErr != nil {
return srcErr
}
srcStat, statErr := src.Stat()
if statErr != nil {
return statErr
}
go func() {
w, _ := session.StdinPipe()
fmt.Fprintln(w, "C0644", srcStat.Size(), targetFile)
if srcStat.Size() > 0 {
io.Copy(w, src)
fmt.Fprint(w, "\x00")
w.Close()
} else {
fmt.Fprint(w, "\x00")
w.Close()
}
}()
if err := session.Run(fmt.Sprintf("scp -t %s", targetFile)); err != nil {
return err
}
return nil
}