RDS Security & Connections
RDS security relies on VPC isolation, security groups, IAM database authentication, and Secrets Manager for credentials. RDS Proxy solves connection pooling at scale — especially critical for Lambda + RDS architectures.
IAM Authentication & Secrets Manager
# IAM database authentication — authenticate with an IAM token instead of a password
# Supported for: MySQL and PostgreSQL
# Token is valid for 15 minutes, generated using AWS credentials
# Enable IAM auth on instance
aws rds modify-db-instance \
--db-instance-identifier myapp-prod-db \
--enable-iam-database-authentication \
--apply-immediately
# Create a DB user for IAM auth (PostgreSQL example)
# (Connect with master user first)
CREATE USER iam_app;
GRANT rds_iam TO iam_app;
GRANT CONNECT ON DATABASE myapp TO iam_app;
GRANT USAGE ON SCHEMA public TO iam_app;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO iam_app;
# Generate auth token from CLI
TOKEN=$(aws rds generate-db-auth-token \
--hostname myapp-prod-db.xxx.us-east-1.rds.amazonaws.com \
--port 5432 \
--username iam_app \
--region us-east-1)
# Connect using the token as password
PGPASSWORD="$TOKEN" psql \
"host=myapp-prod-db.xxx.us-east-1.rds.amazonaws.com \
user=iam_app dbname=myapp sslmode=verify-full \
sslrootcert=us-east-1-bundle.pem"
# Secrets Manager — store and auto-rotate DB credentials
# Create a secret for RDS
aws secretsmanager create-secret \
--name prod/myapp/postgres \
--description "RDS credentials for myapp prod" \
--secret-string '{"username":"dbadmin","password":"mysecretpw","host":"myapp-prod-db.xxx.rds.amazonaws.com","port":5432,"dbname":"myapp"}'
# Enable automatic rotation (every 30 days)
aws secretsmanager rotate-secret \
--secret-id prod/myapp/postgres \
--rotation-lambda-arn arn:aws:lambda:us-east-1:123:function:SecretsManagerRDSPostgreSQLRotationSingleUser \
--rotation-rules AutomaticallyAfterDays=30
# Retrieve secret in application code
aws secretsmanager get-secret-value \
--secret-id prod/myapp/postgres \
--query SecretString --output text | jq -r .passwordSSL/TLS & Encryption
# Encryption at rest — enable at creation time (cannot enable on existing instance)
# Uses KMS key; encrypts storage, backups, read replicas, snapshots
# To encrypt an existing unencrypted instance:
# 1. Take snapshot, 2. Copy snapshot with encryption, 3. Restore from encrypted snapshot
# Create encrypted copy of snapshot
aws rds copy-db-snapshot \
--source-db-snapshot-identifier myapp-unencrypted-snapshot \
--target-db-snapshot-identifier myapp-encrypted-snapshot \
--kms-key-id arn:aws:kms:us-east-1:123:key/mrk-abc123
# SSL/TLS in transit — RDS supports SSL connections for all engines
# Download AWS RDS CA bundle
wget https://truststore.pki.rds.amazonaws.com/us-east-1/us-east-1-bundle.pem
# Force SSL for all PostgreSQL connections (via parameter group)
aws rds modify-db-parameter-group \
--db-parameter-group-name myapp-postgres16-params \
--parameters "ParameterName=rds.force_ssl,ParameterValue=1,ApplyMethod=immediate"
# Connect with SSL (psql)
psql "host=myapp-prod-db.xxx.us-east-1.rds.amazonaws.com \
user=dbadmin dbname=myapp \
sslmode=verify-full sslrootcert=us-east-1-bundle.pem"
# Node.js connection with SSL
# const pool = new Pool({
# host: process.env.DB_HOST,
# ssl: {
# ca: fs.readFileSync("us-east-1-bundle.pem").toString(),
# rejectUnauthorized: true,
# },
# });
# Security groups for RDS:
# Only allow traffic from application tier security group, not 0.0.0.0/0
aws ec2 authorize-security-group-ingress \
--group-id sg-0db123 \
--protocol tcp \
--port 5432 \
--source-group sg-app456 # Only allow from application server SGRDS Proxy
# RDS Proxy — fully managed connection pooler
# Critical for Lambda: Lambda can scale to hundreds of concurrent instances,
# each opening DB connections. Without a proxy, this exhausts max_connections.
# RDS Proxy maintains a pool and multiplexes Lambda connections into fewer DB connections.
# Create an RDS Proxy
aws rds create-db-proxy \
--db-proxy-name myapp-proxy \
--engine-family POSTGRESQL \
--auth '[{"AuthScheme":"SECRETS","SecretArn":"arn:aws:secretsmanager:us-east-1:123:secret:prod/myapp/postgres","IAMAuth":"REQUIRED"}]' \
--role-arn arn:aws:iam::123:role/rds-proxy-role \
--vpc-subnet-ids subnet-private-aaa111 subnet-private-bbb222 \
--vpc-security-group-ids sg-proxy123
# Register DB instance with the proxy
aws rds register-db-proxy-targets \
--db-proxy-name myapp-proxy \
--db-instance-identifiers myapp-prod-db
# Get proxy endpoint (use this in your app instead of the instance endpoint)
aws rds describe-db-proxies \
--db-proxy-name myapp-proxy \
--query "DBProxies[0].Endpoint"
# RDS Proxy IAM auth — connect from Lambda without password (uses IAM token)
# Lambda IAM role must have: rds-db:connect permission on the proxy
# {
# "Effect": "Allow",
# "Action": "rds-db:connect",
# "Resource": "arn:aws:rds-db:us-east-1:123:dbuser:prx-abc123/iam_app"
# }
# RDS Proxy benefits:
# - Connection pooling: reduces DB connections from O(lambda-instances) to O(small-pool)
# - Failover: maintains connections during Multi-AZ failover (app sees <1s interruption vs 30-120s)
# - IAM auth passthrough to DB
# - Secrets Manager rotation without app restartKeep your own version of these notes — editable, searchable, and organised by your stack.
Start free