a5e276534c
Andra lagret läser nu basens utdata i stället för att upprepa någonting. 05-aws.tf hämtar klustret, domänen, certifikatet, registret, rollerna, hinken och hemligheternas namn — ändras något i basen slår det igenom här utan att en rad ändras. Autentiseringen mot klustret sker med en färsk EKS-token i stället för en kubeconfig på disk: inget att distribuera, inget som går ut. Hemligheterna bor i Secrets Manager och speglas in av External Secrets var timme. Terraform ser aldrig värdena, och det är själva poängen — en hemlighet som passerar Terraform hamnar i tillståndsfilen. Roteras en hemlighet följer klustret efter av sig självt. Plattformstjänsten kör som ett tjänstekonto med IRSA. Rollen är bunden till exakt det kontot i den namnrymden, så grannpodden på samma nod får ingenting på köpet. Bilagorna signeras mot S3 med samma roll — inga nycklar existerar att läcka. Postgres-podden är borta. Händelseloggen ligger i Aurora utanför klustret, i ett subnätlager utan routing ut, med automatisk säkerhetskopiering och PITR. Ingressen är en ALB med ACM-certifikat i stället för nginx, och alla tre tjänsterna delar den genom samma gruppnamn. Gitea med egna Actions-runners kör i samma kluster. Därmed finns inget externt beroende kvar: källkod, bygge, register och driftsättning ligger allt i vår AWS-miljö. Runnern och driftsättningen har varsitt tjänstekonto med skilda roller — bygget får publicera till ECR men inte röra klustret, driften tvärtom. Ett komprometterat bygge kan inte driftsätta. Gitea kör SQLite på en EBS-volym i stället för en egen Aurora-databas. En verkstadsinstallation har en handfull utvecklare, och ytterligare en databas att säkerhetskopiera och övervaka är kostnad utan motsvarande nytta. Verifierat: 96 vitest-tester, rotens lint och format:check, terraform fmt på båda lagren och statisk referenskontroll av vardera (88 respektive 35 resurser, inga dinglande referenser, alla count-resurser indexerade). terraform validate kunde inte köras här — registry.terraform.io är blockerad av sessionens egress-policy — så CI-jobbet kör den. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012EQg3rJsrQ1ZNTvkzmQAtt
190 lines
5.1 KiB
Terraform
190 lines
5.1 KiB
Terraform
# Självhostad git med egna byggrunners.
|
|
#
|
|
# Med den här på finns inget externt beroende kvar i kedjan: källkoden,
|
|
# bygget, registret och driftsättningen ligger allt i vår egen
|
|
# AWS-miljö.
|
|
#
|
|
# Gitea Actions använder samma workflow-syntax som GitHub Actions, så
|
|
# .gitea/workflows/felsokning.yml är samma fil som tidigare låg under
|
|
# .github — bara flyttad och pekad på egna runners.
|
|
#
|
|
# Runnern kör byggen. Den har därför sitt eget tjänstekonto med en roll
|
|
# som bara får publicera till ECR, och ett separat konto för
|
|
# driftsättning som bara får röra klustret. Ett komprometterat bygge kan
|
|
# inte driftsätta.
|
|
|
|
locals {
|
|
gitea_pa = var.gitea.aktiv
|
|
}
|
|
|
|
resource "kubernetes_namespace_v1" "gitea" {
|
|
count = local.gitea_pa ? 1 : 0
|
|
|
|
metadata {
|
|
name = "gitea"
|
|
labels = local.etiketter
|
|
}
|
|
}
|
|
|
|
# Byggkontot. Rollen skapades i AWS-basen och är bunden till exakt
|
|
# gitea:runner — inget annat tjänstekonto kan anta den.
|
|
resource "kubernetes_service_account_v1" "runner" {
|
|
count = local.gitea_pa ? 1 : 0
|
|
|
|
metadata {
|
|
name = "runner"
|
|
namespace = kubernetes_namespace_v1.gitea[0].metadata[0].name
|
|
labels = local.etiketter
|
|
|
|
annotations = {
|
|
"eks.amazonaws.com/role-arn" = data.terraform_remote_state.bas.outputs.karta.roller.bygg
|
|
}
|
|
}
|
|
}
|
|
|
|
resource "kubernetes_service_account_v1" "drift" {
|
|
count = local.gitea_pa ? 1 : 0
|
|
|
|
metadata {
|
|
name = "drift"
|
|
namespace = kubernetes_namespace_v1.gitea[0].metadata[0].name
|
|
labels = local.etiketter
|
|
|
|
annotations = {
|
|
"eks.amazonaws.com/role-arn" = data.terraform_remote_state.bas.outputs.karta.roller.drift
|
|
}
|
|
}
|
|
}
|
|
|
|
# Registreringstoken för runnern. Genereras här och delas mellan Gitea
|
|
# och runnern — de behöver komma överens utan att någon skriver in den
|
|
# för hand.
|
|
resource "random_password" "runner_token" {
|
|
count = local.gitea_pa ? 1 : 0
|
|
|
|
length = 40
|
|
special = false
|
|
}
|
|
|
|
resource "kubernetes_secret_v1" "runner" {
|
|
count = local.gitea_pa ? 1 : 0
|
|
|
|
metadata {
|
|
name = "runner-registrering"
|
|
namespace = kubernetes_namespace_v1.gitea[0].metadata[0].name
|
|
}
|
|
|
|
data = {
|
|
token = random_password.runner_token[0].result
|
|
}
|
|
}
|
|
|
|
resource "helm_release" "gitea" {
|
|
count = local.gitea_pa ? 1 : 0
|
|
|
|
name = "gitea"
|
|
repository = "https://dl.gitea.com/charts/"
|
|
chart = "gitea"
|
|
version = "10.6.0"
|
|
namespace = kubernetes_namespace_v1.gitea[0].metadata[0].name
|
|
|
|
# Gitea kan använda en extern databas. Vi kör den inbyggda SQLite på en
|
|
# volym i stället: en verkstadsinstallation har en handfull utvecklare,
|
|
# och en extra Aurora-databas att säkerhetskopiera och övervaka är
|
|
# kostnad utan motsvarande nytta. Volymen ligger på EBS med
|
|
# ögonblicksbilder.
|
|
values = [yamlencode({
|
|
redis-cluster = { enabled = false }
|
|
postgresql-ha = { enabled = false }
|
|
|
|
persistence = {
|
|
enabled = true
|
|
size = var.gitea.lagring
|
|
storageClass = "gp2"
|
|
}
|
|
|
|
gitea = {
|
|
config = {
|
|
server = {
|
|
DOMAIN = var.gitea.doman
|
|
ROOT_URL = "https://${var.gitea.doman}/"
|
|
SSH_DOMAIN = var.gitea.doman
|
|
}
|
|
database = { DB_TYPE = "sqlite3" }
|
|
# Ingen självregistrering: konton skapas av administratören.
|
|
service = {
|
|
DISABLE_REGISTRATION = true
|
|
REQUIRE_SIGNIN_VIEW = true
|
|
}
|
|
actions = { ENABLED = true }
|
|
# Bygg-loggarna hamnar i CloudWatch via containerloggarna.
|
|
log = { LEVEL = "Info" }
|
|
}
|
|
}
|
|
|
|
# Runnern kör i samma namnrymd med byggkontot.
|
|
actions = {
|
|
enabled = true
|
|
statefulset = {
|
|
replicas = var.gitea.runners
|
|
serviceAccountName = kubernetes_service_account_v1.runner[0].metadata[0].name
|
|
}
|
|
existingSecret = kubernetes_secret_v1.runner[0].metadata[0].name
|
|
existingSecretKey = "token"
|
|
}
|
|
})]
|
|
|
|
depends_on = [
|
|
helm_release.external_secrets,
|
|
kubernetes_secret_v1.runner,
|
|
]
|
|
}
|
|
|
|
# Gitea får sin egen väg in genom samma lastbalanserare — grupp-namnet
|
|
# är detsamma, så ALB:n delas i stället för att en till skapas.
|
|
resource "kubernetes_ingress_v1" "gitea" {
|
|
count = local.gitea_pa && var.gitea.doman != "" ? 1 : 0
|
|
|
|
metadata {
|
|
name = "gitea"
|
|
namespace = kubernetes_namespace_v1.gitea[0].metadata[0].name
|
|
labels = local.etiketter
|
|
|
|
annotations = {
|
|
"alb.ingress.kubernetes.io/scheme" = "internet-facing"
|
|
"alb.ingress.kubernetes.io/target-type" = "ip"
|
|
"alb.ingress.kubernetes.io/certificate-arn" = local.aws.certifikat_arn
|
|
"alb.ingress.kubernetes.io/listen-ports" = jsonencode([{ HTTP = 80 }, { HTTPS = 443 }])
|
|
"alb.ingress.kubernetes.io/ssl-redirect" = "443"
|
|
"alb.ingress.kubernetes.io/group.name" = local.namn
|
|
}
|
|
}
|
|
|
|
spec {
|
|
ingress_class_name = "alb"
|
|
|
|
rule {
|
|
host = var.gitea.doman
|
|
|
|
http {
|
|
path {
|
|
path = "/"
|
|
path_type = "Prefix"
|
|
|
|
backend {
|
|
service {
|
|
name = "gitea-http"
|
|
|
|
port {
|
|
number = 3000
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
depends_on = [helm_release.gitea]
|
|
}
|