-
Notifications
You must be signed in to change notification settings - Fork 26
/
main.go
178 lines (155 loc) · 4.11 KB
/
main.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
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
package main
import (
"fmt"
"io"
"io/ioutil"
"net"
"os"
"strings"
"time"
"golang.org/x/crypto/ssh"
"golang.org/x/crypto/ssh/agent"
)
// SSHCommand is a struct that regroups all informations about an ssh command
type SSHCommand struct {
Path string
Env []string
Stdin io.Reader
Stdout io.Writer
Stderr io.Writer
}
// SSHClient struct contains client informations.
type SSHClient struct {
Config *ssh.ClientConfig
Host string
Port int
}
// RunCommand runs a command in a remote host
func (client *SSHClient) RunCommand(cmd *SSHCommand) error {
var (
session *ssh.Session
err error
)
if session, err = client.newSession(); err != nil {
return err
}
defer session.Close()
if err = client.prepareCommand(session, cmd); err != nil {
return err
}
// Must sleep here.
time.Sleep(5)
err = session.Run(cmd.Path)
return err
}
// prepareCommand Prepares the command to be run
func (client *SSHClient) prepareCommand(session *ssh.Session, cmd *SSHCommand) error {
for _, env := range cmd.Env {
variable := strings.Split(env, "=")
if len(variable) != 2 {
continue
}
if err := session.Setenv(variable[0], variable[1]); err != nil {
return err
}
}
if cmd.Stdin != nil {
stdin, err := session.StdinPipe()
if err != nil {
return fmt.Errorf("Unable to setup stdin for session: %v", err)
}
go io.Copy(stdin, cmd.Stdin)
}
if cmd.Stdout != nil {
stdout, err := session.StdoutPipe()
if err != nil {
return fmt.Errorf("Unable to setup stdout for session: %v", err)
}
go io.Copy(cmd.Stdout, stdout)
}
if cmd.Stderr != nil {
stderr, err := session.StderrPipe()
if err != nil {
return fmt.Errorf("Unable to setup stderr for session: %v", err)
}
go io.Copy(cmd.Stderr, stderr)
}
return nil
}
func (client *SSHClient) newSession() (*ssh.Session, error) {
connection, err := ssh.Dial("tcp", fmt.Sprintf("%s:%d", client.Host, client.Port), client.Config)
if err != nil {
return nil, fmt.Errorf("Failed to dial: %s", err)
}
session, err := connection.NewSession()
if err != nil {
return nil, fmt.Errorf("Failed to create session: %s", err)
}
modes := ssh.TerminalModes{
// ssh.ECHO: 0, // disable echoing
ssh.TTY_OP_ISPEED: 14400, // input speed = 14.4kbaud
ssh.TTY_OP_OSPEED: 14400, // output speed = 14.4kbaud
}
// Before we will be able to run the command on the remote machine,
// we should create a pseudo terminal on the remote machine.
// A pseudoterminal (or “pty”) is a pair of virtual character devices
// that provide a bidirectional communication channel.
if err := session.RequestPty("xterm", 800, 400, modes); err != nil {
session.Close()
return nil, fmt.Errorf("request for pseudo terminal failed: %s", err)
}
return session, nil
}
// PublicKeyFile is function that return an ssh.AuthMethod for ssh package.
func PublicKeyFile(file string) ssh.AuthMethod {
buffer, err := ioutil.ReadFile(file)
if err != nil {
return nil
}
key, err := ssh.ParsePrivateKey(buffer)
if err != nil {
return nil
}
return ssh.PublicKeys(key)
}
/*
SSH Agent is a program that runs during user session in *nix system.
It stores the private keys in an encrypted form.
Because typing the passphrase can be tedious,
many users would prefer to using it to store their private keys.
*/
// SSHAgent is a function that return AuthMethod object for ssh package
func SSHAgent() ssh.AuthMethod {
if sshAgent, err := net.Dial("unix", os.Getenv("SSH_AUTH_SOCK")); err == nil {
return ssh.PublicKeysCallback(agent.NewClient(sshAgent).Signers)
}
return nil
}
func main() {
sshConfig := &ssh.ClientConfig{
User: "iskab",
HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
return nil
},
Auth: []ssh.AuthMethod{
SSHAgent(),
},
}
client := &SSHClient{
Config: sshConfig,
Host: "example.com",
Port: 22,
}
cmd := &SSHCommand{
Path: "/bin/bash",
Env: []string{"LC_DIR=/"},
Stdin: os.Stdin,
Stdout: os.Stdout,
Stderr: os.Stderr,
}
fmt.Printf("Running command: %s\n", cmd.Path)
if err := client.RunCommand(cmd); err != nil {
fmt.Fprintf(os.Stderr, "command run error: %s\n", err)
os.Exit(1)
}
}