Files
Claude b147694e31 Härdning av hela TÜV-revisionen
Tio av tolv fynd stängda, två reducerade med skälen utskrivna. Varje
stängning prövas av integrationssviten mot riktig Postgres — 144
kontroller, upp från 100.

T-1  Händelsenyckeln är nu (arende_id, id). Det finns inget delat
     namnrum kvar att ockupera, så attacken saknar yta i stället för att
     vara mildrad. Inom ett ärende bedöms en kollision på klientens
     avtryck — en hash av det kanoniserade innehåll klienten skickade,
     taget före serverns egna fält och före krypteringen, eftersom
     varken raden eller nyttolasten går att jämföra. Identiskt innehåll
     är fortfarande idempotent; samma id med annat innehåll ger 409 och
     skriver ingenting alls, inte heller resten av satsen. Samma attack
     en nivå upp — ärende-id är lika förutsägbart — stängs separat: ett
     id som ägs av en annan organisation ger 409 i stället för att tyst
     låta bli, vilket tidigare lämnade offrets ärende oskapat och varje
     senare synk svarande 404.

T-2  Mätdonet slås upp i registret. Beteckning och kalibrering HÄRLEDS
     därifrån och skriver över det klienten skickade. Okänt mätdon ger
     400. Utgånget avvisas inte — mätningen gjordes — men registrets
     datum följer med, så graderingen faller på registrets uppgift. Ett
     mätvärde utan mätdon får sina påstådda uppgifter borttagna.

T-3  Reducerad, inte stängd. Personnycklarna kuverteras under en
     huvudnyckel utanför databasen. En återställd dump ger nycklar som
     inte öppnas — verifierat genom att starta om utan huvudnyckeln. Vad
     som återstår står utskrivet: en backup tagen FÖRE en radering, plus
     huvudnyckeln, återställer fortfarande uppgifterna.

Därtill: gallringen verkställs nu av ett eget jobb och grupperar på det
blindade fordonsindexet så att en delad nyckel inte gallras för tidigt
(T-4) · AI-avlästa mätvärden bär härkomst (T-5) · bcrypt kostnad 12
(T-6) · åtkomstloggen och raderingsregistret är append-only, med en
smalare regel för personnycklarna som måste kunna förstöras (T-7) ·
åtkomstloggen dokumenterad (T-8) · ett externt regelpaket utan signatur
spärrar avslut (T-9) · CORS faller inte längre öppet (T-10) · exp krävs
i token (T-11) · react-router 7 (T-12, med den kvarvarande avvikelsen
motiverad).

Den motspelande hyresgästen som revisionen efterlyste finns nu som
testform och körs i CI.

---- Vad härdningen själv avslöjade -----------------------------------

Två av rättelserna var kortvarigt fel på samma sätt som fynden, och
bägge fångades bara av att jag försökte bevisa dem:

  T-7-testet var grönt mot en TOM tabell. En radnivåtrigger har inga
  rader att fyra på, så delete lyckades och kontrollen mätte ingenting.

  T-6-testet påstod anropet, inte kostnaden. toContain("gen_salt('bf')")
  hade accepterat kostnad 6 för evigt — och gjorde det, så länge det
  fanns.

Bägge är mönstret revisionen namngav: en kontroll som är riktig i sina
egna termer och oprövad vid sin gräns. Det gäller tester lika mycket som
kod.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012EQg3rJsrQ1ZNTvkzmQAtt
2026-08-06 13:05:16 +00:00
..
2026-08-04 12:39:42 +00:00
2026-08-04 12:39:42 +00:00
2026-08-04 12:39:42 +00:00

AWS-basen

Allt Guidad Felsökning behöver, i eget AWS-konto. Inget GitHub, inget GHCR, ingen extern byggtjänst.

Fil Vad
versions.tf Leverantörer och tillståndsbackend
variables.tf Allt som skiljer en installation från en annan
10-natverk.tf VPC, tre subnätlager, NAT, VPC-endpoints, flödesloggar
20-eks.tf Klustret, noderna, IRSA, tillägg
30-data.tf KMS, Aurora PostgreSQL, S3 för bilagor, ECR
40-hemligheter.tf Secrets Manager och rollerna som får läsa dem
50-doman-observation.tf Route 53, ACM, CloudWatch, larm
outputs.tf Kartan i klartext + värdena arbetslastlagret behöver

