纯 python SSH 隧道
项目描述
作者:帕哈兹
回购:https ://github.com/pahaz/sshtunnel/
受https://github.com/jmagnusson/bgtunnel启发,它不适用于 Windows。
另见:https ://github.com/paramiko/paramiko/blob/master/demos/forward.py
要求
安装
sshtunnel在 PyPI 上,所以只需运行:
pip install sshtunnel
或者
easy_install sshtunnel
或者
conda install -c conda-forge sshtunnel
将其安装在您的环境中。
要从源代码安装,请克隆 repo并运行:
python setup.py install
测试包
为了运行测试,您首先需要 tox并运行:
python setup.py test
使用场景
下图描述了sshtunnel有用的典型场景之一。用户可能需要连接远程服务器的一个端口(即 8080),只有 SSH 端口(通常是端口 22)可以访问。
---------------------------------------------------------------------- | -------------+ | +----------+ LOCAL | | | REMOTE | :22 SSH CLIENT | <== SSH ========> | SERVER | :8080 web service -------------+ | +----------+ | FIREWALL (only port 22 is open) ----------------------------------------------------------------------
图 1:如何通过 SSH 隧道连接到被防火墙阻止的服务。
如果 SSH 服务器允许,也可以访问私人服务器(从REMOTE SERVER的角度来看)从外部(本地客户端的角度)不直接可见。
---------------------------------------------------------------------- | -------------+ | +----------+ +--------- LOCAL | | | REMOTE | | PRIVATE CLIENT | <== SSH ========> | SERVER | <== local ==> | SERVER -------------+ | +----------+ +--------- | FIREWALL (only port 443 is open) ----------------------------------------------------------------------
Fig2 : 如何通过 SSH 隧道连接到PRIVATE SERVER 。
使用示例
API 允许初始化隧道并启动它,或者使用带有上下文的 上下文,这将负责启动和停止隧道:
示例 1
与上图1对应的代码如下,给定远程服务器的地址是 pahaz.urfuclub.ru,密码认证和随机分配的本地绑定端口。
from sshtunnel import SSHTunnelForwarder
server = SSHTunnelForwarder(
'alfa.8iq.dev',
ssh_username="pahaz",
ssh_password="secret",
remote_bind_address=('127.0.0.1', 8080)
)
server.start()
print(server.local_bind_port) # show assigned local port
# work with `SECRET SERVICE` through `server.local_bind_port`.
server.stop()
示例 2
端口转发到无法直接访问的私有服务器的示例,假设密码保护 pkey 身份验证,远程服务器的 SSH 服务正在侦听端口 443 并且该端口在防火墙中打开(图 2):
import paramiko
import sshtunnel
with sshtunnel.open_tunnel(
(REMOTE_SERVER_IP, 443),
ssh_username="",
ssh_pkey="/var/ssh/rsa_key",
ssh_private_key_password="secret",
remote_bind_address=(PRIVATE_SERVER_IP, 22),
local_bind_address=('0.0.0.0', 10022)
) as tunnel:
client = paramiko.SSHClient()
client.load_system_host_keys()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
client.connect('127.0.0.1', 10022)
# do some operations with client session
client.close()
print('FINISH!')
示例 3
Vagrant MySQL 本地端口的端口转发示例:
from sshtunnel import open_tunnel
from time import sleep
with open_tunnel(
('localhost', 2222),
ssh_username="vagrant",
ssh_password="vagrant",
remote_bind_address=('127.0.0.1', 3306)
) as server:
print(server.local_bind_port)
while True:
# press Ctrl-C for stopping
sleep(1)
print('FINISH!')
或者简单地使用 CLI:
(bash)$ python -m sshtunnel -U vagrant -P vagrant -L :3306 -R 127.0.0.1:3306 -p 2222 localhost
示例 4
打开跳过两个隧道的 SSH 会话。SSH 传输和隧道将被守护,它不会等待连接在关闭时停止。
import sshtunnel
from paramiko import SSHClient
with sshtunnel.open_tunnel(
ssh_address_or_host=('GW1_ip', 20022),
remote_bind_address=('GW2_ip', 22),
) as tunnel1:
print('Connection to tunnel1 (GW1_ip:GW1_port) OK...')
with sshtunnel.open_tunnel(
ssh_address_or_host=('localhost', tunnel1.local_bind_port),
remote_bind_address=('target_ip', 22),
ssh_username='GW2_user',
ssh_password='GW2_pwd',
) as tunnel2:
print('Connection to tunnel2 (GW2_ip:GW2_port) OK...')
with SSHClient() as ssh:
ssh.connect('localhost',
port=tunnel2.local_bind_port,
username='target_user',
password='target_pwd',
)
ssh.exec_command(...)
命令行使用
$ sshtunnel --help usage: sshtunnel [-h] [-U SSH_USERNAME] [-p SSH_PORT] [-P SSH_PASSWORD] -R IP:PORT [IP:PORT ...] [-L [IP:PORT [IP:PORT ...]]] [-k SSH_HOST_KEY] [-K KEY_FILE] [-S KEY_PASSWORD] [-t] [-v] [-V] [-x IP:PORT] [-c SSH_CONFIG_FILE] [-z] [-n] [-d [FOLDER [FOLDER ...]]] ssh_address Pure python ssh tunnel utils Version 0.4.0 positional arguments: ssh_address SSH server IP address (GW for SSH tunnels) set with "-- ssh_address" if immediately after -R or -L optional arguments: -h, --help show this help message and exit -U SSH_USERNAME, --username SSH_USERNAME SSH server account username -p SSH_PORT, --server_port SSH_PORT SSH server TCP port (default: 22) -P SSH_PASSWORD, --password SSH_PASSWORD SSH server account password -R IP:PORT [IP:PORT ...], --remote_bind_address IP:PORT [IP:PORT ...] Remote bind address sequence: ip_1:port_1 ip_2:port_2 ... ip_n:port_n Equivalent to ssh -Lxxxx:IP_ADDRESS:PORT If port is omitted, defaults to 22. Example: -R 10.10.10.10: 10.10.10.10:5900 -L [IP:PORT [IP:PORT ...]], --local_bind_address [IP:PORT [IP:PORT ...]] Local bind address sequence: ip_1:port_1 ip_2:port_2 ... ip_n:port_n Elements may also be valid UNIX socket domains: /tmp/foo.sock /tmp/bar.sock ... /tmp/baz.sock Equivalent to ssh -LPORT:xxxxxxxxx:xxxx, being the local IP address optional. By default it will listen in all interfaces (0.0.0.0) and choose a random port. Example: -L :40000 -k SSH_HOST_KEY, --ssh_host_key SSH_HOST_KEY Gateway's host key -K KEY_FILE, --private_key_file KEY_FILE RSA/DSS/ECDSA private key file -S KEY_PASSWORD, --private_key_password KEY_PASSWORD RSA/DSS/ECDSA private key password -t, --threaded Allow concurrent connections to each tunnel -v, --verbose Increase output verbosity (default: ERROR) -V, --version Show version number and quit -x IP:PORT, --proxy IP:PORT IP and port of SSH proxy to destination -c SSH_CONFIG_FILE, --config SSH_CONFIG_FILE SSH configuration file, defaults to ~/.ssh/config -z, --compress Request server for compression over SSH transport -n, --noagent Disable looking for keys from an SSH agent -d [FOLDER [FOLDER ...]], --host_pkey_directories [FOLDER [FOLDER ...]] List of directories where SSH pkeys (in the format `id_*`) may be found
在线文档
文档可以在readthedocs找到。
贡献者
变更日志
- v.0.3.1(帕哈兹)
将打开连接超时增加到 10 秒
- v.0.2.1(Pahaz、Eddie Chiang和kkrasovskii)
修复了 DOWN ( #170 )隧道的孤立线程错误
- v.0.2.0(乔治·里洛夫)
支持无代理命令的 IPv6。使用内置 paramiko 创建套接字逻辑。逻辑首先尝试使用 ipv6 套接字系列,然后是 ipv4 套接字系列。
- v.0.1.5 ( JM 费尔南德斯)
引入block_on_close属性
- v.0.1.4(尼尔斯·泽勒梅克)
允许从~/.ssh加载 pkey
- v.0.1.3(伊格纳西奥·佩鲁弗等人)
更新pkey_file参数以接受使用~的用户文件夹的相对路径
几个错误修正
- v.0.1.2 ( JM 费尔南德斯)
修复 #77
- v.0.1.1 ( JM 费尔南德斯)
修复 #72
- v.0.1.0 ( JM 费尔南德斯)
添加隧道绑定属性
几个错误修正 (#49, #56, #57, #59, #60, #62, #64, #66, ...) ( Pahaz , JM Fernández )
添加 TRACE 日志记录级别 ( JM Fernández )
代码和测试重构 ( JM Fernández )
放弃python3.2支持
- v.0.0.8 ( JM 费尔南德斯)
Merge #31:支持 Unix 域套接字(本地)转发(Dan Harbin)
简化 API ( JM Fernández )
添加基于 sphinx 的文档 ( JM Fernández )
添加allow_agent(修复#36、#46)(JM Fernández)
添加压缩( JM Fernández )
添加__str__方法(JM Fernández)
添加测试功能 ( JM Fernández )
在未提供和跳过 ssh_config 文件时修复默认用户名 ( JM Fernández )
修复网关 IP 无法解析的异常捕获 ( JM Fernández )
小修正 ( JM Fernández )
添加 AppVeyor 支持 ( JM Fernández )
- v.0.0.7 ( JM 费尔南德斯)
现在可以安全地停止和启动隧道 ( #41 ) ( JM Fernández )
添加超时到 SSH 网关和保持活动消息 ( #29 ) ( JM Fernández )
添加-V CLI 选项以显示当前版本 ( JM Fernández )
添加报道 ( JM Fernández )
重构 ( JM Fernández )
- v.0.0.5(帕哈兹)
添加ssh_proxy参数,以及ssh_config(5) ProxyCommand支持 ( Lewis Thompson )
添加一些 python 2.6 兼容性修复 ( Mart Sõmermaa )
paramiko.transport继承传递给SSHTunnelForwarder ( JM Fernández )的记录器处理程序
修复#34、#33、代码风格和文档 ( JM Fernández )
添加测试(Pahaz)
添加 CI 集成 ( Pahaz )
普通包装(帕哈兹)
通过SSHTunnelForwarder.local_is_up ( Pahaz )禁用检查扩展套接字连接[更改默认行为]
- v.0.0.4.2(帕哈兹)
修复 Python < 3.3 #16、#21的 Thread.daemon 模式(Lewis Thompson,Erik Rogers)
- v.0.0.4(帕哈兹)
所有线程(JM Fernández , Pahaz)默认的守护进程模式 -不兼容
将make_ssh_forward_server移动到SSHTunnelForwarder.make_ssh_forward_server ( Pahaz , JM Fernández ) -不兼容
将make_ssh_forward_handler移动到SSHTunnelForwarder.make_ssh_forward_handler_class ( Pahaz , JM Fernández ) -不兼容
将open重命名为open_tunnel ( JM Fernández ) -不兼容
添加 CLI 界面 ( JM Fernández )
支持同时打开多个隧道 ( JM Fernández )
提高稳定性和可读性 ( JM Fernández , Pahaz )
改进伐木 ( JM Fernández , Pahaz )
添加raise_exception_if_any_forwarder_have_a_problem参数以一次打开多个隧道 ( Pahaz )
添加ssh_config_file参数支持 ( JM Fernández )
添加 Python 3 支持 ( JM Fernández , Pahaz )
- v.0.0.3(帕哈兹)
添加线程选项(Cameron Maske)
修复异常错误消息,正确打印目标地址(Gustavo Machado)
修复pip 安装失败(Colin Jermain,Pahaz)