4

I've been following this tutorial [1] to connect to an Aurora RDS cluster from a lambda JavaScript function using IAM authentication. I'm able to get an authentication token, but not able to use the token to get a connection to the data base. Also, I followed these instructions [2] to get an authentication token from CLI, and then use the token to connect 'mysql' command tool. All good from the command line, this kind of tells me that I have followed correctly all the steps from [1].

I'm stuck though. Although I can get an authentication token from my lambda, I can't use it to connect to my data base. My lambda is written in Java Script, and the mysql driver I'm using is [3]. This driver seem to not be compatible with IAM authentication. Here is why I think that:

  • in [1], there is an example for Python, where a mysql driver is being used. In the example, the instructions tell to use

        auth_plugin="mysql_clear_password"
    

    and the driver I'm using [3], from docs, I can't find an option that maps to "auth_plugin". The closer, is a flag called PLUGIN_AUTH, but docs for [3] say:

        Uses the plugin authentication mechanism when connecting to the MySQL server. This feature is not currently
        supported by the Node.js implementation so cannot be turned on. (Default off)
    

Seems like Node doesn't support this.

This is a piece of my code:

       const mysql = require('mysql');
       var config = {
                  host       : theHost,
                  user       : theUser,
                  password   : token, --------> token previously generated
                  database   : theDataBase
                  auth_plugin: "mysql_clear_password",   --------> this option is not really part of driver [3], I was just trying
                                                                                                      something out
                  ssl: {
                        ca: '/var/task/rds-combined-ca-bundle.pem'  ----------> cert downloaded from [4]
                   }
         };
        var connection = mysql.createConnection(config);

The error I'm getting is:

          ERROR error connecting: Error: unable to get local issuer certificate

I have checked that "/var/task/rds-combined-ca-bundle.pem" exists. It is part of the zip package for my function.

If I remove the "ssl" key from the connection object, I get:

          error connecting: Error: ER_NOT_SUPPORTED_AUTH_MODE: Client does not support authentication protocol
          requested by server; consider upgrading MySQL client

From AWS docs, I can't find a good example of using IAM authentication from a Lambda function implemented in JavaScript. So, my questions are:

  • If any, can you provide an example of a Lambda function implemented in Java Script of how to connect to Aurora using IAM authentication?
  • Are you aware if really [3] doesn't support IAM authentication?
  • Is there any other mysql driver that is really compatible with IAM authentication?

Thanks!

References:

[1] https://aws.amazon.com/blogs/database/iam-role-based-authentication-to-amazon-aurora-from-serverless-applications/

[2] https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/UsingWithRDS.IAMDBAuth.Connecting.AWSCLI.html

[3] https://www.npmjs.com/package/mysql

[4] https://s3.amazonaws.com/rds-downloads/rds-combined-ca-bundle.pem

1
  • Check out this example, I think it should work w/ the mysql2 driver. cloudonaut.io/… Commented Oct 23, 2020 at 16:46

2 Answers 2

4

a bit late but mysql library has rds certs inside it as standard

so luckily to solve this issue add 'ssl: "Amazon RDS",' which will use the inbuilt certs

so something like this

const signer = new AWS.RDS.Signer({
        'region': region,
        'username': username,
        'hostname':host,
        'port': port
});
let token = await signer.getAuthToken({})
var config = {
                  host       : host,
                  user       : username,
                  password   : token, --------> token previously generated
                  database   : database
                  ssl: "Amazon RDS"
         };

see mysql docs for more

https://github.com/mysqljs/mysql#ssl-options

SSL options
The ssl option in the connection options takes a string or an object. When given a string, it uses one of the predefined SSL profiles included. The following profiles are included:

"Amazon RDS": this profile is for connecting to an Amazon RDS server and contains the certificates from https://rds.amazonaws.com/doc/rds-ssl-ca-cert.pem and https://s3.amazonaws.com/rds-downloads/rds-combined-ca-bundle.pem

ps: word from the wise, aws rds to node is a bit tricky, there's lots of other tripropes like permissions, enabling iam, granting iam users to watch out for, create a question and ping me if you have other issues as well and i'll answer them there

Sign up to request clarification or add additional context in comments.

Comments

1

This is my working solution:

IAM

Make sure you have a policy like this attached to your function's execution role (snippet is ts CDK):

lambda.addToRolePolicy(new PolicyStatement({
    effect: Effect.ALLOW,
    actions: [
        'rds-db:connect'
    ],
    resources: [
        'arn:aws:rds-db:us-east-1:{account id}:dbuser:{cluster resource id}/{db user name}'
    ]
))

In the Database

Make sure you have added the user, AND granted permission on the db you're trying to connect to:

CREATE USER {db user name} IDENTIFIED WITH AWSAuthenticationPlugin AS 'RDS';
GRANT ALL PRIVILEGES ON {db name}.* TO '{db user name}'@'%';
FLUSH PRIVILEGES;

In your Lambda function

I am using pools, you can do the same thing basically using a single connection. Also, I am using mysql2 since it provides support for auth plugins which is required.

const { Signer } = require('@aws-sdk/rds-signer')
const mysql = require('mysql2')

const { DB_HOST, DB_PORT, DB_USER, DB_NAME } = process.env

async function createPool() {
    const signer = new Signer({
        hostname: DB_HOST,
        port: DB_PORT,
        username: DB_USER
    })
    const token = await signer.getAuthToken()
    console.log('token: ', token)
    const dbConfig = {
        connectionLimit: 10,
        host: DB_HOST,
        user: DB_USER,
        database: DB_NAME,
        port: DB_PORT,
        password: token,
        ssl: 'Amazon RDS',
        authPlugins: {
            'mysql_clear_password': mysql.authPlugins.mysql_clear_password({
                password: token
            })
        }
    }
    return mysql.createPool(dbConfig)
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.