A 07-01 vam deixar una suposició sense garantia: que el token de l'Ana, les capçaleres X-Usuari-* que posa el gateway i les crides de servei-comandes a Catàleg i Clients viatgen per una xarxa en què ningú no escolta ni suplanta. Un JWT ben verificat no serveix de res si algú el pot llegir en trànsit i reutilitzar-lo, i una capçalera X-Usuari-Rols: admin només és fiable si ningú més que el gateway pot arribar al servei. Aquesta lliçó assegura el canal: quines amenaces hi ha en trànsit, com es xifra la vora amb TLS i certificats de Let's Encrypt gestionats per cert-manager, quines opcions hi ha per xifrar i autenticar la comunicació entre serveis (mTLS per aplicació o per service mesh, tancant el que 05-05 va deixar només esmentat), com es protegeixen RabbitMQ i les bases de dades, i com es garanteix la integritat dels webhooks de la passarel·la de pagament. Acaba amb la decisió de TechCorp per a la seva primera fase i una taula que resumeix, canal a canal, amenaça, mesura i on es configura. La verificació de tokens és de 07-01, la validació d'entrada de 07-03 i els secrets, RBAC i NetworkPolicies en detall de 07-04.

Avís. Els certificats, algorismes i configuracions d'aquesta lliçó són didàctics i canvien amb el temps. La configuració TLS real (versions, cipher suites, HSTS, mTLS) s'ha de revisar amb un professional de seguretat i, en l'àmbit de pagaments, amb qui respongui del compliment normatiu (PCI DSS).

Contingut

  1. Amenaces en trànsit i superfície d'un sistema distribuït
  2. TLS a la vora: certificats, cadena de confiança i versions
  3. TLS a l'Ingress amb cert-manager i Let's Encrypt
  4. HSTS, redirecció i com comprovar el TLS de la vora
  5. TLS intern i mTLS entre serveis: per què i amb quines opcions
  6. mTLS per service mesh: PeerAuthentication i AuthorizationPolicy
  7. La decisió de TechCorp per a la fase 1
  8. Seguretat de RabbitMQ i de les bases de dades
  9. Integritat de missatges i webhooks: HMAC, timestamp i nonce
  10. Protecció de les capçaleres internes i gRPC amb TLS
  11. Taula resum: canal, amenaça, mesura i on es configura

  1. Amenaces en trànsit i superfície d'un sistema distribuït

Tot el que viatja per un cable o una xarxa virtual pot patir quatre coses:

Amenaça Què fa l'atacant Exemple a TechCorp Contramesura
Escolta (eavesdropping) Llegeix el trànsit Captura l'Authorization: Bearer de l'Ana en una wifi pública, o COMANDES_DB_URL al clúster Xifratge (TLS)
Manipulació (tampering) Canvia el contingut pel camí Modifica quantitat: 1quantitat: 100 o l'import d'un webhook de pagament Integritat (TLS, signatura HMAC)
Suplantació (spoofing) Es fa passar per una de les parts Un pod maliciós respon com a servei-cataleg o crida Inventari "com si fos Comandes" Autenticació del canal (certificats, mTLS)
Replay Reenvia un missatge legítim capturat Repeteix el webhook pagament.confirmat de com-88213 per confirmar una altra comanda Timestamp + nonce, idempotència

Al monòlit hi havia un sol canal extern (navegador → servidor) i una crida a la base de dades. Al sistema distribuït, la superfície es multiplica: navegador → Ingress → gateway → sis serveis → PostgreSQL/MongoDB/RabbitMQ/Keycloak → passarel·la de pagament externa. La temptació és dividir el món en perímetre (hostil, es xifra) i xarxa interna (de confiança, text pla). El principi zero trust diu el contrari: cap xarxa no és de confiança pel fet de ser "a dins"; cada crida s'autentica i es xifra com si vingués d'Internet. TechCorp l'adopta com a direcció, i a l'apartat 7 decideix fins on arriba en la primera fase.

  1. TLS a la vora: certificats, cadena de confiança i versions

