aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--infra/tf/.envrc.local.template4
-rw-r--r--infra/tf/.gitignore1
-rw-r--r--infra/tf/backups-bucket/main.tf43
-rw-r--r--infra/tf/backups-bucket/outputs.tf24
-rw-r--r--infra/tf/backups-bucket/terraform.tfvars27
-rw-r--r--infra/tf/backups-bucket/variables.tf73
-rw-r--r--infra/tf/backups.nix42
-rw-r--r--infra/tf/cloudflare-dns/main.tf24
-rw-r--r--infra/tf/cloudflare-dns/records.tf196
-rw-r--r--infra/tf/cloudflare-dns/terraform.tfvars3
-rw-r--r--infra/tf/cloudflare-dns/variables.tf11
-rw-r--r--infra/tf/dns.nix138
-rw-r--r--infra/tf/flake-module.nix17
13 files changed, 423 insertions, 180 deletions
diff --git a/infra/tf/.envrc.local.template b/infra/tf/.envrc.local.template
new file mode 100644
index 0000000..82ebf5f
--- /dev/null
+++ b/infra/tf/.envrc.local.template
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+# stored in 1password
+export CLOUDFLARE_API_TOKEN=...
diff --git a/infra/tf/.gitignore b/infra/tf/.gitignore
new file mode 100644
index 0000000..cff6f1b
--- /dev/null
+++ b/infra/tf/.gitignore
@@ -0,0 +1 @@
+/.envrc.local
diff --git a/infra/tf/backups-bucket/main.tf b/infra/tf/backups-bucket/main.tf
new file mode 100644
index 0000000..a86e582
--- /dev/null
+++ b/infra/tf/backups-bucket/main.tf
@@ -0,0 +1,43 @@
+terraform {
+ required_version = ">= 1.0"
+
+ required_providers {
+ google = {
+ source = "hashicorp/google"
+ version = ">= 5.0"
+ }
+ }
+
+ backend "gcs" {
+ bucket = "fcuny-infra-tofu-state"
+ prefix = "backups"
+ }
+}
+
+provider "google" {
+ project = var.project_id
+ region = var.region
+}
+
+resource "google_storage_bucket" "backups" {
+ name = var.bucket_name
+ location = var.location
+ uniform_bucket_level_access = var.uniform_bucket_level_access
+ force_destroy = var.force_destroy
+ public_access_prevention = var.public_access_prevention
+ storage_class = var.storage_class
+
+ # Optional: Add lifecycle rules for cost optimization
+ dynamic "lifecycle_rule" {
+ for_each = var.lifecycle_rules
+ content {
+ condition {
+ age = lifecycle_rule.value.age
+ }
+ action {
+ type = lifecycle_rule.value.action
+ storage_class = lifecycle_rule.value.storage_class
+ }
+ }
+ }
+}
diff --git a/infra/tf/backups-bucket/outputs.tf b/infra/tf/backups-bucket/outputs.tf
new file mode 100644
index 0000000..3ca60c5
--- /dev/null
+++ b/infra/tf/backups-bucket/outputs.tf
@@ -0,0 +1,24 @@
+output "bucket_name" {
+ description = "Name of the created backup bucket"
+ value = google_storage_bucket.backups.name
+}
+
+output "bucket_url" {
+ description = "URL of the backup bucket"
+ value = google_storage_bucket.backups.url
+}
+
+output "bucket_self_link" {
+ description = "Self-link of the backup bucket"
+ value = google_storage_bucket.backups.self_link
+}
+
+output "bucket_location" {
+ description = "Location of the backup bucket"
+ value = google_storage_bucket.backups.location
+}
+
+output "bucket_storage_class" {
+ description = "Storage class of the backup bucket"
+ value = google_storage_bucket.backups.storage_class
+}
diff --git a/infra/tf/backups-bucket/terraform.tfvars b/infra/tf/backups-bucket/terraform.tfvars
new file mode 100644
index 0000000..1d4f2c4
--- /dev/null
+++ b/infra/tf/backups-bucket/terraform.tfvars
@@ -0,0 +1,27 @@
+# GCP Configuration
+project_id = "fcuny-infra"
+region = "us-west1"
+
+# Bucket Configuration
+bucket_name = "fcuny-infra-backups"
+location = "us-west1"
+
+# Bucket Settings
+uniform_bucket_level_access = true
+force_destroy = true
+public_access_prevention = "enforced"
+storage_class = "NEARLINE"
+
+# Lifecycle Rules (optional customization)
+lifecycle_rules = [
+ {
+ age = 365 # After 1 year
+ action = "SetStorageClass"
+ storage_class = "COLDLINE"
+ },
+ {
+ age = 730 # After 2 years
+ action = "SetStorageClass"
+ storage_class = "ARCHIVE"
+ }
+]
diff --git a/infra/tf/backups-bucket/variables.tf b/infra/tf/backups-bucket/variables.tf
new file mode 100644
index 0000000..ffdc1a6
--- /dev/null
+++ b/infra/tf/backups-bucket/variables.tf
@@ -0,0 +1,73 @@
+variable "project_id" {
+ description = "GCP Project ID"
+ type = string
+ default = "fcuny-infra"
+}
+
+variable "region" {
+ description = "GCP Region"
+ type = string
+ default = "us-west1"
+}
+
+variable "bucket_name" {
+ description = "Name of the backup storage bucket"
+ type = string
+ default = "fcuny-infra-backups"
+}
+
+variable "location" {
+ description = "Location for the storage bucket"
+ type = string
+ default = "us-west1"
+}
+
+variable "uniform_bucket_level_access" {
+ description = "Enable uniform bucket-level access"
+ type = bool
+ default = true
+}
+
+variable "force_destroy" {
+ description = "Allow destruction of bucket even if it contains objects"
+ type = bool
+ default = true
+}
+
+variable "public_access_prevention" {
+ description = "Public access prevention setting"
+ type = string
+ default = "enforced"
+
+ validation {
+ condition = contains(["enforced", "inherited"], var.public_access_prevention)
+ error_message = "Public access prevention must be either 'enforced' or 'inherited'."
+ }
+}
+
+variable "storage_class" {
+ description = "Storage class for the bucket"
+ type = string
+ default = "NEARLINE"
+
+ validation {
+ condition = contains(["STANDARD", "NEARLINE", "COLDLINE", "ARCHIVE"], var.storage_class)
+ error_message = "Storage class must be one of: STANDARD, NEARLINE, COLDLINE, ARCHIVE."
+ }
+}
+
+variable "lifecycle_rules" {
+ description = "List of lifecycle rules for the bucket"
+ type = list(object({
+ age = number
+ action = string
+ storage_class = string
+ }))
+ default = [
+ {
+ age = 365
+ action = "SetStorageClass"
+ storage_class = "COLDLINE"
+ }
+ ]
+}
diff --git a/infra/tf/backups.nix b/infra/tf/backups.nix
deleted file mode 100644
index e76ed2e..0000000
--- a/infra/tf/backups.nix
+++ /dev/null
@@ -1,42 +0,0 @@
-{
- pkgs,
-}:
-pkgs.writeTextFile {
- name = "backups.tf.json";
- text = builtins.toJSON ([
- {
- terraform = {
- backend = {
- gcs = {
- bucket = "fcuny-infra-tofu-state";
- prefix = "backups";
- };
- };
- };
- }
- {
- provider = {
- google = [
- {
- project = "fcuny-infra";
- region = "us-west1";
- }
- ];
- };
- }
- {
- resource = {
- google_storage_bucket = {
- "backups" = {
- name = "fcuny-infra-backups";
- location = "us-west1";
- uniform_bucket_level_access = true;
- force_destroy = true;
- public_access_prevention = "enforced";
- storage_class = "NEARLINE";
- };
- };
- };
- }
- ]);
-}
diff --git a/infra/tf/cloudflare-dns/main.tf b/infra/tf/cloudflare-dns/main.tf
new file mode 100644
index 0000000..30442e6
--- /dev/null
+++ b/infra/tf/cloudflare-dns/main.tf
@@ -0,0 +1,24 @@
+terraform {
+ required_version = ">= 1.0"
+
+ required_providers {
+ cloudflare = {
+ source = "cloudflare/cloudflare"
+ version = "~> 5"
+ }
+ }
+
+ backend "gcs" {
+ bucket = "fcuny-infra-tofu-state"
+ prefix = "cloudflare-dns"
+ }
+}
+
+provider "cloudflare" {
+ # API token will be provided via CLOUDFLARE_API_TOKEN environment variable
+}
+
+# Use data source for existing zone instead of managing it
+data "cloudflare_zone" "main" {
+ zone_id = var.zone_id
+}
diff --git a/infra/tf/cloudflare-dns/records.tf b/infra/tf/cloudflare-dns/records.tf
new file mode 100644
index 0000000..b1543d1
--- /dev/null
+++ b/infra/tf/cloudflare-dns/records.tf
@@ -0,0 +1,196 @@
+resource "cloudflare_dns_record" "cname_root_0" {
+ content = "185.199.108.153"
+ name = "fcuny.net"
+ proxied = false
+ ttl = 1
+ type = "A"
+ zone_id = var.zone_id
+}
+
+resource "cloudflare_dns_record" "cname_root_1" {
+ content = "185.199.110.153"
+ name = "fcuny.net"
+ proxied = false
+ ttl = 1
+ type = "A"
+ zone_id = var.zone_id
+}
+
+resource "cloudflare_dns_record" "cname_root_2" {
+ content = "185.199.109.153"
+ name = "fcuny.net"
+ proxied = false
+ ttl = 1
+ type = "A"
+ zone_id = var.zone_id
+}
+
+resource "cloudflare_dns_record" "cname_root_3" {
+ content = "185.199.111.153"
+ name = "fcuny.net"
+ proxied = false
+ ttl = 1
+ type = "A"
+ zone_id = var.zone_id
+}
+
+resource "cloudflare_dns_record" "cname_dkim_0" {
+ content = "fm1.fcuny.net.dkim.fmhosted.com"
+ name = "fm1._domainkey"
+ proxied = false
+ ttl = 60
+ type = "CNAME"
+ zone_id = var.zone_id
+}
+
+resource "cloudflare_dns_record" "cname_dkim_1" {
+ content = "fm2.fcuny.net.dkim.fmhosted.com"
+ name = "fm2._domainkey"
+ proxied = false
+ ttl = 60
+ type = "CNAME"
+ zone_id = var.zone_id
+}
+
+resource "cloudflare_dns_record" "cname_dkim_2" {
+ content = "fm3.fcuny.net.dkim.fmhosted.com"
+ name = "fm3._domainkey"
+ proxied = false
+ ttl = 60
+ type = "CNAME"
+ zone_id = var.zone_id
+}
+
+resource "cloudflare_dns_record" "mx_0" {
+ content = "in1-smtp.messagingengine.com"
+ name = "fcuny.net"
+ priority = 10
+ proxied = false
+ ttl = 1
+ type = "MX"
+ zone_id = var.zone_id
+}
+
+resource "cloudflare_dns_record" "mx_1" {
+ content = "in2-smtp.messagingengine.com"
+ name = "fcuny.net"
+ priority = 20
+ proxied = false
+ ttl = 1
+ type = "MX"
+ zone_id = var.zone_id
+}
+
+resource "cloudflare_dns_record" "srv_caldavs" {
+ name = "_caldavs._tcp"
+ priority = 0
+ proxied = false
+ ttl = 1
+ type = "SRV"
+ zone_id = var.zone_id
+ data = {
+ port = 443
+ priority = 0
+ target = "caldav.fastmail.com"
+ weight = 1
+ }
+}
+
+resource "cloudflare_dns_record" "srv_caldav" {
+ name = "_caldav._tcp"
+ priority = 0
+ proxied = false
+ ttl = 1
+ type = "SRV"
+ zone_id = var.zone_id
+ data = {
+ port = 0
+ priority = 0
+ target = "."
+ weight = 0
+ }
+}
+
+resource "cloudflare_dns_record" "srv_carddavs" {
+ name = "_carddavs._tcp"
+ priority = 0
+ proxied = false
+ ttl = 1
+ type = "SRV"
+ zone_id = var.zone_id
+ data = {
+ port = 443
+ priority = 0
+ target = "carddav.fastmail.com"
+ weight = 1
+ }
+}
+
+resource "cloudflare_dns_record" "srv_carddav" {
+ name = "_carddav._tcp"
+ priority = 0
+ proxied = false
+ ttl = 1
+ type = "SRV"
+ zone_id = var.zone_id
+ data = {
+ port = 0
+ priority = 0
+ target = "."
+ weight = 0
+ }
+}
+
+resource "cloudflare_dns_record" "srv_imaps" {
+ name = "_imaps._tcp"
+ priority = 0
+ proxied = false
+ ttl = 1
+ type = "SRV"
+ zone_id = var.zone_id
+ data = {
+ port = 993
+ priority = 0
+ target = "imap.fastmail.com"
+ weight = 1
+ }
+}
+
+resource "cloudflare_dns_record" "srv_imap" {
+ name = "_imap._tcp"
+ priority = 0
+ proxied = false
+ ttl = 1
+ type = "SRV"
+ zone_id = var.zone_id
+ data = {
+ port = 0
+ priority = 0
+ target = "."
+ weight = 0
+ }
+}
+
+resource "cloudflare_dns_record" "srv_smtp" {
+ name = "_submission._tcp"
+ priority = 0
+ proxied = false
+ ttl = 1
+ type = "SRV"
+ zone_id = var.zone_id
+ data = {
+ port = 587
+ priority = 0
+ target = "smtp.fastmail.com"
+ weight = 1
+ }
+}
+
+resource "cloudflare_dns_record" "txt_spf" {
+ content = "\"v=spf1 include:spf.messagingengine.com ?all\""
+ name = "fcuny.net"
+ proxied = false
+ ttl = 1
+ type = "TXT"
+ zone_id = var.zone_id
+}
diff --git a/infra/tf/cloudflare-dns/terraform.tfvars b/infra/tf/cloudflare-dns/terraform.tfvars
new file mode 100644
index 0000000..5a88de4
--- /dev/null
+++ b/infra/tf/cloudflare-dns/terraform.tfvars
@@ -0,0 +1,3 @@
+# Zone Configuration
+zone_id = "6878e48b5cb81c7d789040632153719d"
+state_bucket = "fcuny-infra-tofu-state"
diff --git a/infra/tf/cloudflare-dns/variables.tf b/infra/tf/cloudflare-dns/variables.tf
new file mode 100644
index 0000000..24a4a35
--- /dev/null
+++ b/infra/tf/cloudflare-dns/variables.tf
@@ -0,0 +1,11 @@
+variable "zone_id" {
+ description = "Cloudflare zone ID"
+ type = string
+ default = "6878e48b5cb81c7d789040632153719d"
+}
+
+variable "state_bucket" {
+ description = "GCS bucket for Terraform state"
+ type = string
+ default = "fcuny-infra-tofu-state"
+}
diff --git a/infra/tf/dns.nix b/infra/tf/dns.nix
deleted file mode 100644
index df0ed65..0000000
--- a/infra/tf/dns.nix
+++ /dev/null
@@ -1,138 +0,0 @@
-{
- pkgs,
-}:
-let
- zoneId = "6878e48b5cb81c7d789040632153719d";
- zoneName = "fcuny.net";
-
- # Helper function to create DNS records with common fields
- mkRecord =
- type: name: content: extra:
- {
- inherit name type;
- zone_id = zoneId;
- ttl = 1;
- proxied = false;
- content = content;
- }
- // extra;
-
- # Helper for A records (typically proxied)
- mkARecord = name: ip: mkRecord "A" name ip { proxied = true; };
-
- # Helper for CNAME records
- mkCNAME = name: target: mkRecord "CNAME" name target { };
-
- # Helper for MX records
- mkMXRecord =
- priority: target:
- mkRecord "MX" zoneName target {
- inherit priority;
- };
-
- # Helper for SRV records with data block
- mkSRVRecord = name: port: target: weight: priority: {
- inherit name;
- type = "SRV";
- zone_id = zoneId;
- ttl = 1;
- proxied = false;
- priority = priority;
- data = {
- inherit
- port
- target
- weight
- priority
- ;
- };
- };
-
- # Helper for TXT records
- mkTXTRecord = name: content: mkRecord "TXT" name content { };
-
-in
-pkgs.writeTextFile {
- name = "cloudflare-dns.tf.json";
- text = builtins.toJSON ([
- {
- terraform = {
- required_providers = {
- cloudflare = {
- source = "cloudflare/cloudflare";
- version = "~> 4.0";
- };
- };
- backend = {
- gcs = {
- bucket = "fcuny-infra-tofu-state";
- prefix = "cloudflare-dns";
- };
- };
- };
- }
- {
- provider = {
- cloudflare = [ { } ];
- };
- }
- {
- # Use data source for existing zone instead of managing it
- data = {
- cloudflare_zone = {
- "main" = {
- name = zoneName;
- };
- };
- };
- }
- {
- resource = {
- cloudflare_record = {
- # A records for root domain
- "cname_root_0" = mkARecord zoneName "185.199.108.153";
- "cname_root_1" = mkARecord zoneName "185.199.110.153";
- "cname_root_2" = mkARecord zoneName "185.199.109.153";
- "cname_root_3" = mkARecord zoneName "185.199.111.153";
-
- # DKIM CNAME records
- "cname_dkim_0" = mkCNAME "fm1._domainkey" "fm1.fcuny.net.dkim.fmhosted.com" // {
- ttl = 60;
- };
- "cname_dkim_1" = mkCNAME "fm2._domainkey" "fm2.fcuny.net.dkim.fmhosted.com" // {
- ttl = 60;
- };
- "cname_dkim_2" = mkCNAME "fm3._domainkey" "fm3.fcuny.net.dkim.fmhosted.com" // {
- ttl = 60;
- };
-
- # Git subdomain via Cloudflare tunnel
- "cname_git" = mkCNAME "git" "b5d5071d-3c09-4379-9d6c-0684c478f151.cfargotunnel.com" // {
- proxied = true;
- };
-
- # MX records
- "mx_0" = mkMXRecord 10 "in1-smtp.messagingengine.com";
- "mx_1" = mkMXRecord 20 "in2-smtp.messagingengine.com";
-
- # SPF TXT record
- "txt_spf" = mkTXTRecord zoneName "v=spf1 include:spf.messagingengine.com ?all";
- };
- };
- }
- {
- resource = {
- cloudflare_record = {
- # SRV records for email services
- "srv_caldavs" = mkSRVRecord "_caldavs._tcp" 443 "caldav.fastmail.com" 1 0;
- "srv_caldav" = mkSRVRecord "_caldav._tcp" 0 "." 0 0;
- "srv_carddavs" = mkSRVRecord "_carddavs._tcp" 443 "carddav.fastmail.com" 1 0;
- "srv_carddav" = mkSRVRecord "_carddav._tcp" 0 "." 0 0;
- "srv_imaps" = mkSRVRecord "_imaps._tcp" 993 "imap.fastmail.com" 1 0;
- "srv_imap" = mkSRVRecord "_imap._tcp" 0 "." 0 0;
- "srv_smtp" = mkSRVRecord "_submission._tcp" 587 "smtp.fastmail.com" 1 0;
- };
- };
- }
- ]);
-}
diff --git a/infra/tf/flake-module.nix b/infra/tf/flake-module.nix
new file mode 100644
index 0000000..a386177
--- /dev/null
+++ b/infra/tf/flake-module.nix
@@ -0,0 +1,17 @@
+{
+ perSystem =
+ { pkgs, ... }:
+ {
+ devShells.terraform = pkgs.mkShellNoCC {
+ packages = with pkgs; [
+ google-cloud-sdk
+ (pkgs.opentofu.withPlugins (p: [
+ p.google
+ p.cloudflare
+ p.external
+ p.null
+ ]))
+ ];
+ };
+ };
+}