hrr_rb_ssh 0.3.0.pre1 → 0.4.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (139) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +0 -3
  3. data/.travis.yml +1 -0
  4. data/README.md +208 -46
  5. data/demo/client.rb +71 -0
  6. data/demo/echo_server.rb +8 -3
  7. data/demo/more_flexible_auth.rb +105 -0
  8. data/demo/multi_step_auth.rb +99 -0
  9. data/demo/server.rb +10 -4
  10. data/demo/subsystem_echo_server.rb +8 -3
  11. data/hrr_rb_ssh.gemspec +6 -6
  12. data/lib/hrr_rb_ssh.rb +1 -1
  13. data/lib/hrr_rb_ssh/algorithm/publickey.rb +0 -1
  14. data/lib/hrr_rb_ssh/algorithm/publickey/ecdsa_sha2.rb +12 -9
  15. data/lib/hrr_rb_ssh/algorithm/publickey/ecdsa_sha2/ecdsa_signature_blob.rb +2 -4
  16. data/lib/hrr_rb_ssh/algorithm/publickey/ecdsa_sha2/public_key_blob.rb +2 -4
  17. data/lib/hrr_rb_ssh/algorithm/publickey/ecdsa_sha2/signature.rb +2 -4
  18. data/lib/hrr_rb_ssh/algorithm/publickey/ssh_dss.rb +10 -7
  19. data/lib/hrr_rb_ssh/algorithm/publickey/ssh_dss/public_key_blob.rb +2 -4
  20. data/lib/hrr_rb_ssh/algorithm/publickey/ssh_dss/signature.rb +2 -4
  21. data/lib/hrr_rb_ssh/algorithm/publickey/ssh_rsa.rb +9 -6
  22. data/lib/hrr_rb_ssh/algorithm/publickey/ssh_rsa/public_key_blob.rb +2 -4
  23. data/lib/hrr_rb_ssh/algorithm/publickey/ssh_rsa/signature.rb +2 -4
  24. data/lib/hrr_rb_ssh/authentication.rb +103 -22
  25. data/lib/hrr_rb_ssh/authentication/constant.rb +14 -0
  26. data/lib/hrr_rb_ssh/authentication/method/keyboard_interactive.rb +44 -7
  27. data/lib/hrr_rb_ssh/authentication/method/keyboard_interactive/context.rb +16 -9
  28. data/lib/hrr_rb_ssh/authentication/method/keyboard_interactive/info_request.rb +7 -6
  29. data/lib/hrr_rb_ssh/authentication/method/keyboard_interactive/info_response.rb +5 -2
  30. data/lib/hrr_rb_ssh/authentication/method/none.rb +23 -7
  31. data/lib/hrr_rb_ssh/authentication/method/none/context.rb +15 -7
  32. data/lib/hrr_rb_ssh/authentication/method/password.rb +28 -7
  33. data/lib/hrr_rb_ssh/authentication/method/password/context.rb +16 -7
  34. data/lib/hrr_rb_ssh/authentication/method/publickey.rb +63 -10
  35. data/lib/hrr_rb_ssh/authentication/method/publickey/algorithm.rb +0 -1
  36. data/lib/hrr_rb_ssh/authentication/method/publickey/algorithm/functionable.rb +32 -8
  37. data/lib/hrr_rb_ssh/authentication/method/publickey/algorithm/signature_blob.rb +2 -4
  38. data/lib/hrr_rb_ssh/authentication/method/publickey/context.rb +11 -2
  39. data/lib/hrr_rb_ssh/client.rb +234 -0
  40. data/lib/hrr_rb_ssh/codable.rb +15 -13
  41. data/lib/hrr_rb_ssh/compat/ruby.rb +0 -1
  42. data/lib/hrr_rb_ssh/connection.rb +145 -75
  43. data/lib/hrr_rb_ssh/connection/channel.rb +342 -109
  44. data/lib/hrr_rb_ssh/connection/channel/channel_type/direct_tcpip.rb +24 -19
  45. data/lib/hrr_rb_ssh/connection/channel/channel_type/forwarded_tcpip.rb +24 -19
  46. data/lib/hrr_rb_ssh/connection/channel/channel_type/session.rb +19 -12
  47. data/lib/hrr_rb_ssh/connection/channel/channel_type/session/proc_chain.rb +0 -2
  48. data/lib/hrr_rb_ssh/connection/channel/channel_type/session/proc_chain/chain_context.rb +0 -3
  49. data/lib/hrr_rb_ssh/connection/channel/channel_type/session/request_type/env.rb +2 -5
  50. data/lib/hrr_rb_ssh/connection/channel/channel_type/session/request_type/env/context.rb +5 -4
  51. data/lib/hrr_rb_ssh/connection/channel/channel_type/session/request_type/exec.rb +2 -5
  52. data/lib/hrr_rb_ssh/connection/channel/channel_type/session/request_type/exec/context.rb +5 -4
  53. data/lib/hrr_rb_ssh/connection/channel/channel_type/session/request_type/pty_req.rb +2 -5
  54. data/lib/hrr_rb_ssh/connection/channel/channel_type/session/request_type/pty_req/context.rb +5 -4
  55. data/lib/hrr_rb_ssh/connection/channel/channel_type/session/request_type/shell.rb +2 -5
  56. data/lib/hrr_rb_ssh/connection/channel/channel_type/session/request_type/shell/context.rb +5 -4
  57. data/lib/hrr_rb_ssh/connection/channel/channel_type/session/request_type/subsystem.rb +2 -5
  58. data/lib/hrr_rb_ssh/connection/channel/channel_type/session/request_type/subsystem/context.rb +5 -4
  59. data/lib/hrr_rb_ssh/connection/channel/channel_type/session/request_type/window_change.rb +2 -5
  60. data/lib/hrr_rb_ssh/connection/channel/channel_type/session/request_type/window_change/context.rb +5 -4
  61. data/lib/hrr_rb_ssh/connection/global_request_handler.rb +14 -12
  62. data/lib/hrr_rb_ssh/connection/request_handler.rb +1 -3
  63. data/lib/hrr_rb_ssh/connection/request_handler/reference_env_request_handler.rb +0 -2
  64. data/lib/hrr_rb_ssh/connection/request_handler/reference_exec_request_handler.rb +4 -6
  65. data/lib/hrr_rb_ssh/connection/request_handler/reference_pty_req_request_handler.rb +10 -12
  66. data/lib/hrr_rb_ssh/connection/request_handler/reference_shell_request_handler.rb +4 -6
  67. data/lib/hrr_rb_ssh/connection/request_handler/reference_window_change_request_handler.rb +0 -2
  68. data/lib/hrr_rb_ssh/error/closed_authentication.rb +1 -1
  69. data/lib/hrr_rb_ssh/error/closed_connection.rb +1 -1
  70. data/lib/hrr_rb_ssh/error/closed_transport.rb +1 -1
  71. data/lib/hrr_rb_ssh/loggable.rb +42 -0
  72. data/lib/hrr_rb_ssh/message/001_ssh_msg_disconnect.rb +2 -4
  73. data/lib/hrr_rb_ssh/message/002_ssh_msg_ignore.rb +2 -4
  74. data/lib/hrr_rb_ssh/message/003_ssh_msg_unimplemented.rb +2 -4
  75. data/lib/hrr_rb_ssh/message/004_ssh_msg_debug.rb +2 -4
  76. data/lib/hrr_rb_ssh/message/005_ssh_msg_service_request.rb +2 -4
  77. data/lib/hrr_rb_ssh/message/006_ssh_msg_service_accept.rb +2 -4
  78. data/lib/hrr_rb_ssh/message/020_ssh_msg_kexinit.rb +2 -4
  79. data/lib/hrr_rb_ssh/message/021_ssh_msg_newkeys.rb +2 -4
  80. data/lib/hrr_rb_ssh/message/030_ssh_msg_kex_dh_gex_request_old.rb +2 -4
  81. data/lib/hrr_rb_ssh/message/030_ssh_msg_kexdh_init.rb +2 -4
  82. data/lib/hrr_rb_ssh/message/030_ssh_msg_kexecdh_init.rb +2 -4
  83. data/lib/hrr_rb_ssh/message/031_ssh_msg_kex_dh_gex_group.rb +2 -4
  84. data/lib/hrr_rb_ssh/message/031_ssh_msg_kexdh_reply.rb +2 -4
  85. data/lib/hrr_rb_ssh/message/031_ssh_msg_kexecdh_reply.rb +2 -4
  86. data/lib/hrr_rb_ssh/message/032_ssh_msg_kex_dh_gex_init.rb +2 -4
  87. data/lib/hrr_rb_ssh/message/033_ssh_msg_kex_dh_gex_reply.rb +2 -4
  88. data/lib/hrr_rb_ssh/message/034_ssh_msg_kex_dh_gex_request.rb +2 -4
  89. data/lib/hrr_rb_ssh/message/050_ssh_msg_userauth_request.rb +2 -4
  90. data/lib/hrr_rb_ssh/message/051_ssh_msg_userauth_failure.rb +2 -4
  91. data/lib/hrr_rb_ssh/message/052_ssh_msg_userauth_success.rb +2 -4
  92. data/lib/hrr_rb_ssh/message/060_ssh_msg_userauth_info_request.rb +2 -4
  93. data/lib/hrr_rb_ssh/message/060_ssh_msg_userauth_pk_ok.rb +2 -4
  94. data/lib/hrr_rb_ssh/message/061_ssh_msg_userauth_info_response.rb +2 -4
  95. data/lib/hrr_rb_ssh/message/080_ssh_msg_global_request.rb +2 -4
  96. data/lib/hrr_rb_ssh/message/081_ssh_msg_request_success.rb +2 -4
  97. data/lib/hrr_rb_ssh/message/082_ssh_msg_request_failure.rb +2 -4
  98. data/lib/hrr_rb_ssh/message/090_ssh_msg_channel_open.rb +2 -4
  99. data/lib/hrr_rb_ssh/message/091_ssh_msg_channel_open_confirmation.rb +2 -4
  100. data/lib/hrr_rb_ssh/message/092_ssh_msg_channel_open_failure.rb +2 -4
  101. data/lib/hrr_rb_ssh/message/093_ssh_msg_channel_window_adjust.rb +2 -4
  102. data/lib/hrr_rb_ssh/message/094_ssh_msg_channel_data.rb +2 -4
  103. data/lib/hrr_rb_ssh/message/095_ssh_msg_channel_extended_data.rb +2 -4
  104. data/lib/hrr_rb_ssh/message/096_ssh_msg_channel_eof.rb +2 -4
  105. data/lib/hrr_rb_ssh/message/097_ssh_msg_channel_close.rb +2 -4
  106. data/lib/hrr_rb_ssh/message/098_ssh_msg_channel_request.rb +3 -5
  107. data/lib/hrr_rb_ssh/message/099_ssh_msg_channel_success.rb +2 -4
  108. data/lib/hrr_rb_ssh/message/100_ssh_msg_channel_failure.rb +2 -4
  109. data/lib/hrr_rb_ssh/server.rb +16 -10
  110. data/lib/hrr_rb_ssh/transport.rb +113 -77
  111. data/lib/hrr_rb_ssh/transport/compression_algorithm/functionable.rb +5 -3
  112. data/lib/hrr_rb_ssh/transport/compression_algorithm/unfunctionable.rb +5 -3
  113. data/lib/hrr_rb_ssh/transport/encryption_algorithm/functionable.rb +5 -3
  114. data/lib/hrr_rb_ssh/transport/encryption_algorithm/unfunctionable.rb +5 -3
  115. data/lib/hrr_rb_ssh/transport/kex_algorithm/diffie_hellman.rb +43 -37
  116. data/lib/hrr_rb_ssh/transport/kex_algorithm/diffie_hellman/h0.rb +2 -4
  117. data/lib/hrr_rb_ssh/transport/kex_algorithm/diffie_hellman_group_exchange.rb +87 -52
  118. data/lib/hrr_rb_ssh/transport/kex_algorithm/diffie_hellman_group_exchange/h0.rb +2 -4
  119. data/lib/hrr_rb_ssh/transport/kex_algorithm/elliptic_curve_diffie_hellman.rb +43 -37
  120. data/lib/hrr_rb_ssh/transport/kex_algorithm/elliptic_curve_diffie_hellman/h0.rb +2 -4
  121. data/lib/hrr_rb_ssh/transport/mac_algorithm/functionable.rb +5 -3
  122. data/lib/hrr_rb_ssh/transport/mac_algorithm/unfunctionable.rb +5 -3
  123. data/lib/hrr_rb_ssh/transport/receiver.rb +8 -7
  124. data/lib/hrr_rb_ssh/transport/sender.rb +5 -3
  125. data/lib/hrr_rb_ssh/transport/sequence_number.rb +0 -4
  126. data/lib/hrr_rb_ssh/transport/server_host_key_algorithm.rb +0 -1
  127. data/lib/hrr_rb_ssh/transport/server_host_key_algorithm/functionable.rb +5 -3
  128. data/lib/hrr_rb_ssh/version.rb +1 -1
  129. metadata +18 -51
  130. data/lib/hrr_rb_ssh/algorithm/publickey/ssh_ed25519.rb +0 -61
  131. data/lib/hrr_rb_ssh/algorithm/publickey/ssh_ed25519/openssh_private_key.rb +0 -29
  132. data/lib/hrr_rb_ssh/algorithm/publickey/ssh_ed25519/openssh_private_key_content.rb +0 -26
  133. data/lib/hrr_rb_ssh/algorithm/publickey/ssh_ed25519/pkey.rb +0 -158
  134. data/lib/hrr_rb_ssh/algorithm/publickey/ssh_ed25519/public_key_blob.rb +0 -23
  135. data/lib/hrr_rb_ssh/algorithm/publickey/ssh_ed25519/signature.rb +0 -23
  136. data/lib/hrr_rb_ssh/authentication/method/publickey/algorithm/ssh_ed25519.rb +0 -21
  137. data/lib/hrr_rb_ssh/compat/ruby/array.rb +0 -14
  138. data/lib/hrr_rb_ssh/logger.rb +0 -56
  139. data/lib/hrr_rb_ssh/transport/server_host_key_algorithm/ssh_ed25519.rb +0 -20
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '08be357edfafa61b913c4f28f6d97a29e7aa0c94eaef933a3ea2dce9bc51c9a3'
4
- data.tar.gz: 4b87367e0ac95045fcfac372fd1e8735e896436a2d26a2987b94daad2244379f
3
+ metadata.gz: 781a7427bc3d26dcddba2be4a74f685d26ecc867fe6a1d937cd19e3b56cd9477
4
+ data.tar.gz: c1ea1ee12f012d1cf66a23518049a9ce127a1ded1536c735330b6f7e3ad22d7d
5
5
  SHA512:
6
- metadata.gz: f830b32cb263e45ca84596b310889cee768fcf870fc78093c818d2a8dff9fa59c09c334bb7942bf56b344d6d9ce9bcff609962880fcd7e4ac9b74aa1a703c8a5
7
- data.tar.gz: 31f317c5fc929b0b04b78fa19d9f85be1f9e4aef4c57474e8fe06039e8f947818eb7e0033da21b80bd9f7e8fa8d3a544c348a468ba31da4cf509812013b2a550
6
+ metadata.gz: c0c79b9c3d00f49b4e8090ec6cf9aa5e58619d62f4bd46a0d2864c3c29d908a92470f4eda4e9ba1b057f765b98214ddcaa5b44e9cd4de1ecd44639afd89d29bd
7
+ data.tar.gz: e9c84a3ad17d39972c6950f4da1b92bca351511d5bc220b342ac7d61f7d70631f60c68ecd5ac5d964d76825b5a67027227bb45ac3f2703788c7d7a548359c730
data/.gitignore CHANGED
@@ -21,7 +21,4 @@ Gemfile.lock
21
21
  .ruby-gemset
22
22
 
23
23
  # additional
24
- .DS_Store
25
- *~
26
- *.swp
27
24
  /yang/
data/.travis.yml CHANGED
@@ -11,6 +11,7 @@ rvm:
11
11
  - 2.4