TLS resol les tres primeres amenaces en un canal: el client verifica la identitat del servidor amb el seu certificat, tots dos acorden claus de sessió i a partir d'aquí tot va xifrat i amb integritat. Els conceptes mínims:

  • Un certificat X.509 lliga un nom (api.techcorp.example) a una clau pública, i el signa una autoritat de certificació (CA). El navegador confia en el certificat perquè confia en la CA (o en la CA intermèdia signada per una arrel que ja porta): això és la cadena de confiança. Un certificat autosignat no té cadena: serveix en local, mai a la vora.
  • Versions: TLS 1.3 és l'actual (més ràpida, sense cipher suites febles); TLS 1.2 es manté per compatibilitat. TLS 1.0/1.1 i SSL estan prohibits. A Kubernetes ho fixa l'ingress controller (ssl-protocols: TLSv1.2 TLSv1.3 al ConfigMap d'ingress-nginx).
  • Terminació TLS: a TechCorp el TLS de l'exterior acaba a l'Ingress (05-02); de l'Ingress al gateway i del gateway als serveis el trànsit va per la xarxa del clúster. És la pràctica habitual: certificats públics en un únic lloc; l'interior es protegeix amb les mesures dels apartats 5-7.
flowchart LR
    N[Navegador / app] -- "HTTPS (TLS 1.3, Let's Encrypt)" --> I[Ingress nginx<br/>acaba TLS]
    I -- "HTTP, xarxa del clúster" --> G[gateway 8080]
    G -- "HTTP + JWT + X-Usuari-*" --> P[servei-comandes 3002]
    P -- "HTTP + token de servei" --> C[servei-cataleg 3001]
    P -- "amqps:// (5671)" --> R[(RabbitMQ)]
    P -- "sslmode=verify-full" --> D[(PostgreSQL)]
    PS[passarel·la de pagament] -- "HTTPS + signatura HMAC" --> I
    subgraph xarxa["Xarxa del clúster: privada + NetworkPolicy (fase 1); mTLS amb mesh (fase 2)"]
      I
      G
      P
      C
      R
      D
    end

  1. TLS a l'Ingress amb cert-manager i Let's Encrypt

cert-manager és un operador de Kubernetes que demana certificats a una CA, els desa en un Secret i els renova sol abans que caduquin. Amb Let's Encrypt (gratuïta, certificats de 90 dies, validació per repte HTTP-01) és la solució estàndard. Instal·lació i emissor:

kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.15.3/cert-manager.yaml
# techcorp/plataforma/k8s/cert-manager/clusterissuer.yaml
apiVersion: cert-manager.io/v1
kind: ClusterIssuer                       # visible des de tots els namespaces (Issuer seria només del seu)
metadata:
  name: letsencrypt-prod
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory     # en proves: acme-staging-v02… (certificats no confiables, sense límits de quota)
    email: [email protected]                         # avisos de caducitat
    privateKeySecretRef: { name: letsencrypt-prod-compte }     # clau del compte ACME, la crea cert-manager
    solvers:
      - http01:
          ingress: { ingressClassName: nginx }                 # repte: Let's Encrypt demana http://api.techcorp.example/.well-known/acme-challenge/…

I l'Ingress del gateway de 05-02, amb la línia tls: que vam deixar comentada i l'anotació que activa cert-manager:

# k8s/gateway/base/ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: gateway
  namespace: techcorp
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-prod           # cert-manager crea un Certificate i omple el Secret
    nginx.ingress.kubernetes.io/ssl-redirect: "true"           # 308 de http:// a https:// (apartat 4)
    nginx.ingress.kubernetes.io/proxy-body-size: 2m
spec:
  ingressClassName: nginx
  tls:
    - hosts: [api.techcorp.example]
      secretName: api-techcorp-tls                             # Secret de tipus kubernetes.io/tls amb tls.crt i tls.key
  rules:
    - host: api.techcorp.example
      http:
        paths:
          - path: /
            pathType: Prefix
            backend: { service: { name: gateway, port: { number: 8080 } } }
kubectl get certificate -n techcorp            # api-techcorp-tls  READY True   (triga ~1 min el primer cop)
kubectl describe certificate api-techcorp-tls -n techcorp | grep -A3 "Not After"   # caduca en 90 dies; es renova als 60

El que passa per sota: cert-manager veu l'anotació, crea un recurs Certificate, genera una clau privada, resol el repte HTTP-01 publicant un Ingress temporal, rep el certificat i l'escriu a api-techcorp-tls; ingress-nginx el recarrega. Renovació: 30 dies abans de caducar repeteix el procés sense intervenció. I com que tota automatització falla alguna vegada (quota de Let's Encrypt esgotada, DNS trencat), l'alerta CertificatCaduca de 06-05 (probe_ssl_earliest_cert_expiry amb Blackbox Exporter, llindar 14 dies) és la xarxa de seguretat: si salta, la renovació fa 16 dies que falla i el runbook plataforma/certificats diu què cal mirar (kubectl describe challenge -n techcorp).

Els mateixos recursos serveixen per a auth.techcorp.example (Keycloak, 07-01) i shop.techcorp.example; amb Traefik com a ingress controller (03-04) l'anotació canvia de nom però cert-manager és el mateix.

  1. HSTS, redirecció i com comprovar el TLS de la vora

  • Redirecció HTTP→HTTPS: ssl-redirect: "true" retorna 308 a qualsevol http://. És necessària però insuficient: la primera petició en clar ja pot ser interceptada.
  • HSTS (Strict-Transport-Security): la capçalera que diu al navegador "durant N segons, ni intentis HTTP amb aquest domini". S'activa al ConfigMap d'ingress-nginx (hsts: "true", hsts-max-age: "31536000", hsts-include-subdomains: "true") o des del gateway amb helmet (07-03). Compte amb preload: és difícil de revertir.
  • Comprovar:
openssl s_client -connect api.techcorp.example:443 -servername api.techcorp.example </dev/null 2>/dev/null \
  | openssl x509 -noout -subject -issuer -dates
# subject=CN = api.techcorp.example
# issuer=C = US, O = Let's Encrypt, CN = R11
# notBefore=Aug 15 09:12:00 2026 GMT / notAfter=Nov 13 09:11:59 2026 GMT

curl -sv https://api.techcorp.example/api/v1/productes?ids=p-501 -o /dev/null 2>&1 | grep -E "SSL connection|subject|expire|strict-transport"
# * SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384
# < strict-transport-security: max-age=31536000; includeSubDomains
curl -sI http://api.techcorp.example/ | head -1      # HTTP/1.1 308 Permanent Redirect

Eines externes com SSL Labs (Qualys) puntuen la configuració completa; TechCorp exigeix una "A" abans de cada auditoria.

  1. TLS intern i mTLS entre serveis: per què i amb quines opcions

Dins del clúster avui tot va en clar: gateway → servei-comandes:3002, Comandes → servei-cataleg:3001, amb el JWT de l'Ana i les capçaleres X-Usuari-* a la vista. És un problema? Si un atacant aconsegueix executar un pod al clúster (una imatge compromesa, 07-04) o accés a un node, pot escoltar aquest trànsit, suplantar un servei i falsificar X-Usuari-* per cridar directament un servei saltant-se el gateway. La xarxa interna és una barrera, no una garantia.

mTLS (TLS mutu) afegeix a TLS l'autenticació del client: tots dos extrems presenten certificat i cadascun verifica el de l'altre contra una CA interna. El resultat és xifratge + identitat de servei a cada connexió: servei-inventari sap criptogràficament que qui crida és servei-comandes, sense refiar-se de capçaleres. Les opcions:

Opció Com Cost Quan
mTLS a l'aplicació Cada servei arrenca amb https.createServer i requestCert: true; certificats d'una CA interna muntats al pod Emetre, muntar, rotar i revocar certificats a sis serveis; codi a cadascun Pocs serveis i sense mesh; per entendre-ho
mTLS per service mesh El sidecar (Istio/Linkerd) xifra i autentica; certificats emesos i rotats pel pla de control Adoptar el mesh (05-05) Quan el mesh ja hi és o hi ha requisit de mTLS obligatori
NetworkPolicy (complement, no substitut) Regles de xarxa: qui pot obrir connexió amb qui Baix Sempre; detall a 07-04. Limita, però no xifra ni autentica

Per entendre què fa el mesh per nosaltres, la versió "a mà" en Node.js. Amb una CA interna (ca.crt) i un certificat per servei (tls.crt/tls.key, emesos per cert-manager amb un Issuer de tipus ca, muntats des d'un Secret a /etc/tls):

// servei-inventari/src/servidor.js (fragment didàctic: mTLS per aplicació)
const https = require('node:https');
const fs = require('node:fs');
const opcions = {
  key:  fs.readFileSync('/etc/tls/tls.key'),
  cert: fs.readFileSync('/etc/tls/tls.crt'),
  ca:   fs.readFileSync('/etc/tls/ca.crt'),   // només s'accepten clients amb certificat signat per la CA interna
  requestCert: true,                          // exigir certificat al client
  rejectUnauthorized: true,                   // i rebutjar la connexió si no és vàlid
  minVersion: 'TLSv1.2'
};
https.createServer(opcions, app).listen(3006);

// I al servei, un middleware que llegeix la identitat del certificat del client:
app.use((req, res, next) => {
  const cert = req.socket.getPeerCertificate();
  req.serveiCridant = cert?.subject?.CN;   // "servei-comandes.techcorp.svc"
  if (req.serveiCridant !== 'servei-comandes.techcorp.svc') return res.status(403).end();  // només Comandes reserva estoc
  next();
});

I el client (crearClientHttp de 06-03) necessitaria un Agent d'https amb la seva key, cert i ca. Funciona, i mostra el preu: cada servei gestiona fitxers de certificat, cal rotar-los (cert-manager ajuda) i reiniciar o recarregar en rotar, la CA interna és un secret crític, i el CN passa a ser un contracte més. Amb sis serveis és assumible; amb vint, no.

  1. mTLS per service mesh: PeerAuthentication i AuthorizationPolicy

Amb Istio (05-05) el mateix resultat són dos recursos, sense tocar codi ni certificats: istiod emet un certificat SPIFFE per ServiceAccount (spiffe://cluster.local/ns/techcorp/sa/servei-comandes), el rota cada 24 h i els sidecars negocien mTLS entre ells.

# techcorp/plataforma/k8s/mesh/peer-authentication.yaml
apiVersion: security.istio.io/v1
kind: PeerAuthentication
metadata:
  name: default
  namespace: techcorp
spec:
  mtls: { mode: STRICT }                  # PERMISSIVE durant la migració (accepta clar i mTLS); STRICT quan tots tenen sidecar
---
# techcorp/plataforma/k8s/mesh/authz-inventari.yaml
apiVersion: security.istio.io/v1
kind: AuthorizationPolicy
metadata:
  name: inventari-nomes-des-de-comandes
  namespace: techcorp
spec:
  selector: { matchLabels: { app: servei-inventari } }
  action: ALLOW                           # amb almenys una regla ALLOW, tot el que no està llistat queda denegat
  rules:
    - from:
        - source: { principals: ["cluster.local/ns/techcorp/sa/servei-comandes"] }   # identitat del certificat, no IP
      to:
        - operation: { methods: ["POST", "DELETE"], paths: ["/v1/reserves", "/v1/reserves/*"] }
    - from:
        - source: { principals: ["cluster.local/ns/monitoring/sa/prometheus"] }
      to:
        - operation: { methods: ["GET"], paths: ["/metrics"] }

Això tanca el que 05-05 va deixar només esmentat: la identitat ve del certificat emès al ServiceAccount (07-04 en crea un per servei precisament per a això), i la política es llegeix com a negoci: "a Inventari només li parla Comandes, i Prometheus només llegeix mètriques". Un pod intrús sense sidecar ni identitat rep RBAC: access denied del proxy d'Inventari abans que la petició arribi a Node. Linkerd té el seu equivalent (Server + AuthorizationPolicy/MeshTLSAuthentication), amb mTLS activat per defecte.

  1. La decisió de TechCorp per a la fase 1

La Marta i l'equip de Plataforma apliquen el criteri de 05-05 (sense mesh a la primera fase) i decideixen, per a la comunicació interna:

Mesura Estat a la fase 1 Motiu
TLS a la vora amb cert-manager/Let's Encrypt, HSTS, redirecció Barat, imprescindible, un sol lloc
Xarxa del clúster privada (nodes sense IP pública, API server restringit) Base del núvol/proveïdor
NetworkPolicies deny-all + regles explícites (07-04) Limita qui parla amb qui sense certificats
Tokens client credentials a les crides internes (07-01) Identitat de servei a nivell d'aplicació, ja implementada
Doble verificació del JWT a cada servei (07-01) Les capçaleres X-Usuari-* no s'accepten com a única font
TLS a RabbitMQ i a les bases de dades (apartat 8) Són els canals amb credencials i dades personals
mTLS entre serveis Ajornat Cost de certificats per aplicació; arribarà amb el mesh

Disparador per reavaluar: l'auditoria de l'àrea de Pagaments prevista per a la fase 2. Si exigeix mTLS obligatori entre serveis (probable al perímetre de servei-pagaments), la via serà un mesh —Linkerd com a primera opció pel seu mTLS automàtic, coherent amb 05-05— i no certificats per aplicació. Mentrestant, la lliçó de l'apartat 5 serveix per saber què s'està comprant.

  1. Seguretat de RabbitMQ i de les bases de dades

RabbitMQ. Tres regles:

  1. Canal xifrat: amqps:// (port 5671) amb certificat del servidor (emès per cert-manager amb la CA interna, o el de Let's Encrypt si el broker té nom públic). El RABBITMQ_URL de 04-03 passa a amqps://comandes:…@rabbitmq:5671/techcorp i l'esquema de zod accepta amqps. Amb amqplib, connect(url, { ca: [fs.readFileSync('/etc/tls/ca.crt')] }).
  2. Un usuari per servei amb permisos mínims, mai guest (que a més només pot connectar des de localhost). Els permisos de RabbitMQ són tres expressions regulars per vhost: configure (declarar), write (publicar / enllaçar), read (consumir). Per a comandes, segons el contracte de 03-02:
# Executat per Plataforma en aprovisionar; contrasenyes generades i desades al Secret comandes-rabbitmq (07-04)
rabbitmqctl add_vhost techcorp
rabbitmqctl delete_user guest
rabbitmqctl add_user comandes "$RABBITMQ_COMANDES_PASSWORD"
rabbitmqctl set_permissions -p techcorp comandes \
  "^(comandes\.saga|comandes\.clients)(\.reintent|\.dlq)?$" \
  "^(techcorp\.esdeveniments|(comandes\.saga|comandes\.clients)(\.reintent|\.dlq)?)$" \
  "^(techcorp\.esdeveniments|techcorp\.esdeveniments\.dlx|(comandes\.saga|comandes\.clients)(\.reintent|\.dlq)?)$"

És a dir: comandes configura només les seves cues (i les seves .reintent/.dlq de 06-03); escriu a l'exchange techcorp.esdeveniments (publicar) i a les seves cues (enllaçar-les i reencuar a .reintent); i llegeix de les seves cues i dels dos exchanges (RabbitMQ exigeix read sobre l'exchange per enllaçar-hi una cua). No pot consumir d'inventari.comandes ni publicar directament a la cua d'un altre. Un comandes compromès no pot llegir esdeveniments de Pagaments.

  1. Contrasenyes a Secrets (comandes-rabbitmq), rotades (07-04), i la interfície de gestió (15672) sense exposar fora del clúster, amb el seu propi usuari administrator.

Bases de dades. La mateixa lògica a PostgreSQL i MongoDB:

  • TLS obligatori: COMANDES_DB_URL=postgres://svc_comandes:…@postgres:5432/techcorp?sslmode=verify-full&sslrootcert=/etc/tls/ca.crt (require xifra però no verifica el certificat; verify-full també evita la suplantació del servidor); al servidor, ssl = on i hostssl a pg_hba.conf per rebutjar connexions en clar. A MongoDB, tls=true&tlsCAFile=… a la URL i net.tls.mode: requireTLS.
  • Usuaris svc_* amb privilegis mínims per esquema, exactament com a 02-04: svc_comandes només té USAGE i SELECT/INSERT/UPDATE/DELETE sobre l'esquema comandes, cap CREATE (les migracions del Job de 05-02 fan servir un usuari a part, mig_comandes, amb més privilegis i només durant el desplegament), i svc_pagaments no pot llegir comandes.* encara que comparteixin instància.
  • Bases de dades no exposades fora del clúster o de la VPC; en serveis gestionats, sense IP pública i amb la xarxa del clúster a la llista de permesos.

  1. Integritat de missatges i webhooks: HMAC, timestamp i nonce

servei-pagaments rep webhooks de la passarel·la de pagament externa (POST /v1/webhooks/passarella) que li diuen "el cobrament ch_7f3a… de 79,70 € s'ha confirmat". Aquesta ruta ha d'estar exposada a Internet (a través del gateway o d'un Ingress propi), i TLS només garanteix el canal, no qui envia: qualsevol que conegui la URL podria enviar un pagament.confirmat fals. Les passarel·les ho resolen amb una signatura HMAC: un secret compartit (PASSARELLA_WEBHOOK_SECRET, al Secret pagaments-passarella), i a cada webhook una capçalera amb t=<timestamp>,v1=<HMAC-SHA256(t + "." + cos)>.

// servei-pagaments/src/rutes/webhooks.js
const { createHmac, timingSafeEqual } = require('node:crypto');
const TOLERANCIA_MS = 5 * 60_000;                                    // 5 minuts: més enllà, replay o rellotge trencat

// Nota: aquesta ruta necessita el cos CRU (Buffer): express.raw({ type: 'application/json' }) en comptes d'express.json()
encaminador.post('/v1/webhooks/passarella', express.raw({ type: 'application/json', limit: '100kb' }), async (req, res, next) => {
  try {
    const signatura = Object.fromEntries((req.get('Passarella-Signature') ?? '').split(',').map((p) => p.split('=')));   // { t, v1 }
    const t = Number(signatura.t);
    if (!t || Math.abs(Date.now() - t * 1000) > TOLERANCIA_MS) throw new ErrorNegoci('WEBHOOK_INVALID', 'timestamp fora de finestra', 400);
    const esperat = createHmac('sha256', config.PASSARELLA_WEBHOOK_SECRET).update(`${t}.`).update(req.body).digest('hex');
    const rebut = Buffer.from(signatura.v1 ?? '', 'hex');
    if (rebut.length !== 32 || !timingSafeEqual(Buffer.from(esperat, 'hex'), rebut)) {   // comparació en temps constant
      req.log.warn({ ip: req.ip }, 'webhook amb signatura invàlida');
      throw new ErrorNegoci('WEBHOOK_INVALID', 'signatura no vàlida', 401);
    }
    const esdeveniment = JSON.parse(req.body);                        // només ara es confia en el cos
    const nou = await repositori.registrarWebhook(esdeveniment.id);   // INSERT … ON CONFLICT DO NOTHING sobre webhooks_rebuts(esdeveniment_id)
    if (!nou) return res.status(200).end();                           // replay o reintent de la passarel·la: idempotent, 200 sense repetir efectes
    await processarPagament(esdeveniment);                            // publica pagament.confirmat / pagament.rebutjat (saga de 02-05)
    res.status(200).end();
  } catch (err) { next(err); }
});

Tres capes: la signatura prova que ho ha enviat qui té el secret i que el cos no ha canviat; el timestamp acota la finestra de replay; i l'identificador de l'esdeveniment com a nonce persistit (webhooks_rebuts) fa innocu qualsevol reenviament, igual que processarUnCop als consumidors de 04-04. La mateixa tècnica serveix per als webhooks que TechCorp envia (a un soci, a un ERP): signatura amb secret per destinatari, i el receptor verifica.

Per als esdeveniments interns per RabbitMQ no se signa cada missatge: el canal ja és amqps amb usuari autenticat i permisos per cua (apartat 8), i la idempotència per esdevenimentId (02-05) neutralitza duplicats i replays. Signar missatges tindria sentit si el broker fos compartit amb tercers.

  1. Protecció de les capçaleres internes i gRPC amb TLS

Dos tancaments breus:

  • Capçaleres internes. A 07-01 el gateway ja elimina qualsevol X-Usuari-* entrant abans de posar les seves. Cal fer el mateix amb X-Forwarded-For/X-Real-IP (l'Ingress les fixa; el gateway no ha d'acceptar les del client per al rate limit de 03-04: app.set('trust proxy', 1) confia només en el salt de l'Ingress) i amb qualsevol capçalera que un servei faci servir com a "de confiança". Regla: una capçalera interna és tan fiable com el conjunt dels qui poden arribar al port —per això les NetworkPolicies de 07-04 i, quan toqui, mTLS.
  • gRPC (03-03): fa servir HTTP/2 i es xifra igual. Servidor amb grpc.ServerCredentials.createSsl(caCert, [{ private_key, cert_chain }], /* checkClientCertificate */ true) i client amb grpc.credentials.createSsl(caCert, key, cert); amb un mesh, el sidecar ho fa i el codi usa createInsecure() cap a localhost. Només esment: TechCorp no exposa gRPC a la fase 1.

  1. Taula resum: canal, amenaça, mesura i on es configura

Canal Amenaça principal Mesura On
Navegador/app → Ingress Escolta, suplantació del lloc TLS 1.2/1.3, Let's Encrypt, HSTS, redirecció Ingress (tls:, anotacions), ClusterIssuer, ConfigMap ingress-nginx
Ingress → gateway → serveis Escolta interna, capçaleres falsificades Xarxa privada + NetworkPolicy + doble verificació JWT; mTLS amb mesh a la fase 2 07-01, 07-04, PeerAuthentication/AuthorizationPolicy
Servei → servei (síncron) Suplantació del cridant Client credentials (07-01); AuthorizationPolicy per identitat amb mesh crearProveidorToken, mesh
Servei → RabbitMQ Escolta de credencials/esdeveniments, publicar/consumir sense permís amqps://, usuari per servei, permisos per vhost/exchange/cua, sense guest rabbitmqctl set_permissions, Secret comandes-rabbitmq
Servei → PostgreSQL/MongoDB Escolta, suplantació del servidor, accés a dades alienes sslmode=verify-full, requireTLS, usuaris svc_* per esquema URL de connexió, pg_hba.conf, rols de 02-04
Passarel·la → servei-pagaments (webhook) Suplantació, manipulació, replay HMAC-SHA256 amb timingSafeEqual, finestra de 5 min, webhooks_rebuts rutes/webhooks.js, Secret pagaments-passarella
Capçaleres internes (X-Usuari-*, X-Forwarded-*) Injecció des de l'exterior Neteja al gateway, trust proxy acotat gateway/servidor.js
gRPC Igual que HTTP createSsl o sidecar Codi o mesh

Errors Comuns i Consells

  • Acabar TLS a l'Ingress i creure que "tot està xifrat". L'interior va en clar; cal decidir-ho conscientment (apartat 7) i compensar-ho amb xarxa i autenticació.
  • Certificats renovats a mà. Algú se n'oblida, el lloc cau un diumenge. cert-manager + l'alerta CertificatCaduca; i provar la renovació amb l'emissor de staging de Let's Encrypt abans d'esgotar la quota del de producció.
  • sslmode=require pensant que verifica el certificat. Només xifra; verify-full és el que impedeix la suplantació del servidor de base de dades.
  • guest/guest a RabbitMQ o un únic usuari techcorp amb .* als tres permisos. Un servei compromès ho llegeix tot. Un usuari per servei amb expressions regulars acotades.
  • Verificar la signatura HMAC sobre el cos ja parsejat i reserialitzat: JSON.stringify(req.body) rarament reprodueix els bytes originals i la signatura falla (o pitjor, es relaxa). Cos cru amb express.raw en aquesta ruta.
  • Comparar signatures amb ===. Filtra informació pel temps; timingSafeEqual sempre.
  • PeerAuthentication STRICT sense sidecar a tots els pods (o amb Prometheus fora del mesh): tot deixa de parlar (05-05). PERMISSIVE primer.
  • Consell: desa al runbook plataforma/certificats les ordres dels apartats 3 i 4; i a cada Secret TLS, qui l'emet i quan caduca (kubectl get certificate -A).

Exercicis

Exercici 1. Un desenvolupador de Notificacions proposa que, per no dependre de cert-manager en local, tots els entorns facin servir sslmode=disable amb PostgreSQL "perquè la base de dades és dins del clúster". Explica quines amenaces queden obertes i dona una configuració per entorn coherent amb la taula de 04-03.

Exercici 2. Escriu els tres permisos de RabbitMQ (configure, write, read) per a l'usuari inventari, sabent que Inventari consumeix d'inventari.comandes (esdeveniments comanda.creada i comanda.cancellada), publica estoc.reservat/estoc.rebutjat/estoc.alliberat a techcorp.esdeveniments, i té les seves cues .reintent i .dlq. Explica què no pot fer amb aquests permisos.

Exercici 3. La passarel·la reenvia el mateix webhook tres vegades (reintents per timeout) i, a més, un atacant en captura un i el reenvia dues hores després. Recorre el codi de l'apartat 9 i digues què passa en cadascun dels quatre casos i quin efecte té en la saga.

Solucions

Solució 1. Sense TLS, qualsevol procés amb accés a la xarxa del clúster (pod compromès, node, sniffer en un switch virtual mal configurat) llegeix les credencials svc_notificacions al handshake i totes les dades (correus i noms de clients: dades personals) a cada consulta; i res no impedeix que un servidor fals respongui com a postgres. Configuració coherent amb la columna d'entorns de 04-03: local sslmode=disable (PostgreSQL a Docker Compose, sense dades reals), staging i producció sslmode=verify-full&sslrootcert=/etc/tls/ca.crt, amb hostssl a pg_hba.conf perquè el servidor rebutgi connexions en clar encara que un servei s'equivoqui. El valor va al Secret notificacions-db, així que el codi no canvia entre entorns; només la URL.

Solució 2.

rabbitmqctl set_permissions -p techcorp inventari \
  "^inventari\.comandes(\.reintent|\.dlq)?$" \
  "^(techcorp\.esdeveniments|inventari\.comandes(\.reintent|\.dlq)?)$" \
  "^(techcorp\.esdeveniments|techcorp\.esdeveniments\.dlx|inventari\.comandes(\.reintent|\.dlq)?)$"

configure: només declara la seva cua i les auxiliars. write: publica a techcorp.esdeveniments (els estoc.*) i a les seves cues (enllaçar-les, reencuar a .reintent). read: consumeix de les seves cues i les pot enllaçar als dos exchanges. No pot: consumir de comandes.saga, pagaments.comandes o qualsevol cua d'un altre (ni tan sols les pot declarar per enllaçar-les); declarar cues fora del seu prefix; ni esborrar o redeclarar l'exchange (configure no l'inclou). Un inventari compromès pot publicar esdeveniments falsos estoc.reservat (per això Comandes valida la càrrega i la correlaciona amb la seva reserva pendent), però no llegeix pagaments ni comandes.

Solució 3. (1) Primer webhook: signatura vàlida, t dins de finestra, esdeveniment.id nou → es processa, es publica pagament.confirmat, la saga avança. (2) Reintents 2 i 3 de la passarel·la (segons després): signatura i t vàlids, però registrarWebhook retorna nou=false200 sense efectes: la passarel·la deixa de reintentar i la saga no veu duplicats (encara que els veiés, processarUnCop els descartaria per esdevenimentId). (3) Reenviament de l'atacant dues hores després: la signatura és vàlida (és el missatge original) però |Date.now() - t| > 5 min400 WEBHOOK_INVALID abans de mirar res; i si l'atacant intentés "refrescar" t, la signatura deixaria de coincidir perquè t forma part del que està signat. (4) Si l'atacant a més hagués pogut esperar menys de 5 minuts: l'esdeveniment.id ja està registrat → 200 sense efectes. En cap cas no es confirma dues vegades ni es confirma una comanda diferent (l'esdeveniment.id i el comandaId van dins del cos signat).

Conclusió

La comunicació de TechCorp ja no descansa en la fe en la xarxa interna. La vora va xifrada amb TLS 1.2/1.3 i certificats de Let's Encrypt que cert-manager (ClusterIssuer letsencrypt-prod, anotació cert-manager.io/cluster-issuer, Secret api-techcorp-tls) emet i renova, amb HSTS, redirecció 308 i l'alerta CertificatCaduca com a xarxa de seguretat; hem vist què és mTLS i quant costa fer-ho per aplicació (https.createServer amb requestCert), i com un mesh el regala amb PeerAuthentication STRICT i una AuthorizationPolicy que només deixa servei-comandes parlar amb Inventari —tancant el que 05-05 va deixar només esmentat—; la decisió de fase 1 és TLS a la vora + xarxa privada + NetworkPolicies + client credentials + doble verificació del JWT, amb mTLS ajornat fins a l'auditoria de Pagaments i Linkerd com a primer candidat; RabbitMQ va per amqps:// amb un usuari per servei i permisos per cua (comandes només publica a techcorp.esdeveniments i consumeix comandes.saga/comandes.clients), les bases de dades amb sslmode=verify-full i usuaris svc_*; els webhooks de la passarel·la arriben signats amb HMAC-SHA256, amb finestra de 5 minuts i webhooks_rebuts contra el replay; i el gateway neteja les capçaleres internes abans de propagar les seves. Amb la identitat (07-01) i el canal resolts, la pregunta següent és què fa cada servei amb el que rep: com valida l'entrada, com evita injeccions, com tracta les dades personals i com s'audita. Aquest és el tema de la lliçó següent: pràctiques de seguretat.

Curs de Microserveis

Mòdul 1: Introducció als Microserveis

Mòdul 2: Disseny de Microserveis

Mòdul 3: Comunicació entre Microserveis

Mòdul 4: Implementació de Microserveis

Mòdul 5: Desplegament i Orquestració

Mòdul 6: Monitoratge i Manteniment

Mòdul 7: Seguretat en Microserveis

Mòdul 8: Casos d'Estudi i Exemples Pràctics

© Copyright 2026. Tots els drets reservats