A Node.js application demonstrating secure database connectivity to AWS RDS using IAM authentication instead of traditional database credentials.
YouTube Companion Guide: This project includes a comprehensive step-by-step YouTube tutorial that walks through the entire setup process. Follow along with the video for detailed visual instructions on each step.
- Project Overview
- Why IAM Authentication for RDS?
- Prerequisites
- AWS Infrastructure Setup
- Database Setup
- Application Configuration
- EC2 Deployment Guide
- IAM Instance Profile Policy
- Testing the Application
- Troubleshooting
- Documentation References
This project demonstrates how to authenticate to AWS RDS MySQL databases using IAM authentication, eliminating the need to store database passwords in your application code. The application uses AWS temporary credentials to generate authentication tokens dynamically, providing enhanced security and auditing capabilities.
The implementation uses a practical, production-ready architecture with:
- Private RDS Database in private subnets with NAT gateway access
- EC2 Instance accessing RDS via IAM authentication tokens
- AWS Systems Manager Session Manager for secure EC2 access (no SSH keys needed)
- SSM Parameter Store for application configuration management
- Docker for containerized application deployment
- ✅ IAM-based authentication to RDS
- ✅ No hardcoded database passwords
- ✅ Temporary token-based access (15-minute expiration)
- ✅ Configuration management via AWS Systems Manager Parameter Store
- ✅ Docker containerization on EC2
- ✅ SSM Session Manager for secure instance access
- ✅ Both password and IAM authentication enabled on RDS
-
No Password Management: Eliminates the need to store, rotate, and manage database passwords in your application.
-
Short-lived Credentials: Authentication tokens expire after 15 minutes, reducing the risk if credentials are compromised.
-
Centralized Access Control: Leverage AWS IAM policies for fine-grained access control across your entire AWS environment.
-
Audit Trail: All database connections are logged in CloudTrail, providing complete visibility into who accessed the database and when.
-
Credential Rotation: AWS automatically rotates the underlying credentials without application intervention.
-
Compliance: Helps meet compliance requirements (SOC 2, PCI-DSS, HIPAA) by eliminating shared passwords and maintaining audit trails.
| Aspect | IAM Authentication | Traditional Passwords |
|---|---|---|
| Credential Storage | AWS Temporary Credentials (EC2 metadata) | Stored in environment/config |
| Lifetime | 15 minutes (short-lived token) | No expiration |
| Rotation | AWS managed | Manual process |
| Audit Trail | CloudTrail logs | Limited visibility |
| Scaling | Seamless across instances | Manual credential management |
- AWS account with appropriate permissions to create:
- VPC with subnets and NAT gateway
- Security groups
- RDS MySQL instance
- EC2 instance
- IAM roles and policies
- Systems Manager Parameter Store
- Node.js 20.x or higher
- npm/yarn package manager
- AWS CLI v2
- MySQL client (for database connection testing)
- Git
This project is primarily managed through the AWS Management Console. All infrastructure setup is done via the console UI, which is demonstrated in the YouTube video companion guide.
This section outlines the AWS infrastructure setup performed via the AWS Management Console. Refer to the YouTube video for detailed visual walkthrough of each step.
- Navigate to VPC Dashboard → VPCs
- Click Create VPC
- Configure:
- Name:
rds-iam-vpc(or your preference) - IPv4 CIDR block:
10.0.0.0/16
- Name:
- Create the VPC
- Once created, go to Subnets and create:
- Public Subnet:
10.0.1.0/24(for NAT Gateway) - AZ a - Private Subnet 1:
10.0.10.0/24- AZ a (for RDS) - Private Subnet 2:
10.0.20.0/24- AZ b (for RDS) - Private Subnet 3:
10.0.30.0/24- AZ c (for RDS)
- Public Subnet:
- Navigate to VPC Dashboard → NAT Gateways
- Click Create NAT Gateway
- Configure:
- Subnet: Select the public subnet (
10.0.1.0/24) - Allocate Elastic IP
- Subnet: Select the public subnet (
- Wait for NAT Gateway to be in "Available" state
- Navigate to VPC Dashboard → Route Tables
Public Route Table:
- Create new route table for public subnet
- Add route:
0.0.0.0/0→ Internet Gateway - Associate with public subnet
Private Route Table:
- Create new route table for private subnets
- Add route:
0.0.0.0/0→ NAT Gateway (created in Step 2) - Associate with all three private subnets
Navigate to VPC Dashboard → Security Groups and create two security groups:
EC2-SG (EC2 Security Group)
- Name:
EC2-SG - Description: Security group for EC2 instance
- Inbound Rules: NONE (SSM Session Manager only)
- Outbound Rules (keep default):
- HTTPS to RDS security group (port 3306)
RDS-SG (RDS Security Group)
- Name:
RDS-SG - Description: Security group for RDS MySQL instance
- Inbound Rules:
- Type: "MySQL/Aurora"
- Port: 3306
- Source: Select
EC2-SGsecurity group
- Outbound Rules: Keep default (all outbound allowed)
This section covers RDS setup and IAM database user creation via the AWS Management Console.
- Navigate to RDS Dashboard → Subnet Groups
- Click Create DB Subnet Group
- Configure:
- Name:
rds-subnet-group - VPC: Select the VPC created earlier
- Availability Zones: Select all 3 AZs (a, b, c)
- Subnets: Select the 3 private subnets (
10.0.10.0/24,10.0.20.0/24,10.0.30.0/24)
- Name:
- Create the subnet group
- Navigate to RDS Dashboard → Databases
- Click Create Database
- Configure as follows:
Database Creation Method
- Select: "Standard Create"
Engine Options
- Engine: "MySQL"
- Version: "MySQL 8.0.x" or latest stable (8.0.35+)
Templates
- Select: "Free tier" or "Dev/Test" (depending on your needs)
DB Instance Identifier
- Name:
rds-iam-mysql(or your preference)
Credentials Settings
- Master username:
admin - Master password: Generate strong password and save securely
- Confirm password: Same as above
Instance Configuration
- DB instance class:
db.t4g.micro(free tier eligible) - Storage: 20 GB (free tier eligible)
Connectivity
- VPC: Select the VPC created earlier
- DB Subnet Group: Select
rds-subnet-group - Public accessibility: "No" (remains in private subnets)
- VPC Security Group: Select
RDS-SG - Availability Zone: Keep as "No preference"
Database Authentication
- CRITICAL: Enable both options:
- ✅ Password authentication
- ✅ IAM database authentication (THIS IS KEY!)
Backup & Monitoring
- Automated backups: Disabled (for educational/cost purposes)
- Enable CloudWatch Logs: Check all log types (optional)
Additional Configuration
- Initial database name:
rds_app_db(or your preference) - Enable automated minor version upgrade: Yes
- Click Create Database
- Wait 5-10 minutes for the instance to reach "Available" status
The RDS master password is securely stored in AWS Secrets Manager:
- Navigate to AWS Secrets Manager → Secrets
- Click Store a new secret
- Configure:
- Secret type: Select Credentials for RDS database
- Username:
admin - Password: The master password you created for RDS
- Database: Select your RDS instance
- Click Store
- Note the secret ARN for future reference
This approach keeps the database password secure and separate from your application code.
Once the RDS instance is available:
-
On your local machine or EC2 instance, connect to RDS as the admin user:
# If you have mysql-client installed locally or on EC2 mysql -h <your-rds-endpoint> -u admin -p
Replace
<your-rds-endpoint>with your RDS endpoint (found in RDS Dashboard) -
When prompted, enter the master password you created
-
Once connected, run the following SQL commands:
-- Create a database user that uses AWS IAM authentication CREATE USER 'app_user'@'%' IDENTIFIED WITH AWSAuthenticationPlugin AS 'RDS'; -- Grant necessary privileges GRANT SELECT, INSERT, UPDATE, DELETE, CREATE ON rds_app_db.* TO 'app_user'@'%'; -- Verify the user was created SELECT User, Host, plugin FROM mysql.user WHERE User = 'app_user';
-
Exit the MySQL prompt:
EXIT;
Note: The
app_useruses AWS authentication instead of a password. Authentication is done via temporary IAM tokens generated by the EC2 instance.
Instead of using a .env file, database configuration is stored in AWS Systems Manager Parameter Store. This approach has several advantages:
- No hardcoded secrets in the codebase
- Centralized configuration management
- Easy updates without redeploying
- Audit trail of configuration changes
- Navigate to AWS Systems Manager → Parameter Store
- Click Create Parameter and create the following parameters (matching the names in
src/config.js):
Parameter 1: Database Endpoint
- Name:
/demo/rds/endpoint - Type: String
- Value: Your RDS endpoint (e.g.,
rds-iam-mysql.cyucot0xapry.us-east-1.rds.amazonaws.com) - Encrypt: No (not encrypted)
Parameter 2: Database Port
- Name:
/demo/rds/port - Type: String
- Value:
3306 - Encrypt: No (not encrypted)
Parameter 3: Database Name
- Name:
/demo/rds/dbname - Type: String
- Value:
rds_app_db - Encrypt: No (not encrypted)
Parameter 4: Database User
- Name:
/demo/rds/username - Type: String
- Value:
app_user - Encrypt: No (not encrypted)
Parameter 5: Database Region
- Name:
/demo/rds/region - Type: String
- Value: Your AWS region (e.g.,
us-east-1) - Encrypt: No (not encrypted)
The application automatically loads these parameters from SSM Parameter Store when it starts. The application code in src/config.js handles parameter retrieval:
// Example of how config.js retrieves parameters
const ssm = new AWS.SSM({ region: process.env.AWS_REGION });
const params = {
Names: [
'/demo/rds/endpoint',
'/demo/rds/port',
'/demo/rds/dbname',
'/demo/rds/username',
'/demo/rds/region'
],
WithDecryption: false // Parameters are stored unencrypted
};
const response = await ssm.getParameters(params).promise();The application uses these values to:
- Connect to RDS using the endpoint
- Generate IAM authentication token using temporary EC2 credentials
- Authenticate as the
app_userusing the token
This section covers deploying the application on an EC2 instance running Ubuntu 24.04 LTS. The EC2 instance is configured with AWS Systems Manager Session Manager for secure access (no SSH keys required).
The EC2 instance needs an IAM role with permissions to:
- Access AWS Systems Manager Session Manager
- Retrieve parameters from Systems Manager Parameter Store
- Generate RDS IAM authentication tokens
- Navigate to IAM Dashboard → Roles
- Click Create role
- Select AWS service as the trusted entity
- Select EC2 as the service
- Click Next
- On the "Add permissions" page, attach the following managed policies:
AmazonSSMManagedInstanceCore(for Session Manager access)
- Click Next → Create role
- Name the role:
RDSIAMAuthRole - Click Create role
- After creating the role, open it in IAM Dashboard
- Click Add inline policy (on the Permissions tab)
- Choose JSON and paste the policy from the IAM Instance Profile Policy section
- Review and create the policy
The instance profile connects the IAM role to your EC2 instance. When created via the console in Step 3, the instance profile is created automatically. If creating separately:
- Navigate to IAM Dashboard → Instance Profiles
- Click Create instance profile
- Name:
RDSIAMAuthProfile - Click Create
- Open the instance profile and click Add roles
- Select
RDSIAMAuthRole
- Navigate to EC2 Dashboard → Instances
- Click Launch Instances
Name and Tags:
- Name:
RDS-IAM-App
Application and OS Images:
- Select: Ubuntu
- Version: Ubuntu Server 24.04 LTS
Instance Type:
- Select:
t3.micro(eligible for free tier)
Key Pair:
- Select: "Proceed without a key pair" (We use Session Manager)
Network Settings:
- VPC: Select the VPC created earlier
- Subnet: Select the public subnet (
10.0.1.0/24) - Auto-assign public IP: Enable (instance will be publicly accessible)
- Security group: Select
EC2-SG
Advanced Details:
- IAM Instance Profile: Select
RDSIAMAuthProfile
- Click Launch Instance
- Wait for the instance to reach "Running" state (2-3 minutes)
- Navigate to AWS Systems Manager → Session Manager
- Click Start session
- Select your EC2 instance (
RDS-IAM-App) - Click Start session
- A terminal should open in your browser
You now have secure shell access to the EC2 instance without needing SSH keys!
Once you have Session Manager access to the EC2 instance:
# Update system packages
sudo apt-get update
sudo apt-get upgrade -y
# Install Git (to clone the repository)
sudo apt-get install -y git
# Install MySQL client (to test database connectivity)
sudo apt-get install -y mysql-client
# Install Docker using official Docker Engine installation
# Reference: https://docs.docker.com/engine/install/ubuntu/# Clone your repository
git clone <your-repository-url>
cd <repository-name>
# Or if you want to upload the code directly:
# You can use the Session Manager file upload feature or use git
# Build the Docker image
docker build -t rds-iam-auth:latest .
# Verify the image was built
docker images# Run the container in detached mode
docker run -d \
--name rds-app \
-p 3000:3000 \
rds-iam-auth:latest
# Check if the container is running
docker ps
# View the container logs (useful for debugging)
docker logs -f rds-appThe application will:
- Automatically retrieve configuration from SSM Parameter Store
- Use the EC2 instance's IAM role to generate an RDS authentication token
- Connect to RDS as the
app_userusing the token - Start serving HTTP requests on port 3000
Since the EC2 instance is in the public subnet, you can access the application directly:
- Navigate to EC2 Dashboard → Security Groups
- Select the
EC2-SGsecurity group - Click Edit inbound rules
- Add a rule:
- Type: Custom TCP
- Port Range: 3000
- Source:
0.0.0.0/0(accessible from anywhere) or your IP address for more security
- Click Save rules
- Note the EC2 instance's public IP address from the EC2 Dashboard
- Access the application via:
http://<EC2-Public-IP>:3000
Note: Since the EC2 instance is in the public subnet with a public IP, you can access the application directly from the internet without needing a load balancer.
View Docker Container Logs:
# Follow logs in real-time
docker logs -f rds-app
# View last 100 lines
docker logs --tail 100 rds-appTest Database Connectivity:
# Check if you can reach the RDS instance
mysql -h <your-rds-endpoint> -u app_user --enable-cleartext-pluginVerify IAM Token Generation: The logs will show successful token generation and database connection. Look for messages indicating successful connections.
The EC2 instance needs the following IAM permissions:
- rds-db:connect - To generate RDS IAM authentication tokens
- ssm:GetParameter & ssm:GetParameters - To retrieve application configuration from Parameter Store
- ssmmessages, ec2messages - For AWS Systems Manager Session Manager (included in managed policy
AmazonSSMManagedInstanceCore)
Attach this inline policy to the RDSIAMAuthRole:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "RDSIAMAuthentication",
"Effect": "Allow",
"Action": [
"rds-db:connect"
],
"Resource": [
"arn:aws:rds-db:<region>:<account_id>:dbuser:<DB_ID>/<DB_IAM_AUTH_USER>"
]
},
{
"Sid": "RetrieveSSMParameters",
"Effect": "Allow",
"Action": [
"ssm:GetParameter",
"ssm:GetParameters"
],
"Resource": [
"arn:aws:ssm:*:<account-id>:parameter/rds-iam-app/*"
]
}
]
}The AmazonSSMManagedInstanceCore managed policy includes:
ssmmessages:CreateControlChannelssmmessages:CreateDataChannelssmmessages:OpenControlChannelssmmessages:OpenDataChannelec2messages:GetMessages
These enable AWS Systems Manager Session Manager access.
- Navigate to IAM Dashboard → Roles
- Select your
RDSIAMAuthRole - Click Add inline policy
- Choose JSON
- Paste the policy JSON above (remember to replace
<account-id>with your AWS account ID) - Review and click Create policy
To verify that the EC2 instance has the correct permissions:
# From Session Manager terminal on the EC2 instance
# Get the current AWS account ID
aws sts get-caller-identity
# Test access to SSM Parameters
aws ssm get-parameters \
--names "/demo/rds/endpoint" "/demo/rds/username" \
--region us-east-1
# Expected output: Should show the parameter values without errorsFrom your EC2 instance via Session Manager:
# List running containers
docker ps
# You should see something like:
# CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS
# abc123def456 rds-iam-auth:latest "node src/server.js" 2 minutes ago Up 2 minutes 0.0.0.0:3000->3000/tcp
# View container logs
docker logs rds-app
# Follow logs in real-time
docker logs -f rds-appFrom your EC2 instance via Session Manager:
# Test connection to RDS using the IAM-authenticated database user
mysql -h <your-rds-endpoint> \
-P 3306 \
-u app_user \
--enable-cleartext-plugin
# If connected successfully, you should see the MySQL prompt: mysql>
# Exit MySQL
EXIT;From your EC2 instance via Session Manager:
# Retrieve one of the parameters the application uses
aws ssm get-parameter \
--name /demo/rds/endpoint \
--region us-east-1
# Expected output:
# {
# "Parameter": {
# "Name": "/demo/rds/endpoint",
# "Type": "String",
# "Value": "rds-iam-mysql.cyucot0xapry.us-east-1.rds.amazonaws.com",
# "Version": 1
# }
# }The application logs will show successful IAM token generation and database connection:
# View application logs
docker logs rds-app
# Look for messages indicating:
# - Successful parameter retrieval from SSM
# - Database connection using IAM token
# - No errors or connection failuresIf you enabled CloudWatch Logs during RDS creation:
- Navigate to CloudWatch Dashboard → Logs → Log Groups
- Look for log groups related to your RDS instance
- Check for connection events and any errors
If you've set up a load balancer or exposed the application:
# From your local machine
curl http://<load-balancer-dns>:3000
# Or from within the EC2 instance
curl http://localhost:3000Solution:
# Check the container logs for error messages
docker logs rds-app
# Look for errors related to:
# - SSM Parameter retrieval
# - IAM token generation
# - Database connectionPossible Causes:
- IAM role missing
ssm:GetParameterorssm:GetParameterspermissions - Parameter names are incorrect in SSM Parameter Store
- Wrong AWS region specified
Solution:
- Verify the IAM role has the correct SSM permissions
- Verify parameter names in SSM Parameter Store match the application expectations
- Verify the EC2 instance has the
RDSIAMAuthProfileinstance profile attached
# Check attached instance profile
aws ec2 describe-instances --instance-ids i-xxxxx --query 'Reservations[0].Instances[0].IamInstanceProfile'Possible Causes:
- IAM database user not created correctly
- IAM role missing
rds-db:connectpermission - RDS instance doesn't have IAM authentication enabled
Solution:
-
Verify the IAM database user exists:
# Connect as admin and check mysql -h <your-rds-endpoint> -u admin -p SELECT User, Host, plugin FROM mysql.user WHERE User = 'app_user';
-
Verify RDS instance has IAM authentication enabled
-
Verify IAM role has
rds-db:connectpermission
Note: This is normal and expected. The application automatically generates a new 15-minute token before expiration. Check logs to ensure token refresh is working.
# Look for log entries about token generation
docker logs rds-app | grep -i "token\|connection"Possible Causes:
- IAM role missing
AmazonSSMManagedInstanceCorepolicy - EC2 instance not assigned an instance profile
- NAT Gateway route not configured
Solution:
- Verify the EC2 instance has the instance profile attached
- Verify the IAM role has the
AmazonSSMManagedInstanceCoremanaged policy - Verify the private subnet route table has a route to the NAT Gateway
Possible Causes:
- Security group rules not configured correctly
- RDS endpoint is incorrect
- Network ACLs blocking traffic
Solution:
- Verify the
RDS-SGallows inbound traffic on port 3306 fromEC2-SG - Verify the RDS endpoint is correct in SSM Parameter Store
- Verify network ACLs in your VPC allow MySQL traffic
# Test connectivity from EC2
telnet <your-rds-endpoint> 3306
# Or using nc (if installed)
nc -zv <your-rds-endpoint> 3306- AWS RDS IAM Database Authentication - Important: Review limitations and requirements
- Setting Up IAM Database Authentication
- MySQL User Provisioning for IAM
- RDS MySQL Security
- AWS Secrets Manager User Guide
- Rotating RDS Database Credentials
- RDS Integration with Secrets Manager
- Docker Official Documentation
- Docker Engine Installation - Ubuntu - Referenced in Step 5 of EC2 Deployment
- Docker Best Practices
- Docker Container Lifecycle
- AWS Well-Architected Framework - Security Pillar
- CloudTrail Logging
- AWS Security Best Practices
- Principle of Least Privilege with IAM
Configuration Management:
- AWS Systems Manager Parameter Store Best Practices
- Secrets Management: Secrets Manager vs Parameter Store
Monitoring and Observability:
Database Administration:
# Build image
docker build -t rds-iam-auth:latest .
# Run container
docker run -d --name rds-app -p 3000:3000 rds-iam-auth:latest
# View logs
docker logs -f rds-app
# Stop container
docker stop rds-app
# Remove container
docker rm rds-app
# Remove image
docker rmi rds-iam-auth:latest# Get SSM parameters
aws ssm get-parameters --names "/demo/rds/endpoint" --region us-east-1
# Get EC2 instance details
aws ec2 describe-instances --instance-ids i-xxxxx
# Get IAM role details
aws iam get-role --role-name RDSIAMAuthRole
# Get RDS instance details
aws rds describe-db-instances --db-instance-identifier rds-iam-mysql# Connect as IAM user
mysql -h <rds-endpoint> -u app_user --enable-cleartext-plugin
# Connect as admin (if needed)
mysql -h <rds-endpoint> -u admin -p
# Create IAM user
CREATE USER 'app_user'@'%' IDENTIFIED WITH AWSAuthenticationPlugin AS 'RDS';
# List users
SELECT User, Host, plugin FROM mysql.user;
# Grant privileges
GRANT SELECT, INSERT, UPDATE, DELETE ON database_name.* TO 'app_user'@'%';This project includes a comprehensive YouTube tutorial that demonstrates:
- VPC and subnet creation
- Security group configuration
- RDS instance creation with IAM authentication enabled
- EC2 instance setup with Systems Manager access
- Docker installation and container deployment
- Testing and troubleshooting
The video provides visual step-by-step guidance for each section of this README.
This project is provided as-is for educational and testing purposes.