Back to blog
AWSBest PracticesCloud SecurityDatabasesIdentity & Access

RDS IAM Authentication Disabled: Replace Database Passwords with Short-Lived Tokens

Learn why RDS IAM database authentication matters, how to enable it with CLI and Terraform, and how to enforce it in CI/CD to kill long-lived DB passwords.

TL;DR

This check flags RDS instances that rely only on static database passwords instead of IAM database authentication. Enabling IAM auth lets you swap long-lived passwords for short-lived, centrally managed tokens. Turn it on with a single modify-db-instance call and grant the matching rds-db:connect IAM permission.

Database credentials are one of the most stubborn forms of long-lived secrets in a cloud environment. They get baked into application config, copied into .env files, pasted into Slack, and shared between services that should never have touched the same login. RDS IAM database authentication gives you a way out: instead of a password that lives forever, your application requests a token that AWS signs and expires after 15 minutes.

The RDS IAM Authentication Disabled check looks for RDS instances where this feature has not been enabled. When it fails, your database is depending entirely on traditional username and password authentication, with all the credential sprawl that comes with it.


What this check detects

The check inspects each RDS DB instance and reads the IAMDatabaseAuthenticationEnabled attribute. If that value is false, the instance is flagged.

IAM database authentication works by generating a temporary authentication token using your AWS IAM credentials. The token is valid for 15 minutes and is used in place of a password when connecting to the database. The database engine validates the token against a database user that has been mapped to the AWS-provided authentication plugin.

Note: IAM authentication is supported on MySQL, PostgreSQL, and MariaDB engines for RDS, as well as Aurora MySQL and Aurora PostgreSQL. It is not available for Oracle or SQL Server. If you run those engines, this check will not apply in the same way and you should focus on Secrets Manager rotation instead.


Why it matters

Static database passwords create several concrete problems that show up during real incidents.

Credentials never expire on their own

A password set once during provisioning tends to stay in place for years. If it leaks through a log file, a compromised CI runner, or a former employee's laptop, that credential keeps working until someone notices and manually rotates it. IAM tokens expire after 15 minutes, so a leaked token has a tiny window of usefulness.

Auditing connection access is hard

With password authentication, AWS has no record of who connected to the database. With IAM authentication, every token generation is an IAM action that flows through CloudTrail. You can see exactly which role or user requested access, when, and from where.

Credential rotation is operationally painful

Rotating a shared database password means coordinating every service that uses it, deploying the new value, and hoping nothing breaks during the cutover. IAM authentication removes the password from the equation entirely for the connecting application, so there is nothing to rotate at the application layer.

Warning: IAM authentication has a connection rate limit. RDS supports a maximum of roughly 200 new IAM-authenticated connections per second. High-throughput services that open many short-lived connections should use connection pooling (for example RDS Proxy or a pooler like PgBouncer) to stay well under that ceiling.


How to fix it

Enabling IAM authentication is a two-part job: turn the feature on at the instance level, then create database users mapped to IAM and grant the IAM permission to connect.

Step 1: Enable IAM authentication on the instance

Using the AWS CLI:

aws rds modify-db-instance \
  --db-instance-identifier my-app-db \
  --enable-iam-database-authentication \
  --apply-immediately

Warning: Modifying an RDS instance can trigger a brief change, and --apply-immediately applies it right away rather than during the next maintenance window. Enabling IAM authentication itself does not require a reboot, but verify on a non-production instance first to confirm behavior on your engine version.

In the AWS Console:

  1. Open the RDS console and select your database instance.
  2. Choose Modify.
  3. Under Database authentication, select Password and IAM database authentication.
  4. Choose Continue, then apply the change immediately or schedule it.

Step 2: Create a database user mapped to IAM

The feature being enabled does nothing on its own. You still need a database user that authenticates through the AWS plugin. For PostgreSQL:

CREATE USER app_service WITH LOGIN;
GRANT rds_iam TO app_service;

For MySQL or MariaDB:

CREATE USER 'app_service'@'%'
  IDENTIFIED WITH AWSAuthenticationPlugin AS 'RDS';

Step 3: Grant the IAM permission to connect

