hrr_rb_ssh 0.1.6 → 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|