summaryrefslogtreecommitdiff
path: root/infrastructure
diff options
context:
space:
mode:
Diffstat (limited to 'infrastructure')
-rw-r--r--infrastructure/.terraform.lock.hcl25
-rw-r--r--infrastructure/acm.tf4
-rw-r--r--infrastructure/cloudfront.tf59
-rw-r--r--infrastructure/locals.tf5
-rw-r--r--infrastructure/main.tf11
-rw-r--r--infrastructure/providers.tf12
-rw-r--r--infrastructure/route53.tf35
-rw-r--r--infrastructure/s3.tf78
-rw-r--r--infrastructure/variables.tf28
9 files changed, 257 insertions, 0 deletions
diff --git a/infrastructure/.terraform.lock.hcl b/infrastructure/.terraform.lock.hcl
new file mode 100644
index 0000000..8790b90
--- /dev/null
+++ b/infrastructure/.terraform.lock.hcl
@@ -0,0 +1,25 @@
+# This file is maintained automatically by "terraform init".
+# Manual edits may be lost in future updates.
+
+provider "registry.terraform.io/hashicorp/aws" {
+ version = "4.67.0"
+ constraints = "~> 4.16"
+ hashes = [
+ "h1:LfOuBkdYCzQhtiRvVIxdP/KGJODa3cRsKjn8xKCTbVY=",
+ "zh:0843017ecc24385f2b45f2c5fce79dc25b258e50d516877b3affee3bef34f060",
+ "zh:19876066cfa60de91834ec569a6448dab8c2518b8a71b5ca870b2444febddac6",
+ "zh:24995686b2ad88c1ffaa242e36eee791fc6070e6144f418048c4ce24d0ba5183",
+ "zh:4a002990b9f4d6d225d82cb2fb8805789ffef791999ee5d9cb1fef579aeff8f1",
+ "zh:559a2b5ace06b878c6de3ecf19b94fbae3512562f7a51e930674b16c2f606e29",
+ "zh:6a07da13b86b9753b95d4d8218f6dae874cf34699bca1470d6effbb4dee7f4b7",
+ "zh:768b3bfd126c3b77dc975c7c0e5db3207e4f9997cf41aa3385c63206242ba043",
+ "zh:7be5177e698d4b547083cc738b977742d70ed68487ce6f49ecd0c94dbf9d1362",
+ "zh:8b562a818915fb0d85959257095251a05c76f3467caa3ba95c583ba5fe043f9b",
+ "zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425",
+ "zh:9c385d03a958b54e2afd5279cd8c7cbdd2d6ca5c7d6a333e61092331f38af7cf",
+ "zh:b3ca45f2821a89af417787df8289cb4314b273d29555ad3b2a5ab98bb4816b3b",
+ "zh:da3c317f1db2469615ab40aa6baba63b5643bae7110ff855277a1fb9d8eb4f2c",
+ "zh:dc6430622a8dc5cdab359a8704aec81d3825ea1d305bbb3bbd032b1c6adfae0c",
+ "zh:fac0d2ddeadf9ec53da87922f666e1e73a603a611c57bcbc4b86ac2821619b1d",
+ ]
+}
diff --git a/infrastructure/acm.tf b/infrastructure/acm.tf
new file mode 100644
index 0000000..f07a0eb
--- /dev/null
+++ b/infrastructure/acm.tf
@@ -0,0 +1,4 @@
+data "aws_acm_certificate" "joeac_ssl_certificate" {
+ domain = local.domain
+ statuses = ["ISSUED"]
+}
diff --git a/infrastructure/cloudfront.tf b/infrastructure/cloudfront.tf
new file mode 100644
index 0000000..4cdc80c
--- /dev/null
+++ b/infrastructure/cloudfront.tf
@@ -0,0 +1,59 @@
+resource "aws_cloudfront_distribution" "joeac" {
+ enabled = true
+ is_ipv6_enabled = true
+ default_root_object = "index.html"
+ price_class = "PriceClass_100"
+
+ aliases = ["joeac.net"]
+
+ origin {
+ domain_name = aws_s3_bucket_website_configuration.website.website_endpoint
+ origin_id = local.website_origin_id
+
+ custom_origin_config {
+ http_port = 80
+ https_port = 443
+ origin_protocol_policy = "http-only"
+ origin_ssl_protocols = ["TLSv1", "TLSv1.1", "TLSv1.2"]
+ }
+ }
+
+ default_cache_behavior {
+ allowed_methods = ["GET", "HEAD", "OPTIONS"]
+ cached_methods = ["GET", "HEAD", "OPTIONS"]
+ target_origin_id = local.website_origin_id
+ cache_policy_id = data.aws_cloudfront_cache_policy.caching_optimized.id
+ viewer_protocol_policy = "redirect-to-https"
+ }
+
+ restrictions {
+ geo_restriction {
+ restriction_type = "none"
+ locations = []
+ }
+ }
+
+ viewer_certificate {
+ acm_certificate_arn = data.aws_acm_certificate.joeac_ssl_certificate.arn
+ ssl_support_method = "sni-only"
+ minimum_protocol_version = "TLSv1.2_2021"
+ }
+
+ depends_on = [aws_cloudfront_origin_access_control.website]
+}
+
+resource "aws_cloudfront_origin_access_control" "website" {
+ name = "website"
+ origin_access_control_origin_type = "s3"
+ signing_behavior = "always"
+ signing_protocol = "sigv4"
+}
+
+data "aws_cloudfront_cache_policy" "caching_optimized" {
+ name = "Managed-CachingOptimized"
+}
+
+locals {
+ website_origin_id = "website"
+}
+
diff --git a/infrastructure/locals.tf b/infrastructure/locals.tf
new file mode 100644
index 0000000..d4fd3bf
--- /dev/null
+++ b/infrastructure/locals.tf
@@ -0,0 +1,5 @@
+locals {
+ aws_region = "us-east-1"
+ domain = "joeac.net"
+}
+
diff --git a/infrastructure/main.tf b/infrastructure/main.tf
new file mode 100644
index 0000000..03b8568
--- /dev/null
+++ b/infrastructure/main.tf
@@ -0,0 +1,11 @@
+terraform {
+ required_providers {
+ aws = {
+ source = "hashicorp/aws"
+ version = "~> 4.16"
+ }
+ }
+
+ required_version = ">= 1.2.0"
+}
+
diff --git a/infrastructure/providers.tf b/infrastructure/providers.tf
new file mode 100644
index 0000000..8e06505
--- /dev/null
+++ b/infrastructure/providers.tf
@@ -0,0 +1,12 @@
+provider "aws" {
+ region = local.aws_region
+ access_key = var.aws_access_key
+ secret_key = var.aws_secret_key
+ default_tags {
+ tags = {
+ project = "joeac-website"
+ owner = "terraform"
+ }
+ }
+}
+
diff --git a/infrastructure/route53.tf b/infrastructure/route53.tf
new file mode 100644
index 0000000..828cec2
--- /dev/null
+++ b/infrastructure/route53.tf
@@ -0,0 +1,35 @@
+# This hosted zone must have NS records which point to the same nameservers as
+# those listed with the domain registrar. Right now, this means manually going
+# into Route53Domains, finding the nameservers, and manually copying these into
+# the NS records for this hosted zone.
+resource "aws_route53_zone" "joeac_zone" {
+ name = "joeac.net"
+}
+
+resource "aws_route53_record" "cloudfront" {
+ # This is the subdomain for the record. Specify the root domain by leaving it blank
+ name = ""
+
+ zone_id = aws_route53_zone.joeac_zone.id
+ type = "A"
+
+ alias {
+ name = aws_cloudfront_distribution.joeac.domain_name
+ zone_id = aws_cloudfront_distribution.joeac.hosted_zone_id
+ evaluate_target_health = false
+ }
+}
+
+resource "aws_route53_record" "cloudfront_aaaa" {
+ # This is the subdomain for the record. Specify the root domain by leaving it blank
+ name = ""
+
+ zone_id = aws_route53_zone.joeac_zone.id
+ type = "AAAA"
+
+ alias {
+ name = aws_cloudfront_distribution.joeac.domain_name
+ zone_id = aws_cloudfront_distribution.joeac.hosted_zone_id
+ evaluate_target_health = false
+ }
+}
diff --git a/infrastructure/s3.tf b/infrastructure/s3.tf
new file mode 100644
index 0000000..7b116a7
--- /dev/null
+++ b/infrastructure/s3.tf
@@ -0,0 +1,78 @@
+resource "aws_s3_bucket" "website" {
+ bucket = local.bucket_name
+}
+
+locals {
+ bucket_name = "${local.domain}-${var.secret_s3_bucket_suffix}"
+}
+
+resource "aws_s3_bucket_website_configuration" "website" {
+ bucket = aws_s3_bucket.website.id
+
+ index_document {
+ suffix = "index.html"
+ }
+
+ error_document {
+ key = "error/index.html"
+ }
+}
+
+resource "aws_s3_bucket_ownership_controls" "website" {
+ bucket = aws_s3_bucket.website.id
+
+ rule {
+ object_ownership = "BucketOwnerPreferred"
+ }
+
+ depends_on = [aws_s3_bucket_public_access_block.website]
+}
+
+resource "aws_s3_bucket_public_access_block" "website" {
+ bucket = aws_s3_bucket.website.id
+
+ block_public_acls = false
+ block_public_policy = false
+ ignore_public_acls = false
+ restrict_public_buckets = false
+}
+
+resource "aws_s3_bucket_acl" "website" {
+ bucket = aws_s3_bucket.website.id
+
+ acl = "public-read"
+
+ depends_on = [aws_s3_bucket_ownership_controls.website]
+}
+
+resource "aws_s3_bucket_versioning" "website" {
+ bucket = aws_s3_bucket.website.id
+
+ versioning_configuration {
+ status = "Disabled"
+ }
+}
+
+resource "aws_s3_bucket_policy" "website" {
+ bucket = aws_s3_bucket.website.id
+ policy = data.aws_iam_policy_document.website.json
+}
+
+# TODO: can we restrict access to just from the CloudFront distro?
+data "aws_iam_policy_document" "website" {
+ statement {
+ sid = "AllowPublicRead"
+ effect = "Allow"
+ resources = [
+ "arn:aws:s3:::${local.bucket_name}",
+ "arn:aws:s3:::${local.bucket_name}/*",
+ ]
+ actions = ["S3:GetObject"]
+ principals {
+ type = "*"
+ identifiers = ["*"]
+ }
+ }
+
+ depends_on = [aws_s3_bucket_public_access_block.website, aws_s3_bucket.website]
+}
diff --git a/infrastructure/variables.tf b/infrastructure/variables.tf
new file mode 100644
index 0000000..224ada4
--- /dev/null
+++ b/infrastructure/variables.tf
@@ -0,0 +1,28 @@
+variable "aws_access_key" {
+ type = string
+ sensitive = true
+ description = "An AWS access key with permission to provision all relevant resources"
+}
+
+variable "aws_secret_key" {
+ type = string
+ sensitive = true
+ description = "The secret corresponding to the provided AWS access key"
+}
+
+variable "secret_s3_bucket_suffix" {
+ type = string
+ sensitive = true
+ description = "This string should be a long string of up to 54 random characters. It will be appended to the S3 bucket name to mitigate the risk of DDoS attacks."
+ nullable = false
+
+ validation {
+ condition = length(var.secret_s3_bucket_suffix) > 12
+ error_message = "This string should be at least 12 characters"
+ }
+
+ validation {
+ condition = length(var.secret_s3_bucket_suffix) <= 54
+ error_message = "This string should be no more than 54 characters long"
+ }
+}