This repository has been archived by the owner on Oct 2, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1
/
handler.go
344 lines (292 loc) · 14.4 KB
/
handler.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
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
package sshserver
import (
"context"
"fmt"
"io"
"net"
"github.com/containerssh/log"
"golang.org/x/crypto/ssh"
)
// Handler is the basic conformanceTestHandler for SSH connections. It contains several methods to handle startup and operations of the
// server
type Handler interface {
// OnReady is called when the server is ready to receive connections. It has an opportunity to return an error to
// abort the startup.
OnReady() error
// OnShutdown is called when a shutdown of the SSH server is desired. The shutdownContext is passed as a deadline
// for the shutdown, after which the server should abort all running connections and return as fast as
// possible.
OnShutdown(shutdownContext context.Context)
// OnNetworkConnection is called when a new network connection is opened. It must either return a
// NetworkConnectionHandler object or an error. In case of an error the network connection is closed.
//
// The ip parameter provides the IP address of the connecting user. The connectionID parameter provides an opaque
// binary identifier for the connection that can be used to track the connection across multiple subsystems.
OnNetworkConnection(client net.TCPAddr, connectionID string) (NetworkConnectionHandler, error)
}
// AuthResponse is the result of the authentication process.
type AuthResponse uint8
const (
// AuthResponseSuccess indicates that the authentication was successful.
AuthResponseSuccess AuthResponse = 1
// AuthResponseFailure indicates that the authentication failed for invalid credentials.
AuthResponseFailure AuthResponse = 2
// AuthResponseUnavailable indicates that the authentication could not be performed because a backend system failed
// to respond.
AuthResponseUnavailable AuthResponse = 3
)
// KeyboardInteractiveQuestion contains a question issued to a user as part of the keyboard-interactive exchange.
type KeyboardInteractiveQuestion struct {
// ID is an optional opaque ID that can be used to identify a question in an answer. Can be left empty.
ID string
// Question is the question text sent to the user.
Question string
// EchoResponse should be set to true to show the typed response to the user.
EchoResponse bool
}
func (k *KeyboardInteractiveQuestion) getID() string {
if k.ID != "" {
return k.ID
}
return k.Question
}
// KeyboardInteractiveQuestions is a list of questions for keyboard-interactive authentication
type KeyboardInteractiveQuestions []KeyboardInteractiveQuestion
func (k *KeyboardInteractiveQuestions) Add(question KeyboardInteractiveQuestion) {
*k = append(*k, question)
}
// KeyboardInteractiveAnswers is a set of answer to a keyboard-interactive challenge.
type KeyboardInteractiveAnswers struct {
// KeyboardInteractiveQuestion is the original question that was answered.
answers map[string]string
}
// Get returns the answer for a question, or an error if no answer is present.
func (k *KeyboardInteractiveAnswers) Get(question KeyboardInteractiveQuestion) (string, error) {
if val, ok := k.answers[question.getID()]; ok {
return val, nil
}
return "", fmt.Errorf("no answer for question")
}
// GetByQuestionText returns the answer for a question text, or an error if no answer is present.
func (k *KeyboardInteractiveAnswers) GetByQuestionText(question string) (string, error) {
if val, ok := k.answers[question]; ok {
return val, nil
}
return "", fmt.Errorf("no answer for question")
}
// NetworkConnectionHandler is an object that is used to represent the underlying network connection and the SSH
// handshake.
type NetworkConnectionHandler interface {
// OnAuthPassword is called when a user attempts a password authentication. The implementation must always supply
// AuthResponse and may supply error as a reason description.
OnAuthPassword(username string, password []byte, clientVersion string) (response AuthResponse, metadata map[string]string, reason error)
// OnAuthPassword is called when a user attempts a pubkey authentication. The implementation must always supply
// AuthResponse and may supply error as a reason description. The pubKey parameter is an SSH key in
// the form of "ssh-rsa KEY HERE".
OnAuthPubKey(username string, pubKey string, clientVersion string) (response AuthResponse, metadata map[string]string, reason error)
// OnAuthKeyboardInteractive is a callback for interactive authentication. The implementer will be passed a callback
// function that can be used to issue challenges to the user. These challenges can, but do not have to contain
// questions.
OnAuthKeyboardInteractive(
user string,
challenge func(
instruction string,
questions KeyboardInteractiveQuestions,
) (answers KeyboardInteractiveAnswers, err error),
clientVersion string,
) (response AuthResponse, metadata map[string]string, reason error)
// OnHandshakeFailed is called when the SSH handshake failed. This method is also called after an authentication
// failure. After this method is the connection will be closed and the OnDisconnect method will be
// called.
OnHandshakeFailed(reason error)
// OnHandshakeSuccess is called when the SSH handshake was successful. It returns connection to process
// requests, or failureReason to indicate that a backend error has happened. In this case, the
// connection will be closed and OnDisconnect will be called.
OnHandshakeSuccess(username string, clientVersion string, metadata map[string]string) (connection SSHConnectionHandler, failureReason error)
// OnDisconnect is called when the network connection is closed.
OnDisconnect()
// OnShutdown is called when a shutdown of the SSH server is desired. The shutdownContext is passed as a deadline
// for the shutdown, after which the server should abort all running connections and return as fast as
// possible.
OnShutdown(shutdownContext context.Context)
}
// ChannelRejection is an error type that also contains a Message and a Reason
type ChannelRejection interface {
log.Message
// Reason contains the SSH-specific reason for the rejection.
Reason() ssh.RejectionReason
}
// SessionChannel contains a set of calls to manipulate the session channel.
type SessionChannel interface {
// Stdin returns the reader for the standard input.
Stdin() io.Reader
// Stdout returns the writer for the standard output.
Stdout() io.Writer
// Stderr returns the writer for the standard error.
Stderr() io.Writer
// ExitStatus sends the program exit status to the client.
ExitStatus(code uint32)
// ExitSignal sends a message to the client indicating that the program exited violently.
ExitSignal(signal string, coreDumped bool, errorMessage string, languageTag string)
// CloseWrite sends an EOF to the client indicating that no more data will be sent on stdout or stderr.
CloseWrite() error
// Close closes the channel for reading and writing.
Close() error
}
// SSHConnectionHandler represents an established SSH connection that is ready to receive requests.
type SSHConnectionHandler interface {
// OnUnsupportedGlobalRequest captures all global SSH requests and gives the implementation an opportunity to log
// the request.
//
// requestID is an ID uniquely identifying the request within the scope connection. The same ID may appear within
// a channel.
OnUnsupportedGlobalRequest(requestID uint64, requestType string, payload []byte)
// OnUnsupportedChannel is called when a new channel is requested of an unsupported type. This gives the implementer
// the ability to log unsupported channel requests.
//
// channelID is an ID uniquely identifying the channel within the connection.
// channelType is the type of channel requested by the client. We only support the "session" channel type
// extraData contains the binary extra data submitted by the client. This is usually empty.
OnUnsupportedChannel(channelID uint64, channelType string, extraData []byte)
// OnSessionChannel is called when a channel of the session type is requested. The implementer must either return
// the channel result if the channel was successful, or failureReason to state why the channel
// should be rejected.
//
// channelID is an ID uniquely identifying the channel within the connection.
// extraData contains the binary extra data submitted by the client. This is usually empty.
// session contains a set of calls that can be used to manipulate the SSH session.
OnSessionChannel(
channelID uint64,
extraData []byte,
session SessionChannel,
) (channel SessionChannelHandler, failureReason ChannelRejection)
// OnShutdown is called when a shutdown of the SSH server is desired. The shutdownContext is passed as a deadline
// for the shutdown, after which the server should abort all running connections and return as fast as
// possible.
OnShutdown(shutdownContext context.Context)
}
// ExitStatus contains the status code with which the program exited.
// See RFC 4254 section 6.10: Returning Exit Status for details. ( https://tools.ietf.org/html/rfc4254#section-6.10 )
type ExitStatus uint32
// SessionChannelHandler is a channel of the "session" type used for interactive and non-interactive sessions
type SessionChannelHandler interface {
//region Channel request initialization
// OnUnsupportedChannelRequest captures channel requests of unsupported types.
//
// requestID is an incrementing number uniquely identifying this request within the channel.
// requestType contains the SSH request type.
// payload is the binary payload.
OnUnsupportedChannelRequest(
requestID uint64,
requestType string,
payload []byte,
)
// OnFailedDecodeChannelRequest is called when a supported channel request was received, but the payload could not
// be decoded.
//
// requestID is an incrementing number uniquely identifying this request within the channel.
// requestType contains the SSH request type.
// payload is the binary payload.
// reason is the reason why the decoding failed.
OnFailedDecodeChannelRequest(
requestID uint64,
requestType string,
payload []byte,
reason error,
)
//endregion
//region Requests before program execution
// OnEnvRequest is called when the client requests an environment variable to be set. The implementation can return
// an error to reject the request.
OnEnvRequest(
requestID uint64,
name string,
value string,
) error
// OnPtyRequest is called when the client requests an interactive terminal to be allocated. The implementation can
// return an error to reject the request.
//
// requestID is an incrementing number uniquely identifying this request within the channel.
// Term is the terminal Name. This is usually set in the TERM environment variable.
// Columns is the number of Columns in the terminal.
// Rows is the number of Rows in the terminal.
// Width is the Width of the terminal in pixels.
// Height is the Height of a terminal in pixels.
// ModeList are the encoded terminal modes the client desires. See RFC4254 section 8 and RFC8160 for details.
OnPtyRequest(
requestID uint64,
term string,
columns uint32,
rows uint32,
width uint32,
height uint32,
modeList []byte,
) error
//endregion
//region Program execution
// OnExecRequest is called when the client request a program to be executed. The implementation can return an error
// to reject the request. This method MUST NOT block beyond initializing the program.
//
// requestID is an incrementing number uniquely identifying this request within the channel.
// program is the Name of the program to be executed.
OnExecRequest(
requestID uint64,
program string,
) error
// OnShell is called when the client requests a shell to be started. The implementation can return an error to
// reject the request. The implementation should send the IO handling into background. It should also
// respect the shutdown context on the Handler. This method MUST NOT block beyond initializing the shell.
//
// requestID is an incrementing number uniquely identifying this request within the channel.
// stdin is a reader for the shell or program to read the stdin.
// stdout is a writer for the shell or program standard output.
// stderr is a writer for the shell or program standard error.
// writeClose closes the stdout and stderr for writing.
// onExit is a callback to send the exit status back to the client.
OnShell(
requestID uint64,
) error
// OnSubsystem is called when the client calls a well-known Subsystem (e.g. sftp). The implementation can return an
// error to reject the request. The implementation should send the IO handling into background. It
// should also respect the shutdown context on the Handler. This method MUST NOT block beyond
// initializing the subsystem.
//
// requestID is an incrementing number uniquely identifying this request within the channel.
// subsystem is the name of the subsystem to be launched (e.g. sftp)
OnSubsystem(
requestID uint64,
subsystem string,
) error
//endregion
//region Requests during program execution
// OnSignal is called when the client requests a Signal to be sent to the running process. The implementation can
// return an error to reject the request.
OnSignal(
requestID uint64,
signal string,
) error
// OnWindow is called when the client requests requests the window size to be changed. This method may be called
// after a program is started. The implementation can return an error to reject the request.
//
// requestID is an incrementing number uniquely identifying this request within the channel.
// Columns is the number of Columns in the terminal.
// Rows is the number of Rows in the terminal.
// Width is the Width of the terminal in pixels.
// Height is the Height of a terminal in pixels.
OnWindow(
requestID uint64,
columns uint32,
rows uint32,
width uint32,
height uint32,
) error
//endregion
//region closing the channel
// OnClose is called when the channel is closed.
OnClose()
// OnShutdown is called when a shutdown of the SSH server is desired. The shutdownContext is passed as a deadline
// for the shutdown, after which the server should abort all running connections and return as fast as
// possible.
OnShutdown(shutdownContext context.Context)
//endregion
}