diff --git a/scenarios/secrets_in_the_cloud/terraform/data.tf b/scenarios/secrets_in_the_cloud/terraform/data.tf index 4cb9439c..0474dd26 100644 --- a/scenarios/secrets_in_the_cloud/terraform/data.tf +++ b/scenarios/secrets_in_the_cloud/terraform/data.tf @@ -1,5 +1,36 @@ data "archive_file" "lambda_zip" { type = "zip" - source_file = "lambda_handler.py" + source_file = "templates/lambda_handler.py" output_path = "lambda_function_payload.zip" } + +data "aws_ami" "amazon_linux_2" { + most_recent = true + + filter { + name = "name" + values = ["amzn2-ami-hvm-*-x86_64-gp2"] + } + + filter { + name = "architecture" + values = ["x86_64"] + } + + filter { + name = "virtualization-type" + values = ["hvm"] + } + + filter { + name = "root-device-type" + values = ["ebs"] + } + + filter { + name = "ena-support" + values = ["true"] + } + + owners = ["amazon"] +} diff --git a/scenarios/secrets_in_the_cloud/terraform/dynamodb.tf b/scenarios/secrets_in_the_cloud/terraform/dynamodb.tf index 464b59ee..400296cb 100644 --- a/scenarios/secrets_in_the_cloud/terraform/dynamodb.tf +++ b/scenarios/secrets_in_the_cloud/terraform/dynamodb.tf @@ -3,14 +3,10 @@ # 2. An AWS DynamoDB Entry (for the Access ID) # 3. An AWS DynamoDB Entry (for the Secret Key) -locals { - dynamodb_suffix = replace(var.cgid, "/[^a-z0-9-.]/", "-") -} - resource "aws_dynamodb_table" "secrets_table" { - name = "secrets-table-${local.dynamodb_suffix}" - billing_mode = "PAY_PER_REQUEST" - hash_key = "key" + name = "secrets-table-${local.cgid_suffix}" + billing_mode = "PAY_PER_REQUEST" + hash_key = "key" attribute { name = "key" @@ -19,7 +15,7 @@ resource "aws_dynamodb_table" "secrets_table" { # Enable server-side encryption using the default AWS KMS key server_side_encryption { - enabled = true + enabled = true } tags = { diff --git a/scenarios/secrets_in_the_cloud/terraform/ec2_instance.tf b/scenarios/secrets_in_the_cloud/terraform/ec2_instance.tf index f63b7776..5ffa0428 100644 --- a/scenarios/secrets_in_the_cloud/terraform/ec2_instance.tf +++ b/scenarios/secrets_in_the_cloud/terraform/ec2_instance.tf @@ -5,42 +5,11 @@ # 4. An AWS Instance Resource # 5. An AWS Security Group Resource -data "aws_ami" "amazon_linux_2" { - most_recent = true - - filter { - name = "name" - values = ["amzn2-ami-hvm-*-x86_64-gp2"] - } - - filter { - name = "architecture" - values = ["x86_64"] - } - - filter { - name = "virtualization-type" - values = ["hvm"] - } - - filter { - name = "root-device-type" - values = ["ebs"] - } - - filter { - name = "ena-support" - values = ["true"] - } - - owners = ["amazon"] -} - -provider "tls" {} resource "tls_private_key" "id_rsa" { algorithm = "RSA" rsa_bits = 2048 } + resource "aws_key_pair" "id_rsa" { key_name = "idrsa-keypair" public_key = tls_private_key.id_rsa.public_key_openssh @@ -50,23 +19,23 @@ resource "aws_instance" "web_app" { ami = data.aws_ami.amazon_linux_2.id instance_type = "t2.micro" key_name = aws_key_pair.id_rsa.key_name - subnet_id = aws_subnet.cg_subnet.id + subnet_id = aws_subnet.subnet.id vpc_security_group_ids = [ aws_security_group.web_app_sg.id, ] - user_data = base64encode(templatefile("user_data.tpl", { + user_data = base64encode(templatefile("templates/user_data.tpl", { aws_access_key_id = aws_iam_access_key.secrets_manager_user_key.id, aws_secret_access_key = aws_iam_access_key.secrets_manager_user_key.secret, private_key = tls_private_key.id_rsa.private_key_pem, })) - + # This sets the EC2 instance's IAM instance profile to the Dynamo DB profile created in iam.tf iam_instance_profile = aws_iam_instance_profile.dynamodb_instance_profile.name metadata_options { - http_tokens = "required" + http_tokens = "required" http_endpoint = "enabled" } @@ -78,7 +47,7 @@ resource "aws_instance" "web_app" { resource "aws_security_group" "web_app_sg" { name = "web_app_sg-${var.cgid}" description = "Allow inbound traffic to the web app and Vault" - vpc_id = aws_vpc.cg_vpc.id + vpc_id = aws_vpc.vpc.id ingress { from_port = 8080 diff --git a/scenarios/secrets_in_the_cloud/terraform/iam.tf b/scenarios/secrets_in_the_cloud/terraform/iam.tf index 71dda62a..9e1c35c0 100644 --- a/scenarios/secrets_in_the_cloud/terraform/iam.tf +++ b/scenarios/secrets_in_the_cloud/terraform/iam.tf @@ -39,25 +39,19 @@ resource "aws_iam_policy" "low_priv_user_s3_policy" { Version = "2012-10-17" Statement = [ { - Action = [ - "s3:ListAllMyBuckets" - ] + Action = "s3:ListAllMyBuckets" Effect = "Allow" Resource = "*" }, { - Action = [ - "s3:ListBucket" - ] + Action = "s3:ListBucket" Effect = "Allow" - Resource = [aws_s3_bucket.cg-secrets-bucket.arn] + Resource = aws_s3_bucket.secrets_bucket.arn }, { - Action = [ - "s3:GetObject" - ] + Action = "s3:GetObject" Effect = "Allow" - Resource = ["${aws_s3_bucket.cg-secrets-bucket.arn}/*"] + Resource = "${aws_s3_bucket.secrets_bucket.arn}/*" }, { Action = [ @@ -66,7 +60,7 @@ resource "aws_iam_policy" "low_priv_user_s3_policy" { "iam:ListAttachedUserPolicies" ] Effect = "Allow" - Resource = [aws_iam_user.low_priv_user.arn] + Resource = aws_iam_user.low_priv_user.arn } ] }) @@ -85,16 +79,12 @@ resource "aws_iam_policy" "low_priv_user_lambda_policy" { Version = "2012-10-17" Statement = [ { - Action = [ - "lambda:ListFunctions" - ] + Action = "lambda:ListFunctions" Effect = "Allow" Resource = "*" }, { - Action = [ - "lambda:InvokeFunction" - ] + Action = "lambda:InvokeFunction" Effect = "Allow" Resource = aws_lambda_function.this.arn } @@ -107,6 +97,7 @@ resource "aws_iam_user_policy_attachment" "low_priv_user_lambda_attachment" { policy_arn = aws_iam_policy.low_priv_user_lambda_policy.arn } + resource "aws_iam_user" "secrets_manager_user" { name = "${var.cgid}-secrets-manager-user" } @@ -115,30 +106,28 @@ resource "aws_iam_access_key" "secrets_manager_user_key" { user = aws_iam_user.secrets_manager_user.name } -resource "aws_iam_role_policy" "lambda_role_policy" { - name = "lambda-role-policy-${var.cgid}" - role = aws_iam_role.lambda_execution.id +resource "aws_iam_user_policy" "secrets_manager_user_policy" { + name = "secrets-manager-policy-${var.cgid}" + user = aws_iam_user.secrets_manager_user.name policy = jsonencode({ Version = "2012-10-17" Statement = [ { Action = [ - "rds:Describe*", - "rds:List*" + "secretsmanager:ListSecrets", + "secretsmanager:DescribeSecret", + "secretsmanager:GetSecretValue" ] - Effect = "Allow" + Effect = "Allow" Resource = "*" - }, - { - Action = "secretsmanager:GetSecretValue" - Effect = "Allow" - Resource = aws_secretsmanager_secret.this.arn } ] }) } + +# Is this even used? resource "aws_iam_role" "secrets_manager_role" { name = "secrets-manager-role-${var.cgid}" @@ -156,25 +145,6 @@ resource "aws_iam_role" "secrets_manager_role" { }) } -resource "aws_iam_user_policy" "secrets_manager_user_policy" { - name = "secrets-manager-policy-${var.cgid}" - user = aws_iam_user.secrets_manager_user.name - - policy = jsonencode({ - Version = "2012-10-17" - Statement = [ - { - Action = [ - "secretsmanager:ListSecrets", - "secretsmanager:DescribeSecret", - "secretsmanager:GetSecretValue" - ] - Effect = "Allow" - Resource = "*" - } - ] - }) -} resource "aws_iam_role" "dynamodb_role" { name = "DavesDancingDoolittle-role" diff --git a/scenarios/secrets_in_the_cloud/terraform/lambda_function.tf b/scenarios/secrets_in_the_cloud/terraform/lambda_function.tf index 3c779a30..77b9a650 100644 --- a/scenarios/secrets_in_the_cloud/terraform/lambda_function.tf +++ b/scenarios/secrets_in_the_cloud/terraform/lambda_function.tf @@ -3,17 +3,28 @@ # - An AWS IAM Role # - An AWS IAM Role Policy +resource "aws_cloudwatch_log_group" "lambda" { + name = "/aws/lambda/cloudgoat-secrets-lambda-${var.cgid}" + retention_in_days = 1 + skip_destroy = false +} + resource "aws_lambda_function" "this" { function_name = "cloudgoat-secrets-lambda-${var.cgid}" filename = data.archive_file.lambda_zip.output_path source_code_hash = data.archive_file.lambda_zip.output_base64sha256 - runtime = "python3.8" + runtime = "python3.13" - role = aws_iam_role.lambda_execution.arn + role = aws_iam_role.lambda_execution.arn handler = "lambda_function.lambda_handler" + logging_config { + log_format = "JSON" + log_group = aws_cloudwatch_log_group.lambda.name + } + environment { variables = { API_KEY = "DavidsDelightfulDonuts2023" @@ -39,7 +50,7 @@ resource "aws_iam_role" "lambda_execution" { } resource "aws_iam_role_policy" "lambda_policy" { - name = "lambda_policy-${var.cgid}" + name = "lambda-policy-${var.cgid}" role = aws_iam_role.lambda_execution.id policy = jsonencode({ @@ -47,25 +58,11 @@ resource "aws_iam_role_policy" "lambda_policy" { Statement = [ { Action = [ - "logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents" ] Effect = "Allow" - Resource = "*" - }, - { - Action = [ - "rds:Describe*", - "rds:ListTagsForResource" - ] - Effect = "Allow" - Resource = "*" - }, - { - Action = "secretsmanager:GetSecretValue" - Effect = "Allow" - Resource = aws_secretsmanager_secret.this.arn + Resource = "${aws_cloudwatch_log_group.lambda.arn}:log-stream:*" } ] }) diff --git a/scenarios/secrets_in_the_cloud/terraform/locals.tf b/scenarios/secrets_in_the_cloud/terraform/locals.tf new file mode 100644 index 00000000..23b20522 --- /dev/null +++ b/scenarios/secrets_in_the_cloud/terraform/locals.tf @@ -0,0 +1,6 @@ +locals { + # Ensure the suffix doesn't contain invalid characters + # Resources names can consist only of lowercase letters, numbers, dots (.), and hyphens (-). + # https://docs.aws.amazon.com/AmazonS3/latest/userguide/bucketnamingrules.html + cgid_suffix = replace(var.cgid, "/[^a-z0-9-.]/", "-") +} diff --git a/scenarios/secrets_in_the_cloud/terraform/outputs.tf b/scenarios/secrets_in_the_cloud/terraform/outputs.tf index 8c33aec7..a54d2d5b 100644 --- a/scenarios/secrets_in_the_cloud/terraform/outputs.tf +++ b/scenarios/secrets_in_the_cloud/terraform/outputs.tf @@ -4,12 +4,12 @@ # - A Low-Privilege Secret Key output "low_priv_access_key" { - value = aws_iam_access_key.low_priv_user_key.id + value = aws_iam_access_key.low_priv_user_key.id description = "Access key ID for the low privilege IAM user." } output "low_priv_secret_key" { - value = aws_iam_access_key.low_priv_user_key.secret + value = aws_iam_access_key.low_priv_user_key.secret description = "Secret access key for the low privilege IAM user." - sensitive = true + sensitive = true } diff --git a/scenarios/secrets_in_the_cloud/terraform/provider.tf b/scenarios/secrets_in_the_cloud/terraform/provider.tf index 105d2cbc..36ff52b1 100644 --- a/scenarios/secrets_in_the_cloud/terraform/provider.tf +++ b/scenarios/secrets_in_the_cloud/terraform/provider.tf @@ -1,6 +1,34 @@ -# This Terraform file initializes the Terraform AWS provider (aka plugin). +terraform { + required_version = ">= 1.5" + + required_providers { + archive = { + source = "hashicorp/archive" + version = ">= 2.7.0" + } + + aws = { + source = "hashicorp/aws" + version = ">= 5.74.0" + } + + tls = { + source = "hashicorp/tls" + version = ">= 4.0.0" + } + } +} provider "aws" { - profile = "${var.profile}" - region = "${var.region}" + profile = var.profile + region = var.region + + default_tags { + tags = { + Stack = var.stack-name + Scenario = var.scenario-name + } + } } + +provider "tls" {} diff --git a/scenarios/secrets_in_the_cloud/terraform/s3_bucket.tf b/scenarios/secrets_in_the_cloud/terraform/s3_bucket.tf index 01dd25a9..2622c764 100644 --- a/scenarios/secrets_in_the_cloud/terraform/s3_bucket.tf +++ b/scenarios/secrets_in_the_cloud/terraform/s3_bucket.tf @@ -1,36 +1,17 @@ # This Terraform file creates the following AWS Simple Storage Service Resources: -# - A bucket suffix variable -# - A web application URL variable # - An AWS S3 Bucket # - An AWS S3 Object -locals { - # Ensure the bucket suffix doesn't contain invalid characters - # "Bucket names can consist only of lowercase letters, numbers, dots (.), and hyphens (-)." - # (per https://docs.aws.amazon.com/AmazonS3/latest/userguide/bucketnamingrules.html) - bucket_suffix = replace(var.cgid, "/[^a-z0-9-.]/", "-") -} - -locals { - web_app_url = "http://${aws_instance.web_app.public_ip}:8080" -} - -resource "aws_s3_bucket" "cg-secrets-bucket" { - bucket = "cg-secrets-bucket-${local.bucket_suffix}" +resource "aws_s3_bucket" "secrets_bucket" { + bucket = "cg-secrets-bucket-${local.cgid_suffix}" force_destroy = true tags = { - Name = "cg-secrets-bucket-${local.bucket_suffix}" - Description = "CloudGoat ${var.cgid} S3 Bucket used for storing seekz." + Description = "CloudGoat ${var.cgid} S3 Bucket used for storing seekz." } } resource "aws_s3_object" "web_app_url" { - bucket = "${aws_s3_bucket.cg-secrets-bucket.id}" - key = "nates_web_app_url.txt" - content = "${local.web_app_url}" - tags = { - Name = "secrets-${var.cgid}" - Stack = "${var.stack-name}" - Scenario = "${var.scenario-name}" - } + bucket = aws_s3_bucket.secrets_bucket.id + key = "nates_web_app_url.txt" + content = "http://${aws_instance.web_app.public_ip}:8080" } diff --git a/scenarios/secrets_in_the_cloud/terraform/secrets_manager.tf b/scenarios/secrets_in_the_cloud/terraform/secrets_manager.tf index e7819dcb..34f2b8c3 100644 --- a/scenarios/secrets_in_the_cloud/terraform/secrets_manager.tf +++ b/scenarios/secrets_in_the_cloud/terraform/secrets_manager.tf @@ -4,34 +4,30 @@ # - An AWS Secret Manager Secret Version # - An AWS Secret Manager Secret Policy -locals { - secrets_suffix = replace(var.cgid, "/[^a-z0-9-.]/", "-") -} - resource "aws_secretsmanager_secret" "this" { - name = "cg-secret-${local.secrets_suffix}" + name = "cg-secret-${local.cgid_suffix}" description = "A secret for CloudGoat scenario" } resource "aws_secretsmanager_secret_version" "this" { - secret_id = aws_secretsmanager_secret.this.id + secret_id = aws_secretsmanager_secret.this.id secret_string = jsonencode({ - super_key = "JohnsJuicyJam" - mega_secret = "BensBubblyBacon" - ultra_mega_secret = "BenjaminsBlazingBungee" - RyansRambunctiousRhinocerosRampage = "Congrats, you have successfully completed Secrets in the Cloud!" + super_key = "JohnsJuicyJam" + mega_secret = "BensBubblyBacon" + ultra_mega_secret = "BenjaminsBlazingBungee" + RyansRambunctiousRhinocerosRampage = "Congrats, you have successfully completed Secrets in the Cloud!" }) } resource "aws_secretsmanager_secret_policy" "this" { secret_arn = aws_secretsmanager_secret.this.arn - policy = jsonencode({ + policy = jsonencode({ Version = "2012-10-17" Statement = [ { - Action = "secretsmanager:GetSecretValue" - Effect = "Allow" - Resource = aws_secretsmanager_secret.this.arn + Action = "secretsmanager:GetSecretValue" + Effect = "Allow" + Resource = aws_secretsmanager_secret.this.arn Principal = "*" } ] diff --git a/scenarios/secrets_in_the_cloud/terraform/lambda_handler.py b/scenarios/secrets_in_the_cloud/terraform/templates/lambda_handler.py similarity index 92% rename from scenarios/secrets_in_the_cloud/terraform/lambda_handler.py rename to scenarios/secrets_in_the_cloud/terraform/templates/lambda_handler.py index 54b68c02..614b4d83 100644 --- a/scenarios/secrets_in_the_cloud/terraform/lambda_handler.py +++ b/scenarios/secrets_in_the_cloud/terraform/templates/lambda_handler.py @@ -1,6 +1,5 @@ import os import json -import boto3 def lambda_handler(event, context): diff --git a/scenarios/secrets_in_the_cloud/terraform/user_data.tpl b/scenarios/secrets_in_the_cloud/terraform/templates/user_data.tpl similarity index 100% rename from scenarios/secrets_in_the_cloud/terraform/user_data.tpl rename to scenarios/secrets_in_the_cloud/terraform/templates/user_data.tpl diff --git a/scenarios/secrets_in_the_cloud/terraform/user-data.sh.tpl b/scenarios/secrets_in_the_cloud/terraform/user-data.sh.tpl deleted file mode 100644 index 511a99f3..00000000 --- a/scenarios/secrets_in_the_cloud/terraform/user-data.sh.tpl +++ /dev/null @@ -1,33 +0,0 @@ -#!/bin/bash - -# This is a terraform template file. - -# It is used to generate the user-data.sh file that is used to configure the EC2 -# instance. - - -yum update -y -amazon-linux-extras install -y lamp-mariadb10.2-php7.2 php7.2 -yum install -y httpd mariadb-server -systemctl start httpd -systemctl enable httpd -usermod -a -G apache ec2-user -chown -R ec2-user:apache /var/www -chmod 2775 /var/www -find /var/www -type d -exec chmod 2775 {} \; -find /var/www -type f -exec chmod 0664 {} \; - -# Install PHP app -echo "" > /var/www/html/db.php - -# Update webapp URL -echo "WebApp URL: ${webapp_url}" > /var/www/html/index.html diff --git a/scenarios/secrets_in_the_cloud/terraform/variables.tf b/scenarios/secrets_in_the_cloud/terraform/variables.tf index 445e0d89..e6ad2ee2 100644 --- a/scenarios/secrets_in_the_cloud/terraform/variables.tf +++ b/scenarios/secrets_in_the_cloud/terraform/variables.tf @@ -1,42 +1,32 @@ -# This Terraform file creates the following variables: -# - A profile variable -# - A AWS region variable -# - A Cloudgoat ID variable -# - A Cloudgoat IP whitelist variable -# - A Public SSH key variable -# - A Private SSH key variable -# - A 'stack' name variable -# - A scenario name variable - -#Required: AWS Profile variable "profile" { - + description = "The AWS profile to use when deploying resources" + type = string } -#Required: AWS Region + variable "region" { - default = "us-west-2" + description = "The AWS region to deploy resources into" + default = "us-east-1" + type = string } -#Required: CGID Variable for unique naming -variable "cgid" { +variable "cgid" { + description = "CGID variable for unique naming between scenarios" + type = string } -#Required: User's Public IP Address(es) + variable "cg_whitelist" { - -} -#SSH Public Key -variable "ssh-public-key-for-ec2" { - default = "../cloudgoat.pub" -} -#SSH Private Key -variable "ssh-private-key-for-ec2" { - default = "../cloudgoat" + description = "User's public IP address(es)" + type = list(string) } -#Stack Name + variable "stack-name" { - default = "CloudGoat" + description = "Name of the stack" + default = "CloudGoat" + type = string } -#Scenario Name + variable "scenario-name" { - default = "secrets_in_the_cloud" + description = "Name of the scenario being deployed" + default = "scenario_template" + type = string } diff --git a/scenarios/secrets_in_the_cloud/terraform/vpc.tf b/scenarios/secrets_in_the_cloud/terraform/vpc.tf index 64f70d9d..57572f28 100644 --- a/scenarios/secrets_in_the_cloud/terraform/vpc.tf +++ b/scenarios/secrets_in_the_cloud/terraform/vpc.tf @@ -1,19 +1,19 @@ -resource "aws_vpc" "cg_vpc" { +resource "aws_vpc" "vpc" { cidr_block = "10.0.0.0/16" tags = { Name = "cg-vpc-${var.cgid}" } } -resource "aws_internet_gateway" "cg_igw" { - vpc_id = aws_vpc.cg_vpc.id +resource "aws_internet_gateway" "igw" { + vpc_id = aws_vpc.vpc.id tags = { Name = "cg-igw-${var.cgid}" } } -resource "aws_subnet" "cg_subnet" { - vpc_id = aws_vpc.cg_vpc.id +resource "aws_subnet" "subnet" { + vpc_id = aws_vpc.vpc.id cidr_block = "10.0.1.0/24" availability_zone = "us-east-1a" # or dynamically get a random availability zone map_public_ip_on_launch = true @@ -22,20 +22,20 @@ resource "aws_subnet" "cg_subnet" { } } -resource "aws_route_table" "cg_route_table" { - vpc_id = aws_vpc.cg_vpc.id +resource "aws_route_table" "route_table" { + vpc_id = aws_vpc.vpc.id tags = { Name = "cg-route-table-${var.cgid}" } } -resource "aws_route" "cg_route" { - route_table_id = aws_route_table.cg_route_table.id +resource "aws_route" "route" { + route_table_id = aws_route_table.route_table.id destination_cidr_block = "0.0.0.0/0" - gateway_id = aws_internet_gateway.cg_igw.id + gateway_id = aws_internet_gateway.igw.id } -resource "aws_route_table_association" "cg_route_table_association" { - subnet_id = aws_subnet.cg_subnet.id - route_table_id = aws_route_table.cg_route_table.id +resource "aws_route_table_association" "route_table_association" { + subnet_id = aws_subnet.subnet.id + route_table_id = aws_route_table.route_table.id }