SSH隧道模式下Flask应用无法连接远程MongoDB求助
问题描述
我在Ubuntu 20.04上用Flask开发GUI,数据来自服务器Docker容器中的MongoDB。Mongo URI为:
mongodb://<USERNAME>:<PASSWORD>@localhost:9999/<DB>?authSource=<AUTHDB>
先通过以下命令建立到服务器的SSH隧道:
ssh -L 9999:localhost:27017 -N <USER>@<HOST>
建立隧道后,Mongo Shell能正常连接数据库,但运行Flask应用时PyMongo报错:
pymongo.errors.ServerSelectionTimeoutError: 127.0.0.1:9999: [Errno 111] Connection refused, Timeout: 30s, Topology Description: <TopologyDescription id: 62fa50c9015294b6e1df5811, topology_type: Unknown, servers: [<ServerDescription ('127.0.0.1', 9999) server_type: Unknown, rtt: None, error=AutoReconnect('127.0.0.1:9999: [Errno 111] Connection refused')>]>
另外,当本地运行Mongo Docker容器时,会出现PyMongo认证失败的错误:
pymongo.errors.OperationFailure: Authentication failed., full error: {'ok': 0.0, 'errmsg': 'Authentication failed.', 'code': 18, 'codeName': 'AuthenticationFailed', '$clusterTime': {'clusterTime': Timestamp(1660576452, 1), 'signature': {'hash': b'\x16\x8c\x11\xb2Uf\xe3\x80\x85\xb0 >\xcb\xbe\xa5S\x8f\xec(g', 'keyId': 7128054189753630722}}, 'operationTime': Timestamp(1660576452, 1)}
注:连接本地Docker部署的MongoDB无问题,SSH连接正常时Mongo Shell能登录,推测远程连接遗漏了步骤,URI本身无问题。
Flask相关代码:
init.py
from flask import Flask from flask_bootstrap import Bootstrap from .frontend import frontend from .database import mongo import os # 补充原代码缺失的os导入 def create_app(configfile=None): app = Flask(__name__) app.config["MONGO_URI"] = "mongodb://<USERNAME>:<PASSWORD>@localhost:9999/<DB>?authSource=<AUTHDB>" Bootstrap(app) mongo.init_app(app) app.register_blueprint(frontend) app.secret_key = os.environ['SECRET_KEY'] app.config['BOOTSTRAP_SERVER_LOCAL'] = True return app
database.py
from flask_pymongo import PyMongo mongo = PyMongo()
frontend.py
from flask import Blueprint from .database import mongo frontend = Blueprint('frontend', __name__) # Rest of file contains routes with calls to database
解决方案
问题1:SSH隧道下PyMongo连接拒绝
原因及修复步骤
- SSH隧道绑定地址限制
默认ssh -L仅绑定127.0.0.1,若Flask应用尝试通过IPv6或主机名访问会失败。修改SSH命令绑定所有地址:
ssh -L 0.0.0.0:9999:localhost:27017 -N <USER>@<HOST>
同时将Mongo URI中的localhost改为127.0.0.1,避免解析到IPv6地址:
app.config["MONGO_URI"] = "mongodb://<USERNAME>:<PASSWORD>@127.0.0.1:9999/<DB>?authSource=<AUTHDB>"
- Flask容器网络隔离
若Flask在Docker容器中运行,本地SSH隧道端口未映射到容器内。可选择两种方式:
- 添加端口映射并使用容器主机别名:
对应URI改为:docker run -p 5000:5000 --add-host=host.docker.internal:host-gateway your-flask-imageapp.config["MONGO_URI"] = "mongodb://<USERNAME>:<PASSWORD>@host.docker.internal:9999/<DB>?authSource=<AUTHDB>" - 使用主机网络模式:
docker run --network=host your-flask-image
- 防火墙/SELinux限制
检查本地防火墙是否允许127.0.0.1:9999的连接:
sudo ufw status
若有规则限制,添加允许规则:
sudo ufw allow in from 127.0.0.1 to 127.0.0.1 port 9999
若启用SELinux,临时关闭测试:
sudo setenforce 0
测试有效后再配置永久规则。
问题2:本地Mongo容器运行时认证失败
原因及修复步骤
- 本地与远程认证配置不一致
本地Mongo容器可能使用了不同的authSource、用户名或密码。进入本地容器确认配置:
docker exec -it <mongo-container-name> mongosh use <LOCAL-AUTHDB> db.getUser("<LOCAL-USERNAME>")
建议用环境变量区分本地和远程配置,避免混用:
import os if os.environ.get('ENV') == 'production': app.config["MONGO_URI"] = "mongodb://<REMOTE-USER>:<REMOTE-PASS>@127.0.0.1:9999/<REMOTE-DB>?authSource=<REMOTE-AUTHDB>" else: app.config["MONGO_URI"] = "mongodb://<LOCAL-USER>:<LOCAL-PASS>@localhost:27017/<LOCAL-DB>?authSource=<LOCAL-AUTHDB>"
- 密码特殊字符未转义
若密码包含@、:、/等特殊字符,需进行URL编码。用Python工具处理:
from urllib.parse import quote_plus username = quote_plus("<USERNAME>") password = quote_plus("<PASSWORD>") app.config["MONGO_URI"] = f"mongodb://{username}:{password}@127.0.0.1:9999/<DB>?authSource=<AUTHDB>"
内容的提问来源于stack exchange,提问作者strK
相关产品推荐
相关产品推荐

