/
04.4-03-sshclient-login-key.go
executable file
·144 lines (122 loc) · 3.47 KB
/
04.4-03-sshclient-login-key.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
// Interactive SSH login with SSH key.
package main
import (
"flag"
"fmt"
"io"
"io/ioutil"
"net"
"os"
// Importing crypto/ssh
"golang.org/x/crypto/ssh"
)
var (
username, serverIP, serverPort, pKeyFile string
)
// Read flags
func init() {
flag.StringVar(&serverPort, "port", "22", "SSH server port")
flag.StringVar(&serverIP, "ip", "127.0.0.1", "SSH server IP")
flag.StringVar(&username, "user", "", "username")
flag.StringVar(&pKeyFile, "pkey", "", "unencrypted private key file")
}
func main() {
// Parse flags
flag.Parse()
// Check if username and pKey have been submitted
if username == "" {
fmt.Println("Must supply username")
os.Exit(2)
}
if pKeyFile == "" {
fmt.Println("Must supply private key")
os.Exit(2)
}
// Now we must read the private key
pKey, err := ioutil.ReadFile(pKeyFile)
if err != nil {
fmt.Println("Failed to read private key from file", err)
os.Exit(2)
}
// Create a signer with the private key
signer, err := ssh.ParsePrivateKey(pKey)
if err != nil {
fmt.Println("Failed to parse private key", err)
os.Exit(2)
}
// Create SSH config
config := &ssh.ClientConfig{
// Username
User: username,
// Each config must have one AuthMethod. Now we use key
Auth: []ssh.AuthMethod{
ssh.PublicKeys(signer),
},
// This callback function validates the server.
// Danger! We are ignoring host info
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
}
// Server address
t := net.JoinHostPort(serverIP, serverPort)
// Connect to the SSH server
sshConn, err := ssh.Dial("tcp", t, config)
if err != nil {
fmt.Printf("Failed to connect to %v\n", t)
fmt.Println(err)
os.Exit(2)
}
// Create new SSH session
session, err := sshConn.NewSession()
if err != nil {
fmt.Printf("Cannot create SSH session to %v\n", t)
fmt.Println(err)
os.Exit(2)
}
// Close the session when main returns
defer session.Close()
// For an interactive session we must redirect IO
session.Stdout = os.Stdout
session.Stderr = os.Stderr
input, err := session.StdinPipe()
if err != nil {
fmt.Println("Error redirecting session input", err)
os.Exit(2)
}
// Setup terminal mode when requesting pty. You can see all terminal modes at
// https://github.com/golang/crypto/blob/master/ssh/session.go#L56 or read
// the RFC for explanation https://tools.ietf.org/html/rfc4254#section-8
termModes := ssh.TerminalModes{
ssh.ECHO: 0, // Disable echo
}
// Request pty
// https://tools.ietf.org/html/rfc4254#section-6.2
// First variable is term environment variable value which specifies terminal.
// term doesn't really matter here, we will use "vt220".
// Next are height and width: (40,80) characters and finall termModes.
err = session.RequestPty("vt220", 40, 80, termModes)
if err != nil {
fmt.Println("RequestPty failed", err)
os.Exit(2)
}
// Also
// if err = session.RequestPty("vt220", 40, 80, termModes); err != nil {
// fmt.Println("RequestPty failed", err)
// os.Exit(2)
// }
// Now we can start a remote shell
err = session.Shell()
if err != nil {
fmt.Println("shell failed", err)
os.Exit(2)
}
// Same as above, a different way to check for errors
// if err = session.Shell(); err != nil {
// fmt.Println("shell failed", err)
// os.Exit(2)
// }
// Endless loop to capture commands
// Note: After exit, we need to ctrl+c to end the application.
for {
io.Copy(input, os.Stdin)
}
}