network-infra-utility 0.2.0 → 0.5.0

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.
Files changed (119) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +21 -0
  3. data/CHANGELOG.md +31 -4
  4. data/Gemfile +2 -0
  5. data/Gemfile.lock +70 -0
  6. data/Rakefile +1 -1
  7. data/bin/dns-query +834 -0
  8. data/bin/geo-doc +135 -0
  9. data/bin/geo-get +1 -1
  10. data/document/ASNum/346/250/241/345/235/227/345/212/237/350/203/275/350/257/264/346/230/216.md +242 -0
  11. data/document/DNSQuery/345/267/245/345/205/267/344/275/277/347/224/250/346/226/271/346/263/225.md +248 -0
  12. data/document/Geo/345/221/275/344/273/244/345/267/245/345/205/267/344/275/277/347/224/250/346/226/271/346/263/225.md +441 -0
  13. data/document/IP/346/250/241/345/235/227/345/212/237/350/203/275/350/257/264/346/230/216.md +297 -0
  14. data/document/MAC/346/250/241/345/235/227/345/212/237/350/203/275/350/257/264/346/230/216.md +296 -0
  15. data/document/SSH/350/277/236/346/216/245/345/256/242/346/210/267/347/253/257/344/275/277/347/224/250/346/226/271/346/263/225.md +764 -0
  16. data/network-infra-utility.gemspec +4 -2
  17. data/network.rb +3 -1
  18. data/service/geodb/GeoAPI.md +1 -0
  19. data/service/geodb/geodb.rb +278 -1
  20. data/service/ssh/README.md +942 -0
  21. data/service/ssh/bin/ssh-client +198 -0
  22. data/service/ssh/design/SSH/350/277/236/346/216/245/345/256/242/346/210/267/347/253/257/345/212/237/350/203/275/351/234/200/346/261/202/346/226/207/346/241/243.md +292 -0
  23. data/service/ssh/design/SSH/350/277/236/346/216/245/345/256/242/346/210/267/347/253/257/350/257/246/347/273/206/350/256/276/350/256/241/346/226/207/346/241/243.md +1521 -0
  24. data/service/ssh/design/SSH/350/277/236/346/216/245/345/256/242/346/210/267/347/253/257/350/275/257/344/273/266/350/256/276/350/256/241/346/226/207/346/241/243.md +2493 -0
  25. data/service/ssh/ext/ssh_core/bin/ssh_core.cmd +28 -0
  26. data/service/ssh/ext/ssh_core/config/sys.config +0 -0
  27. data/service/ssh/ext/ssh_core/config/vm.args +0 -0
  28. data/service/ssh/ext/ssh_core/local_deps/jsx/CHECKSUM +1 -0
  29. data/service/ssh/ext/ssh_core/local_deps/jsx/LICENSE +21 -0
  30. data/service/ssh/ext/ssh_core/local_deps/jsx/README.md +696 -0
  31. data/service/ssh/ext/ssh_core/local_deps/jsx/VERSION +1 -0
  32. data/service/ssh/ext/ssh_core/local_deps/jsx/contents.tar.gz +0 -0
  33. data/service/ssh/ext/ssh_core/local_deps/jsx/metadata.config +15 -0
  34. data/service/ssh/ext/ssh_core/local_deps/jsx/rebar.config +17 -0
  35. data/service/ssh/ext/ssh_core/local_deps/jsx/rebar.lock +1 -0
  36. data/service/ssh/ext/ssh_core/local_deps/jsx/src/jsx.app.src +10 -0
  37. data/service/ssh/ext/ssh_core/local_deps/jsx/src/jsx.erl +506 -0
  38. data/service/ssh/ext/ssh_core/local_deps/jsx/src/jsx_config.erl +393 -0
  39. data/service/ssh/ext/ssh_core/local_deps/jsx/src/jsx_config.hrl +18 -0
  40. data/service/ssh/ext/ssh_core/local_deps/jsx/src/jsx_consult.erl +81 -0
  41. data/service/ssh/ext/ssh_core/local_deps/jsx/src/jsx_decoder.erl +1909 -0
  42. data/service/ssh/ext/ssh_core/local_deps/jsx/src/jsx_encoder.erl +116 -0
  43. data/service/ssh/ext/ssh_core/local_deps/jsx/src/jsx_parser.erl +1214 -0
  44. data/service/ssh/ext/ssh_core/local_deps/jsx/src/jsx_to_json.erl +408 -0
  45. data/service/ssh/ext/ssh_core/local_deps/jsx/src/jsx_to_term.erl +389 -0
  46. data/service/ssh/ext/ssh_core/local_deps/jsx/src/jsx_verify.erl +121 -0
  47. data/service/ssh/ext/ssh_core/local_deps/jsx/src/src/jsx.erl +506 -0
  48. data/service/ssh/ext/ssh_core/local_deps/jsx/src/src/jsx_config.erl +393 -0
  49. data/service/ssh/ext/ssh_core/local_deps/jsx/src/src/jsx_config.hrl +18 -0
  50. data/service/ssh/ext/ssh_core/local_deps/jsx/src/src/jsx_consult.erl +81 -0
  51. data/service/ssh/ext/ssh_core/local_deps/jsx/src/src/jsx_decoder.erl +1909 -0
  52. data/service/ssh/ext/ssh_core/local_deps/jsx/src/src/jsx_encoder.erl +116 -0
  53. data/service/ssh/ext/ssh_core/local_deps/jsx/src/src/jsx_parser.erl +1214 -0
  54. data/service/ssh/ext/ssh_core/local_deps/jsx/src/src/jsx_to_json.erl +408 -0
  55. data/service/ssh/ext/ssh_core/local_deps/jsx/src/src/jsx_to_term.erl +389 -0
  56. data/service/ssh/ext/ssh_core/local_deps/jsx/src/src/jsx_verify.erl +121 -0
  57. data/service/ssh/ext/ssh_core/rebar.config +24 -0
  58. data/service/ssh/ext/ssh_core/rebar.lock +1 -0
  59. data/service/ssh/ext/ssh_core/src/ssh_auth_engine.erl +156 -0
  60. data/service/ssh/ext/ssh_core/src/ssh_channel_stm.erl +232 -0
  61. data/service/ssh/ext/ssh_core/src/ssh_codec.erl +83 -0
  62. data/service/ssh/ext/ssh_core/src/ssh_conn_sup.erl +48 -0
  63. data/service/ssh/ext/ssh_core/src/ssh_conn_worker.erl +535 -0
  64. data/service/ssh/ext/ssh_core/src/ssh_core.app.src +36 -0
  65. data/service/ssh/ext/ssh_core/src/ssh_core_app.erl +11 -0
  66. data/service/ssh/ext/ssh_core/src/ssh_core_sup.erl +46 -0
  67. data/service/ssh/ext/ssh_core/src/ssh_infra_sup.erl +104 -0
  68. data/service/ssh/ext/ssh_core/src/ssh_ipc.hrl +80 -0
  69. data/service/ssh/ext/ssh_core/src/ssh_ipc_coalesce.erl +94 -0
  70. data/service/ssh/ext/ssh_core/src/ssh_ipc_gateway.erl +467 -0
  71. data/service/ssh/ext/ssh_core/src/ssh_ipc_proto.erl +95 -0
  72. data/service/ssh/ext/ssh_core/src/ssh_jump_chain.erl +101 -0
  73. data/service/ssh/ext/ssh_core/src/ssh_keepalive_mgr.erl +222 -0
  74. data/service/ssh/ext/ssh_core/src/ssh_known_hosts_proxy.erl +67 -0
  75. data/service/ssh/ext/ssh_core/src/ssh_port_fwd.erl +225 -0
  76. data/service/ssh/ext/ssh_core/src/ssh_sftp_session.erl +250 -0
  77. data/service/ssh/ext/ssh_core/src/ssh_sftp_sup.erl +62 -0
  78. data/service/ssh/ext/ssh_core_rs/Cargo.lock +2345 -0
  79. data/service/ssh/ext/ssh_core_rs/Cargo.toml +30 -0
  80. data/service/ssh/ext/ssh_core_rs/bin/ssh_core_rs +34 -0
  81. data/service/ssh/ext/ssh_core_rs/bin/ssh_core_rs.cmd +40 -0
  82. data/service/ssh/ext/ssh_core_rs/src/channel.rs +296 -0
  83. data/service/ssh/ext/ssh_core_rs/src/coalesce.rs +143 -0
  84. data/service/ssh/ext/ssh_core_rs/src/codec.rs +71 -0
  85. data/service/ssh/ext/ssh_core_rs/src/conn.rs +628 -0
  86. data/service/ssh/ext/ssh_core_rs/src/gateway.rs +389 -0
  87. data/service/ssh/ext/ssh_core_rs/src/handler.rs +293 -0
  88. data/service/ssh/ext/ssh_core_rs/src/keepalive.rs +194 -0
  89. data/service/ssh/ext/ssh_core_rs/src/main.rs +351 -0
  90. data/service/ssh/ext/ssh_core_rs/src/portfwd.rs +378 -0
  91. data/service/ssh/ext/ssh_core_rs/src/proto.rs +198 -0
  92. data/service/ssh/ext/ssh_core_rs/src/sftp.rs +294 -0
  93. data/service/ssh/lib/network_infra_utility/ssh/automation/macro_engine.rb +213 -0
  94. data/service/ssh/lib/network_infra_utility/ssh/client.rb +257 -0
  95. data/service/ssh/lib/network_infra_utility/ssh/config/schema.rb +90 -0
  96. data/service/ssh/lib/network_infra_utility/ssh/config/settings.rb +103 -0
  97. data/service/ssh/lib/network_infra_utility/ssh/config/store.rb +90 -0
  98. data/service/ssh/lib/network_infra_utility/ssh/ipc/coalesce.rb +83 -0
  99. data/service/ssh/lib/network_infra_utility/ssh/ipc/errors.rb +36 -0
  100. data/service/ssh/lib/network_infra_utility/ssh/ipc/router.rb +212 -0
  101. data/service/ssh/lib/network_infra_utility/ssh/ipc/transport.rb +81 -0
  102. data/service/ssh/lib/network_infra_utility/ssh/security/host_key.rb +211 -0
  103. data/service/ssh/lib/network_infra_utility/ssh/security/vault.rb +211 -0
  104. data/service/ssh/lib/network_infra_utility/ssh/session/history.rb +56 -0
  105. data/service/ssh/lib/network_infra_utility/ssh/session/manager.rb +92 -0
  106. data/service/ssh/lib/network_infra_utility/ssh/session/session.rb +109 -0
  107. data/service/ssh/lib/network_infra_utility/ssh/session/tree.rb +95 -0
  108. data/service/ssh/lib/network_infra_utility/ssh/terminal/ansi_parser.rb +435 -0
  109. data/service/ssh/lib/network_infra_utility/ssh/terminal/buffer.rb +78 -0
  110. data/service/ssh/lib/network_infra_utility/ssh/terminal/emulator.rb +159 -0
  111. data/service/ssh/lib/network_infra_utility/ssh/terminal/logger.rb +195 -0
  112. data/service/ssh/lib/network_infra_utility/ssh/terminal/screen.rb +212 -0
  113. data/service/ssh/lib/network_infra_utility/ssh/terminal/theme.rb +127 -0
  114. data/service/ssh/lib/network_infra_utility/ssh/version.rb +7 -0
  115. data/service/ssh/lib/network_infra_utility/ssh.rb +44 -0
  116. data/support/basic/as_num.rb +221 -0
  117. data/support/basic/mac_address.rb +281 -0
  118. data/version.rb +1 -1
  119. metadata +138 -1
