harbr 0.2.10 → 2.8.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (179) hide show
  1. checksums.yaml +4 -4
  2. data/.DS_Store +0 -0
  3. data/exe/harbr +225 -150
  4. data/lib/examples/container.toml +13 -0
  5. data/lib/harbr/container.rb +14 -10
  6. data/lib/harbr/host.rb +21 -0
  7. data/lib/harbr/version.rb +1 -1
  8. data/lib/harbr.rb +21 -6
  9. data/vendor/bundle/ruby/3.2.0/cache/dddr-1.0.8.gem +0 -0
  10. data/vendor/bundle/ruby/3.2.0/cache/dddr-1.1.0.gem +0 -0
  11. data/vendor/bundle/ruby/3.2.0/cache/dddr-1.1.1.gem +0 -0
  12. data/vendor/bundle/ruby/3.2.0/cache/net-ssh-7.2.1.gem +0 -0
  13. data/vendor/bundle/ruby/3.2.0/gems/dddr-1.0.8/.DS_Store +0 -0
  14. data/vendor/bundle/ruby/3.2.0/gems/dddr-1.0.8/.rspec +3 -0
  15. data/vendor/bundle/ruby/3.2.0/gems/dddr-1.0.8/.standard.yml +3 -0
  16. data/vendor/bundle/ruby/3.2.0/gems/dddr-1.0.8/CHANGELOG.md +5 -0
  17. data/vendor/bundle/ruby/3.2.0/gems/dddr-1.0.8/CODE_OF_CONDUCT.md +84 -0
  18. data/vendor/bundle/ruby/3.2.0/gems/dddr-1.0.8/LICENSE.txt +21 -0
  19. data/vendor/bundle/ruby/3.2.0/gems/dddr-1.0.8/README.md +96 -0
  20. data/vendor/bundle/ruby/3.2.0/gems/dddr-1.0.8/Rakefile +10 -0
  21. data/vendor/bundle/ruby/3.2.0/gems/dddr-1.0.8/hero.png +0 -0
  22. data/vendor/bundle/ruby/3.2.0/gems/dddr-1.0.8/lib/dddr/version.rb +5 -0
  23. data/vendor/bundle/ruby/3.2.0/gems/dddr-1.0.8/lib/dddr.rb +205 -0
  24. data/vendor/bundle/ruby/3.2.0/gems/dddr-1.0.8/sig/dddr.rbs +4 -0
  25. data/vendor/bundle/ruby/3.2.0/gems/dddr-1.1.0/.DS_Store +0 -0
  26. data/vendor/bundle/ruby/3.2.0/gems/dddr-1.1.0/.rspec +3 -0
  27. data/vendor/bundle/ruby/3.2.0/gems/dddr-1.1.0/.standard.yml +3 -0
  28. data/vendor/bundle/ruby/3.2.0/gems/dddr-1.1.0/CHANGELOG.md +5 -0
  29. data/vendor/bundle/ruby/3.2.0/gems/dddr-1.1.0/CODE_OF_CONDUCT.md +84 -0
  30. data/vendor/bundle/ruby/3.2.0/gems/dddr-1.1.0/LICENSE.txt +21 -0
  31. data/vendor/bundle/ruby/3.2.0/gems/dddr-1.1.0/README.md +96 -0
  32. data/vendor/bundle/ruby/3.2.0/gems/dddr-1.1.0/Rakefile +10 -0
  33. data/vendor/bundle/ruby/3.2.0/gems/dddr-1.1.0/hero.png +0 -0
  34. data/vendor/bundle/ruby/3.2.0/gems/dddr-1.1.0/lib/dddr/version.rb +5 -0
  35. data/vendor/bundle/ruby/3.2.0/gems/dddr-1.1.0/lib/dddr.rb +182 -0
  36. data/vendor/bundle/ruby/3.2.0/gems/dddr-1.1.0/sig/dddr.rbs +4 -0
  37. data/vendor/bundle/ruby/3.2.0/gems/dddr-1.1.1/.DS_Store +0 -0
  38. data/vendor/bundle/ruby/3.2.0/gems/dddr-1.1.1/.rspec +3 -0
  39. data/vendor/bundle/ruby/3.2.0/gems/dddr-1.1.1/.standard.yml +3 -0
  40. data/vendor/bundle/ruby/3.2.0/gems/dddr-1.1.1/CHANGELOG.md +5 -0
  41. data/vendor/bundle/ruby/3.2.0/gems/dddr-1.1.1/CODE_OF_CONDUCT.md +84 -0
  42. data/vendor/bundle/ruby/3.2.0/gems/dddr-1.1.1/LICENSE.txt +21 -0
  43. data/vendor/bundle/ruby/3.2.0/gems/dddr-1.1.1/README.md +96 -0
  44. data/vendor/bundle/ruby/3.2.0/gems/dddr-1.1.1/Rakefile +10 -0
  45. data/vendor/bundle/ruby/3.2.0/gems/dddr-1.1.1/hero.png +0 -0
  46. data/vendor/bundle/ruby/3.2.0/gems/dddr-1.1.1/lib/dddr/version.rb +5 -0
  47. data/vendor/bundle/ruby/3.2.0/gems/dddr-1.1.1/lib/dddr.rb +184 -0
  48. data/vendor/bundle/ruby/3.2.0/gems/dddr-1.1.1/sig/dddr.rbs +4 -0
  49. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/.dockerignore +6 -0
  50. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/.github/FUNDING.yml +1 -0
  51. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/.github/config/rubocop_linter_action.yml +4 -0
  52. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/.github/workflows/ci-with-docker.yml +44 -0
  53. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/.github/workflows/ci.yml +94 -0
  54. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/.github/workflows/rubocop.yml +16 -0
  55. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/.gitignore +15 -0
  56. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/.rubocop.yml +22 -0
  57. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/.rubocop_todo.yml +1081 -0
  58. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/CHANGES.txt +738 -0
  59. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/DEVELOPMENT.md +23 -0
  60. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/Dockerfile +29 -0
  61. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/Dockerfile.openssl3 +17 -0
  62. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/Gemfile +13 -0
  63. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/Gemfile.noed25519 +12 -0
  64. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/Gemfile.norbnacl +12 -0
  65. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/ISSUE_TEMPLATE.md +30 -0
  66. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/LICENSE.txt +19 -0
  67. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/Manifest +132 -0
  68. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/README.md +298 -0
  69. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/Rakefile +192 -0
  70. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/SECURITY.md +4 -0
  71. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/THANKS.txt +110 -0
  72. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/appveyor.yml +58 -0
  73. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/docker-compose.yml +25 -0
  74. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/lib/net/ssh/authentication/agent.rb +284 -0
  75. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/lib/net/ssh/authentication/certificate.rb +183 -0
  76. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/lib/net/ssh/authentication/constants.rb +20 -0
  77. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/lib/net/ssh/authentication/ed25519.rb +186 -0
  78. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/lib/net/ssh/authentication/ed25519_loader.rb +31 -0
  79. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/lib/net/ssh/authentication/key_manager.rb +327 -0
  80. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/lib/net/ssh/authentication/methods/abstract.rb +79 -0
  81. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/lib/net/ssh/authentication/methods/hostbased.rb +72 -0
  82. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/lib/net/ssh/authentication/methods/keyboard_interactive.rb +77 -0
  83. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/lib/net/ssh/authentication/methods/none.rb +34 -0
  84. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/lib/net/ssh/authentication/methods/password.rb +80 -0
  85. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/lib/net/ssh/authentication/methods/publickey.rb +137 -0
  86. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/lib/net/ssh/authentication/pageant.rb +497 -0
  87. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/lib/net/ssh/authentication/pub_key_fingerprint.rb +43 -0
  88. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/lib/net/ssh/authentication/session.rb +172 -0
  89. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/lib/net/ssh/buffer.rb +449 -0
  90. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/lib/net/ssh/buffered_io.rb +202 -0
  91. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/lib/net/ssh/config.rb +406 -0
  92. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/lib/net/ssh/connection/channel.rb +694 -0
  93. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/lib/net/ssh/connection/constants.rb +33 -0
  94. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/lib/net/ssh/connection/event_loop.rb +123 -0
  95. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/lib/net/ssh/connection/keepalive.rb +59 -0
  96. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/lib/net/ssh/connection/session.rb +712 -0
  97. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/lib/net/ssh/connection/term.rb +180 -0
  98. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/lib/net/ssh/errors.rb +106 -0
  99. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/lib/net/ssh/key_factory.rb +218 -0
  100. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/lib/net/ssh/known_hosts.rb +265 -0
  101. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/lib/net/ssh/loggable.rb +62 -0
  102. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/lib/net/ssh/packet.rb +106 -0
  103. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/lib/net/ssh/prompt.rb +62 -0
  104. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/lib/net/ssh/proxy/command.rb +123 -0
  105. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/lib/net/ssh/proxy/errors.rb +16 -0
  106. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/lib/net/ssh/proxy/http.rb +98 -0
  107. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/lib/net/ssh/proxy/https.rb +50 -0
  108. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/lib/net/ssh/proxy/jump.rb +54 -0
  109. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/lib/net/ssh/proxy/socks4.rb +67 -0
  110. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/lib/net/ssh/proxy/socks5.rb +140 -0
  111. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/lib/net/ssh/service/forward.rb +426 -0
  112. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/lib/net/ssh/test/channel.rb +147 -0
  113. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/lib/net/ssh/test/extensions.rb +173 -0
  114. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/lib/net/ssh/test/kex.rb +46 -0
  115. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/lib/net/ssh/test/local_packet.rb +53 -0
  116. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/lib/net/ssh/test/packet.rb +101 -0
  117. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/lib/net/ssh/test/remote_packet.rb +40 -0
  118. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/lib/net/ssh/test/script.rb +180 -0
  119. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/lib/net/ssh/test/socket.rb +65 -0
  120. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/lib/net/ssh/test.rb +94 -0
  121. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/lib/net/ssh/transport/algorithms.rb +524 -0
  122. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/lib/net/ssh/transport/chacha20_poly1305_cipher.rb +117 -0
  123. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/lib/net/ssh/transport/chacha20_poly1305_cipher_loader.rb +17 -0
  124. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/lib/net/ssh/transport/cipher_factory.rb +128 -0
  125. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/lib/net/ssh/transport/constants.rb +40 -0
  126. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/lib/net/ssh/transport/ctr.rb +115 -0
  127. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/lib/net/ssh/transport/hmac/abstract.rb +97 -0
  128. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/lib/net/ssh/transport/hmac/md5.rb +10 -0
  129. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/lib/net/ssh/transport/hmac/md5_96.rb +9 -0
  130. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/lib/net/ssh/transport/hmac/none.rb +13 -0
  131. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/lib/net/ssh/transport/hmac/ripemd160.rb +11 -0
  132. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/lib/net/ssh/transport/hmac/sha1.rb +11 -0
  133. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/lib/net/ssh/transport/hmac/sha1_96.rb +9 -0
  134. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/lib/net/ssh/transport/hmac/sha2_256.rb +11 -0
  135. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/lib/net/ssh/transport/hmac/sha2_256_96.rb +9 -0
  136. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/lib/net/ssh/transport/hmac/sha2_256_etm.rb +12 -0
  137. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/lib/net/ssh/transport/hmac/sha2_512.rb +11 -0
  138. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/lib/net/ssh/transport/hmac/sha2_512_96.rb +9 -0
  139. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/lib/net/ssh/transport/hmac/sha2_512_etm.rb +12 -0
  140. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/lib/net/ssh/transport/hmac.rb +47 -0
  141. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/lib/net/ssh/transport/identity_cipher.rb +65 -0
  142. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/lib/net/ssh/transport/kex/abstract.rb +130 -0
  143. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/lib/net/ssh/transport/kex/abstract5656.rb +72 -0
  144. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/lib/net/ssh/transport/kex/curve25519_sha256.rb +39 -0
  145. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/lib/net/ssh/transport/kex/curve25519_sha256_loader.rb +30 -0
  146. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/lib/net/ssh/transport/kex/diffie_hellman_group14_sha1.rb +37 -0
  147. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/lib/net/ssh/transport/kex/diffie_hellman_group14_sha256.rb +11 -0
  148. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/lib/net/ssh/transport/kex/diffie_hellman_group1_sha1.rb +122 -0
  149. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha1.rb +72 -0
  150. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha256.rb +11 -0
  151. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/lib/net/ssh/transport/kex/ecdh_sha2_nistp256.rb +39 -0
  152. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/lib/net/ssh/transport/kex/ecdh_sha2_nistp384.rb +21 -0
  153. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/lib/net/ssh/transport/kex/ecdh_sha2_nistp521.rb +21 -0
  154. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/lib/net/ssh/transport/kex.rb +31 -0
  155. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/lib/net/ssh/transport/key_expander.rb +30 -0
  156. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/lib/net/ssh/transport/openssl.rb +274 -0
  157. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/lib/net/ssh/transport/openssl_cipher_extensions.rb +8 -0
  158. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/lib/net/ssh/transport/packet_stream.rb +301 -0
  159. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/lib/net/ssh/transport/server_version.rb +77 -0
  160. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/lib/net/ssh/transport/session.rb +354 -0
  161. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/lib/net/ssh/transport/state.rb +208 -0
  162. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/lib/net/ssh/verifiers/accept_new.rb +33 -0
  163. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/lib/net/ssh/verifiers/accept_new_or_local_tunnel.rb +33 -0
  164. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/lib/net/ssh/verifiers/always.rb +58 -0
  165. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/lib/net/ssh/verifiers/never.rb +19 -0
  166. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/lib/net/ssh/version.rb +68 -0
  167. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/lib/net/ssh.rb +338 -0
  168. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/net-ssh-public_cert.pem +20 -0
  169. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/net-ssh.gemspec +46 -0
  170. data/vendor/bundle/ruby/3.2.0/gems/net-ssh-7.2.1/support/ssh_tunnel_bug.rb +65 -0
  171. data/vendor/bundle/ruby/3.2.0/specifications/dddr-1.0.8.gemspec +27 -0
  172. data/vendor/bundle/ruby/3.2.0/specifications/dddr-1.1.0.gemspec +27 -0
  173. data/vendor/bundle/ruby/3.2.0/specifications/dddr-1.1.1.gemspec +27 -0
  174. data/vendor/bundle/ruby/3.2.0/specifications/net-ssh-7.2.1.gemspec +38 -0
  175. metadata +174 -9
  176. data/config/manifest.yml +0 -5
  177. data/lib/harbr/job.rb +0 -252
  178. data/lib/harbr/lxd/job.rb +0 -119
  179. data/lib/harbr/lxd/setup.rb +0 -45
