Files
Will Anderson 9330107fcc migrate Vault and Gitea to GKE Autopilot cluster
Implements Option A: move Vault (3x GCE e2-small) and Gitea (Legion k8s)
onto a new GKE Autopilot cluster (neuron-platform, us-central1) managed
through Legion Argo CD.

Terraform (servers/gcp/):
- gke.tf: GKE Autopilot cluster, Workload Identity bindings for Vault (KMS)
  and Gitea (Cloud SQL)
- cloud-sql.tf: gitea database + user on neuron-prod-pg15, gitea GCP SA,
  gitea-database-url and gitea-db-password Secret Manager secrets
- vault-nodes.tf: PENDING DECOMMISSION comment with migration checklist

k8s manifests (servers/gcp/k8s/):
- vault/: namespace.yaml
- gitea/: namespace, serviceaccount (Workload Identity annotation), pvc (50Gi
  standard-rwo), deployment (Gitea + Cloud SQL Auth Proxy sidecar), service,
  configmap (custom CSS carried from Legion), external-secrets (GCP SM provider)
- argocd-apps/: vault-gke.yaml, vault-helm-gke.yaml (Helm chart, HA Raft 3
  replicas, GCP KMS auto-unseal, topologySpread across zones, 10Gi premium-rwo),
  gitea-gke.yaml — all target GKE_CLUSTER_ENDPOINT placeholder

Legion (servers/legion/):
- apps/gke-apps.yaml: App-of-Apps entry point on Legion Argo CD that syncs the
  GKE Application manifests
- k8s/gitea-runner/Dockerfile: add system-level git insteadOf so GKE CI runners
  resolve Gitea in-cluster without Cloudflare Access headers
2026-05-04 20:40:48 -05:00

95 lines
3.8 KiB
Terraform

# ── GKE Autopilot Cluster — neuron-platform ───────────────────────────────────
#
# Regional Autopilot cluster in us-central1. Autopilot manages the node pool
# automatically — no node pools to configure, no VM sizes to choose.
# Pods spread across a/b/c zones automatically by GKE.
#
# Workload Identity is required so Vault pods can access KMS (auto-unseal)
# and Gitea pods can use Cloud SQL Auth Proxy without key files.
#
# After `terraform apply`, register the cluster with Legion Argo CD:
# 1. Get the endpoint: terraform output gke_cluster_endpoint
# 2. Get credentials: gcloud container clusters get-credentials neuron-platform \
# --region us-central1 --project neuron-785695
# 3. Register with Argo CD:
# argocd cluster add <context-name> --name gke-neuron-platform
# 4. Update servers/gcp/k8s/argocd-apps/*.yaml with the actual cluster endpoint.
resource "google_container_cluster" "neuron_platform" {
provider = google-beta
name = "neuron-platform"
location = "us-central1"
project = var.project_id
# Autopilot — GKE manages node pools, scaling, and node security
enable_autopilot = true
release_channel {
channel = "REGULAR"
}
# Workload Identity — required for Vault KMS and Gitea Cloud SQL proxy
workload_identity_config {
workload_pool = "${var.project_id}.svc.id.goog"
}
# Deletion protection — prevent accidental cluster destruction
deletion_protection = true
# Cluster networking — uses default VPC (same as Vault GCE nodes)
ip_allocation_policy {}
# Disable basic auth and client cert (Workload Identity handles auth)
master_auth {
client_certificate_config {
issue_client_certificate = false
}
}
}
# ── Workload Identity bindings ────────────────────────────────────────────────
#
# Each GCP SA gets a binding that allows its corresponding k8s ServiceAccount
# (in the GKE cluster) to impersonate it via Workload Identity.
# The k8s SA must also have the annotation:
# iam.gke.io/gcp-service-account: <gcp-sa-email>
# Vault pods (namespace: vault, k8s SA: vault) → vault-unseal GCP SA
# This lets the Vault StatefulSet access KMS for auto-unseal without key files.
resource "google_service_account_iam_member" "vault_workload_identity" {
service_account_id = google_service_account.vault_unseal.name
role = "roles/iam.workloadIdentityUser"
member = "serviceAccount:${var.project_id}.svc.id.goog[vault/vault]"
depends_on = [google_container_cluster.neuron_platform]
}
# Gitea pod (namespace: gitea, k8s SA: gitea) → gitea GCP SA
# This lets the Cloud SQL Auth Proxy sidecar connect to neuron-prod-pg15.
resource "google_service_account_iam_member" "gitea_workload_identity" {
service_account_id = google_service_account.gitea.name
role = "roles/iam.workloadIdentityUser"
member = "serviceAccount:${var.project_id}.svc.id.goog[gitea/gitea]"
depends_on = [google_container_cluster.neuron_platform]
}
# ── Outputs ───────────────────────────────────────────────────────────────────
output "gke_cluster_endpoint" {
description = "GKE cluster API endpoint — use as destination.server in Argo CD Applications"
value = "https://${google_container_cluster.neuron_platform.endpoint}"
sensitive = true
}
output "gke_cluster_name" {
description = "GKE cluster name"
value = google_container_cluster.neuron_platform.name
}
output "gke_workload_pool" {
description = "Workload Identity pool — used in Workload Identity bindings"
value = "${var.project_id}.svc.id.goog"
}