12
12
  - 2.5
13
13
  - 2.6
14
+ - 2.7
14
15
  - ruby-head
15
16
  os:
16
17
  - linux
data/README.md CHANGED
@@ -5,29 +5,40 @@
5
5
  [![Test Coverage](https://api.codeclimate.com/v1/badges/f5dfdb97d72f24ca5939/test_coverage)](https://codeclimate.com/github/hirura/hrr_rb_ssh/test_coverage)
6
6
  [![Gem Version](https://badge.fury.io/rb/hrr_rb_ssh.svg)](https://badge.fury.io/rb/hrr_rb_ssh)
7
7
 
8
- hrr_rb_ssh is a pure Ruby SSH 2.0 server implementation.
8
+ hrr_rb_ssh is a pure Ruby SSH 2.0 server and client implementation.
9
9
 
10
- With hrr_rb_ssh, it is possible to write an SSH server easily, and also possible to write an original server side application on secure connection provided by SSH protocol.
10
+ With hrr_rb_ssh, it is possible to write an SSH server easily, and also possible to write an original server side application on secure connection provided by SSH protocol. And it supports to write SSH client as well.
11
+
12
+ NOTE: ED25519 public key algorithm is now separated from hrr_rb_ssh. Please refer to [hrr_rb_ssh-ed25519](https://github.com/hirura/hrr_rb_ssh-ed25519).
11
13
 
12
14
  ## Table of Contents
13
15
 
14
16
  - [Installation](#installation)
15
17
  - [Usage](#usage)
18
+ - [Requiring hrr\_rb\_ssh library](#requiring-hrr_rb_ssh-library)
19
+ - [Logging](#logging)
16
20
  - [Writing standard SSH server](#writing-standard-ssh-server)
17
- - [Requiring hrr\_rb\_ssh library](#requiring-hrr_rb_ssh-library)
18
21
  - [Starting server application](#starting-server-application)
19
- - [Logging](#logging)
20
22
  - [Registering pre\-generated secret keys for server host key](#registering-pre-generated-secret-keys-for-server-host-key)
21
23
  - [Defining authentications](#defining-authentications)
22
- - [Password authentication](#password-authentication)
23
- - [Publickey authentication](#publickey-authentication)
24
- - [Keyboard-interactive authentication](#keyboard-interactive-authentication)
25
- - [None authentication (NOT recomended)](#none-authentication-not-recomended)
24
+ - [Single authentication](#single-authentication)
25
+ - [Password authentication](#password-authentication)
26
+ - [Publickey authentication](#publickey-authentication)
27
+ - [Keyboard-interactive authentication](#keyboard-interactive-authentication)
28
+ - [None authentication (NOT recomended)](#none-authentication-not-recomended)
29
+ - [Multi\-step authentication](#multi-step-authentication)
30
+ - [More flexible authentication](#more-flexible-authentication)
26
31
  - [Handling session channel requests](#handling-session-channel-requests)
27
32
  - [Reference request handlers](#reference-request-handlers)
28
33
  - [Custom request handlers](#custom-request-handlers)
29
34
  - [Defining preferred algorithms (optional)](#defining-preferred-algorithms-optional)
30
35
  - [Hiding and/or simulating local SSH version](#hiding-and-or-simulating-local-ssh-version)
36
+ - [Writing SSH client (Experimental)](#writing-ssh-client-experimental)
37
+ - [Starting SSH connection](#starting-ssh-connection)
38
+ - [Executing remote commands](#executing-remote-commands)
39
+ - [exec method](#exec-method)
40
+ - [shell method](#shell-method)
41
+ - [subsystem method](#subsystem-method)
31
42
  - [Demo](#demo)
32
43
  - [Supported Features](#supported-features)
33
44
  - [Connection layer](#connection-layer)
@@ -59,9 +70,7 @@ $ gem install hrr_rb_ssh
59
70
 
60
71
  ## Usage
61
72
 
62
- ### Writing standard SSH server
63
-
64
- #### Requiring `hrr_rb_ssh` library
73
+ ### Requiring `hrr_rb_ssh` library
65
74
 
66
75
  First of all, `hrr_rb_ssh` library needs to be loaded.
67
76
 
@@ -69,6 +78,41 @@ First of all, `hrr_rb_ssh` library needs to be loaded.
69
78
  require 'hrr_rb_ssh'
70
79
  ```
71
80
 
81
+ ### Logging
82
+
83
+ __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.
84
+
85
+ The library provides logging functionality. To enable logging in the library, you are to give a `logger` to `Server.new` or `Client.new`.
86
+
87
+ ```ruby
88
+ HrrRbSsh::Server.new options, logger: logger
89
+ ```
90
+
91
+ or
92
+
93
+ ```ruby
94
+ HrrRbSsh::Client.new target, options, logger: logger
95
+ ```
96
+
97
+ Where, the `logger` variable can be an instance of standard Logger class or user-defined logger class. What the library requires for `logger` variable is that the `logger` instance responds to `#fatal`, `#error`, `#warn`, `#info` and `#debug` with the following syntax.
98
+
99
+ ```ruby
100
+ logger.fatal(progname){ message }
101
+ logger.error(progname){ message }
102
+ logger.warn(progname){ message }
103
+ logger.info(progname){ message }
104
+ logger.debug(progname){ message }
105
+ ```
106
+
107
+ For instance, `logger` variable can be prepared like below.
108
+
109
+ ```ruby
110
+ logger = Logger.new STDOUT
111
+ logger.level = Logger::INFO
112
+ ```
113
+
114
+ ### Writing standard SSH server
115
+
72
116
  #### Starting server application
73
117
 
74
118
  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.
@@ -94,31 +138,6 @@ end
94
138
 
95
139
  Where, an `options` variable is an instance of `Hash`, which has optional (or sometimes almost necessary) values.
96
140
 
97
- #### Logging
98
-
99
- __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.
100
-
101
- The library provides logging functionality. To enable logging of the library, you are to initialize `HrrRbSsh::Logger` class.
102
-
103
- ```ruby
104
- HrrRbSsh::Logger.initialize logger
105
- ```
106
-
107
- 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`.
108
-
109
- For instance, `logger` variable can be prepared like below.
110
-
111
- ```ruby
112
- logger = Logger.new STDOUT
113
- logger.level = Logger::INFO
114
- ```
115
-
116
- To disable logging, you can un-initialize `HrrRbSsh::Logger`.
117
-
118
- ```ruby
119
- HrrRbSsh::Logger.uninitialize
120
- ```
121
-
122
141
  #### Registering pre-generated secret keys for server host key
123
142
 
124
143
  By default, server host keys are generated everytime the gem is loaded. To use pre-generated keys, it is possible to register the keys in HrrRbSsh::Transport through `options` variable. The secret key value must be PEM or DER format string. The below is an example of registering ecdsa-sha2-nistp256 secret key. The supported server host key algorithms are listed later in this document.
@@ -138,7 +157,13 @@ EOB
138
157
 
139
158
  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.
140
159
 
141
- ##### Password authentication
160
+ The library defines a sort of strategies to implement handling authentication.
161
+
162
+ ##### Single authentication
163
+
164
+ Each authenticator returns `true` (or `HrrRbSsh::Authentication::SUCCESS`) or `false` (or `HrrRbSsh::Authentication::FAILURE`). When it is true, the user is accepted. When it is false, the user is not accepted and a subsequent authenticator is called.
165
+
166
+ ###### Password authentication
142
167
 
143
168
  Password authentication is the most simple way to allow users to login to the SSH service. Password authentication requires user-name and password.
144
169
 
@@ -161,9 +186,11 @@ The `context` variable in password authentication context provides the following
161
186
 
162
187
  - `#username` : The username that a remote user tries to authenticate
163
188
  - `#password` : The password that a remote user tries to authenticate
189
+ - `#variables` : A hash instance that is shared in each authenticator and subsequent session channel request handlers
190
+ - `#vars` : The same object that `#variables` returns
164
191
  - `#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.
165
192
 
166
- ##### Publickey authentication
193
+ ###### Publickey authentication
167
194
 
168
195
  The second one is public key authentication. Public key authentication requires user-name, public key algorithm name, and PEM or DER formed public key.
169
196
 
@@ -184,7 +211,7 @@ The `context` variable in public key authentication context provides the `#verif
184
211
 
185
212
  And public keys that is in OpenSSH public key format is now available. To use OpenSSH public keys, it is easy to use $USER_HOME/.ssh/authorized_keys file.
186
213
 
187
- ##### Keyboard-interactive authentication
214
+ ###### Keyboard-interactive authentication
188
215
 
189
216
  The third one is keyboard-interactive authentication. This is also known as challenge-response authentication.
190
217
 
@@ -213,7 +240,7 @@ The `#info_request` method takes four arguments: name, instruction, language tag
213
240
 
214
241
  The responses are listed in the same order as request prompts.
215
242
 
216
- ##### None authentication (NOT recomended)
243
+ ###### None authentication (NOT recomended)
217
244
 
218
245
  The last one is none authentication. None authentication is usually NOT used.
219
246
 
@@ -232,6 +259,73 @@ options['authentication_none_authenticator'] = auth_none
232
259
 
233
260
  In none authentication context, `context` variable provides the `#username` method.
234
261
 
262
+ ##### Multi-step authentication
263
+
264
+ In this strategy that conbines single authentications, it is possible to implement multi-step authentication. In case that the combination is a publickey authentication method and a password authentication method, it is so-called two-factor authentication.
265
+
266
+ A return value of each authentication handler can be `HrrRbSsh::Authentication::PARTIAL_SUCCESS`. The value means that the authentication method returns success and another authenticatoin method is requested (i.e. the authentication method is deleted from the list of authentication that can continue, and then the server sends USERAUTH_FAILURE message with the updated list of authentication that can continue and partial success true). When all preferred authentication methods returns `PARTIAL_SUCCESS` (i.e. there is no more authentication that can continue), then the user is treated as authenticated.
267
+
268
+ ```ruby
269
+ auth_preferred_authentication_methods = ["publickey", "password"]
270
+ auth_publickey = HrrRbSsh::Authentication::Authenticator.new { |context|
271
+ is_verified = some_verification_method(context)
272
+ if is_verified
273
+ HrrRbSsh::Authentication::PARTIAL_SUCCESS
274
+ else
275
+ false
276
+ end
277
+ }
278
+ auth_password = HrrRbSsh::Authentication::Authenticator.new { |context|
279
+ is_verified = some_verification_method(context)
280
+ if is_verified
281
+ HrrRbSsh::Authentication::PARTIAL_SUCCESS
282
+ else
283
+ false
284
+ end
285
+ }
286
+ options['authentication_preferred_authentication_methods'] = auth_preferred_authentication_methods
287
+ options['authentication_publickey_authenticator'] = auth_publickey
288
+ options['authentication_password_authenticator'] = auth_password
289
+ ```
290
+
291
+ ##### More flexible authentication
292
+
293
+ A `context` variable in an authenticator gives an access to remaining authentication methods that can continue. In this strategy, an implementer is able to control the order of authentication methods and to control which authentication methods are used for the user.
294
+
295
+ The below is an example. It is expected that any user must be verified by publickey and then another authentication is requested for the user accordingly.
296
+
297
+ ```ruby
298
+ auth_preferred_authentication_methods = ['none']
299
+ auth_none = HrrRbSsh::Authentication::Authenticator.new{ |context|
300
+ context.authentication_methods.push 'publickey'
301
+ HrrRbSsh::Authentication::PARTIAL_SUCCESS
302
+ }
303
+ auth_publickey = HrrRbSsh::Authentication::Authenticator.new{ |context|
304
+ if some_verification(context)
305
+ case context.username
306
+ when 'user1'
307
+ context.authentiation_methods.push 'keyboard-interactive'
308
+ HrrRbSsh::Authentication::PARTIAL_SUCCESS
309
+ else
310
+ false
311
+ end
312
+ else
313
+ false
314
+ end
315
+ }
316
+ auth_keyboard_interactive = HrrRbSsh::Authentication::Authenticator.new{ |context|
317
+ if some_verification(context)
318
+ true # or HrrRbSsh::Authentication::PARTIAL_SUCCESS; both will accept the user because remaining authentication method is only 'keyboard-interactive' in this case
319
+ else
320
+ false
321
+ end
322
+ }
323
+ options['authentication_preferred_authentication_methods'] = auth_preferred_authentication_methods
324
+ options['authentication_none_authenticator'] = auth_none
325
+ options['authentication_publickey_authenticator'] = auth_publickey
326
+ options['authentication_keyboard_interactive_authenticator'] = auth_keyboard_interactive
327
+ ```
328
+
235
329
  #### Handling session channel requests
236
330
 
237
331
  By default, any channel requests belonging to session channel are implicitly ignored. To handle the requests, defining request handlers are required.
@@ -277,6 +371,8 @@ In `HrrRbSsh::Connection::RequestHandler.new` block, context variable basically
277
371
  - `#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.
278
372
  - `#chain_proc => {|chain| ... }` : When a session channel is opened, a background thread is started and is waitng for a chained 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.
279
373
  - `#close_session` : In most cases, input and output between a client and the server is handled in `#chain_proc` and closing the `#chain_proc` block will lead closing the underlying session channel. This means that to close the underlying session channel it is required to write at least one `#chain_proc` block. If it is not required to use `#chain_proc` block or is required to close the underlying session channel from outside of `#chain_proc` block, `#close_session` can be used. The `#close_session` will close the background thread that calls `#chain_proc` blocks.
374
+ - `#variables => Hash` : A hash instance that is passed from authenticator and is shared in subsequent session channel request handlers
375
+ - `#vars` : The same object that `#variables` returns
280
376
 
281
377
  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`.
282
378
 
@@ -303,9 +399,9 @@ p HrrRbSsh::Transport::EncryptionAlgorithm.list_preferred
303
399
  # => ["aes128-ctr", "aes192-ctr", "aes256-ctr", "aes128-cbc", "3des-cbc", "blowfish-cbc", "cast128-cbc", "aes192-cbc", "aes256-cbc", "arcfour"]
304
400
 
305
401
  p HrrRbSsh::Transport::ServerHostKeyAlgorithm.list_supported
306
- # => ["ssh-dss", "ssh-rsa", "ecdsa-sha2-nistp256", "ecdsa-sha2-nistp384", "ecdsa-sha2-nistp521", "ssh-ed25519"]
402
+ # => ["ssh-dss", "ssh-rsa", "ecdsa-sha2-nistp256", "ecdsa-sha2-nistp384", "ecdsa-sha2-nistp521"]
307
403
  p HrrRbSsh::Transport::ServerHostKeyAlgorithm.list_preferred
308
- # => ["ssh-ed25519", "ecdsa-sha2-nistp521", "ecdsa-sha2-nistp384", "ecdsa-sha2-nistp256", "ssh-rsa", "ssh-dss"]
404
+ # => ["ecdsa-sha2-nistp521", "ecdsa-sha2-nistp384", "ecdsa-sha2-nistp256", "ssh-rsa", "ssh-dss"]
309
405
 
310
406
  p HrrRbSsh::Transport::KexAlgorithm.list_supported
311
407
  # => ["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"]
@@ -340,9 +436,77 @@ options['local_version'] = "SSH-2.0-OpenSSH"
340
436
 
341
437
  Please note that the beginning of the string must be `SSH-2.0-`. Otherwise SSH 2.0 remote peer cannot continue negotiation with the local peer.
342
438
 
439
+ ### Writing SSH client (Experimental)
440
+
441
+ #### Starting SSH connection
442
+
443
+ The client mode can be started with `HrrRbSsh::Client.start`. The method takes `target` and `options` arguments. The `target` that the SSH client connects to can be one of:
444
+
445
+ - (IO) An io that is open for input and output
446
+ - (Array) An array of the target host address or host name and its service port number
447
+ - (String) The target host address or host name; in this case the target service port number will be 22
448
+
449
+ And the `options` contains various parameters for the SSH connection. At least `username` key must be set in the `options`. Also at least one of `password`, `publickey`, or `keyboard-interactive` needs to be set for authentication instead of authenticators that are used in server mode. Also as similar to server mode, it is possible to specify preferred transport algorithms and preferred authentication methods with the same keywords.
450
+
451
+ ```ruby
452
+ target = ['remotehost', 22]
453
+ options = {
454
+ username: 'user1',
455
+ password: 'password1',
456
+ publickey: ['ssh-rsa', "/home/user1/.ssh/id_rsa")],
457
+ authentication_preferred_authentication_methods = ['publickey', 'password'],
458
+ }
459
+ HrrRbSsh::Client.start(target, options) do |conn|
460
+ # Do something here
461
+ # For instance: conn.exec "command"
462
+ end
463
+ ```
464
+
465
+ #### Executing remote commands
466
+
467
+ There are some methods supported in client mode. The methods works as a receiver of `conn` block variable.
468
+
469
+ ##### exec method
470
+
471
+ The `exec` and `exec!` methods execute command on a remote host. Both takes a command argument that is executed in the remote host. And they can take optional `pty` and `env` arguments. When `pty: true` is set, then the command will be executed on a pseudo-TTY. When `env: {'key' => 'value'}` is set, then the environmental variables are set before the command is executed.
472
+
473
+ The `exec!` method returns `[stdout, stderr]` outputs. Once the command is executed and the outputs are completed, then the method returns the value.
474
+
475
+ ```ruby
476
+ conn.exec! "command" # => [stdout, stderr]
477
+ ```
478
+
479
+ On the other hand, `exec` method takes block like the below example and returns exit status of the command. When the command is executed and the outputs and reading them are finished, then `io_out` and `io_err` return EOF.
480
+
481
+ ```ruby
482
+ conn.exec "command" do |io_in, io_out, io_err|
483
+ # Do something here
484
+ end
485
+ ```
486
+
487
+ ##### shell method
488
+
489
+ The `shell` method provides a shell access on a remote host. As similar to `exec` method, it takes block and its block variable is also `io_in, io_out, io_err`. `shell` is always on pseudo-TTY, so it doesn't take `pty` optional argument. It takes `env` optional argument. Exiting shell will leads `io_out` and `io_err` EOF.
490
+
491
+ ```ruby
492
+ conn.shell do |io_in, io_out, io_err|
493
+ # Do something here
494
+ end
495
+ ```
496
+
497
+ ##### subsystem method
498
+
499
+ The `subsystem` method is to start a subsystem on a remote host. The method takes a subsystem name argument and a block. Its block variable is also `io_in, io_out, io_err`. `subsystem` doesn't take `pty` nor `env` optional argument.
500
+
501
+ ```ruby
502
+ conn.subsystem("echo") do |io_in, io_out, io_err|
503
+ # Do something here
504
+ end
505
+ ```
506
+
343
507
  ### Demo
344
508
 
345
- The `demo/server.rb` shows a good example on how to use the hrr_rb_ssh library in SSH server mode.
509
+ The `demo/server.rb` shows a good example on how to use the hrr_rb_ssh library in SSH server mode. And the `demo/client.rb` shows an example on how to use the hrr_rb_ssh library in SSH client mode.
346
510
 
347
511
  ## Supported Features
348
512
 
@@ -367,7 +531,6 @@ The following features are currently supported.
367
531
  - ecdsa-sha2-nistp256
368
532
  - ecdsa-sha2-nistp384
369
533
  - ecdsa-sha2-nistp521
370
- - ssh-ed25519
371
534
  - Keyboard interactive (generic interactive / challenge response) authentication
372
535
 
373
536
  ### Transport layer
@@ -390,7 +553,6 @@ The following features are currently supported.
390
553
  - ecdsa-sha2-nistp256
391
554
  - ecdsa-sha2-nistp384
392
555
  - ecdsa-sha2-nistp521
393
- - ssh-ed25519
394
556
  - Kex algorithm
395
557
  - diffie-hellman-group1-sha1
396
558
  - diffie-hellman-group14-sha1
@@ -422,7 +584,7 @@ Bug reports and pull requests are welcome on GitHub at https://github.com/hirura
422
584
 
423
585
  ## Code of Conduct
424
586
 
425
- Everyone interacting in the HrrRbSsh projects codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/hirura/hrr_rb_ssh/blob/master/CODE_OF_CONDUCT.md).
587
+ Everyone interacting in the HrrRbSsh project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/hirura/hrr_rb_ssh/blob/master/CODE_OF_CONDUCT.md).
426
588
 
427
589
  ## License
428
590
 
data/demo/client.rb ADDED
@@ -0,0 +1,71 @@
1
+ # coding: utf-8
2
+ # vim: et ts=2 sw=2
3
+
4
+ require 'logger'
5
+
6
+ begin
7
+ require 'hrr_rb_ssh'
8
+ rescue LoadError
9
+ $:.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
10
+ require 'hrr_rb_ssh'
11
+ end
12
+
13
+ class MyLoggerFormatter < ::Logger::Formatter
14
+ def call severity, time, progname, msg
15
+ "%s, [%s#%d.%x] %5s -- %s: %s\n" % [severity[0..0], format_datetime(time), Process.pid, Thread.current.object_id, severity, progname, msg2str(msg)]
16
+ end
17
+ end
18
+
19
+ logger = Logger.new STDOUT
20
+ logger.level = Logger::DEBUG
21
+ logger.formatter = MyLoggerFormatter.new
22
+
23
+ target = ['localhost', 10022]
24
+ options = {
25
+ username: 'user1',
26
+ password: 'password1',
27
+ publickey: ['ssh-rsa', "/home/user1/.ssh/id_rsa"],
28
+ keyboard_interactive: [
29
+ 'password1',
30
+ #'password2' # when keyboard-interactive authentication requires 2nd response
31
+ ],
32
+ }
33
+ HrrRbSsh::Client.start(target, options, logger: logger){ |conn|
34
+ puts conn.exec!('ls -l') # => [out, err]
35
+
36
+ puts conn.exec!('ls -l', pty: true) # => [out, err] # "ls -l" command will be run on PTY
37
+
38
+ conn.exec('ls -l', pty: true){ |io_in, io_out, io_err| # => exit status
39
+ while true
40
+ begin
41
+ print io_out.readpartial(10240)
42
+ rescue EOFError
43
+ break
44
+ end
45
+ end
46
+ }
47
+
48
+ conn.shell{ |io_in, io_out, io_err| # => exit status
49
+ t = Thread.new {
50
+ while true
51
+ begin
52
+ print io_out.readpartial(10240)
53
+ rescue EOFError
54
+ break
55
+ end
56
+ end
57
+ }
58
+ io_in.puts "ls -l"
59
+ io_in.puts "exit"
60
+ t.join
61
+ }
62
+
63
+ conn.subsystem("echo"){ |io_in, io_out, io_err| # => exit status
64
+ t = Thread.new {
65
+ print io_out.readpartial(10240) rescue nil
66
+ }
67
+ io_in.puts "string"
68
+ t.join
69
+ io_in.close
70
+ }
71
+ }