7.8 KiB
7.8 KiB
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包。
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:目标主机端口
工作流程
- 尝试通过 SSH 创建
direct-tcpip类型的隧道通道 - 若失败,打印错误日志并返回
- 成功后进入循环:
- 使用
select.select()监听本地 socket 与 SSH channel 的可读事件 - 双向转发数据(本地 ↔ SSH 隧道)
- 使用
- 任一端关闭连接时,清理资源并输出关闭信息
日志输出示例
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()
私有方法,运行于后台线程:
- 连接 SSH 服务器(
connect_ssh_server()) - 获取 SSH 传输通道
- 动态创建
MyForwardServer子类以设置回调 - 动态创建
SubHandler并注入chain_host,chain_port,ssh_transport - 绑定到
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 关闭日志输出。
使用方式
命令行模式(主程序入口)
当直接运行脚本时,进入交互式控制台。
启动命令格式
python ssh_forward.py <本地端口> <目标主机> <目标端口> <SSH主机> <SSH端口> <SSH用户> <SSH密码>
示例
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
- 用户访问
http://localhost:8888 SSHPortForward接收连接- 通过 SSH 隧道建立到
remote_host:remote_port的 direct-tcpip 通道 - 数据双向透明转发
错误处理与日志
- 所有异常均被捕获并输出详细信息(通过
verbose()) - 常见错误包括:
- SSH 认证失败
- 目标主机无法解析(DNS)
- SSH 服务器拒绝端口转发请求
- 网络中断导致连接关闭
建议生产环境根据需要关闭 g_verbose 或重定向日志。
安全注意事项
- 密码明文存储:当前版本在内存中保存明文密码,不适用于高安全场景。建议扩展支持密钥认证(
.pem文件)。 - 无加密本地通信:本地监听仅限
localhost,防止外部访问敏感端口。 - 自动信任主机密钥:使用
AutoAddPolicy(),存在中间人攻击风险。生产环境应验证主机指纹。
扩展建议
| 功能 | 描述 |
|---|---|
| 支持私钥登录 | 添加 pkey 参数,替代密码认证 |
| 配置文件支持 | 使用 JSON/YAML 加载配置,避免命令行长参数 |
| REST API 控制 | 提供 HTTP 接口进行 start/stop 操作 |
| 日志模块替换 | 使用标准 logging 模块代替 print |
| 多隧道支持 | 管理多个并发转发规则 |
| 超时与心跳机制 | 防止长时间空闲断连 |
示例代码调用
# 编程方式使用
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