hrr_rb_ssh 0.1.6 → 0.1.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +296 -1
- data/demo/echo_server.rb +6 -10
- data/demo/server.rb +0 -1
- data/demo/subsystem_echo_server.rb +72 -0
- data/lib/hrr_rb_ssh/authentication/method/none/context.rb +2 -2
- data/lib/hrr_rb_ssh/authentication/method/none.rb +2 -2
- data/lib/hrr_rb_ssh/authentication/method/password/context.rb +2 -2
- data/lib/hrr_rb_ssh/authentication/method/password.rb +2 -2
- data/lib/hrr_rb_ssh/authentication/method/publickey.rb +3 -3
- data/lib/hrr_rb_ssh/authentication.rb +3 -3
- data/lib/hrr_rb_ssh/codable.rb +3 -3
- data/lib/hrr_rb_ssh/connection/channel/channel_type/direct_tcpip.rb +13 -13
- data/lib/hrr_rb_ssh/connection/channel/channel_type/forwarded_tcpip.rb +13 -13
- data/lib/hrr_rb_ssh/connection/channel/channel_type/session.rb +6 -6
- data/lib/hrr_rb_ssh/connection/channel.rb +31 -31
- data/lib/hrr_rb_ssh/connection/global_request_handler.rb +9 -9
- data/lib/hrr_rb_ssh/connection/request_handler/reference_pty_req_request_handler.rb +1 -0
- data/lib/hrr_rb_ssh/connection/request_handler/reference_shell_request_handler.rb +12 -12
- data/lib/hrr_rb_ssh/connection.rb +18 -18
- data/lib/hrr_rb_ssh/logger.rb +25 -23
- data/lib/hrr_rb_ssh/transport/receiver.rb +3 -3
- data/lib/hrr_rb_ssh/transport.rb +19 -19
- data/lib/hrr_rb_ssh/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 10374bce9a4138bfa640400ced86d6579e368db9caa58db8303676620a8cde4b
|
4
|
+
data.tar.gz: 0f42005a94c125d8a403ab07e011bc3398f92604a497091855d5fd4cecc8256d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c9bef9312be2875e17869c401fad888aaadd16936d90c069fd0a61313225b1f12b4ca7f0ea6307adff77520d63797a11de45e0569e48c3df987a1fa503138c7b
|
7
|
+
data.tar.gz: 1dd06737f9600c521fecfc158150cb766038b7afe160e16e9b6bc2e57ec1dd277fbbf659a4faa8990dbb292df2f49ea5e76b8714449ba28cf8f10dad346b6d69
|
data/README.md
CHANGED
@@ -29,11 +29,306 @@ $ gem install hrr_rb_ssh
|
|
29
29
|
|
30
30
|
## Usage
|
31
31
|
|
32
|
+
### Writing standard SSH server
|
33
|
+
|
34
|
+
#### Requiring `hrr_rb_ssh` library
|
35
|
+
|
36
|
+
First of all, `hrr_rb_ssh` library needs to be loaded.
|
37
|
+
|
38
|
+
```ruby
|
39
|
+
require 'hrr_rb_ssh'
|
40
|
+
```
|
41
|
+
|
42
|
+
#### Starting server application
|
43
|
+
|
44
|
+
The library is to run on a socket IO. To start SSH server, running a server IO and accepting a connection are required. The 10022 port number is just an example.
|
45
|
+
|
46
|
+
```ruby
|
47
|
+
options = Hash.new
|
48
|
+
server = TCPServer.new 10022
|
49
|
+
while true
|
50
|
+
t = Thread.new(server.accept) do |io|
|
51
|
+
tran = HrrRbSsh::Transport.new io, HrrRbSsh::Transport::Mode::SERVER, options
|
52
|
+
auth = HrrRbSsh::Authentication.new tran, options
|
53
|
+
conn = HrrRbSsh::Connection.new auth, options
|
54
|
+
conn.start
|
55
|
+
io.close
|
56
|
+
end
|
57
|
+
end
|
58
|
+
```
|
59
|
+
|
60
|
+
Where, an `options` variable is an instance of `Hash`, which has optional (or sometimes almost necessary) values.
|
61
|
+
|
62
|
+
#### Logging
|
63
|
+
|
64
|
+
__IMPORTANT__: DEBUG log level outputs all communications between local and remote in human-readable plain-text including password and any secret. Be careful to use logging.
|
65
|
+
|
66
|
+
The library provides logging functionality. To enable logging of the library, you are to initialize `HrrRbSsh::Logger` class.
|
67
|
+
|
68
|
+
```ruby
|
69
|
+
HrrRbSsh::Logger.initialize logger
|
70
|
+
```
|
71
|
+
|
72
|
+
Where, the `logger` variable can be an instance of standard Logger class or user-defined logger class. What `HrrRbSsh::Logger` class requires for `logger` variable is that the `logger` instance responds to `#fatal`, `#error`, `#warn`, `#info` and `#debug`.
|
73
|
+
|
74
|
+
For instance, `logger` variable can be prepared like below.
|
75
|
+
|
76
|
+
```ruby
|
77
|
+
logger = Logger.new STDOUT
|
78
|
+
logger.level = Logger::INFO
|
79
|
+
```
|
80
|
+
|
81
|
+
To disable logging, you can un-initialize `HrrRbSsh::Logger`.
|
82
|
+
|
83
|
+
```ruby
|
84
|
+
HrrRbSsh::Logger.uninitialize
|
85
|
+
```
|
86
|
+
|
87
|
+
#### Defining authentications
|
88
|
+
|
89
|
+
By default, any authentications get failed. To allow users to login to the SSH service, at least one of the authentication methods must be defined and registered into the instance of HrrRbSsh::Authentication through `options` variable.
|
90
|
+
|
91
|
+
##### Password authentication
|
92
|
+
|
93
|
+
Password authentication is the most simple way to allow users to login to the SSH service. Password authentication requires user-name and password.
|
94
|
+
|
95
|
+
To define a password authentication, the `HrrRbSsh::Authentication::Authenticator.new { |context| ... }` block is used. When the block returns `true`, then the authentication succeeded.
|
96
|
+
|
97
|
+
```ruby
|
98
|
+
auth_password = HrrRbSsh::Authentication::Authenticator.new { |context|
|
99
|
+
user_and_pass = [
|
100
|
+
['user1', 'password1'],
|
101
|
+
['user2', 'password2'],
|
102
|
+
]
|
103
|
+
user_and_pass.any? { |user, pass|
|
104
|
+
context.verify user, pass
|
105
|
+
}
|
106
|
+
}
|
107
|
+
options['authentication_password_authenticator'] = auth_password
|
108
|
+
```
|
109
|
+
|
110
|
+
The `context` variable in password authentication context provides the followings.
|
111
|
+
|
112
|
+
- `#username` : The username that a remote user tries to authenticate
|
113
|
+
- `#password` : The password that a remote user tries to authenticate
|
114
|
+
- `#verify(username, password)` : Returns `true` when username and password arguments match with the context's username and password. Or returns `false` when username and password arguments don't match.
|
115
|
+
|
116
|
+
##### Publickey authentication
|
117
|
+
|
118
|
+
The second one is public key authentication. Public key authentication requires user-name, public key algorithm name, and PEM or DER formed public key.
|
119
|
+
|
120
|
+
To define a public key authentication, the `HrrRbSsh::Authentication::Authenticator.new { |context| ... }` block is used as well. When the block returns `true`, then the authentication succeeded as well. However, `context` variable behaves differently.
|
121
|
+
|
122
|
+
```ruby
|
123
|
+
auth_publickey = HrrRbSsh::Authentication::Authenticator.new { |context|
|
124
|
+
username = 'user1'
|
125
|
+
ecdsa_sha2_nistp256_public_key_algorithm_name = 'ecdsa-sha2-nistp256'
|
126
|
+
ecdsa_sha2_nistp256_public_key = <<-'EOB'
|
127
|
+
-----BEGIN PUBLIC KEY-----
|
128
|
+
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE9DPmu6CIA5VCBaN9wpUP2UUZQ+dw
|
129
|
+
77mTZ7lD+z5cjzF7OL/cPL1/zklAsYaH7z7OcPYRbe24QCG5YfJQZjevJQ==
|
130
|
+
-----END PUBLIC KEY-----
|
131
|
+
EOB
|
132
|
+
[
|
133
|
+
[username, ecdsa_sha2_nistp256_public_key_algorithm_name, ecdsa_sha2_nistp256_public_key],
|
134
|
+
].any? { |username, public_key_algorithm_name, public_key|
|
135
|
+
context.verify username, public_key_algorithm_name, public_key
|
136
|
+
}
|
137
|
+
}
|
138
|
+
options['authentication_publickey_authenticator'] = auth_publickey
|
139
|
+
```
|
140
|
+
|
141
|
+
The `context` variable in public key authentication context provides the `#verify` method. The `#verify` method takes three arguments; username, public key algorithm name and PEM or DER formed public key.
|
142
|
+
|
143
|
+
##### None authentication (NOT recomended)
|
144
|
+
|
145
|
+
The third one is none authentication. None authentication is usually NOT used.
|
146
|
+
|
147
|
+
To define a none authentication, the `HrrRbSsh::Authentication::Authenticator.new { |context| ... }` block is used as well. When the block returns `true`, then the authentication succeeded as well. However, `context` variable behaves differently.
|
148
|
+
|
149
|
+
```ruby
|
150
|
+
auth_none = HrrRbSsh::Authentication::Authenticator.new { |context|
|
151
|
+
if context.username == 'user1'
|
152
|
+
true
|
153
|
+
else
|
154
|
+
false
|
155
|
+
end
|
156
|
+
}
|
157
|
+
options['authentication_none_authenticator'] = auth_none
|
158
|
+
```
|
159
|
+
|
160
|
+
In none authentication context, `context` variable provides the `#username` method.
|
161
|
+
|
162
|
+
#### Handling session channel requests
|
163
|
+
|
164
|
+
By default, any channel requests belonging to session channel are implicitly ignored. To handle the requests, defining request handlers are required.
|
165
|
+
|
166
|
+
##### Reference request handlers
|
167
|
+
|
168
|
+
There are pre-implemented request handlers available for reference as below.
|
169
|
+
|
170
|
+
```ruby
|
171
|
+
options['connection_channel_request_pty_req'] = HrrRbSsh::Connection::RequestHandler::ReferencePtyReqRequestHandler.new
|
172
|
+
options['connection_channel_request_env'] = HrrRbSsh::Connection::RequestHandler::ReferenceEnvRequestHandler.new
|
173
|
+
options['connection_channel_request_shell'] = HrrRbSsh::Connection::RequestHandler::ReferenceShellRequestHandler.new
|
174
|
+
options['connection_channel_request_exec'] = HrrRbSsh::Connection::RequestHandler::ReferenceExecRequestHandler.new
|
175
|
+
options['connection_channel_request_window_change'] = HrrRbSsh::Connection::RequestHandler::ReferenceWindowChangeRequestHandler.new
|
176
|
+
```
|
177
|
+
|
178
|
+
##### Custom request handlers
|
179
|
+
|
180
|
+
It is also possible to define customized request handlers. For instance, echo server can be implemented very easily as below. In this case, echo server works instead of shell and PTY-req and env requests are undefined.
|
181
|
+
|
182
|
+
```ruby
|
183
|
+
conn_echo = HrrRbSsh::Connection::RequestHandler.new { |context|
|
184
|
+
context.io[2].close
|
185
|
+
context.chain_proc { |chain|
|
186
|
+
begin
|
187
|
+
loop do
|
188
|
+
buf = context.io[0].readpartial(10240)
|
189
|
+
break if buf.include?(0x04.chr) # break if ^D
|
190
|
+
context.io[1].write buf
|
191
|
+
end
|
192
|
+
exitstatus = 0
|
193
|
+
rescue => e
|
194
|
+
logger.error([e.backtrace[0], ": ", e.message, " (", e.class.to_s, ")\n\t", e.backtrace[1..-1].join("\n\t")].join)
|
195
|
+
exitstatus = 1
|
196
|
+
ensure
|
197
|
+
context.io[1].close
|
198
|
+
end
|
199
|
+
exitstatus
|
200
|
+
}
|
201
|
+
}
|
202
|
+
options['connection_channel_request_shell'] = conn_echo
|
203
|
+
```
|
204
|
+
|
205
|
+
In `HrrRbSsh::Connection::RequestHandler.new` block, context variable basically provides the followings.
|
206
|
+
|
207
|
+
- `#io => [in, out, err]` : `in` is readable and read data is sent by remote. `out` and `err` are writable. `out` is for standard output and written data is sent as channel data. `err` is for standard error and written data is sent as channel extended data.
|
208
|
+
- `#chain_proc => {|chain| ... }` : When a session channel is opened, a background thread is started and is waitng for a chaned block registered. This `#chain_proc` is used to define how to handle subsequent communications between local and remote. The `chain` variable provides `#call_next` method. In `#proc_chain` block, it is possible to call subsequent block that is defined in another request handler. For instance, shell request must called after pty-req request. The `chain` in pty-req request handler's `#chain_proc` calls `#next_proc` and then subsequent shell request handler's `#chain_proc` will be called.
|
209
|
+
|
210
|
+
And request handler's `context` variable also provides additional methods based on request type. See `lib/hrr_rb_ssh/connection/channel/channel_type/session/request_type/<request type>/context.rb`.
|
211
|
+
|
212
|
+
#### Defining preferred algorithms (optional)
|
213
|
+
|
214
|
+
Preferred encryption, server-host-key, KEX and compression algorithms can be selected and defined.
|
215
|
+
|
216
|
+
```ruby
|
217
|
+
options['transport_preferred_encryption_algorithms'] = %w(aes256-ctr aes128-cbc)
|
218
|
+
options['transport_preferred_server_host_key_algorithms'] = %w(ecdsa-sha2-nistp256 ssh-rsa)
|
219
|
+
options['transport_preferred_kex_algorithms'] = %w(ecdh-sha2-nistp256 diffie-hellman-group14-sha1)
|
220
|
+
options['transport_preferred_mac_algorithms'] = %w(hmac-sha2-256 hmac-sha1)
|
221
|
+
options['transport_preferred_compression_algorithms'] = %w(none)
|
222
|
+
```
|
223
|
+
|
224
|
+
Supported algorithms can be got with each algorithm class's `#list_supported` method, and default preferred algorithms can be got with each algorithm class's `#list_preferred` method.
|
225
|
+
|
226
|
+
Outputs of `#list_preferred` method are ordered as preferred; i.e. the name listed at head is used as most preferred, and the name listed at tail is used as non-preferred.
|
227
|
+
|
228
|
+
```ruby
|
229
|
+
p HrrRbSsh::Transport::EncryptionAlgorithm.list_supported
|
230
|
+
# => ["none", "3des-cbc", "blowfish-cbc", "aes128-cbc", "aes192-cbc", "aes256-cbc", "arcfour", "cast128-cbc", "aes128-ctr", "aes192-ctr", "aes256-ctr"]
|
231
|
+
p HrrRbSsh::Transport::EncryptionAlgorithm.list_preferred
|
232
|
+
# => ["aes128-ctr", "aes192-ctr", "aes256-ctr", "aes128-cbc", "3des-cbc", "blowfish-cbc", "cast128-cbc", "aes192-cbc", "aes256-cbc", "arcfour"]
|
233
|
+
|
234
|
+
p HrrRbSsh::Transport::EncryptionAlgorithm.list_supported
|
235
|
+
# => ["none", "3des-cbc", "blowfish-cbc", "aes128-cbc", "aes192-cbc", "aes256-cbc", "arcfour", "cast128-cbc", "aes128-ctr", "aes192-ctr", "aes256-ctr"]
|
236
|
+
HrrRbSsh::Transport::ServerHostKeyAlgorithm.list_preferred
|
237
|
+
# => ["ecdsa-sha2-nistp521", "ecdsa-sha2-nistp384", "ecdsa-sha2-nistp256", "ssh-rsa", "ssh-dss"]
|
238
|
+
|
239
|
+
p HrrRbSsh::Transport::ServerHostKeyAlgorithm.list_supported
|
240
|
+
# => ["ssh-dss", "ssh-rsa", "ecdsa-sha2-nistp256", "ecdsa-sha2-nistp384", "ecdsa-sha2-nistp521"]
|
241
|
+
p HrrRbSsh::Transport::KexAlgorithm.list_preferred
|
242
|
+
# => ["ecdh-sha2-nistp521", "ecdh-sha2-nistp384", "ecdh-sha2-nistp256", "diffie-hellman-group18-sha512", "diffie-hellman-group17-sha512", "diffie-hellman-group16-sha512", "diffie-hellman-group15-sha512", "diffie-hellman-group14-sha256", "diffie-hellman-group-exchange-sha256", "diffie-hellman-group-exchange-sha1", "diffie-hellman-group14-sha1", "diffie-hellman-group1-sha1"]
|
243
|
+
|
244
|
+
p HrrRbSsh::Transport::KexAlgorithm.list_supported
|
245
|
+
# => ["diffie-hellman-group1-sha1", "diffie-hellman-group14-sha1", "diffie-hellman-group-exchange-sha1", "diffie-hellman-group-exchange-sha256", "diffie-hellman-group14-sha256", "diffie-hellman-group15-sha512", "diffie-hellman-group16-sha512", "diffie-hellman-group17-sha512", "diffie-hellman-group18-sha512", "ecdh-sha2-nistp256", "ecdh-sha2-nistp384", "ecdh-sha2-nistp521"]
|
246
|
+
p HrrRbSsh::Transport::KexAlgorithm.list_preferred
|
247
|
+
# => ["ecdh-sha2-nistp521", "ecdh-sha2-nistp384", "ecdh-sha2-nistp256", "diffie-hellman-group18-sha512", "diffie-hellman-group17-sha512", "diffie-hellman-group16-sha512", "diffie-hellman-group15-sha512", "diffie-hellman-group14-sha256", "diffie-hellman-group-exchange-sha256", "diffie-hellman-group-exchange-sha1", "diffie-hellman-group14-sha1", "diffie-hellman-group1-sha1"]
|
248
|
+
|
249
|
+
p HrrRbSsh::Transport::MacAlgorithm.list_supported
|
250
|
+
# => ["none", "hmac-sha1", "hmac-sha1-96", "hmac-md5", "hmac-md5-96", "hmac-sha2-256", "hmac-sha2-512"]
|
251
|
+
p HrrRbSsh::Transport::MacAlgorithm.list_preferred
|
252
|
+
# => ["hmac-sha2-512", "hmac-sha2-256", "hmac-sha1", "hmac-md5", "hmac-sha1-96", "hmac-md5-96"]
|
253
|
+
|
254
|
+
p HrrRbSsh::Transport::CompressionAlgorithm.list_supported
|
255
|
+
# => ["none", "zlib"]
|
256
|
+
p HrrRbSsh::Transport::CompressionAlgorithm.list_preferred
|
257
|
+
# => ["none", "zlib"]
|
258
|
+
```
|
259
|
+
|
260
|
+
### Demo
|
261
|
+
|
32
262
|
The `demo/server.rb` shows a good example on how to use the hrr_rb_ssh library in SSH server mode.
|
33
263
|
|
34
264
|
## Supported Features
|
35
265
|
|
36
|
-
|
266
|
+
The following features are currently supported.
|
267
|
+
|
268
|
+
### Connection layer
|
269
|
+
|
270
|
+
- Session channel
|
271
|
+
- Shell (PTY-req, env, shell, window-change) request
|
272
|
+
- Exec request
|
273
|
+
- Subsystem request
|
274
|
+
- Local port forwarding (direct-tcpip channel)
|
275
|
+
- Remote port forwarding (tcpip-forward global request and forwarded-tcpip channel)
|
276
|
+
|
277
|
+
### Authentication layer
|
278
|
+
|
279
|
+
- None authentication
|
280
|
+
- Password authentication
|
281
|
+
- Public key authentication
|
282
|
+
- ssh-dss
|
283
|
+
- ssh-rsa
|
284
|
+
- ecdsa-sha2-nistp256
|
285
|
+
- ecdsa-sha2-nistp384
|
286
|
+
- ecdsa-sha2-nistp521
|
287
|
+
|
288
|
+
### Transport layer
|
289
|
+
|
290
|
+
- Encryption algorithm
|
291
|
+
- none
|
292
|
+
- 3des-cbc
|
293
|
+
- blowfish-cbc
|
294
|
+
- aes128-cbc
|
295
|
+
- aes192-cbc
|
296
|
+
- aes256-cbc
|
297
|
+
- arcfour
|
298
|
+
- cast128-cbc
|
299
|
+
- aes128-ctr
|
300
|
+
- aes192-ctr
|
301
|
+
- aes256-ctr
|
302
|
+
- Server host key algorithm
|
303
|
+
- ssh-dss
|
304
|
+
- ssh-rsa
|
305
|
+
- ecdsa-sha2-nistp256
|
306
|
+
- ecdsa-sha2-nistp384
|
307
|
+
- ecdsa-sha2-nistp521
|
308
|
+
- Kex algorithm
|
309
|
+
- diffie-hellman-group1-sha1
|
310
|
+
- diffie-hellman-group14-sha1
|
311
|
+
- diffie-hellman-group-exchange-sha1
|
312
|
+
- diffie-hellman-group-exchange-sha256
|
313
|
+
- diffie-hellman-group14-sha256
|
314
|
+
- diffie-hellman-group15-sha512
|
315
|
+
- diffie-hellman-group16-sha512
|
316
|
+
- diffie-hellman-group17-sha512
|
317
|
+
- diffie-hellman-group18-sha512
|
318
|
+
- ecdh-sha2-nistp256
|
319
|
+
- ecdh-sha2-nistp384
|
320
|
+
- ecdh-sha2-nistp521
|
321
|
+
- Mac algorithm
|
322
|
+
- none
|
323
|
+
- hmac-sha1
|
324
|
+
- hmac-sha1-96
|
325
|
+
- hmac-md5
|
326
|
+
- hmac-md5-96
|
327
|
+
- hmac-sha2-256
|
328
|
+
- hmac-sha2-512
|
329
|
+
- Compression algorithm
|
330
|
+
- none
|
331
|
+
- zlib
|
37
332
|
|
38
333
|
## Contributing
|
39
334
|
|
data/demo/echo_server.rb
CHANGED
@@ -2,7 +2,6 @@
|
|
2
2
|
# vim: et ts=2 sw=2
|
3
3
|
|
4
4
|
require 'logger'
|
5
|
-
require 'pty'
|
6
5
|
require 'socket'
|
7
6
|
|
8
7
|
begin
|
@@ -19,27 +18,24 @@ HrrRbSsh::Logger.initialize logger
|
|
19
18
|
|
20
19
|
|
21
20
|
auth_password = HrrRbSsh::Authentication::Authenticator.new { |context|
|
22
|
-
|
23
|
-
['user1', 'password1'],
|
24
|
-
['user2', 'password2'],
|
25
|
-
]
|
26
|
-
user_and_pass.any? { |user, pass|
|
27
|
-
context.verify user, pass
|
28
|
-
}
|
21
|
+
true # accept any user and password
|
29
22
|
}
|
30
23
|
|
31
24
|
conn_echo = HrrRbSsh::Connection::RequestHandler.new { |context|
|
25
|
+
context.io[2].close
|
32
26
|
context.chain_proc { |chain|
|
33
27
|
begin
|
34
28
|
loop do
|
35
|
-
buf = context.io.readpartial(10240)
|
29
|
+
buf = context.io[0].readpartial(10240)
|
36
30
|
break if buf.include?(0x04.chr) # break if ^D
|
37
|
-
context.io.write buf
|
31
|
+
context.io[1].write buf
|
38
32
|
end
|
39
33
|
exitstatus = 0
|
40
34
|
rescue => e
|
41
35
|
logger.error([e.backtrace[0], ": ", e.message, " (", e.class.to_s, ")\n\t", e.backtrace[1..-1].join("\n\t")].join)
|
42
36
|
exitstatus = 1
|
37
|
+
ensure
|
38
|
+
context.io[1].close
|
43
39
|
end
|
44
40
|
exitstatus
|
45
41
|
}
|
data/demo/server.rb
CHANGED
@@ -0,0 +1,72 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
# vim: et ts=2 sw=2
|
3
|
+
|
4
|
+
require 'logger'
|
5
|
+
require 'socket'
|
6
|
+
|
7
|
+
begin
|
8
|
+
require 'hrr_rb_ssh'
|
9
|
+
rescue LoadError
|
10
|
+
$:.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
11
|
+
require 'hrr_rb_ssh'
|
12
|
+
end
|
13
|
+
|
14
|
+
|
15
|
+
logger = Logger.new STDOUT
|
16
|
+
logger.level = Logger::INFO
|
17
|
+
HrrRbSsh::Logger.initialize logger
|
18
|
+
|
19
|
+
|
20
|
+
auth_password = HrrRbSsh::Authentication::Authenticator.new { |context|
|
21
|
+
true # accept any user and password
|
22
|
+
}
|
23
|
+
|
24
|
+
conn_echo = HrrRbSsh::Connection::RequestHandler.new { |context|
|
25
|
+
context.io[2].close
|
26
|
+
context.chain_proc { |chain|
|
27
|
+
case context.subsystem_name
|
28
|
+
when 'echo'
|
29
|
+
begin
|
30
|
+
loop do
|
31
|
+
begin
|
32
|
+
buf = context.io[0].readpartial(10240)
|
33
|
+
rescue EOFError
|
34
|
+
break
|
35
|
+
end
|
36
|
+
context.io[1].write buf
|
37
|
+
end
|
38
|
+
exitstatus = 0
|
39
|
+
rescue => e
|
40
|
+
logger.error([e.backtrace[0], ": ", e.message, " (", e.class.to_s, ")\n\t", e.backtrace[1..-1].join("\n\t")].join)
|
41
|
+
exitstatus = 1
|
42
|
+
ensure
|
43
|
+
context.io[1].close
|
44
|
+
end
|
45
|
+
else
|
46
|
+
context.io[1].close
|
47
|
+
exitstatus = 0
|
48
|
+
end
|
49
|
+
exitstatus
|
50
|
+
}
|
51
|
+
}
|
52
|
+
|
53
|
+
options = {}
|
54
|
+
options['authentication_password_authenticator'] = auth_password
|
55
|
+
options['connection_channel_request_subsystem'] = conn_echo
|
56
|
+
|
57
|
+
|
58
|
+
server = TCPServer.new 10022
|
59
|
+
while true
|
60
|
+
t = Thread.new(server.accept) do |io|
|
61
|
+
begin
|
62
|
+
tran = HrrRbSsh::Transport.new io, HrrRbSsh::Transport::Mode::SERVER
|
63
|
+
auth = HrrRbSsh::Authentication.new tran, options
|
64
|
+
conn = HrrRbSsh::Connection.new auth, options
|
65
|
+
conn.start
|
66
|
+
rescue => e
|
67
|
+
logger.error([e.backtrace[0], ": ", e.message, " (", e.class.to_s, ")\n\t", e.backtrace[1..-1].join("\n\t")].join)
|
68
|
+
ensure
|
69
|
+
io.close
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -17,8 +17,8 @@ module HrrRbSsh
|
|
17
17
|
end
|
18
18
|
|
19
19
|
def verify username
|
20
|
-
@logger.info
|
21
|
-
@logger.debug
|
20
|
+
@logger.info { "verify username" }
|
21
|
+
@logger.debug { "username is #{username}, @username is #{@username}" }
|
22
22
|
username == @username
|
23
23
|
end
|
24
24
|
end
|
@@ -16,8 +16,8 @@ module HrrRbSsh
|
|
16
16
|
end
|
17
17
|
|
18
18
|
def authenticate userauth_request_message
|
19
|
-
@logger.info
|
20
|
-
@logger.debug
|
19
|
+
@logger.info { "authenticate" }
|
20
|
+
@logger.debug { "userauth request: " + userauth_request_message.inspect }
|
21
21
|
context = Context.new(userauth_request_message[:'user name'])
|
22
22
|
@authenticator.authenticate context
|
23
23
|
end
|
@@ -18,8 +18,8 @@ module HrrRbSsh
|
|
18
18
|
end
|
19
19
|
|
20
20
|
def verify username, password
|
21
|
-
@logger.info
|
22
|
-
@logger.debug
|
21
|
+
@logger.info { "verify username and password" }
|
22
|
+
@logger.debug { "username is #{username}, @username is #{@username}, and password is #{password}, @password is #{@password}" }
|
23
23
|
username == @username and password == @password
|
24
24
|
end
|
25
25
|
end
|
@@ -16,8 +16,8 @@ module HrrRbSsh
|
|
16
16
|
end
|
17
17
|
|
18
18
|
def authenticate userauth_request_message
|
19
|
-
@logger.info
|
20
|
-
@logger.debug
|
19
|
+
@logger.info { "authenticate" }
|
20
|
+
@logger.debug { "userauth request: " + userauth_request_message.inspect }
|
21
21
|
username = userauth_request_message[:'user name']
|
22
22
|
password = userauth_request_message[:'plaintext password']
|
23
23
|
context = Context.new(username, password)
|
@@ -19,15 +19,15 @@ module HrrRbSsh
|
|
19
19
|
def authenticate userauth_request_message
|
20
20
|
public_key_algorithm_name = userauth_request_message[:'public key algorithm name']
|
21
21
|
unless Algorithm.list_preferred.include?(public_key_algorithm_name)
|
22
|
-
@logger.info
|
22
|
+
@logger.info { "unsupported public key algorithm: #{public_key_algorithm_name}" }
|
23
23
|
return false
|
24
24
|
end
|
25
25
|
unless userauth_request_message[:'with signature']
|
26
|
-
@logger.info
|
26
|
+
@logger.info { "public key algorithm is ok, require signature" }
|
27
27
|
public_key_blob = userauth_request_message[:'public key blob']
|
28
28
|
userauth_pk_ok_message public_key_algorithm_name, public_key_blob
|
29
29
|
else
|
30
|
-
@logger.info
|
30
|
+
@logger.info { "verify signature" }
|
31
31
|
username = userauth_request_message[:'user name']
|
32
32
|
algorithm = Algorithm[public_key_algorithm_name].new
|
33
33
|
context = Context.new(username, algorithm, @session_id, userauth_request_message)
|
@@ -73,16 +73,16 @@ module HrrRbSsh
|
|
73
73
|
result = method.authenticate(userauth_request_message)
|
74
74
|
case result
|
75
75
|
when TrueClass
|
76
|
-
@logger.info
|
76
|
+
@logger.info { "verified" }
|
77
77
|
send_userauth_success
|
78
78
|
@username = userauth_request_message[:'user name']
|
79
79
|
@closed = false
|
80
80
|
break
|
81
81
|
when FalseClass
|
82
|
-
@logger.info
|
82
|
+
@logger.info { "verify failed" }
|
83
83
|
send_userauth_failure
|
84
84
|
when String
|
85
|
-
@logger.info
|
85
|
+
@logger.info { "send method specific message to continue" }
|
86
86
|
send_method_specific_message result
|
87
87
|
end
|
88
88
|
else
|
data/lib/hrr_rb_ssh/codable.rb
CHANGED
@@ -22,14 +22,14 @@ module HrrRbSsh
|
|
22
22
|
end
|
23
23
|
|
24
24
|
def encode message, complementary_message={}
|
25
|
-
logger.debug
|
25
|
+
logger.debug { 'encoding message: ' + message.inspect }
|
26
26
|
definition = common_definition + conditional_definition(message.merge complementary_message)
|
27
27
|
definition.map{ |data_type, field_name|
|
28
28
|
begin
|
29
29
|
field_value = if message[field_name].instance_of? ::Proc then message[field_name].call else message[field_name] end
|
30
30
|
data_type.encode( field_value )
|
31
31
|
rescue => e
|
32
|
-
logger.debug
|
32
|
+
logger.debug { "'field_name', 'field_value': #{field_name.inspect}, #{field_value.inspect}" }
|
33
33
|
raise e
|
34
34
|
end
|
35
35
|
}.join
|
@@ -62,7 +62,7 @@ module HrrRbSsh
|
|
62
62
|
if complementary_message.any?
|
63
63
|
decoded_message.merge! decode_recursively(payload_io, complementary_message.to_a).to_h
|
64
64
|
end
|
65
|
-
logger.debug
|
65
|
+
logger.debug { 'decoded message: ' + decoded_message.inspect }
|
66
66
|
decoded_message
|
67
67
|
end
|
68
68
|
end
|
@@ -29,13 +29,13 @@ module HrrRbSsh
|
|
29
29
|
def close
|
30
30
|
begin
|
31
31
|
if @sender_thread_finished && @receiver_thread_finished
|
32
|
-
@logger.info
|
32
|
+
@logger.info { "closing direct-tcpip" }
|
33
33
|
@socket.close
|
34
34
|
@channel.close from=:channel_type_instance
|
35
|
-
@logger.info
|
35
|
+
@logger.info { "direct-tcpip closed" }
|
36
36
|
end
|
37
37
|
rescue => e
|
38
|
-
@logger.error
|
38
|
+
@logger.error { [e.backtrace[0], ": ", e.message, " (", e.class.to_s, ")\n\t", e.backtrace[1..-1].join("\n\t")].join }
|
39
39
|
end
|
40
40
|
end
|
41
41
|
|
@@ -46,24 +46,24 @@ module HrrRbSsh
|
|
46
46
|
begin
|
47
47
|
@channel.io[1].write s.readpartial(10240)
|
48
48
|
rescue EOFError
|
49
|
-
@logger.info
|
49
|
+
@logger.info { "socket is EOF" }
|
50
50
|
@channel.io[1].close
|
51
51
|
break
|
52
52
|
rescue IOError
|
53
|
-
@logger.info
|
53
|
+
@logger.info { "socket is closed" }
|
54
54
|
@channel.io[1].close
|
55
55
|
break
|
56
56
|
rescue => e
|
57
|
-
@logger.error
|
57
|
+
@logger.error { [e.backtrace[0], ": ", e.message, " (", e.class.to_s, ")\n\t", e.backtrace[1..-1].join("\n\t")].join }
|
58
58
|
@channel.io[1].close
|
59
59
|
break
|
60
60
|
end
|
61
61
|
end
|
62
|
-
@logger.info
|
62
|
+
@logger.info { "finishing sender thread" }
|
63
63
|
@sender_thread_finished = true
|
64
64
|
close
|
65
65
|
ensure
|
66
|
-
@logger.info
|
66
|
+
@logger.info { "sender thread finished" }
|
67
67
|
end
|
68
68
|
}
|
69
69
|
end
|
@@ -75,23 +75,23 @@ module HrrRbSsh
|
|
75
75
|
begin
|
76
76
|
s.write @channel.io[0].readpartial(10240)
|
77
77
|
rescue EOFError
|
78
|
-
@logger.info
|
78
|
+
@logger.info { "io is EOF" }
|
79
79
|
s.close_write
|
80
80
|
break
|
81
81
|
rescue IOError
|
82
|
-
@logger.info
|
82
|
+
@logger.info { "socket is closed" }
|
83
83
|
break
|
84
84
|
rescue => e
|
85
|
-
@logger.error
|
85
|
+
@logger.error { [e.backtrace[0], ": ", e.message, " (", e.class.to_s, ")\n\t", e.backtrace[1..-1].join("\n\t")].join }
|
86
86
|
s.close_write
|
87
87
|
break
|
88
88
|
end
|
89
89
|
end
|
90
|
-
@logger.info
|
90
|
+
@logger.info { "finishing receiver thread" }
|
91
91
|
@receiver_thread_finished = true
|
92
92
|
close
|
93
93
|
ensure
|
94
|
-
@logger.info
|
94
|
+
@logger.info { "receiver thread finished" }
|
95
95
|
end
|
96
96
|
}
|
97
97
|
end
|