From 4afffdafebc6768997ab825d7520775a4463c0b8 Mon Sep 17 00:00:00 2001 From: xnoto Date: Sun, 21 Dec 2025 00:03:23 -0700 Subject: [PATCH 1/2] feat: Initial commit --- .checkov.yml | 13 ++++++ .github/workflows/opentofu.yml | 19 +++++++++ .gitignore | 16 +++++++ .pre-commit-config.yaml | 36 ++++++++++++++++ .sops.yaml | 3 ++ .terraform-docs.yml | 18 ++++++++ .tflint.hcl | 12 ++++++ Makefile | 78 ++++++++++++++++++++++++++++++++++ cf-dns.tf | 43 +++++++++++++++++++ main.tf | 12 ++++++ outputs.tf | 4 ++ providers.tf | 20 +++++++++ secrets/secrets.yaml | 28 ++++++++++++ 13 files changed, 302 insertions(+) create mode 100644 .checkov.yml create mode 100644 .github/workflows/opentofu.yml create mode 100644 .gitignore create mode 100644 .pre-commit-config.yaml create mode 100644 .sops.yaml create mode 100644 .terraform-docs.yml create mode 100644 .tflint.hcl create mode 100644 Makefile create mode 100644 cf-dns.tf create mode 100644 main.tf create mode 100644 outputs.tf create mode 100644 providers.tf create mode 100644 secrets/secrets.yaml diff --git a/.checkov.yml b/.checkov.yml new file mode 100644 index 0000000..02f3a56 --- /dev/null +++ b/.checkov.yml @@ -0,0 +1,13 @@ +block-list-secret-scan: [] +compact: true +directory: + - . +download-external-modules: false +evaluate-variables: true +framework: + - all +output: + - cli +quiet: true +soft-fail: true +summary-position: top diff --git a/.github/workflows/opentofu.yml b/.github/workflows/opentofu.yml new file mode 100644 index 0000000..80a5bfa --- /dev/null +++ b/.github/workflows/opentofu.yml @@ -0,0 +1,19 @@ +name: OpenTofu + +on: + pull_request: + branches: + - main + push: + branches: + - main + +permissions: + contents: read + pull-requests: write + +jobs: + opentofu: + uses: makeitworkcloud/shared-workflows/.github/workflows/opentofu.yml@main + secrets: + SOPS_AGE_KEY: ${{ secrets.SOPS_AGE_KEY }} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..45a1d23 --- /dev/null +++ b/.gitignore @@ -0,0 +1,16 @@ +# vim swap files +**/*.sw[po] + +# don't commit terraform state or lock. the repo code is the only state we care about. +# the provider state cache is auto-upgraded by default to ensure compatibility with upstream cloud provider APIs +**/.terraform.lock.hcl +**/.terraform + +# IDE Folders +**/.vscode + +# Mac Finder cache +**/.DS_Store + +# Plan output +plan-output.txt diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..f3e633f --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,36 @@ +repos: +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v6.0.0 + hooks: + - id: check-case-conflict + - id: check-merge-conflict + - id: check-symlinks + - id: check-vcs-permalinks + - id: destroyed-symlinks + - id: detect-private-key + - id: mixed-line-ending + - id: trailing-whitespace +- repo: https://github.com/antonbabenko/pre-commit-terraform + rev: v1.104.0 + hooks: + - id: terraform_validate + args: + - --hook-config=--retry-once-with-cleanup=true + - --args=-no-color + - --tf-init-args=-reconfigure + - --tf-init-args=-upgrade + - id: terraform_tflint + args: + - --args=--minimum-failure-severity=error + - --args=--config=__GIT_WORKING_DIR__/.tflint.hcl + - id: terraform_checkov + args: + - --args=--config-file __GIT_WORKING_DIR__/.checkov.yml + - id: terraform_fmt + args: + - --args=-no-color + - --args=-diff + - --args=-recursive + - id: terraform_docs + args: + - --args=--config=.terraform-docs.yml diff --git a/.sops.yaml b/.sops.yaml new file mode 100644 index 0000000..8967c45 --- /dev/null +++ b/.sops.yaml @@ -0,0 +1,3 @@ +--- +creation_rules: + - age: age152ek83tm4fj5u70r3fecytn4kg7c5xca24erjchxexx4pfqg6das7q763l diff --git a/.terraform-docs.yml b/.terraform-docs.yml new file mode 100644 index 0000000..2cbbb30 --- /dev/null +++ b/.terraform-docs.yml @@ -0,0 +1,18 @@ +formatter: "markdown" + +output: + file: "README.md" + mode: replace + +settings: + color: false + lockfile: false + +sort: + enabled: true + by: name + +# recursive can't be enabled until this bug is fixed: +# https://github.com/terraform-docs/terraform-docs/issues/654 +recursive: + enabled: false diff --git a/.tflint.hcl b/.tflint.hcl new file mode 100644 index 0000000..062eb57 --- /dev/null +++ b/.tflint.hcl @@ -0,0 +1,12 @@ +plugin "terraform" { + enabled = true + preset = "recommended" +} + +rule "terraform_required_providers" { + enabled = false +} + +rule "terraform_required_version" { + enabled = false +} diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..08a54ca --- /dev/null +++ b/Makefile @@ -0,0 +1,78 @@ +SHELL := /bin/bash +TERRAFORM := $(shell which tofu) +S3_REGION := $(shell sops decrypt secrets/secrets.yaml | grep ^s3_region | cut -d ' ' -f 2) +S3_BUCKET := $(shell sops decrypt secrets/secrets.yaml | grep ^s3_bucket | cut -d ' ' -f 2) +S3_KEY := $(shell sops decrypt secrets/secrets.yaml | grep ^s3_key | cut -d ' ' -f 2) +S3_ACCESS_KEY := $(shell sops decrypt secrets/secrets.yaml | grep ^s3_access_key | cut -d ' ' -f 2) +S3_SECRET_KEY := $(shell sops decrypt secrets/secrets.yaml | grep ^s3_secret_key | cut -d ' ' -f 2) + +.PHONY: help init plan apply migrate test pre-commit-check-deps pre-commit-install-hooks argcd-login + +help: + @echo "General targets" + @echo "----------------" + @echo + @echo "\thelp: show this help text" + @echo "\tclean: removes all .terraform directories" + @echo + @echo "Terraform targets" + @echo "-----------------" + @echo + @echo "\tinit: run 'terraform init'" + @echo "\ttest: run pre-commmit checks" + @echo "\tplan: run 'terraform plan'" + @echo "\tapply: run 'terraform apply'" + @echo "\tmigrate; run terraform init -migrate-state" + @echo + @echo "One-time repo init targets" + @echo "--------------------------" + @echo + @echo "\tpre-commit-install-hooks: install pre-commit hooks" + @echo "\tpre-commit-check-deps: check pre-commit dependencies" + @echo + +clean: + @find . -name .terraform -type d | xargs -I {} rm -rf {} + +init: clean .terraform/terraform.tfstate + +.terraform/terraform.tfstate: + @${TERRAFORM} init -reconfigure -upgrade -input=false -backend-config="key=${S3_KEY}" -backend-config="bucket=${S3_BUCKET}" -backend-config="region=${S3_REGION}" -backend-config="access_key=${S3_ACCESS_KEY}" -backend-config="secret_key=${S3_SECRET_KEY}" + +plan: init .terraform/plan + +.terraform/plan: + @${TERRAFORM} plan -compact-warnings -no-color -out tfplan.bin + @${TERRAFORM} show -no-color tfplan.bin | tee plan-output.txt + @rm -f tfplan.bin + +apply: init .terraform/apply + +.terraform/apply: + @${TERRAFORM} apply -auto-approve -compact-warnings + +migrate: + @echo "First use -make init- using the old S3 backend, then run -make migrate- to use the new one." + @${TERRAFORM} init -migrate-state -backend-config="key=${S3_KEY}" -backend-config="bucket=${S3_BUCKET}" -backend-config="region=${S3_REGION}" -backend-config="access_key=${S3_ACCESS_KEY}" -backend-config="secret_key=${S3_SECRET_KEY}" + +test: .git/hooks/pre-commit + @pre-commit run -a + +DEPS_PRE_COMMIT=$(shell which pre-commit || echo "pre-commit not found") +DEPS_TERRAFORM_DOCS=$(shell which terraform-docs || echo "terraform-docs not found") +DEPS_TFLINT=$(shell which tflint || echo "tflint not found,") +DEPS_CHECKOV=$(shell which checkov || echo "checkov not found,") +DEPS_JQ=$(shell which jq || echo "jq not found,") +pre-commit-check-deps: + @echo "Checking for pre-commit and its dependencies:" + @echo " pre-commit: ${DEPS_PRE_COMMIT}" + @echo " terraform-docs: ${DEPS_TERRAFORM_DOCS}" + @echo " tflint: ${DEPS_TFLINT}" + @echo " checkov: ${DEPS_CHECKOV}" + @echo " jq: ${DEPS_JQ}" + @echo "" + +pre-commit-install-hooks: .git/hooks/pre-commit + +.git/hooks/pre-commit: pre-commit-check-deps + @pre-commit install --install-hooks diff --git a/cf-dns.tf b/cf-dns.tf new file mode 100644 index 0000000..e7c637e --- /dev/null +++ b/cf-dns.tf @@ -0,0 +1,43 @@ +resource "cloudflare_dns_record" "root" { + zone_id = local.zone_id + type = "CNAME" + name = "@" + content = "makeitwork.cloud.s3-website.us-west-2.amazonaws.com" + proxied = true + ttl = 1 +} + +resource "cloudflare_dns_record" "www" { + zone_id = local.zone_id + type = "CNAME" + name = "www" + content = "makeitwork.cloud.s3-website.us-west-2.amazonaws.com" + proxied = true + ttl = 1 +} + +resource "cloudflare_dns_record" "mx_primary" { + zone_id = local.zone_id + type = "MX" + name = "@" + content = "mx1.privateemail.com" + priority = 10 + ttl = 1 +} + +resource "cloudflare_dns_record" "mx_secondary" { + zone_id = local.zone_id + type = "MX" + name = "@" + content = "mx2.privateemail.com" + priority = 20 + ttl = 1 +} + +resource "cloudflare_dns_record" "spf" { + zone_id = local.zone_id + type = "TXT" + name = "@" + content = "v=spf1 include:spf.privateemail.com ~all" + ttl = 1 +} diff --git a/main.tf b/main.tf new file mode 100644 index 0000000..6c7f701 --- /dev/null +++ b/main.tf @@ -0,0 +1,12 @@ +data "sops_file" "secret_vars" { + source_file = "${path.module}/secrets/secrets.yaml" +} + +locals { + account_id = data.sops_file.secret_vars.data["cloudflare_account_id"] + zone_id = data.sops_file.secret_vars.data["cloudflare_zone_id"] +} + +data "cloudflare_zone" "makeitwork_cloud" { + zone_id = local.zone_id +} diff --git a/outputs.tf b/outputs.tf new file mode 100644 index 0000000..5aef4d1 --- /dev/null +++ b/outputs.tf @@ -0,0 +1,4 @@ +output "zone_name" { + description = "Cloudflare zone name" + value = data.cloudflare_zone.makeitwork_cloud.name +} diff --git a/providers.tf b/providers.tf new file mode 100644 index 0000000..055d938 --- /dev/null +++ b/providers.tf @@ -0,0 +1,20 @@ +terraform { + required_version = "> 1.3" + + backend "s3" {} + + required_providers { + sops = { + source = "carlpett/sops" + } + cloudflare = { + source = "cloudflare/cloudflare" + } + } +} + +provider "sops" {} + +provider "cloudflare" { + api_token = data.sops_file.secret_vars.data["cloudflare_api_token"] +} diff --git a/secrets/secrets.yaml b/secrets/secrets.yaml new file mode 100644 index 0000000..f55bbe3 --- /dev/null +++ b/secrets/secrets.yaml @@ -0,0 +1,28 @@ +s3_bucket: ENC[AES256_GCM,data:jT+AwsE2YfnumTBuPPqQTVFn/juML+Bi,iv:Sxw+IxDdWfczBQwJhbhCSTCf/Jt8jmSvacYu1WW3qWM=,tag:iJ1W6X2fLsdItrMzX7v1ew==,type:str] +s3_key: ENC[AES256_GCM,data:Izipy2MdiTx5XAnosdmFHWI=,iv:9zuSRx381hnc+bKMjGTNPU+LG4UHTC++OvcAAJugNk0=,tag:GSt3l+7l8d0TLRXoMpmLMA==,type:str] +s3_region: ENC[AES256_GCM,data:k3DctzAskFYM,iv:IhoNmH219ySrM5U39I2W4008abAWpQaajpX/CsxyunA=,tag:XiUr2Tq3sSfFHRKONOv7yw==,type:str] +s3_access_key: ENC[AES256_GCM,data:wQsT7hUqlcOk/MNk5GGlXlPFC84=,iv:UMqn1nVjreRVWu1RKFMksJlz7v9azhYG5oK+1hN9+cw=,tag:t3j1grOOnATBSbOD2vFNKg==,type:str] +s3_secret_key: ENC[AES256_GCM,data:SAv6odIom2BPEYUtHrzdxvRaQcB3Ik1oZi8tO8edrJ/mcH8dRqSJRA==,iv:GJJ9QZXcnFpIHxs/d4UbI6iFl0gMThSZbqXclgLNXpY=,tag:irM0u94+MdoRxNKfEpSugQ==,type:str] +cloudflare_api_token: ENC[AES256_GCM,data:xSrXTp/uRw2NPk5vXutq8AsKzzyn3Y/hpW5K8Jik5kiTCfenZRHKtw==,iv:4ozBTzr1zK50AQ4ropFg7KVHA26AzqOPIaFcAQ+leyA=,tag:gKWOk4CQh7AfF2hFkeE23w==,type:str] +cloudflare_account_id: ENC[AES256_GCM,data:fVKqNcHAnRtsq+D3EkcIISq4JBJFCsekUsURBsTqiZY=,iv:L0NKM/qyvt/SSeKzKpfNqSkmanfCExa9vH/NVHSamco=,tag:n0chWSay6kPF8MPgJsgNSg==,type:str] +cloudflare_zone_id: ENC[AES256_GCM,data:D4ZmzpT9DOy/IA7Mbqh1Vq3ZQvRlggk9rq9HPjyZOw0=,iv:ieYg2a48Zl/hnEwWuvn1Oni+rtG70NB9ZicOAOpKPXA=,tag:5kq2Sw3ehBs24uOBUF1Xlg==,type:str] +tunnel_id_ansible: ENC[AES256_GCM,data:GBgP6RsNDOL/Bapfq4OIpmlOzWAvAwGbdNgi/JSc5lt0BoKI,iv:fl7pP9yFaBeubDpGICqbJGeZkbSQ//qOX/taeDoODQ4=,tag:U4V5Y7/X0qbM0TNUEcFCWg==,type:str] +tunnel_id_bastion: ENC[AES256_GCM,data:4AWDkLEL7MVsYuo38u3s1dmpoFbb8rK2CFqjU8Mi2PHEbOwl,iv:Vh97dhliB3nei2dwHZxM2wCNyb2xsdkivI6w7rRIJHY=,tag:0B73hIJYKC0G59XBacFZag==,type:str] +tunnel_id_grafana: ENC[AES256_GCM,data:HX8kcSJLji10jiPxACXi5m1W0v6pqtXz9FvyG9sJ8WW4gnyB,iv:7zIfOAlKR9EkPL2L7LY/HidxxuJurLQqYxUpe6GnDms=,tag:vrl/c2OXwkAbs9e+uAqMvg==,type:str] +tunnel_id_plex: ENC[AES256_GCM,data:NO5SUtyjI1FhG6PSLkh8Dba3eAI/b5h1469OPzGCs3w8ur5Q,iv:qcVNn0/7Sk+6YCDxHwbrl5e1Iv3wWKA3fec7dVY1FrA=,tag:3cGgRx+sFNZv7y4rrvX3IQ==,type:str] +tunnel_id_statping_ng: ENC[AES256_GCM,data:XLU8NI0TMjfvPH9XwGyGUKI/d4BbZzyQ86JOmF5Ai5Ta10ME,iv:kR45l89zUDsWkEc2VrippH7KUO/RIZjiksvoHGz3djA=,tag:cvwON6c+qjMJCfWQgdb/vw==,type:str] +sops: + age: + - recipient: age152ek83tm4fj5u70r3fecytn4kg7c5xca24erjchxexx4pfqg6das7q763l + enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB4K2ZkQWNxMDNUc1A2SDhO + cEM1amR3RUN1Z0kyelZSTVlGS3RPbXRiWTFVCkw4QURFTkk5QTlOZlRORktTVHlN + MWw5QlJSOVcwdXFyanltU0dlcGllVm8KLS0tIEVpZ3BmQW1hcUVqK0U4Qkp6akp6 + enZWZVdMQ0s1T3NHRUdvaUhZNTVVYlkKimr9myWIePyJOw3yCpcO3l4WivlBUUoz + BpvuAdvjA31LIjhnxSzVTLTTNOsPWFjGGtF6hGbzgZKNOHTEoOWv6g== + -----END AGE ENCRYPTED FILE----- + lastmodified: "2025-12-21T06:56:17Z" + mac: ENC[AES256_GCM,data:MxSYDacnrooivtTB7U8jQ8LugKqdkVgKLvvo89uSUwafykIzEdgqeo+lQBhOpsOOIY894Lmr1oe6F+AQ4O/JT9tSNCtWjMYezusUwo3v/UNL1lf0IsaTxWjiQrgeBQdkeQH0Hnlr/npjePvUzSgfFjPlr80VUGLUWkegtrmdS+M=,iv:0EZMNKTKjhl7Dly5D91OAmuA+hSAeCZRfCPsh6a2pMM=,tag:GBqB73rWSrVNka5VVzTqYw==,type:str] + unencrypted_suffix: _unencrypted + version: 3.10.2 From 18fe2d1529e50b57e11e92e8616f6b14949e9506 Mon Sep 17 00:00:00 2001 From: xnoto Date: Sun, 21 Dec 2025 00:05:44 -0700 Subject: [PATCH 2/2] fix: cleanup tunnels --- outputs.tf | 4 ---- secrets/secrets.yaml | 9 ++------- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/outputs.tf b/outputs.tf index 5aef4d1..e69de29 100644 --- a/outputs.tf +++ b/outputs.tf @@ -1,4 +0,0 @@ -output "zone_name" { - description = "Cloudflare zone name" - value = data.cloudflare_zone.makeitwork_cloud.name -} diff --git a/secrets/secrets.yaml b/secrets/secrets.yaml index f55bbe3..3312630 100644 --- a/secrets/secrets.yaml +++ b/secrets/secrets.yaml @@ -6,11 +6,6 @@ s3_secret_key: ENC[AES256_GCM,data:SAv6odIom2BPEYUtHrzdxvRaQcB3Ik1oZi8tO8edrJ/mc cloudflare_api_token: ENC[AES256_GCM,data:xSrXTp/uRw2NPk5vXutq8AsKzzyn3Y/hpW5K8Jik5kiTCfenZRHKtw==,iv:4ozBTzr1zK50AQ4ropFg7KVHA26AzqOPIaFcAQ+leyA=,tag:gKWOk4CQh7AfF2hFkeE23w==,type:str] cloudflare_account_id: ENC[AES256_GCM,data:fVKqNcHAnRtsq+D3EkcIISq4JBJFCsekUsURBsTqiZY=,iv:L0NKM/qyvt/SSeKzKpfNqSkmanfCExa9vH/NVHSamco=,tag:n0chWSay6kPF8MPgJsgNSg==,type:str] cloudflare_zone_id: ENC[AES256_GCM,data:D4ZmzpT9DOy/IA7Mbqh1Vq3ZQvRlggk9rq9HPjyZOw0=,iv:ieYg2a48Zl/hnEwWuvn1Oni+rtG70NB9ZicOAOpKPXA=,tag:5kq2Sw3ehBs24uOBUF1Xlg==,type:str] -tunnel_id_ansible: ENC[AES256_GCM,data:GBgP6RsNDOL/Bapfq4OIpmlOzWAvAwGbdNgi/JSc5lt0BoKI,iv:fl7pP9yFaBeubDpGICqbJGeZkbSQ//qOX/taeDoODQ4=,tag:U4V5Y7/X0qbM0TNUEcFCWg==,type:str] -tunnel_id_bastion: ENC[AES256_GCM,data:4AWDkLEL7MVsYuo38u3s1dmpoFbb8rK2CFqjU8Mi2PHEbOwl,iv:Vh97dhliB3nei2dwHZxM2wCNyb2xsdkivI6w7rRIJHY=,tag:0B73hIJYKC0G59XBacFZag==,type:str] -tunnel_id_grafana: ENC[AES256_GCM,data:HX8kcSJLji10jiPxACXi5m1W0v6pqtXz9FvyG9sJ8WW4gnyB,iv:7zIfOAlKR9EkPL2L7LY/HidxxuJurLQqYxUpe6GnDms=,tag:vrl/c2OXwkAbs9e+uAqMvg==,type:str] -tunnel_id_plex: ENC[AES256_GCM,data:NO5SUtyjI1FhG6PSLkh8Dba3eAI/b5h1469OPzGCs3w8ur5Q,iv:qcVNn0/7Sk+6YCDxHwbrl5e1Iv3wWKA3fec7dVY1FrA=,tag:3cGgRx+sFNZv7y4rrvX3IQ==,type:str] -tunnel_id_statping_ng: ENC[AES256_GCM,data:XLU8NI0TMjfvPH9XwGyGUKI/d4BbZzyQ86JOmF5Ai5Ta10ME,iv:kR45l89zUDsWkEc2VrippH7KUO/RIZjiksvoHGz3djA=,tag:cvwON6c+qjMJCfWQgdb/vw==,type:str] sops: age: - recipient: age152ek83tm4fj5u70r3fecytn4kg7c5xca24erjchxexx4pfqg6das7q763l @@ -22,7 +17,7 @@ sops: enZWZVdMQ0s1T3NHRUdvaUhZNTVVYlkKimr9myWIePyJOw3yCpcO3l4WivlBUUoz BpvuAdvjA31LIjhnxSzVTLTTNOsPWFjGGtF6hGbzgZKNOHTEoOWv6g== -----END AGE ENCRYPTED FILE----- - lastmodified: "2025-12-21T06:56:17Z" - mac: ENC[AES256_GCM,data:MxSYDacnrooivtTB7U8jQ8LugKqdkVgKLvvo89uSUwafykIzEdgqeo+lQBhOpsOOIY894Lmr1oe6F+AQ4O/JT9tSNCtWjMYezusUwo3v/UNL1lf0IsaTxWjiQrgeBQdkeQH0Hnlr/npjePvUzSgfFjPlr80VUGLUWkegtrmdS+M=,iv:0EZMNKTKjhl7Dly5D91OAmuA+hSAeCZRfCPsh6a2pMM=,tag:GBqB73rWSrVNka5VVzTqYw==,type:str] + lastmodified: "2025-12-21T07:05:35Z" + mac: ENC[AES256_GCM,data:YoRZKokhD2W9xQhLpGW0Apdqk4+dbd/UUGN2pcItchkH5GSrpG2zXk4NWJqlVX99VLuC+5avqGjk5YLjWKVhDmFvg/gcakO7eok1p5czA1zyoVEpiODvAQtvQAWCEiurlbWYuR4kvl7c19LxkC2NLfR+AVQLgp9HmKtFjgAEn+U=,iv:WJBQ1jLBCY06qjyhYOnEALuDJ9DVCqvDzIZz42UNIbc=,tag:FHgnXKhueIriyN6mFrOPZQ==,type:str] unencrypted_suffix: _unencrypted version: 3.10.2