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
191 lines
4.3 KiB
Terraform
191 lines
4.3 KiB
Terraform
# Vem får prata med vem.
|
|
#
|
|
# Utan nätverkspolicyer är ett Kubernetes-kluster platt: vilken pod som
|
|
# helst når databasen på 5432. Här stängs allt först, och bara de
|
|
# faktiska flödena öppnas — samma flöden som står i local.dataflode.
|
|
#
|
|
# Kräver en CNI som tillämpar NetworkPolicy (Cilium, Calico, Antrea).
|
|
# Med en CNI som inte gör det är de här reglerna dokumentation, inte
|
|
# skydd — kontrollera det innan ni litar på dem.
|
|
|
|
# Grundregel: ingen inkommande trafik alls i namnrymden.
|
|
resource "kubernetes_network_policy_v1" "neka_allt_in" {
|
|
metadata {
|
|
name = "neka-allt-inkommande"
|
|
namespace = kubernetes_namespace_v1.denna.metadata[0].name
|
|
labels = local.etiketter
|
|
}
|
|
|
|
spec {
|
|
pod_selector {}
|
|
policy_types = ["Ingress"]
|
|
}
|
|
}
|
|
|
|
# De tre webbtjänsterna: bara från ingress-kontrollern.
|
|
resource "kubernetes_network_policy_v1" "tjanster_in" {
|
|
for_each = toset(["web", "plattform", "ai-orkester"])
|
|
|
|
metadata {
|
|
name = "${each.key}-endast-fran-ingress"
|
|
namespace = kubernetes_namespace_v1.denna.metadata[0].name
|
|
labels = local.etiketter
|
|
}
|
|
|
|
spec {
|
|
pod_selector {
|
|
match_labels = { app = each.key }
|
|
}
|
|
|
|
policy_types = ["Ingress"]
|
|
|
|
ingress {
|
|
from {
|
|
# ALB når podarna direkt på deras IP (target-type: ip), så
|
|
# trafiken kommer från VPC:ns adressrymd och inte från en pod.
|
|
ip_block {
|
|
cidr = "0.0.0.0/0"
|
|
}
|
|
}
|
|
|
|
ports {
|
|
port = tostring(local.port_container)
|
|
protocol = "TCP"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
# Utgående: DNS åt alla, i övrigt bara det som faktiskt behövs.
|
|
resource "kubernetes_network_policy_v1" "dns_ut" {
|
|
metadata {
|
|
name = "dns-ut"
|
|
namespace = kubernetes_namespace_v1.denna.metadata[0].name
|
|
labels = local.etiketter
|
|
}
|
|
|
|
spec {
|
|
pod_selector {}
|
|
policy_types = ["Egress"]
|
|
|
|
egress {
|
|
ports {
|
|
port = "53"
|
|
protocol = "UDP"
|
|
}
|
|
|
|
ports {
|
|
port = "53"
|
|
protocol = "TCP"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
# Klienten är statisk och ringer ingenting från podden.
|
|
resource "kubernetes_network_policy_v1" "web_ut" {
|
|
metadata {
|
|
name = "web-inget-utgaende"
|
|
namespace = kubernetes_namespace_v1.denna.metadata[0].name
|
|
labels = local.etiketter
|
|
}
|
|
|
|
spec {
|
|
pod_selector {
|
|
match_labels = { app = "web" }
|
|
}
|
|
|
|
policy_types = ["Egress"]
|
|
# Ingen egress-regel = inget utgående (utöver DNS-policyn ovan).
|
|
}
|
|
}
|
|
|
|
# Plattformen: databasen internt, och HTTPS ut till kundernas
|
|
# leverantörer. Klustrets och molnets interna adresser är undantagna —
|
|
# samma gräns som koden själv upprätthåller (pekarInat), här en gång till
|
|
# på nätverksnivå. Två oberoende spärrar för samma sak.
|
|
resource "kubernetes_network_policy_v1" "plattform_ut" {
|
|
metadata {
|
|
name = "plattform-utgaende"
|
|
namespace = kubernetes_namespace_v1.denna.metadata[0].name
|
|
labels = local.etiketter
|
|
}
|
|
|
|
spec {
|
|
pod_selector {
|
|
match_labels = { app = "plattform" }
|
|
}
|
|
|
|
policy_types = ["Egress"]
|
|
|
|
# Aurora ligger utanför klustret, i ett eget subnätlager.
|
|
egress {
|
|
ports {
|
|
port = "5432"
|
|
protocol = "TCP"
|
|
}
|
|
}
|
|
|
|
egress {
|
|
to {
|
|
ip_block {
|
|
cidr = "0.0.0.0/0"
|
|
|
|
except = [
|
|
"10.0.0.0/8",
|
|
"172.16.0.0/12",
|
|
"192.168.0.0/16",
|
|
"169.254.0.0/16", # molnets metadatatjänst
|
|
"127.0.0.0/8",
|
|
"100.64.0.0/10",
|
|
]
|
|
}
|
|
}
|
|
|
|
ports {
|
|
port = "443"
|
|
protocol = "TCP"
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
# Orkestern: bara HTTPS ut till Claude.
|
|
resource "kubernetes_network_policy_v1" "orkester_ut" {
|
|
metadata {
|
|
name = "ai-orkester-utgaende"
|
|
namespace = kubernetes_namespace_v1.denna.metadata[0].name
|
|
labels = local.etiketter
|
|
}
|
|
|
|
spec {
|
|
pod_selector {
|
|
match_labels = { app = "ai-orkester" }
|
|
}
|
|
|
|
policy_types = ["Egress"]
|
|
|
|
egress {
|
|
to {
|
|
ip_block {
|
|
cidr = "0.0.0.0/0"
|
|
|
|
except = [
|
|
"10.0.0.0/8",
|
|
"172.16.0.0/12",
|
|
"192.168.0.0/16",
|
|
"169.254.0.0/16",
|
|
"127.0.0.0/8",
|
|
]
|
|
}
|
|
}
|
|
|
|
ports {
|
|
port = "443"
|
|
protocol = "TCP"
|
|
}
|
|
}
|
|
}
|
|
}
|