You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 21:24:57