The connecting role needs the rds-db:connect action scoped to the specific database user. Note the resource ARN uses the DB resource ID (the db-XXXX value), not the instance name.

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "rds-db:connect",
      "Resource": "arn:aws:rds-db:us-east-1:123456789012:dbuser:db-ABCDEFGHIJKL01234/app_service"
    }
  ]
}

Step 4: Connect using a token

Your application generates a token at connect time. Here is the pattern for PostgreSQL using the CLI to produce the token:

TOKEN=$(aws rds generate-db-auth-token \
  --hostname my-app-db.abc123.us-east-1.rds.amazonaws.com \
  --port 5432 \
  --username app_service \
  --region us-east-1)

PGPASSWORD="$TOKEN" psql \
  "host=my-app-db.abc123.us-east-1.rds.amazonaws.com \
   port=5432 dbname=appdb user=app_service sslmode=require"

Tip: Most AWS SDKs can generate the token natively, so you do not need to shell out to the CLI in production. The RDS Proxy also handles IAM authentication and token generation for you, which is a clean way to add IAM auth to an existing app without rewriting connection logic.

Note: IAM authentication requires SSL/TLS. Always connect with sslmode=require (or stricter) and verify the RDS certificate. Without TLS the token would travel in cleartext, which defeats the purpose.


Doing it with infrastructure as code

If you manage RDS through Terraform, enable the flag in your resource definition so it stays enforced across applies:

resource "aws_db_instance" "app" {
  identifier                          = "my-app-db"
  engine                              = "postgres"
  instance_class                      = "db.t3.medium"
  iam_database_authentication_enabled = true
  storage_encrypted                   = true
  # ... other settings
}

For CloudFormation:

Resources:
  AppDatabase:
    Type: AWS::RDS::DBInstance
    Properties:
      DBInstanceIdentifier: my-app-db
      Engine: postgres
      DBInstanceClass: db.t3.medium
      EnableIAMDatabaseAuthentication: true
      StorageEncrypted: true

How to prevent it from happening again

Fixing one instance is easy. Keeping every new instance compliant is the real goal.

Gate it in CI/CD

Add a policy check to your pipeline so a Terraform plan that omits IAM authentication never merges. With Checkov, the relevant control is built in:

checkov -d ./infra --check CKV_AWS_161

You can also write a custom OPA/Conftest rule against the Terraform plan JSON:

package main

deny[msg] {
  resource := input.resource_changes[_]
  resource.type == "aws_db_instance"
  resource.change.after.iam_database_authentication_enabled == false
  msg := sprintf("RDS instance '%s' must enable IAM database authentication", [resource.address])
}

Enforce with an SCP or Config rule

AWS Config does not ship a managed rule for this specific attribute, but you can deploy a custom Config rule backed by a Lambda function that checks IAMDatabaseAuthenticationEnabled and reports non-compliant instances. Pair that with an EventBridge rule that alerts when a new instance is created without it.

Tip: Lensix runs the rds_noiamauthentication check continuously across your accounts, so you get notified the moment a new instance drifts out of policy rather than discovering it during an audit.


Best practices

  • Use IAM auth for service-to-database access, not human break-glass. Application roles benefit most from short-lived tokens. For rare human access, scope tightly and log everything.
  • Combine with RDS Proxy for connection-heavy workloads. The proxy pools connections and handles token refresh, which keeps you under the IAM connection rate limit and improves resilience.
  • Keep a tightly controlled admin password as a fallback. IAM auth does not replace the master user. Store that credential in Secrets Manager with automatic rotation and restrict who can read it.
  • Scope rds-db:connect to specific users. Do not grant it with a wildcard resource. Map each application role to the exact database user it needs.
  • Require TLS everywhere. Enforce encrypted connections at the parameter group level so a misconfigured client cannot fall back to plaintext.
  • Audit token generation in CloudTrail. Set up an alert for unusual generate-db-auth-token activity, such as a role connecting from an unexpected region or principal.

IAM database authentication will not eliminate every database security concern, but it removes one of the most persistent ones: the password that never goes away. Enabling it is cheap, the operational change is small, and the payoff is fewer secrets to manage and a complete audit trail of who connected to your data.