NodeJS通过SSH Tunnel连接AWS RDS MySQL超时问题求助
Node.js 通过 SSH Tunnel 连接 AWS RDS MySQL 超时问题求助
我和伙伴合作开发项目,后端采用Node.js,数据库使用AWS RDS MySQL,目前核心问题是无法在Node.js环境中通过SSH Tunnel正常与数据库建立通信。以下是我整理的配置代码及API调用报错信息,恳请提供帮助或建议!
SSH Tunnel配置
const { createTunnel } = require ('tunnel-ssh'); const fs = require('fs'); const path = require('path'); require('dotenv').config(); async function createSSHTunnel(srcAddr = process.env.SSH_HOST, srcPort = process.env.SSH_PORT) { const tunnelOptions = { autoClose: true, }; const serverOptions = { port: srcPort, }; const sshOptions = { host: process.env.SSH_HOST, port: parseInt(process.env.SSH_PORT), username: process.env.SSH_User, privateKey: fs.readFileSync(path.join(__dirname, 'JD KeyPair.pem')), }; const forwardOptions = { srcAddr: srcAddr, srcPort: srcPort, dstAddr: process.env.DB_Host, dstPort: parseInt(process.env.DB_Port), }; try { await createTunnel( tunnelOptions, serverOptions, sshOptions, forwardOptions ); } catch (error) { if (error.code === "EADDRINUSE") { // 假设该端口仅被SSH隧道占用,复用现有连接 console.log(`复用${srcAddr}:${srcPort}上已有的SSH隧道。`); return { srcAddr, srcPort }; } else { throw error; } } console.log(`SSH隧道已成功创建在${srcAddr}:${srcPort}。`); return { srcAddr, srcPort }; } module.exports = { createSSHTunnel };
数据库配置
const { createPool } = require('mysql2/promise'); const { createSSHTunnel } = require ('./sshTunnel'); require('dotenv').config(); const { srcAddr, srcPort } = createSSHTunnel(); const db = createPool({ host: process.env.DB_Host, port: process.env.DB_Port, user: process.env.DB_User, password: process.env.DB_Pass, database: process.env.DB_Data, waitForConnections: true, connectionLimit: 10, queueLimit: 0, }); module.exports = db;
API调用示例
router.post('/retrieveCardInfo', async (req, res) => { const cardID = req.body.CardID.CardID; try { const uploadCardStatus = await db.query('SELECT * FROM cardstorage WHERE cardID = ?', [cardID]); if (typeof uploadCardStatus[0][0] !== 'undefined') { return res.send(uploadCardStatus[0][0]); } return res.json({errorMessage: "Card Not Found"}); } catch (err) { return res.json({ errorMessage: 'A Server Error Occured!' }); } });
API执行报错信息
code: 'ETIMEDOUT', errno: undefined, sql: undefined, sqlState: undefined, sqlMessage: undefined
问题排查及解决方案
- 异步初始化未等待(核心问题)
createSSHTunnel是异步函数,但数据库配置中直接解构赋值未使用await,导致连接池在隧道建立前就尝试连接RDS,必然超时。修改数据库配置:
const { createPool } = require('mysql2/promise'); const { createSSHTunnel } = require ('./sshTunnel'); require('dotenv').config(); let db; // 用立即执行异步函数确保隧道建立后再初始化连接池 (async () => { try { const { srcAddr, srcPort } = await createSSHTunnel(); db = createPool({ host: srcAddr, // 连接隧道本地监听地址,而非直接连RDS port: srcPort, user: process.env.DB_User, password: process.env.DB_Pass, database: process.env.DB_Data, waitForConnections: true, connectionLimit: 10, queueLimit: 0, }); console.log('数据库连接池已初始化'); } catch (err) { console.error('初始化数据库连接池失败:', err); } })(); module.exports = db;
- SSH隧道监听地址修正
forwardOptions中的srcAddr应设为'localhost'或'127.0.0.1'(本地监听),而非SSH_HOST,否则可能因监听公网地址导致无法访问。修改createSSHTunnel的默认参数:
async function createSSHTunnel(srcAddr = 'localhost', srcPort = process.env.SSH_PORT) { // ... 其余代码不变 }
- 验证网络连通性
- 先用SSH命令手动连接堡垒机,再从堡垒机连接RDS,确认网络通路:
ssh -i "JD KeyPair.pem" ${SSH_User}@${SSH_HOST} # 登录堡垒机后执行 mysql -h ${DB_Host} -P ${DB_Port} -u ${DB_User} -p - 检查堡垒机安全组是否允许Node.js服务器IP访问22端口,RDS安全组是否允许堡垒机内网IP访问3306端口。
- 私钥文件权限
在Linux/macOS环境下,确保私钥文件JD KeyPair.pem权限为600,否则SSH会拒绝使用:
chmod 600 "JD KeyPair.pem"
- 依赖版本兼容性
若tunnel-ssh版本存在参数兼容问题,可考虑改用ssh2库手动实现隧道,控制更精细。
内容的提问来源于stack exchange,提问作者JonD
相关产品推荐
相关产品推荐

