Files
alva/felsokning/infra/terraform/10-namnrymd.tf
T
Claude a5e276534c Arbetslasten kopplad till AWS — kedjan är helt egen
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
2026-08-04 12:53:08 +00:00

116 lines
3.2 KiB
Terraform

# Namnrymden, tjänstekontot och hemligheterna.
#
# Hemligheterna bor i AWS Secrets Manager, inte här. External Secrets
# speglar dem in i klustret så att podarna kan läsa dem som vanliga
# miljövariabler — men sanningskällan är Secrets Manager, med rotation,
# revisionsspår per läsning och kryptering med egen KMS-nyckel.
#
# Terraform ser aldrig värdena, och det är själva poängen: en hemlighet
# som passerar Terraform hamnar i tillståndsfilen.
resource "kubernetes_namespace_v1" "denna" {
metadata {
name = var.namnrymd
labels = local.etiketter
}
}
# Tjänstekontot plattformstjänsten kör som. IRSA-annoteringen binder det
# till rollen basen skapade — och rollen är i sin tur bunden till exakt
# det här kontot i den här namnrymden. Ingen annan pod kan anta den.
resource "kubernetes_service_account_v1" "plattform" {
metadata {
name = "plattform"
namespace = kubernetes_namespace_v1.denna.metadata[0].name
labels = local.etiketter
annotations = {
"eks.amazonaws.com/role-arn" = local.aws.plattform_roll
}
}
}
# Var hemligheterna hämtas ifrån. Autentiseringen sker med
# tjänstekontots roll — inga nycklar att distribuera.
resource "kubernetes_manifest" "hemlighetskalla" {
manifest = {
apiVersion = "external-secrets.io/v1beta1"
kind = "SecretStore"
metadata = {
name = "aws"
namespace = kubernetes_namespace_v1.denna.metadata[0].name
}
spec = {
provider = {
aws = {
service = "SecretsManager"
region = local.aws.region
auth = {
jwt = {
serviceAccountRef = {
name = kubernetes_service_account_v1.plattform.metadata[0].name
}
}
}
}
}
}
}
depends_on = [helm_release.external_secrets]
}
# Speglingen. Namnen till vänster är miljövariablerna tjänsterna läser;
# till höger nycklarna i Secrets Manager.
resource "kubernetes_manifest" "hemligheter" {
manifest = {
apiVersion = "external-secrets.io/v1beta1"
kind = "ExternalSecret"
metadata = {
name = "felsokning-hemligheter"
namespace = kubernetes_namespace_v1.denna.metadata[0].name
}
spec = {
# Roteras en hemlighet i Secrets Manager följer klustret efter inom
# en timme, utan att någon behöver göra något.
refreshInterval = "1h"
secretStoreRef = {
name = "aws"
kind = "SecretStore"
}
target = {
name = "felsokning-hemligheter"
creationPolicy = "Owner"
}
data = [
{
secretKey = "DATABASE_URL"
remoteRef = { key = local.aws.hemlighet_databas, property = "url" }
},
{
secretKey = "ANTHROPIC_API_KEY"
remoteRef = { key = local.aws.hemlighet_app, property = "anthropic_api_key" }
},
{
secretKey = "JWT_SECRET"
remoteRef = { key = local.aws.hemlighet_app, property = "jwt_secret" }
},
{
secretKey = "INTEGRATION_NYCKEL"
remoteRef = { key = local.aws.hemlighet_app, property = "integration_nyckel" }
},
]
}
}
depends_on = [kubernetes_manifest.hemlighetskalla]
}