This project implements a fully automated, end-to-end IAM authentication flow between:
- A Node.js application\
- Amazon RDS Proxy\
- Amazon RDS (MySQL)
The architecture eliminates long-lived database credentials and enforces short-lived IAM-based authentication from the application layer to the database.
All infrastructure is provisioned using a modular Terraform architecture, and CI/CD is implemented using GitHub Actions with OIDC federation.
modules/
├── vpc/
├── iam/
├── ec2/
├── rds/
├── security_group/
├── alb/
├── s3/
└── ecr/
Application generates short-lived IAM token:
aws rds generate-db-auth-token --hostname <proxy-endpoint> --port 3306 --region us-east-1 --username node_app- Uses RDS-managed master secret (Secrets Manager)
- Backend pooling handled by RDS Proxy
- MySQL users configured with AWSAuthenticationPlugin
A secure shell-based bootstrap script:
- Reads generated
rds.conf - Retrieves master credentials from Secrets Manager
- Creates IAM-enabled admin user
- Switches to IAM authentication
- Creates application users
- Enforces SSL
- Grants least-privilege permissions
Example SQL:
CREATE USER 'node_app'@'%'
IDENTIFIED WITH AWSAuthenticationPlugin AS 'RDS';
ALTER USER 'node_app'@'%' REQUIRE SSL;
GRANT SELECT, INSERT, UPDATE, DELETE ON node_db.* TO 'node_app'@'%';- Terraform validate
- TFLint
- Checkov scan
- Terraform plan
- Docker build
- Trivy scan
- Terraform apply
- Extract outputs
- Upload rds.conf artifact
- Build & push image to ECR
- OIDC-based AWS authentication
- Terraform destroy
- OIDC federation (no static AWS keys)
- No DB passwords stored in environment variables
- SSL enforced at MySQL level
- Least privilege IAM policies
- Terraform static analysis
- Container image scanning
The "Hidden" API Challenge
A core highlight of this project is the resolution of a documented contradiction in the AWS RDS API.
The Issue: While the AWS Console and API documentation mark the
SecretArnas optional when usingIAM_AUTH, the API internally rejects the request with anInvalidParameterValue: Missing SecretArnerror if you attempt to customize theauthblock (e.g., settingiam_auth = "REQUIRED"or forcingMYSQL_NATIVE_PASSWORD).
The Solution: This project demonstrates how to satisfy the AWS API validator by linking the RDS Master User Secret (managed by RDS) while still enforcing a 100% IAM-based flow for the application.
Flag Meaning Common Use Case
-z Zero length: True if [[ -z "$DB_PASSWORD" ]] (Check if
the string is empty a secret is missing)
-n Non-zero length: [[ -n "$GIT_TAG" ]] (Check if a
True if string is version exists)
NOT empty
-e Exists: True if file [[ -e "tfplan" ]] (Check if plan
or directory exists file was created)
-f File: Exists and is [[ -f "rds.conf" ]] (Verify config
regular file file exists)
-d Directory: Exists [[ -d "Terraform/" ]] (Verify
and is directory directory structure)
In production-grade scripts, always enable safety mode:
set -euxo pipefailExit immediately if a command fails. Prevents execution from continuing
after failures like terraform init.
Treat unset variables as errors. Prevents dangerous expansions like:
rm -rf /$UNDEFINED_VAR
If any command in a pipeline fails, the entire pipeline fails. Ensures hidden failures don't pass silently.
Prints each command before execution. Extremely useful for debugging GitHub Actions logs.
- https://docs.aws.amazon.com/elasticloadbalancing/latest/application/enable-access-logging.html
- https://docs.aws.amazon.com/elasticloadbalancing/latest/application/describe-ssl-policies.html
- https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/rds-proxy-creating.html
- hashicorp/terraform-provider-aws#46461
- https://docs.aws.amazon.com/elasticloadbalancing/latest/APIReference/API_ModifyLoadBalancerAttributes.html
aws ssm start-session \
--target i-0123456789abcdef \
--document-name AWS-StartInteractiveCommand \
--parameters 'command=["bash -l"]'-- Create a database user that uses AWS IAM authentication and mandates SSL
CREATE USER 'app_user'@'%'
IDENTIFIED WITH AWSAuthenticationPlugin AS 'RDS';
-- Require SSL for this specific user
ALTER USER 'app_user'@'%' REQUIRE SSL;
-- Grant necessary privileges
GRANT SELECT, INSERT, UPDATE, DELETE, CREATE ON rds_app_db.* TO 'app_user'@'%';
-- Verify the user setup (now checking for SSL requirement)
SELECT User, Host, plugin, ssl_type
FROM mysql.user
WHERE User = 'app_user';aws rds generate-db-auth-token \
--hostname <proxy-endpoint> \
--port 3306 \
--region <rds_proxy_region> \
--username <rds_db_username_with_auth_plugin>aws ssm start-session \
--target instance-id \
--document-name AWS-StartPortForwardingSessionToRemoteHost \
--parameters '{"host":["mydb.example.us-east-2.rds.amazonaws.com"],"portNumber":["3306"], "localPortNumber":["3306"]}'aws ecr get-login-password --region <region> | docker login --username AWS --password-stdin <account_id>.dkr.ecr.us-east-1.amazonaws.com- End-to-end IAM authentication
- Zero long-lived secrets
- Fully automated provisioning
- Secure DevSecOps pipeline
- Modular Terraform architecture