@@ -0,0 +1,942 @@
1
+ ---
2
+ AIGC:
3
+ ContentProducer: '001191110102MAD55U9H0F10002'
4
+ ContentPropagator: '001191110102MAD55U9H0F10002'
5
+ Label: '1'
6
+ ProduceID: '327b0e14-d4aa-4064-bd5a-c596fad52e74'
7
+ PropagateID: '327b0e14-d4aa-4064-bd5a-c596fad52e74'
8
+ ReservedCode1: 'c77ee856-ff93-4d59-bbda-652be5dd5d7b'
9
+ ReservedCode2: 'c77ee856-ff93-4d59-bbda-652be5dd5d7b'
10
+ ---
11
+
12
+ # SSH 连接客户端
13
+
14
+ 版本 1.0.0
15
+
16
+ Ruby + 双引擎架构的 SSH 连接客户端。SSH 协议核心由**可插拔双引擎**提供:默认 **Rust 引擎**(`ssh_core_rs`,基于 russh),可选 **Erlang 引擎**(`ssh_core`,基于 OTP ssh);Ruby 负责调度、终端渲染、配置与自动化,与引擎通过本地 socket 上的 JSON-RPC 2.0 通信,两端 IPC 协议完全一致,切换引擎无需改动 Ruby 代码。
17
+
18
+ ---
19
+
20
+ ## 目录
21
+
22
+ - [环境要求](#环境要求)
23
+ - [安装与编译](#安装与编译)
24
+ - [快速开始](#快速开始)
25
+ - [CLI 命令参考](#cli-命令参考)
26
+ - [connect — 连接服务器](#connect--连接服务器)
27
+ - [batch — 批量执行命令](#batch--批量执行命令)
28
+ - [themes — 列出配色主题](#themes--列出配色主题)
29
+ - [version — 显示版本](#version--显示版本)
30
+ - [连接参数详解](#连接参数详解)
31
+ - [配置文件](#配置文件)
32
+ - [settings.yml](#settingsyml)
33
+ - [sessions.yml](#sessionsyml)
34
+ - [known\_hosts.yml](#known_hostsyml)
35
+ - [凭据加密(Vault)](#凭据加密vault)
36
+ - [终端配色主题](#终端配色主题)
37
+ - [编程 API 使用](#编程-api-使用)
38
+ - [Client — 生命周期管理](#client--生命周期管理)
39
+ - [Session — 会话操作](#session--会话操作)
40
+ - [Terminal::Emulator — 终端交互](#terminalemulator--终端交互)
41
+ - [Automation::MacroEngine — 登录宏](#automationmacroengine--登录宏)
42
+ - [Security::HostKey — 主机密钥管理](#securityhostkey--主机密钥管理)
43
+ - [RPC 方法列表](#rpc-方法列表)
44
+ - [架构说明](#架构说明)
45
+ - [启动流程](#启动流程)
46
+ - [分工](#分工)
47
+ - [关键模块在运行时的角色](#关键模块在运行时的角色)
48
+ - [项目结构](#项目结构)
49
+
50
+ ---
51
+
52
+ ## 环境要求
53
+
54
+ | 组件 | 版本要求 | 说明 |
55
+ |------|---------|------|
56
+ | **Ruby** | >= 3.0 | 运行 CLI 和 Ruby 端逻辑 |
57
+ | **Rust** | rustc/cargo(稳定版) | 编译和运行默认 SSH 核心引擎(`ssh_core_rs`) |
58
+ | **Erlang/OTP** | >= 26 | 编译和运行可选 Erlang 引擎(`ssh_core`) |
59
+ | **rebar3** | 任意版本 | 编译 Erlang 引擎 |
60
+ | **OpenSSL** | 系统自带 | Vault 加密 |
61
+
62
+ > **说明**:默认引擎为 Rust(`ssh_core_rs`),仅使用 Rust 即可运行全部功能;Erlang 引擎作为可插拔备选,需要时再安装 Erlang/OTP 与 rebar3。
63
+
64
+ ### Ruby 依赖
65
+
66
+ 以下 gem 在运行时需要(均为标准库或常见 gem):
67
+
68
+ - `thor` — CLI 框架
69
+ - `openssl` — Vault 加密(AES-256-GCM)
70
+ - `yaml` — 配置文件解析
71
+ - `base64` — 数据编码
72
+
73
+ ---
74
+
75
+ ## 安装与编译
76
+
77
+ ### 1. 编译 Rust 核心引擎(默认)
78
+
79
+ ```bash
80
+ cd service/ssh/ext/ssh_core_rs
81
+ cargo build --release
82
+ ```
83
+
84
+ 编译成功后,二进制输出到 `target/release/ssh_core_rs`(Windows 为 `ssh_core_rs.exe`)。
85
+
86
+ > **注意**:`bin/ssh_core_rs`(Linux/macOS)与 `bin/ssh_core_rs.cmd`(Windows)启动脚本会在首次启动时自动编译;也可按上述命令预先编译。
87
+
88
+ ### 2. 编译 Erlang 核心引擎(可选备选)
89
+
90
+ ```bash
91
+ cd service/ssh/ext/ssh_core
92
+ rebar3 compile
93
+ ```
94
+
95
+ 编译成功后,beam 文件输出到 `_build/default/lib/ssh_core/ebin/`。
96
+
97
+ > **注意**:jsx 依赖已内嵌为本地源码(`local_deps/jsx/src/src/`),通过 `src_dirs` 直接编译,无需从 hex.pm 拉取。
98
+
99
+ ### 3. 验证引擎可启动
100
+
101
+ **Rust 引擎:**
102
+
103
+ ```bash
104
+ cd service/ssh/ext/ssh_core_rs
105
+ bin/ssh_core_rs # Linux/macOS;Windows 为 bin\ssh_core_rs.cmd
106
+ ```
107
+
108
+ **Erlang 引擎:**
109
+
110
+ **Linux/macOS:**
111
+
112
+ ```bash
113
+ cd service/ssh/ext/ssh_core
114
+ erl -noshell -noinput -pa _build/default/lib/ssh_core/ebin \
115
+ -eval "application:ensure_all_started(ssh_core), io:format(\"ok~n\"), timer:sleep(1000), halt()."
116
+ ```
117
+
118
+ **Windows:**
119
+
120
+ ```cmd
121
+ cd service\ssh\ext\ssh_core
122
+ bin\ssh_core.cmd
123
+ ```
124
+
125
+ 引擎启动后会生成端点文件(`%TEMP%\ssh_core_<username>.endpoint`),包含 IPC 监听地址和认证 Token。
126
+
127
+ ### 4. 验证 Ruby 端
128
+
129
+ ```bash
130
+ cd service/ssh
131
+ ruby bin/ssh-client version
132
+ # 输出: 1.0.0
133
+
134
+ ruby bin/ssh-client themes
135
+ # 输出所有内置主题名称
136
+ ```
137
+
138
+ ---
139
+
140
+ ## 快速开始
141
+
142
+ ### 连接到服务器
143
+
144
+ ```bash
145
+ # 密钥认证
146
+ ruby bin/ssh-client connect 10.0.0.1 -u admin -i ~/.ssh/id_ed25519
147
+
148
+ # 密码认证(交互输入)
149
+ ruby bin/ssh-client connect 10.0.0.1 -u admin --password
150
+
151
+ # 跳板机连接(语法示例;CLI 默认 Rust 引擎不支持 jumps,见下方注意)
152
+ ruby bin/ssh-client connect 192.168.1.100 -u deploy -J jumpuser@10.0.0.1:22
153
+
154
+ # 指定端口和配色主题
155
+ ruby bin/ssh-client connect 10.0.0.1 -u admin -p 2222 --theme dracula
156
+ ```
157
+
158
+ 连接成功后进入交互模式,输入命令回车发送,输入 `exit` 或 `quit` 断开。
159
+
160
+ > **注意**:`-J` 跳板机参数依赖引擎对 `jumps` 的支持,目前仅 **Erlang 引擎**实现,而 CLI 默认使用 Rust 引擎,且 CLI 层暂未提供后端切换选项,因此 CLI 的 `-J` 在默认配置下不会生效;如需使用跳板机,请通过编程 API 创建 `Client.new(backend: :erlang)` 并传入 `jumps` 参数。
161
+
162
+ ### 编程方式使用
163
+
164
+ ```ruby
165
+ require_relative "service/ssh/lib/network_infra_utility/ssh"
166
+
167
+ client = NetworkInfraUtility::SSH::Client.new
168
+ client.start_engine
169
+
170
+ session = client.connect(
171
+ host: "10.0.0.1",
172
+ port: 22,
173
+ user: "admin",
174
+ key_path: "/home/user/.ssh/id_ed25519"
175
+ )
176
+
177
+ terminal = session.open_terminal
178
+ terminal.puts("show version")
179
+ # ... 读取输出 ...
180
+
181
+ session.disconnect
182
+ client.stop
183
+ ```
184
+
185
+ ---
186
+
187
+ ## CLI 命令参考
188
+
189
+ CLI 入口为 `bin/ssh-client`,基于 [Thor](https://github.com/rails/thor) 框架。
190
+
191
+ ### connect — 连接服务器
192
+
193
+ ```
194
+ ssh-client connect HOST [options]
195
+ ```
196
+
197
+ 连接到指定 SSH 服务器并进入交互终端。
198
+
199
+ | 选项 | 简写 | 类型 | 默认值 | 说明 |
200
+ |------|------|------|--------|------|
201
+ | `--user` | `-u` | String | **必填** | SSH 用户名 |
202
+ | `--port` | `-p` | Integer | 22 | SSH 端口 |
203
+ | `--key` | `-i` | String | — | 私钥文件路径 |
204
+ | `--jump` | `-J` | String | — | 跳板机,格式 `user@host:port`,多个用逗号分隔(**仅 Erlang 引擎支持**,CLI 默认 Rust 引擎下不生效) |
205
+ | `--name` | — | String | — | 会话名称 |
206
+ | `--password` | — | Boolean | false | 使用密码认证(交互式输入,不回显) |
207
+ | `--theme` | — | String | default | 终端配色主题,见[主题列表](#终端配色主题) |
208
+
209
+ **示例:**
210
+
211
+ ```bash
212
+ # 基本连接
213
+ ssh-client connect 10.0.0.1 -u admin
214
+
215
+ # 密钥 + 跳板机 + 自定义端口(-J 仅 Erlang 引擎支持,CLI 默认 Rust 引擎下不生效)
216
+ ssh-client connect 192.168.1.100 -u deploy -p 2222 -i ~/.ssh/deploy_key -J jump@10.0.0.1:22 --theme nord
217
+
218
+ # 密码认证
219
+ ssh-client connect 10.0.0.1 -u admin --password
220
+ # 将提示: Password: (输入不回显)
221
+ ```
222
+
223
+ **交互模式:** 连接后进入读-发-收循环。输入命令回车发送到远端,输入 `exit` 或 `quit` 断开连接,`Ctrl+C` 退出。
224
+
225
+ ### batch — 批量执行命令
226
+
227
+ ```
228
+ ssh-client batch FILE [options]
229
+ ```
230
+
231
+ 从 YAML 文件加载多个会话定义,并行或串行在所有会话上执行同一条命令。
232
+
233
+ | 选项 | 简写 | 类型 | 默认值 | 说明 |
234
+ |------|------|------|--------|------|
235
+ | `--command` | `-c` | String | **必填** | 要执行的命令 |
236
+ | `--serial` | — | Boolean | false | 串行执行(默认并行) |
237
+
238
+ **会话文件格式(YAML):**
239
+
240
+ ```yaml
241
+ # 单条会话(Hash)
242
+ host: 10.0.0.1
243
+ user: admin
244
+ port: 22
245
+ key_path: /home/user/.ssh/id_ed25519
246
+
247
+ # 多条会话(Array)
248
+ - host: 10.0.0.1
249
+ user: admin
250
+ key_path: /home/user/.ssh/id_ed25519
251
+ - host: 10.0.0.2
252
+ user: root
253
+ password: secret
254
+ - host: 10.0.0.3
255
+ user: deploy
256
+ port: 2222
257
+ key_path: /home/user/.ssh/deploy_key
258
+ ```
259
+
260
+ **示例:**
261
+
262
+ ```bash
263
+ # 并行执行(默认)
264
+ ssh-client batch servers.yml -c "uptime"
265
+
266
+ # 串行执行
267
+ ssh-client batch servers.yml -c "show version" --serial
268
+ ```
269
+
270
+ ### themes — 列出配色主题
271
+
272
+ ```
273
+ ssh-client themes
274
+ ```
275
+
276
+ 列出所有内置配色主题名称。
277
+
278
+ ```bash
279
+ $ ssh-client themes
280
+ default
281
+ solarized-dark
282
+ solarized-light
283
+ dracula
284
+ monokai
285
+ nord
286
+ gruvbox-dark
287
+ one-dark
288
+ tokyo-night
289
+ catppuccin-mocha
290
+ github-dark
291
+ ```
292
+
293
+ ### version — 显示版本
294
+
295
+ ```
296
+ ssh-client version
297
+ ```
298
+
299
+ 输出版本号。
300
+
301
+ ---
302
+
303
+ ## 连接参数详解
304
+
305
+ 连接参数(`spec`)是一个 Hash,以下字段被引擎识别:
306
+
307
+ | 字段 | 类型 | 必填 | 说明 |
308
+ |------|------|------|------|
309
+ | `host` | String | 是 | 目标主机 IP 或域名 |
310
+ | `user` | String | 是 | SSH 用户名 |
311
+ | `port` | Integer | 否 | SSH 端口,默认 22 |
312
+ | `name` | String | 否 | 会话显示名称 |
313
+ | `key_path` | String | 否 | 私钥文件路径 |
314
+ | `key_dir` | String | 否 | 密钥搜索目录(默认 `.`,即引擎工作目录) |
315
+ | `auth` | Hash | 否 | 认证配置,见下表 |
316
+ | `jumps` | Array | 否 | 跳板机链(仅 Erlang 引擎),见下表 |
317
+ | `terminal` | Hash | 否 | 终端配置,如 `{ theme: "dracula" }` |
318
+ | `proxy` | Hash | 否 | 代理配置(仅 Erlang 引擎,SOCKS5 / HTTP CONNECT) |
319
+ | `connect_timeout_ms` | Integer | 否 | 连接超时(毫秒),默认 60000 |
320
+ | `inactivity_timeout_s` | Integer | 否 | 空闲断开时间(秒),默认不因空闲断开(由 keepalive 管理) |
321
+ | `algorithms` | Hash | 否 | 自定义算法列表,如 `{ kex: [...], cipher: [...] }` |
322
+
323
+ > **引擎支持差异**:`jumps`(跳板机链)与 `proxy`(代理)目前仅 **Erlang 引擎**实现(OTP ssh 原生支持 + 自研 SOCKS5/HTTP CONNECT 握手);**Rust 引擎暂未实现**,传入会被忽略。若需使用跳板机/代理,请用 `Client.new(backend: :erlang)` 切换后端。
324
+
325
+ ### auth 字段
326
+
327
+ | 认证类型 | auth 值 | 说明 |
328
+ |---------|---------|------|
329
+ | 密钥认证 | `{ type: "publickey" }` | 私钥路径通过 `key_path` 指定 |
330
+ | 密码认证 | `{ type: "password", password: "..." }` | 密码明文或 Vault 引用 |
331
+ | 键盘交互 | `{ type: "keyboard_interactive", responses: ["..."] }` | 交互式认证 |
332
+
333
+ 密码字段支持 [Vault 引用](#凭据加密vault),格式为 `~vault:<key>`。连接前由 Ruby 端自动解密替换。
334
+
335
+ ### jumps 字段
336
+
337
+ 跳板机链,数组形式,连接顺序从前到后(**仅 Erlang 引擎**):
338
+
339
+ ```ruby
340
+ jumps: [
341
+ { user: "jump", host: "10.0.0.1", port: 22 },
342
+ { user: "bastion", host: "192.168.1.1", port: 2222 }
343
+ ]
344
+ ```
345
+
346
+ ### proxy 字段(仅 Erlang 引擎)
347
+
348
+ SOCKS5(默认)或 HTTP CONNECT 代理:
349
+
350
+ ```ruby
351
+ proxy: {
352
+ type: "socks5", # 或 "http",默认 "socks5"
353
+ host: "127.0.0.1",
354
+ port: 1080,
355
+ username: "proxy_user", # 可选
356
+ password: "proxy_pass" # 可选
357
+ }
358
+ ```
359
+
360
+ ---
361
+
362
+ ## 配置文件
363
+
364
+ 所有配置文件默认存放在 `~/.network-infra-utility/` 目录下。
365
+
366
+ ### settings.yml
367
+
368
+ 全局设置文件。
369
+
370
+ ```yaml
371
+ # 主密码(用于 Vault 加解密,请妥善保管)
372
+ master_password: "your-master-password"
373
+
374
+ # 日志目录(不设则默认 ~/.network-infra-utility/logs/)
375
+ log_dir: ~/.network-infra-utility/logs
376
+
377
+ # 配置目录
378
+ config_dir: ~/.network-infra-utility
379
+
380
+ # 默认终端类型
381
+ default_terminal: xterm-256color
382
+
383
+ # 默认回滚行数
384
+ default_scrollback: 10000
385
+
386
+ # 默认心跳间隔(秒)
387
+ default_keepalive_interval: 30
388
+
389
+ # 默认配色主题
390
+ default_theme: dark
391
+ ```
392
+
393
+ ### sessions.yml
394
+
395
+ 会话列表持久化文件。由 `Config::Store` 管理,支持原子写入(先写 `.tmp` 再 rename)。
396
+
397
+ ```yaml
398
+ version: 1
399
+ groups:
400
+ - id: prod
401
+ name: 生产环境
402
+ parent: ~
403
+ collapsed: false
404
+ sessions:
405
+ - id: sess_001
406
+ name: web-server-1
407
+ group: prod
408
+ host: 10.0.0.1
409
+ port: 22
410
+ user: admin
411
+ tags: [web, production]
412
+ auth:
413
+ type: publickey
414
+ terminal:
415
+ theme: dracula
416
+ macro:
417
+ enabled: true
418
+ steps:
419
+ - action: "enable\n"
420
+ wait_pattern: "#"
421
+ delay: 0.5
422
+ on_fail: continue
423
+ - action: "terminal monitor\n"
424
+ wait_pattern: "#"
425
+ delay: 0.3
426
+ on_fail: abort
427
+ ```
428
+
429
+ **会话字段定义(Schema v1):**
430
+
431
+ | 字段 | 类型 | 必填 | 说明 |
432
+ |------|------|------|------|
433
+ | `host` | String | 是 | 主机地址 |
434
+ | `user` | String | 是 | 用户名 |
435
+ | `port` | Integer | 否 | 端口 |
436
+ | `name` | String | 否 | 会话名称 |
437
+ | `group` | String | 否 | 所属分组 ID |
438
+ | `tags` | Array | 否 | 标签列表 |
439
+ | `auth` | Hash | 否 | 认证配置 |
440
+ | `terminal` | Hash | 否 | 终端配置 |
441
+ | `jumps` | Array | 否 | 跳板机链 |
442
+ | `port_forwards` | Array | 否 | 端口转发规则 |
443
+ | `macro` | Hash | 否 | 登录宏配置 |
444
+ | `keepalive` | Hash | 否 | 心跳配置 |
445
+ | `auto_reconnect` | Hash | 否 | 自动重连配置 |
446
+
447
+ ### known_hosts.yml
448
+
449
+ 主机密钥指纹存储。首次连接时提示用户确认,确认后自动保存。
450
+
451
+ ```yaml
452
+ "10.0.0.1:22":
453
+ :fingerprint: "SHA256:abc123..."
454
+ :added_at: "2026-01-15T10:30:00+08:00"
455
+ "192.168.1.100:2222":
456
+ :fingerprint: "SHA256:def456..."
457
+ :added_at: "2026-01-16T14:00:00+08:00"
458
+ ```
459
+
460
+ **裁决逻辑:**
461
+
462
+ | 场景 | 行为 |
463
+ |------|------|
464
+ | 首次连接 | 提示用户确认(`y/N`),确认后保存指纹 |
465
+ | 指纹匹配 | 自动通过,无交互 |
466
+ | 指纹变更 | 拒绝连接并告警,需手动删除条目后重连 |
467
+
468
+ ---
469
+
470
+ ## 凭据加密(Vault)
471
+
472
+ Vault 使用 **AES-256-GCM** 加密算法保护敏感凭据(如 SSH 密码),加密参数:
473
+
474
+ | 参数 | 值 |
475
+ |------|-----|
476
+ | 算法 | AES-256-GCM |
477
+ | KDF | PBKDF2-HMAC-SHA256 |
478
+ | 迭代次数 | 100,000 |
479
+ | Salt 长度 | 32 字节(每条独立随机) |
480
+ | Key 长度 | 32 字节 |
481
+ | Nonce 长度 | 12 字节 |
482
+
483
+ ### 加密存储
484
+
485
+ ```ruby
486
+ client = NetworkInfraUtility::SSH::Client.new
487
+ # client.vault 是 Vault 实例
488
+
489
+ # 存储密码
490
+ client.vault.store("web_server_pass", "MySecret123!")
491
+
492
+ # 在连接参数中引用
493
+ session = client.connect(
494
+ host: "10.0.0.1",
495
+ user: "admin",
496
+ auth: { type: "password", password: "~vault:web_server_pass" }
497
+ )
498
+ # Vault 会自动将 ~vault:web_server_pass 解密为明文密码
499
+ ```
500
+
501
+ ### vault.yml 格式
502
+
503
+ ```yaml
504
+ web_server_pass:
505
+ :salt: <Base64>
506
+ :nonce: <Base64>
507
+ :tag: <Base64>
508
+ :data: <Base64>
509
+ ```
510
+
511
+ 文件权限:Linux 下 `chmod 600`,Windows 下通过 `icacls` 限制为当前用户。
512
+
513
+ ---
514
+
515
+ ## 终端配色主题
516
+
517
+ 内置 11 套配色方案:
518
+
519
+ | 主题名 | 风格 | 背景色 |
520
+ |--------|------|--------|
521
+ | `default` | VS Code Dark | #1e1e1e |
522
+ | `solarized-dark` | Solarized Dark | #002b36 |
523
+ | `solarized-light` | Solarized Light | #fdf6e3 |
524
+ | `dracula` | Dracula | #282a36 |
525
+ | `monokai` | Monokai | #272822 |
526
+ | `nord` | Nord | #2e3440 |
527
+ | `gruvbox-dark` | Gruvbox Dark | #282828 |
528
+ | `one-dark` | One Dark | #282c34 |
529
+ | `tokyo-night` | Tokyo Night | #1a1b26 |
530
+ | `catppuccin-mocha` | Catppuccin Mocha | #1e1e2e |
531
+ | `github-dark` | GitHub Dark | #0d1117 |
532
+
533
+ 每个主题包含:背景色 `bg`、前景色 `fg`、光标色 `cursor`、16 色调色板 `palette`。
534
+
535
+ ### 自定义主题
536
+
537
+ 在 `~/.network-infra-utility/themes/<name>.yml` 创建自定义主题:
538
+
539
+ ```yaml
540
+ bg: "#1a1b26"
541
+ fg: "#a9b1d6"
542
+ cursor: "#c0caf5"
543
+ palette:
544
+ - "#15161e" # 0 黑
545
+ - "#f7768e" # 1 红
546
+ - "#9ece6a" # 2 绿
547
+ - "#e0af68" # 3 黄
548
+ - "#7aa2f7" # 4 蓝
549
+ - "#bb9af7" # 5 洋红
550
+ - "#7dcfff" # 6 青
551
+ - "#a9b1d6" # 7 白
552
+ - "#414868" # 8 亮黑
553
+ - "#f7768e" # 9 亮红
554
+ - "#9ece6a" # 10 亮绿
555
+ - "#e0af68" # 11 亮黄
556
+ - "#7aa2f7" # 12 亮蓝
557
+ - "#bb9af7" # 13 亮洋红
558
+ - "#7dcfff" # 14 亮青
559
+ - "#c0caf5" # 15 亮白
560
+ ```
561
+
562
+ ---
563
+
564
+ ## 编程 API 使用
565
+
566
+ ### Client — 生命周期管理
567
+
568
+ `SSH::Client` 是主入口,管理引擎生命周期和子系统协调。默认后端为 **Rust**(`ssh_core_rs`),可指定 `backend: :erlang` 切换到 Erlang 引擎。
569
+
570
+ ```ruby
571
+ client = NetworkInfraUtility::SSH::Client.new # 默认 Rust 引擎
572
+ client_erl = NetworkInfraUtility::SSH::Client.new(backend: :erlang) # 切换 Erlang 引擎
573
+ ```
574
+
575
+ | 方法 | 说明 |
576
+ |------|------|
577
+ | `start_engine` | 启动引擎子进程(按 `backend` 选择 Rust/Erlang),建立 IPC 连接,注册反向 RPC |
578
+ | `connect(spec)` | 发起 SSH 连接,返回 `Session::Session` 对象 |
579
+ | `stop` | 停止引擎,断开所有连接,清理资源 |
580
+ | `started?` | 引擎是否已启动 |
581
+ | `ipc` | `IPC::Router` 实例,用于底层 RPC 调用 |
582
+ | `sessions` | `Session::Manager` 实例,管理所有会话 |
583
+ | `vault` | `Security::Vault` 实例,凭据加密 |
584
+ | `host_key` | `Security::HostKey` 实例,主机密钥管理 |
585
+ | `settings` | `Config::Settings` 实例,全局配置 |
586
+ | `backend` | 当前引擎后端(`:rust` 或 `:erlang`) |
587
+ | `on_engine_exit(&block)` | 设置引擎异常退出回调(`:crashed` / `:disconnected`) |
588
+
589
+ **基本用法:**
590
+
591
+ ```ruby
592
+ client = NetworkInfraUtility::SSH::Client.new
593
+ client.start_engine
594
+
595
+ begin
596
+ # 终端主题通过 spec[:terminal][:theme] 传递(也可不传,用默认主题)
597
+ session = client.connect(
598
+ host: "10.0.0.1", user: "admin",
599
+ key_path: "~/.ssh/id_ed25519",
600
+ terminal: { theme: "dracula" }
601
+ )
602
+ terminal = session.open_terminal
603
+
604
+ terminal.puts("show version")
605
+ # ... 读取终端输出 ...
606
+
607
+ session.disconnect
608
+ ensure
609
+ client.stop
610
+ end
611
+ ```
612
+
613
+ ### Session — 会话操作
614
+
615
+ `SSH::Session::Session` 封装单个 SSH 会话。
616
+
617
+ | 方法 | 说明 |
618
+ |------|------|
619
+ | `open_terminal(term_type:, cols:, rows:)` | 打开终端通道,返回 `Terminal::Emulator` |
620
+ | `close_terminal` | 关闭终端通道 |
621
+ | `add_port_forward(type, local_port, remote_host, remote_port)` | 添加端口转发,返回 `rule_id` |
622
+ | `remove_port_forward(rule_id)` | 移除端口转发规则 |
623
+ | `disconnect` | 断开 SSH 连接 |
624
+ | `connected?` | 是否已连接 |
625
+ | `on_closed(reason)` | 连接被远端关闭时的回调 |
626
+
627
+ **属性:** `session_id`, `conn_id`, `spec`, `name`, `host`, `port`, `user`, `fingerprint`, `terminal`, `file_manager`, `port_forwards`, `tags`, `group`, `status`
628
+
629
+ **端口转发示例:**
630
+
631
+ ```ruby
632
+ session = client.connect(host: "10.0.0.1", user: "admin", key_path: "~/.ssh/id_ed25519")
633
+
634
+ # 本地端口转发:将本地 8080 转发到远程 80
635
+ rule_id = session.add_port_forward(:local, 8080, "127.0.0.1", 80)
636
+
637
+ # ... 使用本地 8080 端口 ...
638
+
639
+ # 移除转发
640
+ session.remove_port_forward(rule_id)
641
+ ```
642
+
643
+ ### Terminal::Emulator — 终端交互
644
+
645
+ `SSH::Terminal::Emulator` 提供终端模拟和数据收发。
646
+
647
+ | 方法 | 说明 |
648
+ |------|------|
649
+ | `send(data)` | 发送原始数据到 SSH 通道 |
650
+ | `puts(text)` | 发送一行(自动追加 `\r`) |
651
+ | `feed(data)` | 输入从 SSH 收到的原始字节(内部解析 ANSI 转义) |
652
+ | `resize(cols, rows)` | 变更窗口大小 |
653
+ | `theme=` | 设置配色主题(名称) |
654
+ | `search(pattern)` | 搜索回滚缓冲区内容 |
655
+ | `export(path)` | 导出回滚缓冲区为文本 |
656
+ | `start_logging(path, max_size:, rotate:)` | 启动会话日志轮转 |
657
+ | `stop_logging` | 停止日志记录 |
658
+
659
+ **属性:** `conn_id`, `channel_id`, `screen`, `buffer`, `theme`, `logger`
660
+
661
+ **会话日志示例:**
662
+
663
+ ```ruby
664
+ terminal = session.open_terminal
665
+ terminal.start_logging("session_20260115.log", max_size: 100 * 1024 * 1024, rotate: 10)
666
+
667
+ # ... 交互过程中自动记录 ...
668
+ # 文件达到 max_size 时自动轮转,保留最近 rotate 个文件
669
+
670
+ terminal.stop_logging
671
+ ```
672
+
673
+ ### Automation::MacroEngine — 登录宏
674
+
675
+ 连接后自动执行预设命令序列,支持等待匹配和失败处理。最多 50 步。
676
+
677
+ ```ruby
678
+ session = client.connect(host: "10.0.0.1", user: "admin", key_path: "~/.ssh/id_ed25519")
679
+ terminal = session.open_terminal
680
+
681
+ macro = NetworkInfraUtility::SSH::Automation::MacroEngine.new(session)
682
+
683
+ # 添加步骤
684
+ macro.add_step(action: "enable\n", wait_pattern: "Password:", delay: 0.5)
685
+ macro.add_step(action: "admin123\n", wait_pattern: "#", on_fail: :abort)
686
+ macro.add_step(action: "terminal monitor\n", wait_pattern: "#", delay: 0.3)
687
+
688
+ # 设置失败时的交互回调(on_fail: :ask 时触发)
689
+ macro.on_ask do |step, index|
690
+ puts "Step #{index} timeout: #{step.action}"
691
+ puts "Continue? (y/n)"
692
+ STDIN.gets.chomp =~ /^y/i ? :continue : :abort
693
+ end
694
+
695
+ # 执行宏
696
+ result = macro.run do |step, index, total|
697
+ puts "[#{index}/#{total}] #{step.action.strip}"
698
+ end
699
+ # result => :completed 或 : aborted
700
+ ```
701
+
702
+ **Step 参数:**
703
+
704
+ | 参数 | 类型 | 默认值 | 说明 |
705
+ |------|------|--------|------|
706
+ | `action` | String | — | 要发送的命令 |
707
+ | `wait_pattern` | String/Regexp/nil | nil | 等待匹配的输出,nil 不等待 |
708
+ | `delay` | Float | 0 | 发送前延迟(秒) |
709
+ | `on_fail` | Symbol | :continue | 超时行为:`:continue` / `:abort` / `:ask` |
710
+
711
+ **从配置文件加载(sessions.yml 中的 macro 字段):**
712
+
713
+ ```ruby
714
+ macro.load_from_config({
715
+ enabled: true,
716
+ steps: [
717
+ { action: "enable\n", wait_pattern: "Password:", delay: 0.5 },
718
+ { action: "admin123\n", wait_pattern: "#", on_fail: "abort" }
719
+ ]
720
+ })
721
+ macro.run
722
+ ```
723
+
724
+ ### Security::HostKey — 主机密钥管理
725
+
726
+ ```ruby
727
+ host_key = client.host_key
728
+
729
+ # 手动添加信任条目
730
+ host_key.add("10.0.0.1", 22, "SHA256:abc123...")
731
+
732
+ # 查询
733
+ entry = host_key.get("10.0.0.1", 22)
734
+ # => { fingerprint: "SHA256:abc123...", added_at: "2026-01-15T..." }
735
+
736
+ # 删除
737
+ host_key.remove("10.0.0.1", 22)
738
+
739
+ # 列出全部
740
+ host_key.list.each do |entry|
741
+ puts "#{entry[:host]}:#{entry[:port]} - #{entry[:fingerprint]}"
742
+ end
743
+ ```
744
+
745
+ **自定义交互回调:**
746
+
747
+ ```ruby
748
+ host_key.on_prompt do |host, port, fingerprint|
749
+ puts "首次连接 #{host}:#{port}"
750
+ puts "指纹: #{fingerprint}"
751
+ print "信任? (y/N) "
752
+ STDIN.gets.chomp =~ /^y/i ? :accept : :reject
753
+ end
754
+
755
+ host_key.on_key_changed do |host, port, old_fp, new_fp|
756
+ warn "警告: #{host}:#{port} 主机密钥变更!"
757
+ warn " 旧: #{old_fp}"
758
+ warn " 新: #{new_fp}"
759
+ end
760
+ ```
761
+
762
+ ---
763
+
764
+ ## RPC 方法列表
765
+
766
+ Ruby 端通过 `IPC::Router` 向引擎发起 JSON-RPC 2.0 请求。Rust 与 Erlang 引擎实现同一套协议,方法列表完全一致。以下是全部已注册的 RPC 方法:
767
+
768
+ ### 连接管理
769
+
770
+ | 方法 | 参数 | 返回 | 说明 |
771
+ |------|------|------|------|
772
+ | `conn.connect` | 连接 spec(host, user, port, auth, jumps 等) | `{ conn_id, fingerprint }` | 发起 SSH 连接 |
773
+ | `conn.disconnect` | `{ id }` | `{ ok: true }` | 断开指定连接 |
774
+ | `conn.list` | `{}` | 连接列表 | 列出所有活动连接 |
775
+ | `conn.reconnect` | `{ id }` | — | 重连指定连接 |
776
+
777
+ ### 通道管理
778
+
779
+ | 方法 | 参数 | 返回 | 说明 |
780
+ |------|------|------|------|
781
+ | `channel.open` | `{ conn_id, type, term, cols, rows }` | `{ channel_id }` | 打开通道(shell/exec) |
782
+ | `channel.send` | `{ id, data }` | — | 发送数据(Base64 编码) |
783
+ | `channel.close` | `{ id }` | — | 关闭通道 |
784
+ | `channel.window_change` | `{ id, cols, rows }` | — | 变更窗口大小 |
785
+
786
+ ### SFTP 文件传输
787
+
788
+ | 方法 | 参数 | 返回 | 说明 |
789
+ |------|------|------|------|
790
+ | `sftp.open` | `{ conn_id }` | `{ sftp_id }` | 打开 SFTP 会话 |
791
+ | `sftp.list_dir` | `{ sftp_id, path }` | 目录列表 | 列出远程目录 |
792
+ | `sftp.download` | `{ sftp_id, remote, local }` | — | 下载文件 |
793
+ | `sftp.upload` | `{ sftp_id, local, remote }` | — | 上传文件 |
794
+ | `sftp.mkdir` | `{ sftp_id, path }` | — | 创建远程目录 |
795
+ | `sftp.remove` | `{ sftp_id, path }` | — | 删除远程文件 |
796
+ | `sftp.stat` | `{ sftp_id, path }` | 文件信息 | 查询文件属性 |
797
+
798
+ ### 端口转发
799
+
800
+ | 方法 | 参数 | 返回 | 说明 |
801
+ |------|------|------|------|
802
+ | `portfwd.add` | `{ conn_id, type, local_port, remote_host, remote_port }` | `{ rule_id }` | 添加转发规则 |
803
+ | `portfwd.remove` | `{ conn_id, rule_id }` | — | 移除转发规则 |
804
+ | `portfwd.list` | `{ conn_id }` | 规则列表 | 列出转发规则 |
805
+
806
+ ### 引擎管理
807
+
808
+ | 方法 | 参数 | 返回 | 说明 |
809
+ |------|------|------|------|
810
+ | `engine.ping` | `{}` | `{ ok: true }` | 心跳检测 |
811
+ | `engine.stats` | `{}` | 运行时统计 | 引擎统计信息 |
812
+ | `engine.shutdown` | `{}` | — | 关闭引擎 |
813
+ | `bye` | `{}` | — | 断开 IPC 连接 |
814
+
815
+ ### 保活管理
816
+
817
+ | 方法 | 参数 | 返回 | 说明 |
818
+ |------|------|------|------|
819
+ | `keepalive.set_interval` | `{ interval_ms }` | — | 设置心跳间隔(设为 0 禁用) |
820
+ | `keepalive.get_interval` | `{}` | 当前间隔 | 查询心跳间隔 |
821
+ | `keepalive.get_status` | `{}` | 连接保活状态 | 查询各连接保活状态 |
822
+
823
+ ### 反向 RPC(引擎 → Ruby)
824
+
825
+ | 方法 | 触发时机 | 说明 |
826
+ |------|---------|------|
827
+ | `hostkey.resolve` | 引擎连接前校验主机密钥 | Ruby 返回 `accept` / `reject` / `once` |
828
+
829
+ ### 推送事件(引擎 → Ruby,notification)
830
+
831
+ | 方法 | 说明 |
832
+ |------|------|
833
+ | `channel.data` | 通道收到远端数据 |
834
+ | `channel.eof` | 通道收到 EOF |
835
+ | `channel.extended_data` | 通道收到扩展数据(如 stderr) |
836
+ | `conn.ready` | 连接已就绪 |
837
+ | `conn.failed` | 连接失败 |
838
+ | `conn.closed` | 连接被远端关闭 |
839
+
840
+ ---
841
+
842
+ ## 架构说明
843
+
844
+ ### 启动流程
845
+
846
+ ```
847
+ 用户执行 Ruby 进程 引擎进程(Rust / Erlang)
848
+ ──────── ────────── ──────────
849
+ ssh-client connect 10.0.0.1
850
+ -u admin
851
+ │ │ │
852
+ ▼ ▼ │
853
+ ┌──────────┐ ┌──────────────┐ │
854
+ │ Thor CLI │──调用──▶ │ SSH::Client │ │
855
+ │ 参数解析 │ │ .new │ │
856
+ └──────────┘ └──────┬───────┘ │
857
+ │ │
858
+ start_engine() │
859
+ │ │
860
+ ┌────────┴────────┐ │
861
+ │ Process.spawn │────拉起子进程────────▶│ ssh_core_rs(Rust)
862
+ │ 按 backend 选择 │ │ 或 ssh_core(Erlang)
863
+ │ 引擎二进制 │ │ 写入端点文件
864
+ └────────┬────────┘ │
865
+ │ │
866
+ 读取端点文件,建立 IPC 连接(JSON-RPC) │
867
+ │◀════════socket════════════════▶│
868
+ │ │
869
+ vault.resolve_credentials(密码解密) │
870
+ │ │
871
+ IPC call "conn.connect" ────────────────▶│ ssh:connect() / russh
872
+ │ │ 建立SSH连接
873
+ │◀────返回 conn_id ──────────────│
874
+ │ │
875
+ session.open_terminal │
876
+ IPC call "channel.open" ───────────────▶│ 打开shell通道
877
+ │◀────返回 channel_id ───────────│
878
+ │ │
879
+ 进入交互循环 │
880
+ ┌──────────────┐ │
881
+ │ 用户输入命令 │──IPC──▶ channel.send ──▶│ 发往远程服务器
882
+ │ 终端输出显示 │◀─IPC─── channel.data ───│ 收到服务器响应
883
+ └──────────────┘ │
884
+ │ │
885
+ 输入 exit → 关闭连接 → 停止引擎进程 │
886
+ ```
887
+
888
+ ### 分工
889
+
890
+ | | Ruby | Rust / Erlang 引擎 |
891
+ |---|---|---|
892
+ | **职责** | 调度、配置、终端渲染、加密 | SSH 协议、连接管理、通道复用 |
893
+ | **进程** | 主进程 | 子进程(引擎与 Ruby 隔离,崩溃不影响 Ruby) |
894
+ | **通信** | 发 JSON-RPC 请求,收推送事件 | 响应请求,推送 channel.data 等事件 |
895
+
896
+ ### 关键模块在运行时的角色
897
+
898
+ - **`bin/ssh-client`** — 入口,Thor 解析参数后调 `Client`
899
+ - **`SSH::Client`** — 总指挥,按 `backend` 拉起对应引擎、建立 IPC、组合各子系统
900
+ - **`IPC::Router`** — 管所有 JSON-RPC 往来,含订阅/反向 RPC
901
+ - **`Security::Vault`** — 连接前把 `~vault:xxx` 凭据引用解密成明文
902
+ - **`Security::HostKey`** — 引擎连接时反问 Ruby"这主机密钥信不信"(双向 RPC)
903
+ - **`Terminal::Emulator`** — 收到远端字节后输入给 `AnsiParser`,解析 ANSI 转义、驱动 `Screen` 渲染
904
+
905
+ 简单说就是:**Ruby 是大脑(管配置/界面/自动化),引擎是手脚(Rust/Erlang 二选一,管 SSH 协议执行),两者通过本地 socket 上的 JSON-RPC 协作。Rust 引擎默认启用;Erlang 引擎与 Rust 引擎 IPC 协议完全一致,`Client.new(backend: :erlang)` 一行切换,Ruby 其余代码零改动。**
906
+
907
+ ---
908
+
909
+ ## 项目结构
910
+
911
+ ```
912
+ service/ssh/
913
+ ├── bin/ # CLI 入口
914
+ │ └── ssh-client # Thor 命令行脚本
915
+ ├── design/ # 设计文档
916
+ │ ├── SSH连接客户端功能需求文档.md
917
+ │ ├── SSH连接客户端软件设计文档.md # HLD
918
+ │ └── SSH连接客户端详细设计文档.md # LLD
919
+ ├── ext/ssh_core_rs/ # Rust 核心引擎(默认)
920
+ │ ├── src/ # Rust 源码(conn/channel/sftp/portfwd/keepalive 等)
921
+ │ ├── bin/ # 启动脚本(ssh_core_rs / ssh_core_rs.cmd)
922
+ │ ├── Cargo.toml
923
+ │ └── target/ # 编译输出
924
+ ├── ext/ssh_core/ # Erlang 核心引擎(可选备选)
925
+ │ ├── src/ # Erlang 源码(18 模块)
926
+ │ ├── local_deps/jsx/ # jsx JSON 库(本地源码)
927
+ │ ├── config/ # sys.config / vm.args
928
+ │ ├── bin/ # 启动脚本(ssh_core / ssh_core.cmd)
929
+ │ └── test/ # Common Test 套件(占位)
930
+ ├── lib/network_infra_utility/
931
+ │ └── ssh.rb # Ruby 统一入口
932
+ │ └── ssh/ # Ruby 源码
933
+ │ ├── client.rb # 主入口,生命周期管理(backend 选择)
934
+ │ ├── version.rb
935
+ │ ├── ipc/ # JSON-RPC 通信层
936
+ │ ├── session/ # 会话管理
937
+ │ ├── terminal/ # 终端模拟(ANSI 解析 + 屏幕渲染)
938
+ │ ├── security/ # 凭据加密(Vault)+ 主机密钥校验
939
+ │ ├── automation/ # 登录宏引擎
940
+ │ └── config/ # 全局设置
941
+ └── spec/ # RSpec 测试(占位)
942
+ ```