Två lager, i ordning

cd felsokning/infra/aws
terraform init && terraform apply        # basen
terraform output karta                   # hela AWS-sidan i klartext

cd ../terraform
terraform init && terraform apply        # arbetslasten i klustret

Anledningen till uppdelningen är inte smak. En enda apply som både skapar ett EKS-kluster och schemalägger in i det är en känd fälla: kubernetes-leverantören måste konfigureras med uppgifter som inte finns förrän klustret är skapat, och planen blir därför opålitlig.

Vad som gör det till "efter bästa praxis"

Nätet i tre lager. Publikt bara för lastbalanserare och NAT, privat för noderna, och ett eget datalager för Aurora utan routing ut alls. Att databasen inte kan nå internet är ett skydd som inte hänger på att en säkerhetsgrupp är rätt konfigurerad.

VPC-endpoints för S3, ECR, loggar, Secrets Manager, STS och ELB. Bilddragningar och loggar lämnar aldrig nätet — och NAT-notan sjunker rejält, eftersom bilddragningar annars dominerar den.

IRSA i stället för nodroller. Plattformens tjänstekonto har en egen roll bunden till exakt en namnrymd och ett tjänstekonto. Grannpodden på samma nod får ingenting på köpet. IMDSv2 obligatoriskt med hoppgräns 1 gör dessutom att en pod inte kan låna nodens roll via metadatatjänsten.

Delade rättigheter mellan bygge och drift. Byggrollen får publicera till ECR men inte röra klustret. Driftrollen får röra klustret men inte publicera bilder. Ett komprometterat bygge kan därför inte driftsätta.

Egna KMS-nycklar, tre stycken: en för hemligheter i etcd, en för databas och bilagor, en för loggar. Åtkomstkontroll och revisionsspår per nyckel, och möjligheten att rotera en utan att röra de andra.

Oföränderliga ECR-taggar. En tagg som pekat på ett bygge kan inte peka på ett annat, så "vilken kod kör i produktion" har ett entydigt svar.

Ingen DeleteObject för bilagorna. De hör till en append-only händelselogg, och tjänsten har ingen anledning att kunna radera dem.

Flödesloggar bara för avvisad trafik. Allt hade blivit dyrt brus; avvisningarna är det som säger något när en spärr slår till — eller borde ha gjort det.

Larm som betyder något. Fyra stycken, varav ett larmar på saknad data: uteblir mätvärdet för säkerhetskopiering finns ingen backup, och en backup man tror finns är värre än ingen.

Efter första apply

terraform output karta listar det under kvar_att_gora:

  1. Claude-nyckeln. Terraform ska inte känna den.
    aws secretsmanager put-secret-value \
      --secret-id felsokning-produktion/app \
      --secret-string "$(aws secretsmanager get-secret-value --secret-id felsokning-produktion/app \
         --query SecretString --output text | jq '.anthropic_api_key = "sk-ant-…"')"
    
  2. Schemat. Kör felsokning/infra/postgres-init.sql mot Aurora — det är den som lägger in append-only-triggarna. Samma fil som integrationstestet kör, så de kan inte glida isär.
  3. Begränsa tillatna_api_cidr. Standard är öppet mot internet eftersom klustret annars inte går att nå från en ny miljö. Snäva in till kontorets och runnernas adresser.
  4. Sätt larm_epost. Utan den går larmen ingenstans.

Kostnad — det som faktiskt kostar

NAT-gateways (en per zon), Auroras minsta kapacitet och EKS kontrollplan är golvet och löper oavsett last. nat_per_zon = false halverar nätkostnaden i test men gör en zon till enskild felkälla för all utgående trafik — fel avvägning i produktion.

Verifiering

terraform validate kunde inte köras i utvecklingsmiljön — registry.terraform.io är blockerad av sessionens egress-policy, så leverantörerna gick inte att hämta. Det som kontrollerats är terraform fmt och en statisk referenskontroll (88 resurser, 5 datakällor, inga dinglande referenser, alla count-resurser indexerade). Kör terraform init -backend=false && terraform validate innan första apply.