必须使用 server.local_bind_port 获取动态端口,不可硬写27017;URI host 必须为 127.0.0.1,认证由 MongoDB 处理,需正确配置 authMechanism、防火墙及私钥权限。
sshtunnel 连接 MongoDB 必须用 server.local_bind_port,不能硬写 27017
跳板机转发后,mongodb 实际监听的本地端口是动态分配的(比如 49152),不是你本地的 27017。硬写 mongoclient('localhost', 27017) 会连错——哪怕 ssh 命令里用了 -l27017:...,那只是 shell 层面的映射,python 里走的是 sshtunnelforwarder 自己开的临时端口。
-
server.start()后必须读server.local_bind_port,这是唯一可靠的端口号 - 不要在 URI 里拼
mongodb://user:pass@127.0.0.1:27017/...—— 身份认证应由 MongoDB 服务端处理,不是靠隧道层透传 - 如果 MongoDB 在跳板机本机(即
remote_bind_address=('127.0.0.1', 27017)),确保跳板机上 MongoDB 绑定了127.0.0.1或0.0.0.0,而不是只监听内网 IP
认证失败?检查 authMechanism 和连接地址是否混淆
常见错误是把跳板机地址、MongoDB 地址、URI 中的 host 混成一坨。实际只有两个地址要分清:跳板机 SSH 登录地址(ssh_address_or_host)和 MongoDB 真实地址(remote_bind_address)。URI 里的 host 必须是 127.0.0.1,因为隧道已把远端服务“拉”到本地。
- URI 应为
mongodb://user:pass@127.0.0.1:{port}/db?authMechanism=SCRAM-SHA-1,其中{port}是server.local_bind_port - 阿里云 MongoDB 默认用
SCRAM-SHA-1;自建 MongoDB 4.0+ 推荐显式加?authMechanism=SCRAM-SHA-256,否则可能报Authentication failed - 别把
mongo_host(比如dds-bp1abc123.mongodb.rds.aliyuncs.com)直接塞进 URI 的 host 位——它根本不可达,也不该被 Python 直连
程序退出时忘记关隧道?用 atexit.register() 或 try/finally
SSHTunnelForwarder 启动后会在后台维持一个 SSH 进程。如果 Python 脚本异常退出或没手动调 server.stop(),这个进程会卡住,下次运行时报 Address already in use 或阻塞在 start()。
- 推荐在启动后立刻注册清理:
import atexit; atexit.register(server.stop) - 更稳妥的做法是包在
try/finally里,尤其在脚本含多次数据库操作时 - 避免把
server定义成局部变量——函数返回后引用丢失,atexit注册会失效;建议作为模块级变量或上下文管理器使用
私钥登录比密码更安全,但路径和权限容易出错
用 ssh_pkey 参数传私钥路径时,Windows 下反斜杠不识别,Linux/macOS 下文件权限太松(比如 644)会导致 SSH 拒绝加载。
- 私钥路径统一用正斜杠或
os.path.join(),例如r'D:/keys/id_rsa'或'/home/user/.ssh/id_rsa' - Linux/macOS 上执行
chmod 600 /path/to/id_rsa,否则报错Invalid key或SSHException: Not a valid RSA private key file - 若私钥有密码,必须传
ssh_private_key_password='xxx';漏掉会卡在server.start()不报错,但连接超时
remote_bind_address 指向的 MongoDB IP 和端口仍可能被跳板机自身的 iptables 或 firewalld 拦截——这跟 Python 代码无关,但错误现象完全一样:连接超时、拒绝连接。

















