Skip to content

Commit 02c307e

Browse files
authored
Setup AWS conformance CI pipeline (#247)
* Setup AWS conformance CI pipeline * Remove unused AWS conformance module outputs * Address comment
1 parent f060966 commit 02c307e

File tree

17 files changed

+615
-8
lines changed

17 files changed

+615
-8
lines changed
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
name: AWS Conformance CI
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
8+
# This prevents two workflows from running at the same time.
9+
# This workflows calls terragrunt, which does not allow concurrent runs.
10+
concurrency:
11+
group: aws-conformance-ci
12+
cancel-in-progress: false
13+
14+
permissions:
15+
id-token: write
16+
contents: read
17+
18+
env:
19+
TF_VERSION: "1.11.3"
20+
TG_VERSION: "0.77.7"
21+
TG_DIR: "deployment/live/aws/conformance/ci/"
22+
ECR_REPOSITORY_CONFORMANCE: static-ct-ci/conformance:latest
23+
ECR_REPOSITORY_HAMMER: static-ct-ci/hammer:latest
24+
AWS_REGION: us-east-1
25+
26+
jobs:
27+
aws-conformance-ci:
28+
runs-on: ubuntu-latest
29+
steps:
30+
- name: Configure AWS credentials
31+
uses: aws-actions/configure-aws-credentials@ececac1a45f3b08a01d2dd070d28d111c5fe6722 # v4.1.0
32+
with:
33+
role-to-assume: ${{ secrets.AWS_IAMROLE_GITHUB_CI }}
34+
aws-region: ${{ env.AWS_REGION }}
35+
36+
- name: Checkout code
37+
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
38+
with:
39+
persist-credentials: false
40+
41+
# Authenticate with ECR to push the conformance and hammer images.
42+
- name: Login to Amazon ECR
43+
id: login-ecr
44+
uses: aws-actions/amazon-ecr-login@062b18b96a7aff071d4dc91bc00c4c1a7945b076 # v2.0.1
45+
46+
# Build the conformance image and push it to ECR. This will be used later on by Terragrunt.
47+
- name: Build, tag, and push Conformance image to Amazon ECR
48+
shell: bash
49+
env:
50+
ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
51+
ECR_REPOSITORY: ${{ env.ECR_REPOSITORY_CONFORMANCE }}
52+
run: |
53+
docker build -f ./cmd/aws/Dockerfile . -t sctfe-aws:latest
54+
docker build -f ./cmd/aws/ci/Dockerfile . -t "$ECR_REGISTRY/$ECR_REPOSITORY"
55+
docker push "$ECR_REGISTRY/$ECR_REPOSITORY"
56+
echo "Pushed image to $ECR_REGISTRY/$ECR_REPOSITORY"
57+
58+
# Build the CT hammer image and push it to ECR. This will be used later on by Terragrunt.
59+
- name: Build, tag, and push CT Hammer image to Amazon ECR
60+
shell: bash
61+
env:
62+
ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
63+
ECR_REPOSITORY: ${{ env.ECR_REPOSITORY_HAMMER }}
64+
run: |
65+
docker build -f ./internal/hammer/Dockerfile . -t tesseract-hammer:latest
66+
docker build -f ./internal/hammer/ci/Dockerfile . -t "$ECR_REGISTRY/$ECR_REPOSITORY"
67+
docker push "$ECR_REGISTRY/$ECR_REPOSITORY"
68+
echo "Pushed image to $ECR_REGISTRY/$ECR_REPOSITORY"
69+
70+
# Destroy any pre-existing deployment/live/aws/conformance/ci env.
71+
# This might happen if a previous integration test workflow has failed.
72+
- name: Terragrunt destroy pre conformance test
73+
uses: gruntwork-io/terragrunt-action@aee21a7df999be8b471c2a8564c6cd853cb674e1 # v2.1.8
74+
with:
75+
tf_version: ${{ env.TF_VERSION }}
76+
tg_version: ${{ env.TG_VERSION }}
77+
tg_dir: ${{ env.TG_DIR }}
78+
tg_command: "destroy"
79+
env:
80+
ECS_EXECUTION_ROLE: ${{ secrets.AWS_IAMROLE_ECS_EXECUTION }}
81+
ECS_CONFORMANCE_TASK_ROLE: ${{ secrets.AWS_IAMROLE_ECS_CONFORMANCE_TASK }}
82+
83+
# Apply the deployment/live/aws/conformance/ci terragrunt config.
84+
- name: Terragrunt apply
85+
uses: gruntwork-io/terragrunt-action@aee21a7df999be8b471c2a8564c6cd853cb674e1 # v2.1.8
86+
with:
87+
tf_version: ${{ env.TF_VERSION }}
88+
tg_version: ${{ env.TG_VERSION }}
89+
tg_dir: ${{ env.TG_DIR }}
90+
tg_command: "apply"
91+
env:
92+
ECS_EXECUTION_ROLE: ${{ secrets.AWS_IAMROLE_ECS_EXECUTION }}
93+
ECS_CONFORMANCE_TASK_ROLE: ${{ secrets.AWS_IAMROLE_ECS_CONFORMANCE_TASK }}
94+
INPUT_POST_EXEC_1: |
95+
echo "ECS_CLUSTER=$(terragrunt output -raw ecs_cluster)" >> "$GITHUB_ENV"
96+
INPUT_POST_EXEC_2: |
97+
echo "VPC_SUBNETS=$(terragrunt output -json vpc_subnets)" >> "$GITHUB_ENV"
98+
99+
# Run CT Hammer using the defined task definition.
100+
# This step returns the CT Hammer task's exit code.
101+
- name: Run CT Hammer
102+
shell: bash
103+
run: |
104+
echo "Will launch a hammer ECS task."
105+
HAMMER_ARN=$(aws ecs run-task \
106+
--cluster="$ECS_CLUSTER" \
107+
--task-definition=hammer \
108+
--count=1 \
109+
--launch-type=FARGATE \
110+
--network-configuration='{"awsvpcConfiguration": {"assignPublicIp":"ENABLED","subnets": '$VPC_SUBNETS'}}' \
111+
--query 'tasks[0].taskArn')
112+
echo "Hammer task running, ARN: $HAMMER_ARN."
113+
echo "Waiting for task to stop..."
114+
aws ecs wait tasks-stopped --cluster="$ECS_CLUSTER" --tasks=[$HAMMER_ARN]
115+
echo "The task has stopped. Fetching exit code and returning this action with it."
116+
exit $(aws ecs describe-tasks --cluster="$ECS_CLUSTER" --tasks=[$HAMMER_ARN] --query 'tasks[0].containers[0].exitCode')
117+
118+
# Destroy the deployment/live/aws/conformance/ci env.
119+
- name: Terragrunt destroy pre conformance test
120+
uses: gruntwork-io/terragrunt-action@aee21a7df999be8b471c2a8564c6cd853cb674e1 # v2.1.8
121+
with:
122+
tf_version: ${{ env.TF_VERSION }}
123+
tg_version: ${{ env.TG_VERSION }}
124+
tg_dir: ${{ env.TG_DIR }}
125+
tg_command: "destroy"
126+
env:
127+
ECS_EXECUTION_ROLE: ${{ secrets.AWS_IAMROLE_ECS_EXECUTION }}
128+
ECS_CONFORMANCE_TASK_ROLE: ${{ secrets.AWS_IAMROLE_ECS_CONFORMANCE_TASK }}

cmd/aws/ci/Dockerfile

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
FROM sctfe-aws:latest AS base
2+
3+
# Build release image
4+
FROM alpine:3.20.2@sha256:0a4eaa0eecf5f8c050e5bba433f58c052be7587ee8af3e8b3910ef9ab5fbe9f5
5+
6+
# Copy the hammer test root CA certificate into the container
7+
COPY ./internal/hammer/testdata/test_root_ca_cert.pem /bin/
8+
9+
# Copy the sctfe-aws binary
10+
COPY --from=base /bin/sctfe-aws /bin/
11+
12+
ENTRYPOINT ["/bin/sctfe-aws"]

deployment/live/aws/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@
22

33
This directory contains Terragrunt configs we use to run TesseraCT logs and other related pieces of infrastructure:
44
- `test`: configures a test log using a EC2 VM
5+
- `ci`: configures a conformance log with the [hammer](/internal/hammer/) for continuous integration testing
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# AWS TesseraCT CI Environment
2+
3+
## Overview
4+
5+
This config uses the [aws/conformance](/deployment/modules/aws/tesseract/conformance) module to
6+
define a CI environment to run TesseraCT, backed by Trillian Tessera.
7+
8+
At a high level, this environment consists of:
9+
10+
- Aurora MySQL database
11+
- S3 Bucket
12+
- Secrets Manager
13+
- ECS
14+
15+
## Manual deployment
16+
17+
Configure an AWS profile on your workstation using your prefered method, (e.g
18+
[sso](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-sso.html)
19+
or [credential
20+
files](https://docs.aws.amazon.com/cli/v1/userguide/cli-configure-files.html))
21+
22+
Set the required environment variables:
23+
24+
```shell
25+
export AWS_PROFILE={VALUE}
26+
```
27+
28+
Optionally, customize the AWS region (defaults to "us-east-1"), prefix, and base
29+
name for resources (defaults to "static-ct-ci" and "conformance"):
30+
31+
```shell
32+
export TESSERACT_BASE_NAME={VALUE}
33+
export TESSERACT_PREFIX_NAME={VALUE}
34+
```
35+
36+
Resources will be named using a `${TESSERACT_PREFIX_NAME}-${TESSERACT_BASE_NAME}`
37+
convention.
38+
39+
Terraforming the project can be done by:
40+
1. `cd` to the relevant directory for the environment to deploy/change (e.g. `ci`)
41+
2. Run `terragrunt apply`
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
terraform {
2+
source = "${get_repo_root()}/deployment/modules/aws//tesseract/conformance"
3+
}
4+
5+
include "root" {
6+
path = find_in_parent_folders()
7+
expose = true
8+
}
9+
10+
inputs = include.root.locals
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
locals {
2+
env = path_relative_to_include()
3+
account_id = "${get_aws_account_id()}"
4+
region = get_env("AWS_REGION", "us-east-1")
5+
base_name = get_env("TESSERA_BASE_NAME", "conformance")
6+
origin_suffix = get_env("TESSERA_ORIGIN_SUFFIX", "")
7+
prefix_name = get_env("TESSERA_PREFIX_NAME", "static-ct-${local.env}")
8+
ecr_registry = get_env("ECR_REGISTRY", "${local.account_id}.dkr.ecr.${local.region}.amazonaws.com")
9+
ecr_repository_conformance = get_env("ECR_REPOSITORY_CONFORMANCE", "static-ct-${local.env}/conformance:latest")
10+
ecr_repository_hammer = get_env("ECR_REPOSITORY_HAMMER", "static-ct-${local.env}/hammer:latest")
11+
ecs_execution_role = get_env("ECS_EXECUTION_ROLE")
12+
ecs_conformance_task_role = get_env("ECS_CONFORMANCE_TASK_ROLE")
13+
ephemeral = true
14+
}
15+
16+
remote_state {
17+
backend = "s3"
18+
19+
config = {
20+
region = local.region
21+
bucket = "${local.prefix_name}-${local.base_name}-terraform-state"
22+
key = "terraform.tfstate"
23+
s3_bucket_tags = {
24+
name = "terraform_state_storage"
25+
}
26+
use_lockfile = true
27+
}
28+
}

deployment/live/aws/terragrunt.hcl

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,12 @@ remote_state {
1111
backend = "s3"
1212

1313
config = {
14-
region = local.region
15-
bucket = "${local.prefix_name}-${local.base_name}-terraform-state"
16-
key = "terraform.tfstate"
14+
region = local.region
15+
bucket = "${local.prefix_name}-${local.base_name}-terraform-state"
16+
key = "terraform.tfstate"
1717
s3_bucket_tags = {
1818
name = "terraform_state_storage"
1919
}
20-
use_lockfile = true
20+
use_lockfile = true
2121
}
2222
}

deployment/live/aws/test/terragrunt.hcl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ terraform {
33
}
44

55
locals {
6-
env = "test"
7-
base_name = get_env("TESSERA_BASE_NAME", "${local.env}-static-ct")
8-
ephemeral = true
6+
env = "test"
7+
base_name = get_env("TESSERA_BASE_NAME", "${local.env}-static-ct")
8+
ephemeral = true
99
}
1010

1111
include "root" {

deployment/modules/aws/insecuretlskey/main.tf

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,10 @@ resource "tls_private_key" "ecdsa_p256" {
1919
algorithm = "ECDSA"
2020
ecdsa_curve = "P256"
2121
}
22+
23+
# Use locals to process the secret string
24+
locals {
25+
# Base64 representation (stripping PEM headers/footers and whitespace)
26+
# regexreplace removes the BEGIN/END lines and any whitespace characters (\s+)
27+
public_key_base64_der = trimspace(replace(replace(replace(tls_private_key.ecdsa_p256.public_key_pem, "-----BEGIN PUBLIC KEY-----", ""), "-----END PUBLIC KEY-----", ""), "\n", ""))
28+
}

deployment/modules/aws/insecuretlskey/outputs.tf

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,11 @@ output "tls_private_key_ecdsa_p256_public_key_pem" {
33
sensitive = true
44
}
55

6+
output "tls_private_key_ecdsa_p256_public_key_base64_der" {
7+
value = local.public_key_base64_der
8+
sensitive = true
9+
}
10+
611
output "tls_private_key_ecdsa_p256_private_key_pem" {
712
value = tls_private_key.ecdsa_p256.private_key_pem
813
sensitive = true

deployment/modules/aws/secretsmanager/main.tf

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ provider "aws" {
1515
# Secrets Manager
1616
resource "aws_secretsmanager_secret" "sctfe_ecdsa_p256_public_key" {
1717
name = "${var.base_name}-ecdsa-p256-public-key"
18+
recovery_window_in_days = 0
1819

1920
tags = {
2021
label = "tesseract-public-key"
@@ -28,7 +29,8 @@ resource "aws_secretsmanager_secret_version" "sctfe_ecdsa_p256_public_key" {
2829

2930
resource "aws_secretsmanager_secret" "sctfe_ecdsa_p256_private_key" {
3031
name = "${var.base_name}-ecdsa-p256-private-key"
31-
32+
recovery_window_in_days = 0
33+
3234
tags = {
3335
label = "tesseract-private-key"
3436
}

deployment/modules/aws/storage/main.tf

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,14 @@ resource "aws_rds_cluster_instance" "cluster_instances" {
3939

4040
force_destroy = var.ephemeral
4141
}
42+
43+
# Data source to get the secret details using the ARN provided by the cluster
44+
data "aws_secretsmanager_secret_version" "db_credentials" {
45+
# The secret ARN is available in the master_user_secret block (it's a list)
46+
secret_id = aws_rds_cluster.log_rds_cluster.master_user_secret[0].secret_arn
47+
48+
depends_on = [
49+
aws_rds_cluster.log_rds_cluster,
50+
aws_rds_cluster_instance.cluster_instances
51+
]
52+
}

deployment/modules/aws/storage/outputs.tf

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,14 @@ output "s3_bucket_name" {
22
value = aws_s3_bucket.log_bucket.id
33
}
44

5+
output "s3_bucket_arn" {
6+
value = aws_s3_bucket.log_bucket.arn
7+
}
8+
9+
output "s3_bucket_regional_domain_name" {
10+
value = aws_s3_bucket.log_bucket.bucket_regional_domain_name
11+
}
12+
513
output "rds_aurora_cluster_endpoint" {
614
value = aws_rds_cluster.log_rds_cluster.endpoint
715
sensitive = true
@@ -19,3 +27,9 @@ output "rds_aurora_cluster_master_user_secret" {
1927
value = aws_rds_cluster.log_rds_cluster.master_user_secret
2028
sensitive = true
2129
}
30+
31+
output "rds_aurora_cluster_master_user_secret_unsafe" {
32+
description = "Retrieved RDS Aurora DB Password (UNSAFE - AVOID IN PRODUCTION)"
33+
value = jsondecode(data.aws_secretsmanager_secret_version.db_credentials.secret_string)["password"]
34+
sensitive = true # Mark as sensitive, but it can still be exposed
35+
}

0 commit comments

Comments
 (0)