5

I have an AWS RDS DB running MySQL 5.6.39, with IAM DB Authentication Enabled.

First of all, I completed with success the Tutorial: Configuring a Lambda Function to Access Amazon RDS in an Amazon VPC and this was my starting point for the next steps.

I want to log in with IAM credentials and so, following this and this tutorials, I did:

  1. When I created the RDS MySQL instance, I selected Enabling IAM database authentication.

  2. Created a user named lambda:

    CREATE USER 'lambda' IDENTIFIED WITH AWSAuthenticationPlugin as 'RDS';
    GRANT ALL PRIVILEGES ON test_db.* TO 'lambda'@'%';
    FLUSH PRIVILEGES;
    
  3. Created an IAM policy, and attached it to the role I was using as an Execution role for my lambda function:

    {
      "Version": "2012-10-17",
      "Statement": [
        {
          "Effect": "Allow",
          "Action": [
            "rds-db:connect"
          ],
          "Resource": [
            "<DB-ARN>/lambda"
          ]
        }
      ]
    }
    
  4. Created a lambda function:

    import sys
    import boto3
    import logging
    import pymysql
    
    #rds settings
    rds_host  = "<RDS-ENDPOINT>"
    username = "lambda"
    db_name = "test_db"
    
    logger = logging.getLogger()
    logger.setLevel(logging.INFO)
    
    client = boto3.client('rds',region_name='eu-west-2')
    token = client.generate_db_auth_token(rds_host,3306, name)
    ssl = {'ca': 'rds-combined-ca-bundle.pem'} 
    logger.info("token: "+ token)
    
    conn = pymysql.connect(rds_host, user=username, passwd=token, db=db_name, connect_timeout=5, ssl=ssl)
    
    logger.info("SUCCESS: Connection to RDS mysql instance succeeded")
    def handler(event, context):
      ...
    
  5. I got the following error:

    error: (1045, "Access denied for user 'lambda'@'<LAMBDA_IP>' (using password: YES)")
    

In an attempt to find If it was a python error I used AWS CLI, from an EC2 instance with the policy attached.

  1. Get the token:

    aws rds generate-db-auth-token --hostname <RDS-ENDPOINT> --port 3306 --username lambda
    
  2. Connect to the DB, using the token I got in the last step:

    mysql -h <RDS-ENDPOINT> -u lambda --enable-cleartext-plugin --password='<TOKEN>'
    
  3. I got the same error:

    mysql: [Warning] Using a password on the command line interface can be insecure.
    ERROR 1045 (28000): Access denied for user 'lambda'@'<EC2_IP>' (using password: YES)
    
7
  • 1
    Note that no authentication happens when you generate the token. The tokens are exactly like a pre-signed URL - they are generated locally, without any interaction with any API, and not actually checked until you try to use them. Commented May 13, 2018 at 18:37
  • Do you mean that probably the problem is in how I'm getting the token? My most significant doubt was in the policy because AWS Management Console gave me this info IAM does not recognize this service. The service might include a typo or might be a previewed or custom service.
    – NBajanca
    Commented May 13, 2018 at 18:41
  • In the policy, the resource portion of your ARN (after the account number and the following :) needs to include the literal string dbuser: + the db identifier (which begins with db-) + / + the username. The IAM console doesn't understand these policies, so that warning is normal. Commented May 13, 2018 at 18:42
  • 1
    Also, no, I didn't intend to suggest that there is a problem with the way you are getting the token, but rather checking to be sure that you understand that being able to get a token does not prove anything one way or another about your configuration. Any user can get a token, even with invalid or fictitious credentials. It won't work for logging in, but it will still be generated. Commented May 13, 2018 at 18:47
  • Changed the Resource according to what you said. It failed again but I will wait some time to make sure the changes have propagated
    – NBajanca
    Commented May 13, 2018 at 18:55

2 Answers 2

4

The policy is not correct!

The Resource is not the DB ARN, but "arn:aws:rds-db:<AWS_REGION>:<AWS_ACCOUNT_ID>:dbuser:<AWS_DB_RESOURCE_ID>/<DB_USERNAME>"

To get this information from the management console, you can go to:

  • AWS_REGION - The region code, for example eu-west-2 or any other from here.
  • AWS_ACCOUNT_ID - Get it from the Account Settings.
  • AWS_DB_RESOURCE_ID - Find it in Details\Configuration\Resource ID in the DB page and it starts with db-.
  • DB_USERNAME - Is lambda because it was the one created in step 2.

By the way, and as Michael - sqlbot pointed out here and in this answer, the generation of the token is local so getting it should not be interpreted as getting a correct password.

1
  • NBajanca, @Michael - sqlbot - I am getting the same exact error and not able to figure out what is going wrong. Already spent almost half a day on this and still haven't reached anywhere. My IAM Policy is exactly same as you mentioned. With you Step 2 in AWS CLI above, are you asked to enter a password? If yes, what password did you enter for 'lambda'? I am getting asked a password not matter what I enter I get the error. How come you are not specifying "--ssl-ca=<PEM file>" in your mysql command? I posted a question on this just few minutes ago: stackoverflow.com/q/50383753/5277048.
    – Gauzy
    Commented May 17, 2018 at 5:29
0

I was seeing this error as well after triple-checking the configuration of the database as well as IAM and role attachments. It turned out to be my connector inside my Python lambda function.

My goal was to connect to my RDS MySQL database using sqlalchemy + pymysql. I first figured out the necessary parameters for pymysql and then converted them over to use in sqlalchemy.

Here is my pymysql solution:

conn =  pymysql.connect(
# Maybe this was the parameter I was missing, to tell it to connect without a standard password
   auth_plugin_map={'mysql_clear_password':None}, 
   host=ENDPOINT, 
   user=USER, 
   password=token, 
   port=PORT, 
   database=DBNAME, 
   ssl_ca='RDS_Certificate.pem', 
   ssl_verify_identity=False)

and the sqlalchemy solution:

DBHostname = DBCLUSTER_HOSTNAME
DBPort = DBCLUSTER_PORT
DBUsername = DBCLUSTER_USER
DBName = DBCLUSTER_NAME

engine = sqlalchemy.create_engine(
    "mysql+pymysql:///"
)  # connection params will be set by the event callback

@sqlalchemy.event.listens_for(engine, "do_connect")
def provide_token(dialect, conn_rec, cargs, cparams):
    client = boto3.client("rds")
    token = client.generate_db_auth_token(
        DBHostname=DBHostname,
        Port=int(DBPort),
        DBUsername=DBUsername,
        Region=REGION,
    )

    # set up db connection parameters, alternatively we can get these from boto3 describe_db_instances
    cparams["host"] = DBHostname
    cparams["port"] = int(DBPort)
    cparams["user"] = DBUsername
    cparams["password"] = token
    cparams["database"] = DBName
    cparams["ssl"] = {
        "ca": "ssl-ca-bundle.pem",
        "verify_identity": False,
    }
    cparams["auth_plugin_map"] = {
        "mysql_clear_password": None,
    }

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.