Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[QUESTION]: Connecting to Amazon RDS with IAM credentials using TypeORM #4724

Closed
achansonjr opened this issue Sep 10, 2019 · 3 comments · Fixed by #5673
Closed

[QUESTION]: Connecting to Amazon RDS with IAM credentials using TypeORM #4724

achansonjr opened this issue Sep 10, 2019 · 3 comments · Fixed by #5673

Comments

@achansonjr
Copy link

Issue type:

[X] question
[ ] bug report
[ ] feature request
[ ] documentation issue

Database system/driver:

[ ] cordova
[ ] mongodb
[ ] mssql
[ ] mysql / mariadb
[ ] oracle
[X] postgres
[ ] cockroachdb
[ ] sqlite
[ ] sqljs
[ ] react-native
[ ] expo

TypeORM version:

[X] latest
[ ] @next
[ ] 0.x.x (or put your version here)

Steps to reproduce or a small repository showing the problem:

Other environment considerations.

Postgres instance in AWS RDS, using AWS IAM authentication.
How do I allow users to connect to Amazon RDS with IAM credentials?

Using RDS.Signer we generate a token in our node application at application startup.

  const creds = await credentialProviderChain.resolvePromise();
  const token = await Promise.resolve(getRDSAuthToken(creds));

  const databaseOptions: ConnectionOptions = {
    type: 'postgres',
    host: Environment.AWS_RDS_POSTGRES_HOST,
    port: Environment.AWS_RDS_POSTGRES_PORT,
    username: Environment.AWS_RDS_POSTGRES_USER,
    password: token,
    database: Environment.AWS_RDS_POSTGRES_DATABASE,
    migrationsRun: true,
    ssl: {
      rejectUnauthorized: false,
      ca: readFileSync('/path/to/pemfile/rds-combined-ca-us-gov-bundle.pem').toString(),
    },
    entities: [
      Entity1,
      Entity2,
      Entity3,
      Entity4,
      Entity5,
    ],
    migrations: [
      `${__dirname}/migration/*.js`,
    ],
    logging: 'all',
    logger: 'advanced-console',
    synchronize: false,
  };

const databaseConnection = await createConnection(databaseOptions);
const entity1Repo = databaseConnection.getCustomRepository(Entity1);
const entity2Repo = databaseConnection.getCustomRepository(Entity2);
const entity3Repo = databaseConnection.getCustomRepository(Entity3);
const entity4Repo = databaseConnection.getCustomRepository(Entity4);

So that's our ConnectionOptions object. We are able to connect to the entity, and even have migrations running. So we know that the token that is being generated is providing us an active connection. We pass our repos to the routes that need access to those entities throughout our express app.

According to IAM Database Authentication for MySQL and PostgreSQL:

An authentication token is a unique string of characters that Amazon RDS generates on request. Authentication tokens are generated using AWS Signature Version 4. Each token has a lifetime of 15 minutes. You don't need to store user credentials in the database, because authentication is managed externally using IAM. You can also still use standard database authentication.

What happens next is that after 15 minutes the AWS IAM token generated by RDS.Signer goes away and we don't have a way to refresh the connection in Typeorm, nor in our repositories.

We don't really want to recreate a token every request, but there doesn't seem to be a way to use ephemeral credentials in IAM RDS authentication with Typeorm, as there doesn't seem to be a good location to have Typeorm connection or repositories check to see if their connection is still valid, then regenerate whatever ephemeral credentials that might be necessary.

We can use named user accounts in the RDS Postgres database, but thought it would be nice from a security standpoint to be able to utilize the AWS IAM RDS token generation off of the EC2 ephemeral role capability.

@robbiet480
Copy link
Contributor

robbiet480 commented Mar 12, 2020

@achansonjr Fix is in #5673. Thankfully this exact feature was added to node-postgres at brianc/node-postgres#1926 middle of last year. My PR simply changes the TypeScript definition to allow passing a function into password. It shouldn't stop you from implementing your code now though (I believe), as long as you just provide a POJO. Something like this worked for me:

password: async () => {
  if(process.env.DB_CONNECTION_STYLE === "rds_iam") {
    const signer = new AWS.RDS.Signer({
      region: (new AWS.Config()).region,
      hostname: process.env.DB_HOST,
      port: Number(process.env.DB_PORT),
      username: process.env.DB_USERNAME,
    });
    return signer.getAuthToken({});
  }
  return process.env.DB_PASSWORD;
},

@Sachin1678
Copy link

@robbiet480 I am using @nest.js/typeorm. I tried to pass this function but it gives error that PAM authentication failed for user.. it works fine I fetch the token and pass it. Can you please help. I am also using IAM authentication and wants to auto-renew this token. Do I need to create connection on each request or is there any other way?
@achansonjr

@chathunb
Copy link

@robbiet480 I am using @nest.js/typeorm. I tried to pass this function but it gives error that PAM authentication failed for user.. it works fine I fetch the token and pass it. Can you please help. I am also using IAM authentication and wants to auto-renew this token. Do I need to create connection on each request or is there any other way? @achansonjr

@Sachin1678 This solution worked for me for both aws rds proxy IAM auth and aws rds cluster IAM auth without any issue it renews the token from time to time and keeps the connection with the rds

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants