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
116 lines
3.2 KiB
Terraform
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]
|
|
}
|