Skip to content

Commit

Permalink
[TERRAFORM] Add aws-ec2-zero-trust (#201)
Browse files Browse the repository at this point in the history
  • Loading branch information
marco-lancini authored Oct 31, 2024
1 parent d3a601a commit 7953b15
Show file tree
Hide file tree
Showing 27 changed files with 1,033 additions and 10 deletions.
21 changes: 11 additions & 10 deletions terraform/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@ This folder contains the relevant sources needed by a few custom Terraform modul

## Modules

| Module | Description |
| --------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| [aws-ecs-zero-trust](aws-ecs-zero-trust/) | Automates the setup needed to use CloudFlare Tunnel to securely access a Flask webapp running in a private subnet in ECS on Fargate, as described in [Zero Trust Access to Private Webapps on AWS ECS with Cloudflare Tunnel](http://blog.marcolancini.it/2023/blog-cloudflare-tunnel-zero-trust-ecs/) |
| [aws-gdrive-backups](aws-gdrive-backups/) | Automates the setup of an ECS Task needed to backup a GDrive folder, as described in [Automated GDrive Backups with ECS and S3](https://www.marcolancini.it/2021/blog-gdrive-backups-with-ecs/) |
| [aws-github-backups](aws-github-backups/) | Automates the setup of an ECS Task needed to backup a Github account, as described in [Automated Github Backups with ECS and S3](https://www.marcolancini.it/2021/blog-github-backups-with-ecs/) |
| [aws-oidc-ci](aws-oidc-ci/) | Automates the setup of OIDC federation between AWS and Github Actions/Gitlab CI |
| [aws-security-reviewer](aws-security-reviewer/) | Setup roles and users needed to perform a security audit of AWS accounts, as described in [Cross Account Auditing in AWS and GCP](https://www.marcolancini.it/2019/blog-cross-account-auditing/) |
| [cloudflare-gateway-adblocking](cloudflare-gateway-adblocking/) | Mimic the Pi-hole's behaviour using only serverless technologies (Cloudflare Gateway, to be precise), as described in [Serverless Ad Blocking with Cloudflare Gateway](https://blog.marcolancini.it/2022/blog-serverless-ad-blocking-with-cloudflare-gateway/) |
| [cloudflare-pages](cloudflare-pages/) | Creates a Cloudflare Pages application with Zero Trust Authentication, where only the `allowed_emails` are allowed to access the application |
| [digitalocean-algovpn](digitalocean-algovpn/) | DigitalOcean droplet hosting an Algo VPN server |
| Module | Description |
| --------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [aws-ec2-zero-trust](aws-ec2-zero-trust/) | Automates the setup needed to use CloudFlare Access Applications and Tunnels to securely access webapps running on an EC2 instance, as described in [Building a Self-Hosted App Runner on EC2 with Cloudflare Zero Trust Access](http://blog.marcolancini.it/2024/blog-building-selfhosted-apprunner-ec2-cloudflare-zero-trust-access/) |
| [aws-ecs-zero-trust](aws-ecs-zero-trust/) | Automates the setup needed to use CloudFlare Tunnel to securely access a Flask webapp running in a private subnet in ECS on Fargate, as described in [Zero Trust Access to Private Webapps on AWS ECS with Cloudflare Tunnel](http://blog.marcolancini.it/2023/blog-cloudflare-tunnel-zero-trust-ecs/) |
| [aws-gdrive-backups](aws-gdrive-backups/) | Automates the setup of an ECS Task needed to backup a GDrive folder, as described in [Automated GDrive Backups with ECS and S3](https://www.marcolancini.it/2021/blog-gdrive-backups-with-ecs/) |
| [aws-github-backups](aws-github-backups/) | Automates the setup of an ECS Task needed to backup a Github account, as described in [Automated Github Backups with ECS and S3](https://www.marcolancini.it/2021/blog-github-backups-with-ecs/) |
| [aws-oidc-ci](aws-oidc-ci/) | Automates the setup of OIDC federation between AWS and Github Actions/Gitlab CI |
| [aws-security-reviewer](aws-security-reviewer/) | Setup roles and users needed to perform a security audit of AWS accounts, as described in [Cross Account Auditing in AWS and GCP](https://www.marcolancini.it/2019/blog-cross-account-auditing/) |
| [cloudflare-gateway-adblocking](cloudflare-gateway-adblocking/) | Mimic the Pi-hole's behaviour using only serverless technologies (Cloudflare Gateway, to be precise), as described in [Serverless Ad Blocking with Cloudflare Gateway](https://blog.marcolancini.it/2022/blog-serverless-ad-blocking-with-cloudflare-gateway/) |
| [cloudflare-pages](cloudflare-pages/) | Creates a Cloudflare Pages application with Zero Trust Authentication, where only the `allowed_emails` are allowed to access the application |
| [digitalocean-algovpn](digitalocean-algovpn/) | DigitalOcean droplet hosting an Algo VPN server |
10 changes: 10 additions & 0 deletions terraform/aws-ec2-zero-trust/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Building a Self-Hosted AppRunner on EC2 with Cloudflare Zero Trust Access

This module automates the setup needed
to use CloudFlare Access Applications and Tunnels to securely access webapps running on an EC2 instance,
without exposing them to the public internet,
as described in
[Building a Self-Hosted App Runner on EC2 with Cloudflare Zero Trust Access](http://blog.marcolancini.it/2024/blog-building-selfhosted-apprunner-ec2-cloudflare-zero-trust-access/).


![](https://blog.marcolancini.it/images/posts/blog_apprunner_highlevel.png)
32 changes: 32 additions & 0 deletions terraform/aws-ec2-zero-trust/docker/cloudflared/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# ==============================================================================
# Download cloudflared
# ==============================================================================
FROM debian:bookworm-slim as build

# Install dependencies
RUN apt-get update && \
apt-get install -y curl

# Download cloudflared
RUN curl -L https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64 -o cloudflared && \
chmod +x cloudflared && \
mv cloudflared /usr/local/bin/

# ==============================================================================
# Run from alpine
# ==============================================================================
FROM alpine:3.12

# Install dependencies
RUN apk update && apk add --no-cache aws-cli

# Get cloudflared from build
COPY --from=build /usr/local/bin/cloudflared /usr/local/bin/cloudflared

# Copy script
WORKDIR /etc/cloudflared
COPY ./entrypoint.sh /etc/cloudflared/entrypoint.sh
RUN chmod +x /etc/cloudflared/entrypoint.sh

# Run script
ENTRYPOINT ["/etc/cloudflared/entrypoint.sh"]
78 changes: 78 additions & 0 deletions terraform/aws-ec2-zero-trust/docker/cloudflared/entrypoint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
#! /bin/sh

set -ueo pipefail

# ==============================================================================
# CONFIG
# ==============================================================================
#
# STATIC
#
PATH_CLOUDFLARED="/etc/cloudflared"
PATH_CREDENTIALS="${PATH_CLOUDFLARED}/credentials.json"
PATH_CONFIG="${PATH_CLOUDFLARED}/config.yml"

#
# ENV VARS
#
VAR_ORIGIN_URL=${ORIGIN_URL}
VAR_TUNNEL_CREDENTIALS_SSM_NAME=${TUNNEL_CREDENTIALS_SSM_NAME}

# ==============================================================================
# PREPARE CLOUDFLARED CONFIG
# ==============================================================================
#
# Create folder
#
mkdir -p ${PATH_CLOUDFLARED}

#
# Fetch secrets
#
echo "[*] Fetching Cloudflared Tunnel: credentials JSON..."
VAR_TUNNEL_CREDENTIALS=$(aws ssm get-parameter --region eu-west-1 --name "${VAR_TUNNEL_CREDENTIALS_SSM_NAME}" --with-decryption --query "Parameter.Value" --output text)
echo "$VAR_TUNNEL_CREDENTIALS" > $PATH_CREDENTIALS

#
# Extract tunnel UUID
#
VAR_TUNNEL_UUID=$(grep 'TunnelID' $PATH_CREDENTIALS | sed 's/.*TunnelID": "\(.*\)".*/\1/')

#
# Create config file
#
echo -e "tunnel: ${VAR_TUNNEL_UUID}
credentials-file: ${PATH_CREDENTIALS}
url: ${VAR_ORIGIN_URL}
no-autoupdate: true" > $PATH_CONFIG


# ==============================================================================
# CHECK CONNECTION
# ==============================================================================
set +ex

# Check connection with origin
echo "[*] Checking connection with origin..."
for i in {1..60}
do
wget ${VAR_ORIGIN_URL} 1>/dev/null 2>&1

if [ $? -ne 0 ]; then
echo "Attempt to connect to origin failed: ${VAR_ORIGIN_URL}"
sleep 1
else
echo "Successfully connected to origin: ${VAR_ORIGIN_URL}"
break
fi
done


# ==============================================================================
# RUN TUNNEL
# ==============================================================================
set -ex

# Run tunnel
echo "[*] Starting tunnel..."
cloudflared tunnel --config ${PATH_CONFIG} run ${VAR_TUNNEL_UUID}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# AppRunner

AppRunner is a long-lived EC2 instance used to run apps via docker-compose.

This module creates:

- An EC2 instance (`aws-private-ec2` module) based on an Amazon Linux 2023 AMI, and `instance_user_data` specified in `cloud-config.yaml`
- An ECR repository for hosting the `cloudflared` Docker image
- One Cloudflare Access Application for each app specified
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
#cloud-config
# https://cloudinit.readthedocs.io

#
# General settings
#
timezone: Europe/London
locale: en_GB.UTF-8
keyboard:
layout: gb
variant: ''

#
# Hostname
#
hostname: apprunner
create_hostname_file: true

#
# Group definitions
#
groups:
- ubuntu: [root,sys]
- docker

#
# Users definitions
#
users:
- default
- name: ec2-user
gecos: EC2 Default User
shell: /bin/bash
primary_group: ec2-user
sudo: ALL=(ALL) NOPASSWD:ALL
groups: users, admin, docker
lock_passwd: false
homedir: /home/ec2-user/

#
# Package definitions
#
package_update: true
package_upgrade: true
packages:
- ca-certificates
- docker

#
# Startup commands
#
runcmd:
# Start the SSM agent
- sudo systemctl enable amazon-ssm-agent
- sudo systemctl start amazon-ssm-agent
# Install Docker
- sudo systemctl enable docker
- sudo systemctl start docker
# Install Docker Compose
- sudo curl -L "https://github.com/docker/compose/releases/download/v2.29.7/docker-compose-linux-x86_64" -o /usr/local/bin/docker-compose
- sudo chmod +x /usr/local/bin/docker-compose
# Install micro
- cd /tmp/
- curl https://getmic.ro | bash
- sudo mv micro /usr/local/bin
# Switch to the ec2-user user
- sudo su ec2-user
# Chown user directory
- sudo chown -R ec2-user:ec2-user /home/ec2-user/
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# ==============================================================================
# Cloudflare Access Applications
# ==============================================================================
module "cf_apps" {
for_each = var.apps
source = "../cloudflare-access-app"

# Cloudflare Config
cloudflare_account_id = var.cloudflare_account_id
cloudflare_zone_id = var.cloudflare_zone_id

# Cloudflare Tunnel
tunnel_name = "${var.prefix}_${each.value.name}"
tunnel_dnsname = each.value.name
tunnel_hostname = each.value.hostname
tunnel_origin = each.value.origin

# Cloudflare Access Application
cloudflare_app_name = "${var.prefix}_${each.value.name}"
cloudflare_app_allowed_emails = var.cloudflare_app_allowed_emails

tags = var.tags
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# ==============================================================================
# ECR
# ==============================================================================
resource "aws_ecr_repository" "cloudflared" {
name = "cloudflared"

image_tag_mutability = "MUTABLE"
force_delete = true

tags = var.tags
}

resource "aws_ecr_lifecycle_policy" "cloudflared" {
repository = aws_ecr_repository.cloudflared.name

policy = <<EOF
{
"rules": [
{
"rulePriority": 1,
"description": "Expire untagged images older than 1 days",
"selection": {
"tagStatus": "untagged",
"countType": "sinceImagePushed",
"countUnit": "days",
"countNumber": 1
},
"action": {
"type": "expire"
}
}
]
}
EOF
}
Loading

0 comments on commit 7953b15

Please sign in to comment.