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
- Amenaces en trànsit i superfície d'un sistema distribuït
- TLS a la vora: certificats, cadena de confiança i versions
- TLS a l'
Ingressamb cert-manager i Let's Encrypt - HSTS, redirecció i com comprovar el TLS de la vora
- TLS intern i mTLS entre serveis: per què i amb quines opcions
- mTLS per service mesh:
PeerAuthenticationiAuthorizationPolicy - La decisió de TechCorp per a la fase 1
- Seguretat de RabbitMQ i de les bases de dades
- Integritat de missatges i webhooks: HMAC, timestamp i nonce
- Protecció de les capçaleres internes i gRPC amb TLS
- Taula resum: canal, amenaça, mesura i on es configura
- 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: 1 → quantitat: 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.
- 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.3al 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
- TLS a l'
Ingress amb cert-manager i Let's Encrypt
Ingress amb cert-manager i Let's Encryptcert-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 60El 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.
- HSTS, redirecció i com comprovar el TLS de la vora
- Redirecció HTTP→HTTPS:
ssl-redirect: "true"retorna308a qualsevolhttp://. É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 ambhelmet(07-03). Compte ambpreload: é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 RedirectEines externes com SSL Labs (Qualys) puntuen la configuració completa; TechCorp exigeix una "A" abans de cada auditoria.
- 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.
- mTLS per service mesh:
PeerAuthentication i AuthorizationPolicy
PeerAuthentication i AuthorizationPolicyAmb 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.
- 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ó | Sí | Barat, imprescindible, un sol lloc |
| Xarxa del clúster privada (nodes sense IP pública, API server restringit) | Sí | Base del núvol/proveïdor |
| NetworkPolicies deny-all + regles explícites (07-04) | Sí | Limita qui parla amb qui sense certificats |
| Tokens client credentials a les crides internes (07-01) | Sí | Identitat de servei a nivell d'aplicació, ja implementada |
| Doble verificació del JWT a cada servei (07-01) | Sí | Les capçaleres X-Usuari-* no s'accepten com a única font |
| TLS a RabbitMQ i a les bases de dades (apartat 8) | Sí | 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.
- Seguretat de RabbitMQ i de les bases de dades
RabbitMQ. Tres regles:
- 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). ElRABBITMQ_URLde 04-03 passa aamqps://comandes:…@rabbitmq:5671/techcorpi l'esquema de zod acceptaamqps. Ambamqplib,connect(url, { ca: [fs.readFileSync('/etc/tls/ca.crt')] }). - Un usuari per servei amb permisos mínims, mai
guest(que a més només pot connectar des delocalhost). Els permisos de RabbitMQ són tres expressions regulars per vhost:configure(declarar),write(publicar / enllaçar),read(consumir). Per acomandes, 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.
- 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 usuariadministrator.
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(requirexifra però no verifica el certificat;verify-fulltambé evita la suplantació del servidor); al servidor,ssl = onihostsslapg_hba.confper rebutjar connexions en clar. A MongoDB,tls=true&tlsCAFile=…a la URL inet.tls.mode: requireTLS. - Usuaris
svc_*amb privilegis mínims per esquema, exactament com a 02-04:svc_comandesnomés téUSAGEiSELECT/INSERT/UPDATE/DELETEsobre l'esquemacomandes, capCREATE(les migracions delJobde 05-02 fan servir un usuari a part,mig_comandes, amb més privilegis i només durant el desplegament), isvc_pagamentsno pot llegircomandes.*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.
- 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.
- 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 ambX-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 ambgrpc.credentials.createSsl(caCert, key, cert); amb un mesh, el sidecar ho fa i el codi usacreateInsecure()cap alocalhost. Només esment: TechCorp no exposa gRPC a la fase 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=requirepensant que verifica el certificat. Només xifra;verify-fullés el que impedeix la suplantació del servidor de base de dades.guest/guesta RabbitMQ o un únic usuaritechcorpamb.*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 ambexpress.rawen aquesta ruta. - Comparar signatures amb
===. Filtra informació pel temps;timingSafeEqualsempre. PeerAuthentication STRICTsense sidecar a tots els pods (o amb Prometheus fora del mesh): tot deixa de parlar (05-05).PERMISSIVEprimer.- Consell: desa al runbook
plataforma/certificatsles ordres dels apartats 3 i 4; i a cadaSecretTLS, 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=false → 200 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 min → 400 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
- Conceptes Bàsics de Microserveis
- Avantatges i Desavantatges dels Microserveis
- Comparació amb l'Arquitectura Monolítica
- Quan Adoptar Microserveis: Criteris de Decisió
- El Cas Pràctic del Curs: la Botiga Online de TechCorp
Mòdul 2: Disseny de Microserveis
- Principis de Disseny de Microserveis
- Descomposició d'Aplicacions Monolítiques
- Definició de Bounded Contexts
- Gestió de Dades: una Base de Dades per Servei
- Consistència Distribuïda: Sagues, CQRS i Event Sourcing
Mòdul 3: Comunicació entre Microserveis
- APIs RESTful
- Missatgeria Asíncrona
- Protocols de Comunicació: gRPC, GraphQL
- API Gateway i Backend for Frontend
- Descobriment de Serveis i Balanceig de Càrrega
- Contractes i Versionat d'APIs
Mòdul 4: Implementació de Microserveis
- Elecció de Tecnologies i Eines
- Desenvolupament d'un Microservei Simple
- Gestió de Configuració
- Integració Pràctica: Consumir APIs i Publicar Esdeveniments
- Proves en Microserveis: Unitàries, d'Integració i de Contracte
Mòdul 5: Desplegament i Orquestració
- Contenidors i Docker
- Orquestració amb Kubernetes
- CI/CD per a Microserveis
- Estratègies de Desplegament: Rolling, Blue-Green i Canary
- Service Mesh: Istio i Linkerd
Mòdul 6: Monitoratge i Manteniment
- Monitoratge i Logging
- Traçabilitat Distribuïda amb OpenTelemetry
- Gestió d'Errors i Recuperació
- Escalabilitat i Rendiment
- SLOs, Alertes i Gestió d'Incidents
Mòdul 7: Seguretat en Microserveis
- Autenticació i Autorització
- Seguretat en la Comunicació
- Pràctiques de Seguretat
- Seguretat en Contenidors i Kubernetes
