Node.js配置SSH隧道连接AWS EC2与RDS时遇连接超时错误
问题描述
我已经通过SSH隧道用pgAdmin连接上了私有RDS,EC2安全组也开放了所有流量,但本地用Node.js代码连接数据库时一直报连接超时错误。以下是相关代码和终端的超时错误信息:
tunnel.js(与私钥同目录)
const { createTunnel } = require ('tunnel-ssh'); const fs = require('fs'); const path = require("path"); require('dotenv').config(); async function createSSHTunnel(srcAddr = process.env.EC_HOST, srcPort = 22) { console.log(" ssh tunnel started") const tunnelOptions = { autoClose: true, }; const serverOptions = { port: 12345, }; const sshOptions = { host: process.env.EC2_HOST, port: 22, username: "ec2-user", privateKey: fs.readFileSync(path.join(__dirname,"dev-jump-box.pem")) }; file = fs.readFileSync(path.join(__dirname,"dev-jump-box.pem")) console.log(file) const forwardOptions = { srcAddr: 'localhost', srcPort: 12345, dstAddr: process.env.DB_HOST, dstPort:5432, }; try { console.log(" try tunnel") await createTunnel( tunnelOptions, serverOptions, sshOptions, forwardOptions ); console.log(" try tunnel ended") } catch (error) { if (error.code === "EADDRINUSE") { console.log(`Returning existing SSH tunnel on ${srcAddr}:${srcPort}.`); return { srcAddr, srcPort }; } else { throw error; } } console.log(`SSH tunnel successfully created on ${srcAddr}:${srcPort}.`); return { srcAddr, srcPort }; } module.exports = { createSSHTunnel };
database.js
const Pool = require("pg").Pool; const dotenv = require('dotenv') const { createSSHTunnel } = require ('./tunnel'); dotenv.config() console.log(" "+process.env.DB_DATABASE) const { srcAddr, srcPort } = createSSHTunnel(); const pool = new Pool({ host: process.env.DB_HOST, user:process.env.DB_USER, password:process.env.DB_PASSWORD, database:process.env.DB_DATABASE, port: process.5432, max: 80, connectionTimeoutMillis:60000, idleTimeoutMillis:500 }); module.exports = pool;
attachDbclientMiddleware.js
const pool = require('../Util/database'); exports.attachDbClient = async(req, res, next) =>{ pool.connect() .then(client => { req.dbClient = client; next(); }) .catch(err => { console.error('Error connecting to database:', err); res.status(500).json({ error: 'Internal Server Error' }); }); console.log("client ") }
终端错误信息
Error connecting to database: Error: Connection terminated due to connection timeout at Connection. (/home/tahmid/Projects/mobile_dev_back/mobile_backend_EIDPOINT/node_modules/pg/lib/client.js:132:73) at Object.onceWrapper (node:events:641:28) at Connection.emit (node:events:527:28) at Connection.emit (node:domain:475:12) at Socket. (/home/tahmid/Projects/mobile_dev_back/mobile_backend_EIDPOINT/node_modules/pg/lib/connection.js:57:12) at Socket.emit (node:events:527:28) at Socket.emit (node:domain:475:12) at TCP. (node:net:688:12)
解决方案
你的代码存在3个核心问题,逐一修复即可解决超时问题:
1. 数据库连接配置错误
database.js里有两处明显错误:
port: process.5432是语法错误,且你应该连接本地SSH隧道的端口12345,而非RDS的5432端口host应该写localhost,因为隧道是在本地监听端口转发请求到RDS
修复后的连接池配置:
const pool = new Pool({ host: 'localhost', // 改为本地地址,对应隧道监听地址 user: process.env.DB_USER, password: process.env.DB_PASSWORD, database: process.env.DB_DATABASE, port: 12345, // 改为隧道的本地监听端口 max: 80, connectionTimeoutMillis: 60000, idleTimeoutMillis: 500 });
2. 异步隧道初始化未等待
createSSHTunnel是异步函数,但你直接同步解构返回值,导致数据库连接池在隧道建立完成前就发起连接,必然超时。需修改初始化逻辑,确保隧道就绪后再创建连接池:
修改后的database.js:
const Pool = require("pg").Pool; const dotenv = require('dotenv') const { createSSHTunnel } = require ('./tunnel'); dotenv.config() console.log(" "+process.env.DB_DATABASE) let pool; // 用异步立即执行函数确保隧道建立后再初始化连接池 (async () => { await createSSHTunnel(); pool = new Pool({ host: 'localhost', user: process.env.DB_USER, password: process.env.DB_PASSWORD, database: process.env.DB_DATABASE, port: 12345, max: 80, connectionTimeoutMillis: 60000, idleTimeoutMillis: 500 }); })(); // 导出获取连接池的方法,避免未初始化时被调用 module.exports = () => pool;
对应的中间件attachDbclientMiddleware.js也要同步修改,确保连接池就绪后再使用:
const getPool = require('../Util/database'); exports.attachDbClient = async(req, res, next) =>{ const pool = getPool(); if (!pool) { return res.status(500).json({ error: '数据库连接池未初始化' }); } try { const client = await pool.connect(); req.dbClient = client; next(); } catch (err) { console.error('连接数据库失败:', err); res.status(500).json({ error: '内部服务器错误' }); } console.log("client ") }
3. SSH隧道返回值冗余
tunnel.js中createSSHTunnel的返回值逻辑混乱,你创建的是本地12345端口的隧道,直接返回固定值即可:
修改tunnel.js的返回部分:
console.log(`SSH隧道已成功创建在 localhost:12345.`); return { srcAddr: 'localhost', srcPort: 12345 };
额外检查点
- 确认本地防火墙未阻止12345端口的流量
- 验证
tunnel-ssh版本为最新稳定版,避免兼容性问题 - 再次确认EC2安全组允许你的本地IP访问22端口
内容的提问来源于stack exchange,提问作者common task
相关产品推荐
相关产品推荐

