RDS
03 / 07

Security & Connections

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 .password

SSL/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 SG

RDS 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 restart

Keep your own version of these notes — editable, searchable, and organised by your stack.

Start free