apppublic/aidocs/port_forward.md
2025-10-05 11:23:33 +08:00

267 lines
7.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# SSH 端口转发工具技术文档
## 概述
本项目实现了一个基于 `Paramiko` 的 SSH 端口转发Port Forwarding客户端支持通过 SSH 隧道将本地端口映射到远程目标主机和端口。该工具可用于安全地访问内网服务或绕过防火墙限制。
主要功能:
- 建立 SSH 连接到跳板机SSH Server
- 在本地监听指定端口
- 将所有连接通过 SSH 隧道转发至目标主机remote_host:remote_port
- 支持动态配置、后台运行与手动启停控制
---
## 依赖库
| 库名 | 用途 |
|------|------|
| `paramiko` | 实现 SSH 客户端连接与通道管理 |
| `socket` | 网络通信、地址解析 |
| `select` | I/O 多路复用,用于双向数据转发 |
| `SocketServer` / `socketserver` | 构建 TCP 服务器以监听本地端口(兼容 Python 2/3 |
| `appPublic.background.Background` | 后台线程执行任务 |
> ⚠️ 注意:需确保已安装 `paramiko` 和 `appPublic` 包。
```bash
pip install paramiko
```
---
## 核心类说明
### 1. `ForwardServer` —— 多线程 TCP 服务器
继承自 `SocketServer.ThreadingTCPServer`,用于在本地监听端口并接受入站连接。
#### 特性
- `daemon_threads = True`:子线程随主线程退出而终止。
- `allow_reuse_address = True`:允许重用本地地址(避免端口占用问题)。
- `server_ready`:标志服务器是否已启动完成。
- `ready_callback`:服务器就绪时调用的回调函数。
#### 方法
| 方法 | 说明 |
|------|------|
| `service_actions()` | 重写父类方法,在首次服务启动后触发 `ready_callback` 回调 |
| `shutdown()` | 关闭服务器,并重置 `server_ready` 状态 |
---
### 2. `Handler` —— 请求处理器
继承自 `SocketServer.BaseRequestHandler`,处理每一个来自客户端的连接请求。
#### 属性(动态绑定)
- `ssh_transport`SSH 传输通道(由外部注入)
- `chain_host`:目标主机地址(远程实际服务地址)
- `chain_port`:目标主机端口
#### 工作流程
1. 尝试通过 SSH 创建 `direct-tcpip` 类型的隧道通道
2. 若失败,打印错误日志并返回
3. 成功后进入循环:
- 使用 `select.select()` 监听本地 socket 与 SSH channel 的可读事件
- 双向转发数据(本地 ↔ SSH 隧道)
4. 任一端关闭连接时,清理资源并输出关闭信息
#### 日志输出示例
```
Connected! Tunnel open ('127.0.0.1', 50000) -> ('192.168.1.10', 22) -> ('10.0.0.5', 80)
Tunnel closed from ('127.0.0.1', 50000)
```
---
### 3. `SSHPortForward` —— 主控制器类
封装完整的 SSH 端口转发逻辑,提供简洁的接口用于启动/停止服务。
#### 构造函数参数
| 参数 | 类型 | 描述 |
|------|------|------|
| `local_port` | int | 本地监听端口(如 8080 |
| `remote_host` | str | 目标主机域名或 IP通过 SSH 跳转访问的目标) |
| `remote_port` | int | 目标主机端口 |
| `ssh_host` | str | SSH 服务器地址(跳板机) |
| `ssh_port` | int | SSH 服务端口(通常为 22 |
| `ssh_user` | str | SSH 登录用户名 |
| `ssh_password` | str | SSH 登录密码 |
#### 内部属性
| 属性 | 类型 | 描述 |
|------|------|------|
| `self.running` | bool | 当前服务是否正在运行 |
| `self._ready` | bool | 服务是否已准备就绪(可用于健康检查) |
| `self.ssh` | SSHClient | Paramiko SSH 客户端实例 |
| `self.transport` | Transport | SSH 传输层对象 |
| `self.forward_server` | ForwardServer | 本地监听服务器实例 |
#### 公共方法
##### `run()`
启动端口转发服务(异步非阻塞)。若已在运行,则忽略。
内部使用 `Background` 类在新线程中执行 `_run()`
##### `_run()`
私有方法,运行于后台线程:
1. 连接 SSH 服务器(`connect_ssh_server()`
2. 获取 SSH 传输通道
3. 动态创建 `MyForwardServer` 子类以设置回调
4. 动态创建 `SubHandler` 并注入 `chain_host`, `chain_port`, `ssh_transport`
5. 绑定到 `localhost:local_port` 并开始监听(`serve_forever()`
##### `stop()`
停止当前运行的服务:
- 调用 `forward_server.shutdown()`
- 关闭 server socket
- 断开 SSH 连接transport 和 client
##### `service_ready()`
回调函数,当本地服务器成功启动后被调用,打印提示信息并将 `_ready` 设为 `True`
---
### 4. 辅助函数
#### `connect_ssh_server(host, port, user, password)`
建立 SSH 连接并返回 `SSHClient` 实例。
- 自动添加未知主机密钥(`AutoAddPolicy`
- 返回已连接的 SSH 客户端
#### `verbose(s)`
条件性打印调试信息,受全局变量 `g_verbose` 控制。
默认开启(`g_verbose = True`),可设为 `False` 关闭日志输出。
---
## 使用方式
### 命令行模式(主程序入口)
当直接运行脚本时,进入交互式控制台。
#### 启动命令格式
```bash
python ssh_forward.py <本地端口> <目标主机> <目标端口> <SSH主机> <SSH端口> <SSH用户> <SSH密码>
```
#### 示例
```bash
python ssh_forward.py 8888 example.internal 80 gateway.example.com 22 alice mypass123
```
此命令表示:
> 所有发往本机 `8888` 端口的流量,将通过 `gateway.example.com` 的 SSH 隧道,转发至 `example.internal:80`
#### 交互命令
程序启动后会显示菜单:
```
start) start server,
stop) stop server
quit) quit
```
输入对应指令即可操作服务状态。
---
## 工作原理图解
```
+-------------+ +------------------+ +--------------------+
| | | | | |
| Client | ----> | Local Listener | ----> | SSH Tunnel | ----> Target Service
| (e.g. curl) | | (SSHPortForward) | | (via ssh_host) |
| | | | | |
+-------------+ +------------------+ +--------------------+
localhost:8888
```
1. 用户访问 `http://localhost:8888`
2. `SSHPortForward` 接收连接
3. 通过 SSH 隧道建立到 `remote_host:remote_port` 的 direct-tcpip 通道
4. 数据双向透明转发
---
## 错误处理与日志
- 所有异常均被捕获并输出详细信息(通过 `verbose()`
- 常见错误包括:
- SSH 认证失败
- 目标主机无法解析DNS
- SSH 服务器拒绝端口转发请求
- 网络中断导致连接关闭
建议生产环境根据需要关闭 `g_verbose` 或重定向日志。
---
## 安全注意事项
1. **密码明文存储**:当前版本在内存中保存明文密码,不适用于高安全场景。建议扩展支持密钥认证(`.pem` 文件)。
2. **无加密本地通信**:本地监听仅限 `localhost`,防止外部访问敏感端口。
3. **自动信任主机密钥**:使用 `AutoAddPolicy()`,存在中间人攻击风险。生产环境应验证主机指纹。
---
## 扩展建议
| 功能 | 描述 |
|------|------|
| 支持私钥登录 | 添加 `pkey` 参数,替代密码认证 |
| 配置文件支持 | 使用 JSON/YAML 加载配置,避免命令行长参数 |
| REST API 控制 | 提供 HTTP 接口进行 start/stop 操作 |
| 日志模块替换 | 使用标准 `logging` 模块代替 `print` |
| 多隧道支持 | 管理多个并发转发规则 |
| 超时与心跳机制 | 防止长时间空闲断连 |
---
## 示例代码调用
```python
# 编程方式使用
tunnel = SSHPortForward(
local_port=9000,
remote_host="internal.db",
remote_port=3306,
ssh_host="jumpbox.company.com",
ssh_port=22,
ssh_user="dev",
ssh_password="secret"
)
tunnel.run() # 启动
# ... 使用一段时间 ...
tunnel.stop() # 停止
```
---
## 版权与许可
- 作者:未知(请补充)
- 依赖开源库Paramiko (LGPL), Python 标准库
- 使用遵循相应许可证要求
---
> ✅ 文档版本v1.0
> 📅 最后更新2025-04-05