node.js - 使用 AWS 生成的凭证连接到 RDS Postgres 时密码身份验证失败

标签 node.js postgresql amazon-web-services aws-cdk

我尝试从 ECS 实例连接到 RDS Postgres 数据库,但不断收到错误:用户密码身份验证失败。两者都是使用 AWS CDK 配置的。我仅提供数据库用户名并让 AWS 生成一个密码,该密码通过 secret 环境变量提供给 ECS 实例。我正在 ECS 实例中记录环境变量,我可以看到它正在接收数据库连接信息,包括生成的凭据。

这是我的应用程序代码

import * as dotenv from "dotenv";
import * as Koa from "koa";
import * as Knex from "knex";

dotenv.config();
const { env } = process;
const port = parseInt(env.PORT || "3000", 10);

function getDatabaseConnection(): Knex.Config["connection"] {
  const {
    DATABASE_URL,
    DATABASE_HOST,
    DATABASE_NAME,
    DATABASE_CREDENTIALS,
    DATABASE_PORT = "5432",
  } = env;

  if (DATABASE_URL) {
    return DATABASE_URL;
  }

  if (!DATABASE_HOST)
    throw new Error("Missing required env var: DATABASE_HOST");
  if (!DATABASE_NAME)
    throw new Error("Missing required env var: DATABASE_NAME");
  if (!DATABASE_CREDENTIALS)
    throw new Error("Missing required env var: DATABASE_CREDENTIALS");

  const { username, password } = JSON.parse(DATABASE_CREDENTIALS);

  return {
    host: DATABASE_HOST,
    user: username,
    password: password,
    port: parseInt(DATABASE_PORT, 10),
    database: DATABASE_NAME,
  };
}

main();
async function main() {
  const databaseConnection = getDatabaseConnection();

  console.log(`Connecting to database:`, databaseConnection);

  let db: Knex | null = Knex({
    client: "pg",
    connection: databaseConnection,
    acquireConnectionTimeout: 5000,
  });

  await db.raw("SELECT 10 + 15").catch((error) => {
    console.log("Test query failed", error);
    db = null;
  });

  const app = new Koa().use(async (context) => {
    try {
      context.response.body = {
        hello: "world",
        queryResult: db ? await db.raw("SELECT 10 + 15") : null,
      };
    } catch (error) {
      context.response.body = {
        error: { ...error, message: error.message },
      };
    }
  });

  await new Promise((resolve) => app.listen(port, resolve));

  console.log(`Listening on port ${port}`);
}

以及我的 AWS CDK 堆栈

import { v4 as uuid } from "uuid";
import ec2 = require("@aws-cdk/aws-ec2");
import ecs = require("@aws-cdk/aws-ecs");
import cdk = require("@aws-cdk/core");
import { RemovalPolicy } from "@aws-cdk/core";
import rds = require("@aws-cdk/aws-rds");
import ecsPatterns = require("@aws-cdk/aws-ecs-patterns");
import path = require("path");
import { Port, InstanceClass, InstanceSize } from "@aws-cdk/aws-ec2";
import { Secret } from "@aws-cdk/aws-secretsmanager";

const app = new cdk.App();
const stack = new cdk.Stack(app, process.env.STACK_NAME || "HelloWorld");
const vpc = new ec2.Vpc(stack, "MyVpc", { maxAzs: 2 });
const cluster = new ecs.Cluster(stack, "Cluster", { vpc });

const port = 3000;
const databasePort = 5432;
const databaseUser = "app";
const databaseName = "app_db";

cluster.addCapacity("cluser_capacity", {
  instanceType: ec2.InstanceType.of(InstanceClass.T2, InstanceSize.MICRO),
  maxCapacity: 1,
});

const database = new rds.DatabaseInstance(stack, "AppDatabase", {
  vpc,
  engine: rds.DatabaseInstanceEngine.POSTGRES,
  instanceClass: ec2.InstanceType.of(
    ec2.InstanceClass.T2,
    ec2.InstanceSize.MICRO
  ),
  removalPolicy: RemovalPolicy.DESTROY,
  databaseName: databaseName,
  masterUsername: databaseUser,
  deletionProtection: false,
  port: databasePort,
});

const apiService = new ecsPatterns.ApplicationLoadBalancedFargateService(
  stack,
  "AppService",
  {
    cluster,
    publicLoadBalancer: true,
    taskImageOptions: {
      environment: {
        PORT: port.toString(),
        DATABASE_PORT: databasePort.toString(),
        DATABASE_HOST: database.dbInstanceEndpointAddress,
        DATABASE_USER: databaseUser,
        DATABASE_NAME: databaseName,
      },
      secrets: database.secret
        ? {
            DATABASE_CREDENTIALS: ecs.Secret.fromSecretsManager(
              database.secret
            ),
          }
        : {},
      containerPort: port,
      image: ecs.ContainerImage.fromAsset(
        path.resolve(__dirname, "..", "local-image")
      ),
    },
  }
);

database.connections.allowFrom(apiService.service, Port.tcp(databasePort));

app.synth();

为什么 RDS 生成的用户名/密码不起作用?

最佳答案

如果您在创建时遇到此问题,可能是因为您没有命名数据库。该名称与实例不同,AWS 默认为未命名数据库。

在 AWS 控制台中,导航到 RDS > 数据库 > your_database > 配置

Is the "DB name" field populated?如果不是,请删除该数据库并重新创建它。

在创建菜单的最后,look under "Additional Configuration". Add an "Initial database name" of your choice 。这现在是您的数据库名称。

Now you can use this database name to connect via boto3

关于node.js - 使用 AWS 生成的凭证连接到 RDS Postgres 时密码身份验证失败,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61168133/

相关文章:

javascript - 如何正确使用 sequelize 和 promise

MySQL 在 node.js 服务器上的空闲时间后给出 "read ECONNRESET"错误

postgresql - 对根元素 postgres 的 CTE 查询

java - 如何锁定共享变量以在AWS lambda中执行写入操作

javascript - 亚马逊 Alexa - 捕获完整的成绩单

node.js - 无法在nodejs应用程序中加载angular2的node_modules文件

node.js - 使用 mongoose 将 mongo 对象的字段设置为空

sql - PostgreSQL 索引未用于查询

python psycopg2选择时区的current_timestamp问题

ruby-on-rails - Elastic Beanstalk:找不到带有可执行包的 gem bundler (>= 0.a) (Gem::GemNotFoundException)