-
Notifications
You must be signed in to change notification settings - Fork 21
Expand file tree
/
Copy pathssh_test_server.go
More file actions
149 lines (122 loc) · 3.16 KB
/
Copy pathssh_test_server.go
File metadata and controls
149 lines (122 loc) · 3.16 KB
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
package proxy
import (
"bufio"
"fmt"
"log"
"net"
"sync"
"time"
"golang.org/x/crypto/ssh"
)
const jb = "jumpbox"
type checker struct {
expired bool
timeout time.Duration
lastMessageTime time.Time
sync.RWMutex
}
func (c *checker) check(reqs <-chan *ssh.Request) {
for {
select {
case req, ok := <-reqs:
if !ok {
continue
}
if req.Type == "bosh-cli-keep-alive@bosh.io" {
c.lastMessageTime = time.Now()
}
if req.WantReply {
req.Reply(false, nil) //nolint:errcheck
}
default:
if c.lastMessageTime.Add(c.timeout).Before(time.Now()) {
c.Lock()
c.expired = true
c.Unlock()
}
}
}
}
func StartTestSSHServer(httpServerURL, sshPrivateKey, userName string) string {
if userName == "" {
userName = jb
}
signer, err := ssh.ParsePrivateKey([]byte(sshPrivateKey))
if err != nil {
log.Fatal("Failed to parse private key: ", err)
}
config := &ssh.ServerConfig{
PublicKeyCallback: func(c ssh.ConnMetadata, pubKey ssh.PublicKey) (*ssh.Permissions, error) {
if c.User() != userName {
return nil, fmt.Errorf("unknown user: %q", c.User())
}
if string(signer.PublicKey().Marshal()) == string(pubKey.Marshal()) {
return nil, nil
}
return nil, fmt.Errorf("unknown public key for %q", c.User())
},
}
config.AddHostKey(signer)
listener, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
log.Fatal("failed to listen for connection: ", err)
}
c := &checker{
lastMessageTime: time.Now(),
timeout: 5000 * time.Millisecond,
}
go func() {
for {
nConn, err := listener.Accept()
if err != nil {
log.Fatal("failed to accept incoming connection: ", err)
}
_, chans, reqs, err := ssh.NewServerConn(nConn, config)
if err != nil {
log.Println("failed to handshake: ", err)
continue
}
go c.check(reqs)
for newChannel := range chans {
if newChannel.ChannelType() != "direct-tcpip" {
newChannel.Reject(ssh.UnknownChannelType, "unknown channel type") //nolint:errcheck
continue
}
c.RLock()
expired := c.expired
c.RUnlock()
if expired {
newChannel.Reject(ssh.ConnectionFailed, "no keepalive sent, you died") //nolint:errcheck
continue
}
channel, _, err := newChannel.Accept()
if err != nil {
log.Fatalf("Could not accept channel: %v", err)
}
defer channel.Close() //nolint:errcheck,staticcheck
data, err := bufio.NewReader(channel).ReadString('\n')
if err != nil {
log.Fatalf("Can't read data from channel: %v", err)
}
httpConn, err := net.Dial("tcp", httpServerURL)
if err != nil {
log.Fatalf("Could not open connection to http server: %v", err)
}
defer httpConn.Close() //nolint:errcheck,staticcheck
_, err = httpConn.Write([]byte(data + "\r\n\r\n"))
if err != nil {
log.Fatalf("Could not write to http server: %v", err)
}
data, err = bufio.NewReader(httpConn).ReadString('\n')
if err != nil {
log.Fatalf("Can't read data from http conn: %v", err)
}
_, err = channel.Write([]byte(data))
if err != nil {
log.Fatalf("Can't write data to channel: %v", err)
}
}
}
}()
return listener.Addr().String()
}