本地Node.js通过SSH隧道连接AWS RDS Postgres的问题求助
AWS RDS SSH隧道本地连接问题解决方案
问题背景
我有一个运行在EC2实例上的Node.js应用,可成功连接AWS RDS Postgres数据库并执行SQL语句返回结果。现在希望在本地机器运行该应用,尝试使用EC2的密钥对创建SSH隧道:
ssh -i ec2-db-keypair.pem -N -L localhost:8080:database-test-1.XXX.eu-west-2.rds.amazonaws.com:5432 ec2-user@ec2-XXX.eu-west-2.compute.amazonaws.com
本地Node服务监听8081端口,访问/db路由时使用以下连接池配置(该配置在EC2实例上可正常工作):
server.listen({ port: 8081 }, (err, address) => { if (err) { console.error(err); process.exit(1); } console.log(`Server listening at ${address}`); }); server.get("/db", async (request, reply) => { console.log("connect to db using slonik"); const pem = fs .readFileSync(path.join(__dirname, "../../eu-west-2-bundle.pem")) .toString(); const pool = createPool( // ec2 instance instead of localhost:8080 I use: database-test-1.XXX.eu-west-2.rds.amazonaws.com:5432 `postgresql://postges:mydbpassword@localhost:8080/postgres`, { ssl: { ca: pem, rejectUnauthorized: true, }, } ); pool.connect(async (connection) => { try { const resultNodejsMyTableRows = await connection.query( sql`SELECT * FROM nodejs_mytable ORDER BY id ASC` ); console.log("nodejs_mytable=", { resultNodejsMyTableRows }); } catch (err) { console.log(err); } }); return "look at console for responses\n"; });
本地运行时出现错误:
Hostname/IP does not match certificate's altnames: Host: localhost. is not in the cert's altnames: DNS:database-test-1.XXX.eu-west-2.rds.amazonaws.com
SSH隧道用法是否正确?
你的SSH隧道命令本身是正确的,它能将本地8080端口的流量转发到EC2实例,再由EC2转发至RDS数据库的5432端口。问题出在Node.js连接数据库时的SSL证书验证逻辑。
错误原因
AWS RDS的SSL证书是绑定到其专属域名(database-test-1.XXX.eu-west-2.rds.amazonaws.com)的,而你本地连接时使用的是localhost:8080,SSL验证过程中会检查连接的主机名是否与证书中的域名匹配,因此出现了不匹配的错误。
修正方案
方法1:修改本地hosts映射(推荐)
通过把RDS域名映射到本地回环地址,让SSL验证认为你连接的是真实RDS域名,同时流量通过SSH隧道转发:
- 打开本地hosts文件:
- Windows:
C:\Windows\System32\drivers\etc\hosts - macOS/Linux:
/etc/hosts
- Windows:
- 添加一行映射:
127.0.0.1 database-test-1.XXX.eu-west-2.rds.amazonaws.com - 修改Node.js的数据库连接字符串,将
localhost:8080换回RDS原域名:`postgresql://postges:mydbpassword@database-test-1.XXX.eu-west-2.rds.amazonaws.com:8080/postgres`
方法2:临时关闭SSL主机名验证(仅本地调试用)
如果只是临时本地调试,可以跳过SSL主机名验证,但绝对不要在生产环境使用:
修改连接池的SSL配置,添加checkServerIdentity: () => undefined:
const pool = createPool( `postgresql://postges:mydbpassword@localhost:8080/postgres`, { ssl: { ca: pem, rejectUnauthorized: true, checkServerIdentity: () => undefined // 跳过主机名验证 }, } );
内容的提问来源于stack exchange,提问作者Inspiraller
相关产品推荐
相关产品推荐

