feat(week8): JWT auth, SSM Parameter Store, and UI overhaul
This commit is contained in:
+51
-12
@@ -1,6 +1,6 @@
|
||||
# ──────────────────────────────────────────────
|
||||
# Data Source — Latest Amazon Linux 2023 AMI
|
||||
# ──────────────────────────────────────────────
|
||||
# --------------------------------------------------
|
||||
# Data Source - Latest Amazon Linux 2023 AMI
|
||||
# --------------------------------------------------
|
||||
data "aws_ami" "amazon_linux_2023" {
|
||||
most_recent = true
|
||||
owners = ["amazon"]
|
||||
@@ -16,9 +16,9 @@ data "aws_ami" "amazon_linux_2023" {
|
||||
}
|
||||
}
|
||||
|
||||
# ──────────────────────────────────────────────
|
||||
# --------------------------------------------------
|
||||
# EC2 Instance
|
||||
# ──────────────────────────────────────────────
|
||||
# --------------------------------------------------
|
||||
resource "aws_instance" "app" {
|
||||
ami = data.aws_ami.amazon_linux_2023.id
|
||||
instance_type = "t3.micro"
|
||||
@@ -48,6 +48,45 @@ resource "aws_instance" "app" {
|
||||
systemctl enable amazon-ssm-agent
|
||||
systemctl start amazon-ssm-agent
|
||||
|
||||
# --------------------------------------------------
|
||||
# Fetch secrets from SSM Parameter Store at runtime.
|
||||
# Credentials are never stored in user data or code.
|
||||
# --------------------------------------------------
|
||||
DB_HOST=$(aws ssm get-parameter \
|
||||
--name "/${var.project_name}/db_host" \
|
||||
--with-decryption \
|
||||
--query "Parameter.Value" \
|
||||
--output text \
|
||||
--region ${var.aws_region})
|
||||
|
||||
DB_NAME=$(aws ssm get-parameter \
|
||||
--name "/${var.project_name}/db_name" \
|
||||
--with-decryption \
|
||||
--query "Parameter.Value" \
|
||||
--output text \
|
||||
--region ${var.aws_region})
|
||||
|
||||
DB_USER=$(aws ssm get-parameter \
|
||||
--name "/${var.project_name}/db_username" \
|
||||
--with-decryption \
|
||||
--query "Parameter.Value" \
|
||||
--output text \
|
||||
--region ${var.aws_region})
|
||||
|
||||
DB_PASSWORD=$(aws ssm get-parameter \
|
||||
--name "/${var.project_name}/db_password" \
|
||||
--with-decryption \
|
||||
--query "Parameter.Value" \
|
||||
--output text \
|
||||
--region ${var.aws_region})
|
||||
|
||||
JWT_SECRET=$(aws ssm get-parameter \
|
||||
--name "/${var.project_name}/jwt_secret" \
|
||||
--with-decryption \
|
||||
--query "Parameter.Value" \
|
||||
--output text \
|
||||
--region ${var.aws_region})
|
||||
|
||||
# Authenticate to ECR
|
||||
aws ecr get-login-password --region ${var.aws_region} | \
|
||||
docker login --username AWS --password-stdin \
|
||||
@@ -57,17 +96,17 @@ resource "aws_instance" "app" {
|
||||
docker pull ${var.aws_account_id}.dkr.ecr.${var.aws_region}.amazonaws.com/task-backend:latest
|
||||
docker pull ${var.aws_account_id}.dkr.ecr.${var.aws_region}.amazonaws.com/task-frontend:latest
|
||||
|
||||
# Run backend — DB credentials passed as environment variables
|
||||
# Values come from Terraform variables which are sourced from GitHub Secrets
|
||||
# Run backend with secrets fetched from SSM
|
||||
docker run -d \
|
||||
--name task-backend \
|
||||
--restart unless-stopped \
|
||||
-p 3000:3000 \
|
||||
-e DB_HOST=${var.db_host} \
|
||||
-e DB_HOST=$DB_HOST \
|
||||
-e DB_PORT=5432 \
|
||||
-e DB_NAME=${var.db_name} \
|
||||
-e DB_USER=${var.db_username} \
|
||||
-e DB_PASSWORD=${var.db_password} \
|
||||
-e DB_NAME=$DB_NAME \
|
||||
-e DB_USER=$DB_USER \
|
||||
-e DB_PASSWORD=$DB_PASSWORD \
|
||||
-e JWT_SECRET=$JWT_SECRET \
|
||||
${var.aws_account_id}.dkr.ecr.${var.aws_region}.amazonaws.com/task-backend:latest
|
||||
|
||||
# Wait for backend to be ready
|
||||
@@ -102,4 +141,4 @@ resource "aws_instance" "app" {
|
||||
lifecycle {
|
||||
ignore_changes = [ami, user_data]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+37
-11
@@ -1,9 +1,8 @@
|
||||
# ──────────────────────────────────────────────
|
||||
# IAM Role — EC2 instance identity
|
||||
# ──────────────────────────────────────────────
|
||||
# --------------------------------------------------
|
||||
# IAM Role - EC2 instance identity
|
||||
# --------------------------------------------------
|
||||
resource "aws_iam_role" "ec2_role" {
|
||||
name = "${var.project_name}-ec2-role"
|
||||
|
||||
assume_role_policy = jsonencode({
|
||||
Version = "2012-10-17"
|
||||
Statement = [
|
||||
@@ -16,7 +15,6 @@ resource "aws_iam_role" "ec2_role" {
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
tags = {
|
||||
Name = "${var.project_name}-ec2-role"
|
||||
Project = var.project_name
|
||||
@@ -24,9 +22,9 @@ resource "aws_iam_role" "ec2_role" {
|
||||
}
|
||||
}
|
||||
|
||||
# ──────────────────────────────────────────────
|
||||
# --------------------------------------------------
|
||||
# Policy Attachments
|
||||
# ──────────────────────────────────────────────
|
||||
# --------------------------------------------------
|
||||
resource "aws_iam_role_policy_attachment" "ssm" {
|
||||
role = aws_iam_role.ec2_role.name
|
||||
policy_arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
|
||||
@@ -37,16 +35,44 @@ resource "aws_iam_role_policy_attachment" "ecr" {
|
||||
policy_arn = "arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly"
|
||||
}
|
||||
|
||||
# ──────────────────────────────────────────────
|
||||
# --------------------------------------------------
|
||||
# Inline policy - read SSM parameters for this project
|
||||
# Scoped to only the parameters this app needs.
|
||||
# --------------------------------------------------
|
||||
resource "aws_iam_role_policy" "ssm_parameters" {
|
||||
name = "${var.project_name}-ssm-read"
|
||||
role = aws_iam_role.ec2_role.id
|
||||
|
||||
policy = jsonencode({
|
||||
Version = "2012-10-17"
|
||||
Statement = [
|
||||
{
|
||||
Effect = "Allow"
|
||||
Action = [
|
||||
"ssm:GetParameter",
|
||||
"ssm:GetParameters",
|
||||
"ssm:GetParametersByPath"
|
||||
]
|
||||
Resource = "arn:aws:ssm:${var.aws_region}:${var.aws_account_id}:parameter/${var.project_name}/*"
|
||||
},
|
||||
{
|
||||
Effect = "Allow"
|
||||
Action = ["kms:Decrypt"]
|
||||
Resource = "*"
|
||||
}
|
||||
]
|
||||
})
|
||||
}
|
||||
|
||||
# --------------------------------------------------
|
||||
# Instance Profile
|
||||
# ──────────────────────────────────────────────
|
||||
# --------------------------------------------------
|
||||
resource "aws_iam_instance_profile" "ec2_profile" {
|
||||
name = "${var.project_name}-ec2-profile"
|
||||
role = aws_iam_role.ec2_role.name
|
||||
|
||||
tags = {
|
||||
Name = "${var.project_name}-ec2-profile"
|
||||
Project = var.project_name
|
||||
Environment = var.environment
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
# --------------------------------------------------
|
||||
# SSM Parameter Store
|
||||
# Stores all runtime secrets for the EC2 instance.
|
||||
# EC2 fetches these at startup - no secrets in user data.
|
||||
# --------------------------------------------------
|
||||
|
||||
resource "aws_ssm_parameter" "db_host" {
|
||||
name = "/${var.project_name}/db_host"
|
||||
description = "RDS instance hostname"
|
||||
type = "SecureString"
|
||||
value = var.db_host
|
||||
|
||||
tags = {
|
||||
Project = var.project_name
|
||||
Environment = var.environment
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_ssm_parameter" "db_name" {
|
||||
name = "/${var.project_name}/db_name"
|
||||
description = "PostgreSQL database name"
|
||||
type = "SecureString"
|
||||
value = var.db_name
|
||||
|
||||
tags = {
|
||||
Project = var.project_name
|
||||
Environment = var.environment
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_ssm_parameter" "db_username" {
|
||||
name = "/${var.project_name}/db_username"
|
||||
description = "PostgreSQL master username"
|
||||
type = "SecureString"
|
||||
value = var.db_username
|
||||
|
||||
tags = {
|
||||
Project = var.project_name
|
||||
Environment = var.environment
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_ssm_parameter" "db_password" {
|
||||
name = "/${var.project_name}/db_password"
|
||||
description = "PostgreSQL master password"
|
||||
type = "SecureString"
|
||||
value = var.db_password
|
||||
|
||||
tags = {
|
||||
Project = var.project_name
|
||||
Environment = var.environment
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_ssm_parameter" "jwt_secret" {
|
||||
name = "/${var.project_name}/jwt_secret"
|
||||
description = "JWT signing secret for authentication"
|
||||
type = "SecureString"
|
||||
value = var.jwt_secret
|
||||
|
||||
tags = {
|
||||
Project = var.project_name
|
||||
Environment = var.environment
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@ variable "aws_region" {
|
||||
}
|
||||
|
||||
variable "aws_account_id" {
|
||||
description = "AWS account ID — set in terraform.tfvars, never hardcoded"
|
||||
description = "AWS account ID - set in terraform.tfvars, never hardcoded"
|
||||
type = string
|
||||
}
|
||||
|
||||
@@ -57,7 +57,13 @@ variable "db_password" {
|
||||
}
|
||||
|
||||
variable "db_host" {
|
||||
description = "RDS instance hostname — output from aws_db_instance.tasks.address"
|
||||
description = "RDS instance hostname - output from aws_db_instance.tasks.address"
|
||||
type = string
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
variable "jwt_secret" {
|
||||
description = "JWT signing secret for authentication"
|
||||
type = string
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user