@@ -0,0 +1,94 @@
1
+ require 'net/ssh/transport/session'
2
+ require 'net/ssh/connection/session'
3
+ require 'net/ssh/test/kex'
4
+ require 'net/ssh/test/socket'
5
+
6
+ module Net
7
+ module SSH
8
+ # This module may be used in unit tests, for when you want to test that your
9
+ # SSH state machines are really doing what you expect they are doing. You will
10
+ # typically include this module in your unit test class, and then build a
11
+ # "story" of expected sends and receives:
12
+ #
13
+ # require 'minitest/autorun'
14
+ # require 'net/ssh/test'
15
+ #
16
+ # class MyTest < Minitest::Test
17
+ # include Net::SSH::Test
18
+ #
19
+ # def test_exec_via_channel_works
20
+ # story do |session|
21
+ # channel = session.opens_channel
22
+ # channel.sends_exec "ls"
23
+ # channel.gets_data "result of ls"
24
+ # channel.gets_close
25
+ # channel.sends_close
26
+ # end
27
+ #
28
+ # assert_scripted do
29
+ # result = nil
30
+ #
31
+ # connection.open_channel do |ch|
32
+ # ch.exec("ls") do |success|
33
+ # ch.on_data { |c, data| result = data }
34
+ # ch.on_close { |c| c.close }
35
+ # end
36
+ # end
37
+ #
38
+ # connection.loop
39
+ # assert_equal "result of ls", result
40
+ # end
41
+ # end
42
+ # end
43
+ #
44
+ # See Net::SSH::Test::Channel and Net::SSH::Test::Script for more options.
45
+ #
46
+ # Note that the Net::SSH::Test system is rather finicky yet, and can be kind
47
+ # of frustrating to get working. Any suggestions for improvement will be
48
+ # welcome!
49
+ module Test
50
+ # If a block is given, yields the script for the test socket (#socket).
51
+ # Otherwise, simply returns the socket's script. See Net::SSH::Test::Script.
52
+ def story
53
+ Net::SSH::Test::Extensions::IO.with_test_extension { yield socket.script if block_given? }
54
+ return socket.script
55
+ end
56
+
57
+ # Returns the test socket instance to use for these tests (see
58
+ # Net::SSH::Test::Socket).
59
+ def socket(options = {})
60
+ @socket ||= Net::SSH::Test::Socket.new
61
+ end
62
+
63
+ # Returns the connection session (Net::SSH::Connection::Session) for use
64
+ # in these tests. It is a fully functional SSH session, operating over
65
+ # a mock socket (#socket).
66
+ def connection(options = {})
67
+ @connection ||= Net::SSH::Connection::Session.new(transport(options), options)
68
+ end
69
+
70
+ # Returns the transport session (Net::SSH::Transport::Session) for use
71
+ # in these tests. It is a fully functional SSH transport session, operating
72
+ # over a mock socket (#socket).
73
+ def transport(options = {})
74
+ @transport ||= Net::SSH::Transport::Session.new(
75
+ options[:host] || "localhost",
76
+ options.merge(kex: "test", host_key: "ssh-rsa", append_all_supported_algorithms: true, verify_host_key: :never, proxy: socket(options))
77
+ )
78
+ end
79
+
80
+ # First asserts that a story has been described (see #story). Then yields,
81
+ # and then asserts that all items described in the script have been
82
+ # processed. Typically, this is called immediately after a story has
83
+ # been built, and the SSH commands being tested are then executed within
84
+ # the block passed to this assertion.
85
+ def assert_scripted
86
+ raise "there is no script to be processed" if socket.script.events.empty?
87
+
88
+ Net::SSH::Test::Extensions::IO.with_test_extension { yield }
89
+ assert socket.script.events.empty?, "there should not be any remaining scripted events, but there are still" \
90
+ "#{socket.script.events.length} pending"
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,524 @@
1
+ require 'net/ssh/buffer'
2
+ require 'net/ssh/known_hosts'
3
+ require 'net/ssh/loggable'
4
+ require 'net/ssh/transport/cipher_factory'
5
+ require 'net/ssh/transport/constants'
6
+ require 'net/ssh/transport/hmac'
7
+ require 'net/ssh/transport/kex'
8
+ require 'net/ssh/transport/kex/curve25519_sha256_loader'
9
+ require 'net/ssh/transport/server_version'
10
+ require 'net/ssh/authentication/ed25519_loader'
11
+
12
+ module Net
13
+ module SSH
14
+ module Transport
15
+ # Implements the higher-level logic behind an SSH key-exchange. It handles
16
+ # both the initial exchange, as well as subsequent re-exchanges (as needed).
17
+ # It also encapsulates the negotiation of the algorithms, and provides a
18
+ # single point of access to the negotiated algorithms.
19
+ #
20
+ # You will never instantiate or reference this directly. It is used
21
+ # internally by the transport layer.
22
+ class Algorithms
23
+ include Loggable
24
+ include Constants
25
+
26
+ # Define the default algorithms, in order of preference, supported by Net::SSH.
27
+ DEFAULT_ALGORITHMS = {
28
+ host_key: %w[ecdsa-sha2-nistp521-cert-v01@openssh.com
29
+ ecdsa-sha2-nistp384-cert-v01@openssh.com
30
+ ecdsa-sha2-nistp256-cert-v01@openssh.com
31
+ ecdsa-sha2-nistp521
32
+ ecdsa-sha2-nistp384
33
+ ecdsa-sha2-nistp256
34
+ ssh-rsa-cert-v01@openssh.com
35
+ ssh-rsa-cert-v00@openssh.com
36
+ ssh-rsa
37
+ rsa-sha2-256
38
+ rsa-sha2-512],
39
+
40
+ kex: %w[ecdh-sha2-nistp521
41
+ ecdh-sha2-nistp384
42
+ ecdh-sha2-nistp256
43
+ diffie-hellman-group-exchange-sha256
44
+ diffie-hellman-group14-sha256
45
+ diffie-hellman-group14-sha1],
46
+
47
+ encryption: %w[aes256-ctr aes192-ctr aes128-ctr],
48
+
49
+ hmac: %w[hmac-sha2-512-etm@openssh.com hmac-sha2-256-etm@openssh.com
50
+ hmac-sha2-512 hmac-sha2-256
51
+ hmac-sha1]
52
+ }.freeze
53
+
54
+ if Net::SSH::Transport::ChaCha20Poly1305CipherLoader::LOADED
55
+ DEFAULT_ALGORITHMS[:encryption].unshift(
56
+ 'chacha20-poly1305@openssh.com'
57
+ )
58
+ end
59
+ if Net::SSH::Authentication::ED25519Loader::LOADED
60
+ DEFAULT_ALGORITHMS[:host_key].unshift(
61
+ 'ssh-ed25519-cert-v01@openssh.com',
62
+ 'ssh-ed25519'
63
+ )
64
+ end
65
+
66
+ if Net::SSH::Transport::Kex::Curve25519Sha256Loader::LOADED
67
+ DEFAULT_ALGORITHMS[:kex].unshift(
68
+ 'curve25519-sha256',
69
+ 'curve25519-sha256@libssh.org'
70
+ )
71
+ end
72
+
73
+ # Define all algorithms, with the deprecated, supported by Net::SSH.
74
+ ALGORITHMS = {
75
+ host_key: DEFAULT_ALGORITHMS[:host_key] + %w[ssh-dss],
76
+
77
+ kex: DEFAULT_ALGORITHMS[:kex] +
78
+ %w[diffie-hellman-group-exchange-sha1
79
+ diffie-hellman-group1-sha1],
80
+
81
+ encryption: DEFAULT_ALGORITHMS[:encryption] +
82
+ %w[aes256-cbc aes192-cbc aes128-cbc
83
+ rijndael-cbc@lysator.liu.se
84
+ blowfish-ctr blowfish-cbc
85
+ cast128-ctr cast128-cbc
86
+ 3des-ctr 3des-cbc
87
+ idea-cbc
88
+ none],
89
+
90
+ hmac: DEFAULT_ALGORITHMS[:hmac] +
91
+ %w[hmac-sha2-512-96 hmac-sha2-256-96
92
+ hmac-sha1-96
93
+ hmac-ripemd160 hmac-ripemd160@openssh.com
94
+ hmac-md5 hmac-md5-96
95
+ none],
96
+
97
+ compression: %w[none zlib@openssh.com zlib],
98
+ language: %w[]
99
+ }.freeze
100
+
101
+ # The underlying transport layer session that supports this object
102
+ attr_reader :session
103
+
104
+ # The hash of options used to initialize this object
105
+ attr_reader :options
106
+
107
+ # The kex algorithm to use settled on between the client and server.
108
+ attr_reader :kex
109
+
110
+ # The type of host key that will be used for this session.
111
+ attr_reader :host_key
112
+
113
+ # The type of the cipher to use to encrypt packets sent from the client to
114
+ # the server.
115
+ attr_reader :encryption_client
116
+
117
+ # The type of the cipher to use to decrypt packets arriving from the server.
118
+ attr_reader :encryption_server
119
+
120
+ # The type of HMAC to use to sign packets sent by the client.
121
+ attr_reader :hmac_client
122
+
123
+ # The type of HMAC to use to validate packets arriving from the server.
124
+ attr_reader :hmac_server
125
+
126
+ # The type of compression to use to compress packets being sent by the client.
127
+ attr_reader :compression_client
128
+
129
+ # The type of compression to use to decompress packets arriving from the server.
130
+ attr_reader :compression_server
131
+
132
+ # The language that will be used in messages sent by the client.
133
+ attr_reader :language_client
134
+
135
+ # The language that will be used in messages sent from the server.
136
+ attr_reader :language_server
137
+
138
+ # The hash of algorithms preferred by the client, which will be told to
139
+ # the server during algorithm negotiation.
140
+ attr_reader :algorithms
141
+
142
+ # The session-id for this session, as decided during the initial key exchange.
143
+ attr_reader :session_id
144
+
145
+ # Returns true if the given packet can be processed during a key-exchange.
146
+ def self.allowed_packet?(packet)
147
+ (1..4).include?(packet.type) ||
148
+ (6..19).include?(packet.type) ||
149
+ (21..49).include?(packet.type)
150
+ end
151
+
152
+ # Instantiates a new Algorithms object, and prepares the hash of preferred
153
+ # algorithms based on the options parameter and the ALGORITHMS constant.
154
+ def initialize(session, options = {})
155
+ @session = session
156
+ @logger = session.logger
157
+ @options = options
158
+ @algorithms = {}
159
+ @pending = @initialized = false
160
+ @client_packet = @server_packet = nil
161
+ prepare_preferred_algorithms!
162
+ end
163
+
164
+ # Start the algorithm negotation
165
+ def start
166
+ raise ArgumentError, "Cannot call start if it's negotiation started or done" if @pending || @initialized
167
+
168
+ send_kexinit
169
+ end
170
+
171
+ # Request a rekey operation. This will return immediately, and does not
172
+ # actually perform the rekey operation. It does cause the session to change
173
+ # state, however--until the key exchange finishes, no new packets will be
174
+ # processed.
175
+ def rekey!
176
+ @client_packet = @server_packet = nil
177
+ @initialized = false
178
+ send_kexinit
179
+ end
180
+
181
+ # Called by the transport layer when a KEXINIT packet is received, indicating
182
+ # that the server wants to exchange keys. This can be spontaneous, or it
183
+ # can be in response to a client-initiated rekey request (see #rekey!). Either
184
+ # way, this will block until the key exchange completes.
185
+ def accept_kexinit(packet)
186
+ info { "got KEXINIT from server" }
187
+ @server_data = parse_server_algorithm_packet(packet)
188
+ @server_packet = @server_data[:raw]
189
+ if !pending?
190
+ send_kexinit
191
+ else
192
+ proceed!
193
+ end
194
+ end
195
+
196
+ # A convenience method for accessing the list of preferred types for a
197
+ # specific algorithm (see #algorithms).
198
+ def [](key)
199
+ algorithms[key]
200
+ end
201
+
202
+ # Returns +true+ if a key-exchange is pending. This will be true from the
203
+ # moment either the client or server requests the key exchange, until the
204
+ # exchange completes. While an exchange is pending, only a limited number
205
+ # of packets are allowed, so event processing essentially stops during this
206
+ # period.
207
+ def pending?
208
+ @pending
209
+ end
210
+
211
+ # Returns true if no exchange is pending, and otherwise returns true or
212
+ # false depending on whether the given packet is of a type that is allowed
213
+ # during a key exchange.
214
+ def allow?(packet)
215
+ !pending? || Algorithms.allowed_packet?(packet)
216
+ end
217
+
218
+ # Returns true if the algorithms have been negotiated at all.
219
+ def initialized?
220
+ @initialized
221
+ end
222
+
223
+ def host_key_format
224
+ case host_key
225
+ when /^([a-z0-9-]+)-cert-v\d{2}@openssh.com$/
226
+ Regexp.last_match[1]
227
+ else
228
+ host_key
229
+ end
230
+ end
231
+
232
+ private
233
+
234
+ # Sends a KEXINIT packet to the server. If a server KEXINIT has already
235
+ # been received, this will then invoke #proceed! to proceed with the key
236
+ # exchange, otherwise it returns immediately (but sets the object to the
237
+ # pending state).
238
+ def send_kexinit
239
+ info { "sending KEXINIT" }
240
+ @pending = true
241
+ packet = build_client_algorithm_packet
242
+ @client_packet = packet.to_s
243
+ session.send_message(packet)
244
+ proceed! if @server_packet
245
+ end
246
+
247
+ # After both client and server have sent their KEXINIT packets, this
248
+ # will do the algorithm negotiation and key exchange. Once both finish,
249
+ # the object leaves the pending state and the method returns.
250
+ def proceed!
251
+ info { "negotiating algorithms" }
252
+ negotiate_algorithms
253
+ exchange_keys
254
+ @pending = false
255
+ end
256
+
257
+ # Prepares the list of preferred algorithms, based on the options hash
258
+ # that was given when the object was constructed, and the ALGORITHMS
259
+ # constant. Also, when determining the host_key type to use, the known
260
+ # hosts files are examined to see if the host has ever sent a host_key
261
+ # before, and if so, that key type is used as the preferred type for
262
+ # communicating with this server.
263
+ def prepare_preferred_algorithms!
264
+ options[:compression] = %w[zlib@openssh.com zlib] if options[:compression] == true
265
+
266
+ ALGORITHMS.each do |algorithm, supported|
267
+ algorithms[algorithm] = compose_algorithm_list(
268
+ supported, options[algorithm] || DEFAULT_ALGORITHMS[algorithm],
269
+ options[:append_all_supported_algorithms]
270
+ )
271
+ end
272
+
273
+ # for convention, make sure our list has the same keys as the server
274
+ # list
275
+
276
+ algorithms[:encryption_client ] = algorithms[:encryption_server ] = algorithms[:encryption]
277
+ algorithms[:hmac_client ] = algorithms[:hmac_server ] = algorithms[:hmac]
278
+ algorithms[:compression_client] = algorithms[:compression_server] = algorithms[:compression]
279
+ algorithms[:language_client ] = algorithms[:language_server ] = algorithms[:language]
280
+
281
+ if !options.key?(:host_key)
282
+ # make sure the host keys are specified in preference order, where any
283
+ # existing known key for the host has preference.
284
+
285
+ existing_keys = session.host_keys
286
+ host_keys = existing_keys.flat_map { |key| key.respond_to?(:ssh_types) ? key.ssh_types : [key.ssh_type] }.uniq
287
+ algorithms[:host_key].each do |name|
288
+ host_keys << name unless host_keys.include?(name)
289
+ end
290
+ algorithms[:host_key] = host_keys
291
+ end
292
+ end
293
+
294
+ # Composes the list of algorithms by taking supported algorithms and matching with supplied options.
295
+ def compose_algorithm_list(supported, option, append_all_supported_algorithms = false)
296
+ return supported.dup unless option
297
+
298
+ list = []
299
+ option = Array(option).compact.uniq
300
+
301
+ if option.first && option.first.start_with?('+', '-')
302
+ list = supported.dup
303
+
304
+ appends = option.select { |opt| opt.start_with?('+') }.map { |opt| opt[1..-1] }
305
+ deletions = option.select { |opt| opt.start_with?('-') }.map { |opt| opt[1..-1] }
306
+
307
+ list.concat(appends)
308
+
309
+ deletions.each do |opt|
310
+ if opt.include?('*')
311
+ opt_escaped = Regexp.escape(opt)
312
+ algo_re = /\A#{opt_escaped.gsub('\*', '[A-Za-z\d\-@\.]*')}\z/
313
+ list.delete_if { |existing_opt| algo_re.match(existing_opt) }
314
+ else
315
+ list.delete(opt)
316
+ end
317
+ end
318
+
319
+ list.uniq!
320
+ else
321
+ list = option
322
+
323
+ if append_all_supported_algorithms
324
+ supported.each { |name| list << name unless list.include?(name) }
325
+ end
326
+ end
327
+
328
+ unsupported = []
329
+ list.select! do |name|
330
+ is_supported = supported.include?(name)
331
+ unsupported << name unless is_supported
332
+ is_supported
333
+ end
334
+
335
+ lwarn { %(unsupported algorithm: `#{unsupported}') } unless unsupported.empty?
336
+
337
+ list
338
+ end
339
+
340
+ # Parses a KEXINIT packet from the server.
341
+ def parse_server_algorithm_packet(packet)
342
+ data = { raw: packet.content }
343
+
344
+ packet.read(16) # skip the cookie value
345
+
346
+ data[:kex] = packet.read_string.split(/,/)
347
+ data[:host_key] = packet.read_string.split(/,/)
348
+ data[:encryption_client] = packet.read_string.split(/,/)
349
+ data[:encryption_server] = packet.read_string.split(/,/)
350
+ data[:hmac_client] = packet.read_string.split(/,/)
351
+ data[:hmac_server] = packet.read_string.split(/,/)
352
+ data[:compression_client] = packet.read_string.split(/,/)
353
+ data[:compression_server] = packet.read_string.split(/,/)
354
+ data[:language_client] = packet.read_string.split(/,/)
355
+ data[:language_server] = packet.read_string.split(/,/)
356
+
357
+ # TODO: if first_kex_packet_follows, we need to try to skip the
358
+ # actual kexinit stuff and try to guess what the server is doing...
359
+ # need to read more about this scenario.
360
+ # first_kex_packet_follows = packet.read_bool
361
+
362
+ return data
363
+ end
364
+
365
+ # Given the #algorithms map of preferred algorithm types, this constructs
366
+ # a KEXINIT packet to send to the server. It does not actually send it,
367
+ # it simply builds the packet and returns it.
368
+ def build_client_algorithm_packet
369
+ kex = algorithms[:kex].join(",")
370
+ host_key = algorithms[:host_key].join(",")
371
+ encryption = algorithms[:encryption].join(",")
372
+ hmac = algorithms[:hmac].join(",")
373
+ compression = algorithms[:compression].join(",")
374
+ language = algorithms[:language].join(",")
375
+
376
+ Net::SSH::Buffer.from(:byte, KEXINIT,
377
+ :long, [rand(0xFFFFFFFF), rand(0xFFFFFFFF), rand(0xFFFFFFFF), rand(0xFFFFFFFF)],
378
+ :mstring, [kex, host_key, encryption, encryption, hmac, hmac],
379
+ :mstring, [compression, compression, language, language],
380
+ :bool, false, :long, 0)
381
+ end
382
+
383
+ # Given the parsed server KEX packet, and the client's preferred algorithm
384
+ # lists in #algorithms, determine which preferred algorithms each has
385
+ # in common and set those as the selected algorithms. If, for any algorithm,
386
+ # no type can be settled on, an exception is raised.
387
+ def negotiate_algorithms
388
+ @kex = negotiate(:kex)
389
+ @host_key = negotiate(:host_key)
390
+ @encryption_client = negotiate(:encryption_client)
391
+ @encryption_server = negotiate(:encryption_server)
392
+ @hmac_client = negotiate(:hmac_client)
393
+ @hmac_server = negotiate(:hmac_server)
394
+ @compression_client = negotiate(:compression_client)
395
+ @compression_server = negotiate(:compression_server)
396
+ @language_client = negotiate(:language_client) rescue ""
397
+ @language_server = negotiate(:language_server) rescue ""
398
+
399
+ debug do
400
+ "negotiated:\n" +
401
+ %i[kex host_key encryption_server encryption_client hmac_client hmac_server
402
+ compression_client compression_server language_client language_server].map do |key|
403
+ "* #{key}: #{instance_variable_get("@#{key}")}"
404
+ end.join("\n")
405
+ end
406
+ end
407
+
408
+ # Negotiates a single algorithm based on the preferences reported by the
409
+ # server and those set by the client. This is called by
410
+ # #negotiate_algorithms.
411
+ def negotiate(algorithm)
412
+ match = self[algorithm].find { |item| @server_data[algorithm].include?(item) }
413
+
414
+ if match.nil?
415
+ raise Net::SSH::Exception, "could not settle on #{algorithm} algorithm\n"\
416
+ "Server #{algorithm} preferences: #{@server_data[algorithm].join(',')}\n"\
417
+ "Client #{algorithm} preferences: #{self[algorithm].join(',')}"
418
+ end
419
+
420
+ return match
421
+ end
422
+
423
+ # Considers the sizes of the keys and block-sizes for the selected ciphers,
424
+ # and the lengths of the hmacs, and returns the largest as the byte requirement
425
+ # for the key-exchange algorithm.
426
+ def kex_byte_requirement
427
+ sizes = [8] # require at least 8 bytes
428
+
429
+ sizes.concat(CipherFactory.get_lengths(encryption_client))
430
+ sizes.concat(CipherFactory.get_lengths(encryption_server))
431
+
432
+ sizes << HMAC.key_length(hmac_client)
433
+ sizes << HMAC.key_length(hmac_server)
434
+
435
+ sizes.max
436
+ end
437
+
438
+ # Instantiates one of the Transport::Kex classes (based on the negotiated
439
+ # kex algorithm), and uses it to exchange keys. Then, the ciphers and
440
+ # HMACs are initialized and fed to the transport layer, to be used in
441
+ # further communication with the server.
442
+ def exchange_keys
443
+ debug { "exchanging keys" }
444
+
445
+ need_bytes = kex_byte_requirement
446
+ algorithm = Kex::MAP[kex].new(self, session,
447
+ client_version_string: Net::SSH::Transport::ServerVersion::PROTO_VERSION,
448
+ server_version_string: session.server_version.version,
449
+ server_algorithm_packet: @server_packet,
450
+ client_algorithm_packet: @client_packet,
451
+ need_bytes: need_bytes,
452
+ minimum_dh_bits: options[:minimum_dh_bits],
453
+ logger: logger)
454
+ result = algorithm.exchange_keys
455
+
456
+ secret = result[:shared_secret].to_ssh
457
+ hash = result[:session_id]
458
+ digester = result[:hashing_algorithm]
459
+
460
+ @session_id ||= hash
461
+
462
+ key = Proc.new { |salt| digester.digest(secret + hash + salt + @session_id) }
463
+
464
+ iv_client = key["A"]
465
+ iv_server = key["B"]
466
+ key_client = key["C"]
467
+ key_server = key["D"]
468
+ mac_key_client = key["E"]
469
+ mac_key_server = key["F"]
470
+
471
+ parameters = { shared: secret, hash: hash, digester: digester }
472
+
473
+ cipher_client = CipherFactory.get(
474
+ encryption_client,
475
+ parameters.merge(iv: iv_client, key: key_client, encrypt: true)
476
+ )
477
+ cipher_server = CipherFactory.get(
478
+ encryption_server,
479
+ parameters.merge(iv: iv_server, key: key_server, decrypt: true)
480
+ )
481
+
482
+ mac_client =
483
+ if cipher_client.implicit_mac?
484
+ cipher_client.implicit_mac
485
+ else
486
+ HMAC.get(hmac_client, mac_key_client, parameters)
487
+ end
488
+ mac_server =
489
+ if cipher_server.implicit_mac?
490
+ cipher_server.implicit_mac
491
+ else
492
+ HMAC.get(hmac_server, mac_key_server, parameters)
493
+ end
494
+
495
+ session.configure_client cipher: cipher_client, hmac: mac_client,
496
+ compression: normalize_compression_name(compression_client),
497
+ compression_level: options[:compression_level],
498
+ rekey_limit: options[:rekey_limit],
499
+ max_packets: options[:rekey_packet_limit],
500
+ max_blocks: options[:rekey_blocks_limit]
501
+
502
+ session.configure_server cipher: cipher_server, hmac: mac_server,
503
+ compression: normalize_compression_name(compression_server),
504
+ rekey_limit: options[:rekey_limit],
505
+ max_packets: options[:rekey_packet_limit],
506
+ max_blocks: options[:rekey_blocks_limit]
507
+
508
+ @initialized = true
509
+ end
510
+
511
+ # Given the SSH name for some compression algorithm, return a normalized
512
+ # name as a symbol.
513
+ def normalize_compression_name(name)
514
+ case name
515
+ when "none" then false
516
+ when "zlib" then :standard
517
+ when "zlib@openssh.com" then :delayed
518
+ else raise ArgumentError, "unknown compression type `#{name}'"
519
+ end
520
+ end
521
+ end
522
+ end
523
+ end
524
+ end