La lliçó anterior va deixar inventari exigint un token signat abans de moure una unitat d'estoc. Però aquest token, el telèfon de l'Anna a la comanda P-2026-000123, l'esdeveniment comanda.creada que viatja per comandes.esdeveniments i les còpies de seguretat de km0_inventari a MinIO continuen circulant i reposant en clar. Qualsevol amb accés a un segment de xarxa, a un disc reciclat, a un volum de Docker o a un bucket mal configurat els llegeix o, pitjor, els modifica sense que ningú se n'adoni. Saber qui és qui no serveix si la conversa es pot escoltar i alterar.
Aquesta lliçó tracta de la criptografia aplicada a una plataforma distribuïda: no com es dissenyen els algorismes, sinó què garanteix cadascun, quan fer servir quin i com integrar-lo sense cometre els errors que converteixen un xifratge correcte en un d'inútil. Veurem xifratge simètric i asimètric, hashes, HMAC i signatures; TLS per a les dades en trànsit, amb gRPC entre comandes i inventari com a cas pràctic; xifratge en repòs a disc, base de dades i columna, amb envelope encryption per al telèfon de l'Anna; xifratge del costat del servidor a MinIO i als backups; i la protecció de dades personals: minimització, pseudonimització per al llac de dades i xifratge com a base del dret a l'oblit. Queden per a 06-04 l'autenticació mútua amb certificats (mTLS) i la custòdia operativa de les claus (Vault).
Contingut
- Tres propietats: confidencialitat, integritat, autenticitat
- Xifratge simètric: AES-GCM, claus, nonces i etiquetes
- Xifratge asimètric i xifratge híbrid
- Hashes, HMAC i signatures digitals
- Taula resum: quina primitiva per a quin problema
- Dades en trànsit: TLS
- TLS a gRPC entre
comandesiinventari - TLS a PostgreSQL, Kafka, Redis i MinIO
- Dades en repòs: disc, base de dades, columna
- Envelope encryption: el telèfon de l'Anna
- Gestió de claus: rotació, entorns i on no van
- Dades personals: minimització, pseudonimització, anonimització i dret a l'oblit
- Mapa de protecció de Quilòmetre Zero
- Errors Comuns i Consells
- Exercicis
- Conclusió
- Tres propietats: confidencialitat, integritat, autenticitat
Quan diem "protegir una dada" barregem tres garanties diferents que s'aconsegueixen amb eines diferents:
| Propietat | Pregunta | Amenaça | Eina |
|---|---|---|---|
| Confidencialitat | Només ho llegeix qui toca? | Escolta a la xarxa, disc robat, bucket públic | Xifratge (simètric, asimètric, TLS) |
| Integritat | Ha canviat pel camí? | Alteració d'un esdeveniment, d'un fitxer, d'un preu en trànsit | Hash, MAC, signatura; el tag d'AES-GCM |
| Autenticitat | Ho ha produït qui diu? | Suplantació de comandes a Kafka, servidor inventari fals |
MAC (amb clau compartida), signatura digital, certificat TLS |
Xifrar no dona integritat per si sol: amb alguns modes de xifratge clàssics (AES-CBC sense MAC) un atacant pot capgirar bits del text xifrat i produir canvis controlats al text en clar sense conèixer la clau. Per això la pràctica moderna és el xifratge autenticat (AEAD), que combina totes dues en una sola operació. I a les tres se n'hi sol afegir una quarta, el no-repudi (que l'autor no pugui negar l'autoria), que només la signatura digital proporciona, perquè el MAC el poden generar tots els que comparteixen la clau.
- Xifratge simètric: AES-GCM, claus, nonces i etiquetes
En el xifratge simètric la mateixa clau xifra i desxifra. És ràpid (AES té instruccions dedicades a les CPU: diversos GB/s per nucli) i és el que es fa servir per a tot el volum de dades: discos, fitxers, columnes, i el cos d'una connexió TLS. El seu problema és la distribució de la clau: els dos extrems l'han de tenir, i fer-los-la arribar de manera segura és precisament el problema que resol el xifratge asimètric.
L'estàndard actual és AES-256-GCM (o ChaCha20-Poly1305 on no hi ha acceleració AES). Té cinc peces:
- Clau: 32 bytes aleatoris. Del seu secret en depèn tot.
- Nonce (number used once): 12 bytes que no es repeteixen mai amb la mateixa clau. No és secret: es desa al costat del text xifrat. La seva funció és que xifrar dues vegades el mateix missatge doni resultats diferents.
- Text xifrat: de la mateixa mida que l'original.
- Etiqueta d'autenticació (tag): 16 bytes que garanteixen la integritat. En desxifrar, si un sol bit del xifrat (o de les dades associades) ha canviat, la verificació falla i no es retorna res.
- Dades associades (AAD): informació en clar que no es xifra però sí que s'autentica: per exemple, el
comanda_ida què pertany un telèfon xifrat, perquè ningú no pugui copiar el telèfon xifrat de l'Anna a la comanda del Marc.
# km0/serveis/comu/cripto.py
# pip install cryptography
import os
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
def generar_clau() -> bytes:
return AESGCM.generate_key(bit_length=256) # 32 bytes d'os.urandom
def xifrar(clau: bytes, text: bytes, aad: bytes = b"") -> bytes:
nonce = os.urandom(12) # NOU a cada xifratge
xifrat_i_tag = AESGCM(clau).encrypt(nonce, text, aad)
return nonce + xifrat_i_tag # el nonce viatja davant, en clar
def desxifrar(clau: bytes, blob: bytes, aad: bytes = b"") -> bytes:
nonce, xifrat_i_tag = blob[:12], blob[12:]
return AESGCM(clau).decrypt(nonce, xifrat_i_tag, aad) # InvalidTag si alguna cosa ha canviat
if __name__ == "__main__":
k = generar_clau()
blob = xifrar(k, b"+34 600 123 456", aad=b"P-2026-000123")
print(len(blob)) # 12 + 15 + 16 = 43 bytes
print(desxifrar(k, blob, aad=b"P-2026-000123")) # b'+34 600 123 456'
try:
desxifrar(k, blob, aad=b"P-2026-000124") # una altra comanda: l'AAD no quadra
except Exception as e:
print(type(e).__name__) # InvalidTagPer què no reutilitzar mai un nonce. GCM genera internament un flux de bits (keystream) a partir de la clau i el nonce, i fa XOR amb el missatge. Si dos missatges es xifren amb el mateix parell clau-nonce, el XOR dels dos textos xifrats és igual al XOR dels dos textos en clar: l'atacant elimina el xifratge sense conèixer la clau, i a més pot recuperar la clau d'autenticació i falsificar etiquetes. Amb nonces aleatoris de 96 bits, la probabilitat de col·lisió esdevé preocupant cap als 2³² missatges amb la mateixa clau (uns 4.000 milions): més que suficient per a una clau per servei i per any, però és la raó per la qual les claus es roten i per la qual un comptador (nonce seqüencial) és preferible quan hi ha un únic escriptor que pot garantir que no es repeteix.
- Xifratge asimètric i xifratge híbrid
En el xifratge asimètric hi ha un parell de claus: el que es xifra amb la pública només es desxifra amb la privada, i el que se signa amb la privada ho verifica qualsevol amb la pública. Resol la distribució: inventari publica la seva clau pública i qualsevol li pot enviar un secret que només ell obre. Dues famílies:
| Família | Exemples | Mida de clau per a ~128 bits de seguretat | Ús |
|---|---|---|---|
| RSA | RSA-2048, RSA-3072 | 2048-3072 bits | Signatures (RS256 de 06-01), certificats; xifratge de claus petites |
| Corbes el·líptiques | ECDSA P-256, Ed25519 (signatura), X25519 (intercanvi de claus) | 256 bits | Signatures (ES256, EdDSA), intercanvi de claus a TLS 1.3; més ràpid i compacte |
El que no es fa és xifrar dades voluminoses amb RSA: és centenars de vegades més lent que AES i només pot xifrar missatges més curts que la clau. La solució universal és el xifratge híbrid: es genera una clau simètrica aleatòria per al missatge, es xifra el missatge amb AES-GCM, i es xifra aquesta clau (32 bytes) amb la clau pública del destinatari. TLS fa exactament això a cada connexió (apartat 6), i l'envelope encryption de l'apartat 10 és el mateix patró aplicat a l'emmagatzematge.
- Hashes, HMAC i signatures digitals
Un hash criptogràfic (SHA-256, SHA-3, BLAKE2) resumeix qualsevol entrada en un valor fix de manera que és inviable trobar l'entrada a partir del hash, o dues entrades amb el mateix hash. Dona integritat només si el hash arriba per un canal fiable: un atacant que pot canviar el fitxer pot canviar el hash que l'acompanya. Per això hi ha dues construccions amb clau:
- HMAC (hash-based message authentication code):
HMAC(clau, missatge). Només qui té la clau el pot generar o verificar. Dona integritat i autenticitat entre parts que comparteixen un secret; no dona no-repudi (qualsevol de les parts el va poder generar). És el que signa els URL presignats de MinIO (04-03) i els JWT HS256. - Signatura digital (RSA-PSS, ECDSA, Ed25519): hash del missatge xifrat amb la clau privada. Qualsevol la verifica amb la pública; només el signant la va poder produir. Integritat, autenticitat i no-repudi. És RS256 a 06-01 i la base dels certificats TLS.
Un cas pràctic d'HMAC a Quilòmetre Zero: l'embolcall d'esdeveniments de 02-04. Avui qualsevol procés amb accés a Kafka pot publicar un comanda.creada fals a comandes.esdeveniments, o modificar un esdeveniment reproduït des del llac. Afegir un HMAC de l'embolcall, amb una clau que només tenen els productors legítims i els consumidors, permet a inventari i analitica descartar esdeveniments manipulats:
# km0/serveis/comu/esdeveniments_signats.py
import hmac, hashlib, json
def _canonic(esdeveniment: dict) -> bytes:
"""Serialització determinista: mateixes claus, mateix ordre, sense espais.
Sense això, dos JSON equivalents donarien HMAC diferents."""
sense_signatura = {k: v for k, v in esdeveniment.items() if k != "signatura"}
return json.dumps(sense_signatura, sort_keys=True, separators=(",", ":"),
ensure_ascii=False).encode()
def signar_esdeveniment(esdeveniment: dict, clau: bytes) -> dict:
esdeveniment["signatura"] = hmac.new(clau, _canonic(esdeveniment), hashlib.sha256).hexdigest()
return esdeveniment
def verificar_esdeveniment(esdeveniment: dict, clau: bytes) -> bool:
esperada = hmac.new(clau, _canonic(esdeveniment), hashlib.sha256).hexdigest()
# compare_digest: temps constant, per no filtrar quants bytes coincideixen
return hmac.compare_digest(esperada, esdeveniment.get("signatura", ""))Al productor de comandes (02-04), abans de producer.send, es crida signar_esdeveniment(esdeveniment, CLAU_ESDEVENIMENTS); a cada consumidor, if not verificar_esdeveniment(esdeveniment, CLAU_ESDEVENIMENTS): descartar_i_alertar(). La clau es distribueix amb el gestor de secrets de 06-04. Quan els consumidors no hagin de poder produir (per exemple, un tercer que llegeix repartiment.panell), l'embolcall se signa amb Ed25519 en lloc d'HMAC: el mateix codi amb cryptography.hazmat.primitives.asymmetric.ed25519.
- Taula resum: quina primitiva per a quin problema
| Necessitat | Primitiva | Clau | Exemple a Quilòmetre Zero |
|---|---|---|---|
| Xifrar volum de dades | AES-256-GCM (AEAD) | Simètrica | Telèfon de l'Anna a km0_comandes; volums; backups |
| Enviar una clau a algú / acordar-la | RSA-OAEP, X25519 (ECDH) | Asimètrica | Handshake TLS; envelope encryption amb KMS |
| Detectar alteració amb secret compartit | HMAC-SHA256 | Simètrica | Embolcall de comandes.esdeveniments; URL presignats |
| Detectar alteració sense secret compartit, amb autoria | Signatura Ed25519 / ECDSA / RSA-PSS | Asimètrica | JWT RS256 (06-01); certificats; esdeveniments cap a tercers |
| Empremta d'un contingut | SHA-256 | Cap | Deduplicar fotos a km0-fotos; ETag; hash encadenat d'auditoria (06-05) |
| Desar contrasenyes | Argon2id / bcrypt | Cap (sal) | identitat (06-01) |
| Derivar claus d'una de mestra | HKDF | Simètrica | Una clau per servei i propòsit a partir d'una arrel |
- Dades en trànsit: TLS
TLS (Transport Layer Security) és el protocol que xifra i autentica una connexió TCP: HTTPS és HTTP sobre TLS, i gRPC, PostgreSQL, Kafka i Redis el suporten de manera nativa. Combina tot l'anterior: xifratge asimètric per acordar una clau, simètric per al trànsit, signatures per autenticar el servidor, hashes per a la integritat.
El handshake de TLS 1.3, resumit:
sequenceDiagram
participant C as Client (comandes)
participant S as Servidor (inventari)
C->>S: ClientHello: versions, suites, clau pública efímera (X25519)
S->>C: ServerHello: suite triada, clau pública efímera
Note over C,S: Tots dos deriven la mateixa clau simètrica (ECDH).<br/>A partir d'aquí tot va xifrat.
S->>C: Certificate (cadena) + CertificateVerify (signatura amb la privada) + Finished
C->>C: Verifica la cadena fins a una CA de confiança,<br/>que el nom coincideix (SAN), dates, revocació
C->>S: Finished
C->>S: Dades d'aplicació (gRPC) xifrades amb AES-GCM / ChaCha20
Conceptes que convé fixar:
- Certificat: la clau pública del servidor més la seva identitat (nom DNS al camp Subject Alternative Name, SAN), signades per una autoritat de certificació (CA). El client confia en la CA (té el seu certificat arrel al magatzem de confiança) i, per transitivitat, en el servidor.
- Cadena de confiança: arrel → intermèdia → servidor. L'arrel es guarda fora de línia; les intermèdies signen els certificats dels servidors. A Internet, les arrels les distribueixen els sistemes operatius i els navegadors; dins de Quilòmetre Zero, la CA serà pròpia (aquesta lliçó la crea amb OpenSSL; 06-04 la converteix en una PKI amb emissió automàtica i certificats per a clients).
- Secret perfecte cap endavant (forward secrecy): a TLS 1.3 la clau de sessió s'acorda amb claus efímeres que es destrueixen; robar la clau privada del servidor demà no permet desxifrar el trànsit capturat avui.
- TLS 1.3 (2018) va eliminar les suites febles, va reduir el handshake a un viatge d'anada i tornada i xifra el certificat del servidor. TLS 1.0 i 1.1 estan obsolets; 1.2 s'accepta si està ben configurat. Configura sempre
mínim 1.2, preferit 1.3. - Terminació: la connexió TLS pot acabar al mateix servei o en un component davant seu (el gateway de 06-05 terminarà el TLS d'Internet). Entre el terminador i el servei, si hi ha xarxa, cal TLS un altre cop.
El que TLS no fa per defecte: autenticar el client. inventari sabrà que parla amb xifratge, i comandes sabrà que parla amb el veritable inventari, però inventari no sap si el client és comandes o un procés qualsevol. Això és mTLS, i és la lliçó 06-04.
- TLS a gRPC entre
comandes i inventari
comandes i inventariSubstituirem l'add_insecure_port que 02-03 va deixar "sense TLS de moment". Primer, una CA interna i un certificat de servidor amb OpenSSL:
# km0/certs/generar.sh (desenvolupament; en producció ho farà la PKI de 06-04)
set -e
mkdir -p km0/certs && cd km0/certs
# 1. CA arrel de Quilòmetre Zero (clau EC P-256, vàlida 10 anys, només en desenvolupament)
openssl ecparam -name prime256v1 -genkey -noout -out km0-ca.key
openssl req -x509 -new -key km0-ca.key -sha256 -days 3650 \
-subj "/CN=Quilometre Zero CA interna" -out km0-ca.crt
# 2. Clau i CSR per a inventari, amb SAN (nom DNS a Compose i localhost per a proves)
openssl ecparam -name prime256v1 -genkey -noout -out inventari.key
openssl req -new -key inventari.key -subj "/CN=inventari" -out inventari.csr
cat > inventari.ext <<EOF
subjectAltName = DNS:inventari, DNS:inventari.km0.internal, DNS:localhost
extendedKeyUsage = serverAuth
EOF
# 3. La CA signa el certificat d'inventari (90 dies: els certificats curts es roten, no es vigilen)
openssl x509 -req -in inventari.csr -CA km0-ca.crt -CAkey km0-ca.key -CAcreateserial \
-days 90 -sha256 -extfile inventari.ext -out inventari.crt
openssl x509 -in inventari.crt -noout -subject -dates -ext subjectAltName
rm inventari.csr inventari.extEl detall que més fallades produeix és el SAN: el client compara el nom al qual s'ha connectat (inventari, el nom del servei a Compose) amb els del certificat; si no coincideix, rebutja la connexió encara que la signatura sigui vàlida. El CN ja no compta per a aquesta comprovació.
Servidor (serveis/inventari/servidor.py), només canvia l'arrencada:
import grpc
from concurrent import futures
def _llegir(ruta):
with open(ruta, "rb") as f:
return f.read()
credencials = grpc.ssl_server_credentials(
private_key_certificate_chain_pairs=[(_llegir("certs/inventari.key"),
_llegir("certs/inventari.crt"))],
# root_certificates i require_client_auth arriben a 06-04 (mTLS)
)
servidor = grpc.server(futures.ThreadPoolExecutor(max_workers=10),
interceptors=[AuthInterceptor()]) # el de 06-01 continua aquí
inventari_pb2_grpc.add_InventariServicer_to_server(InventariServicer(), servidor)
servidor.add_secure_port("0.0.0.0:50051", credencials) # abans: add_insecure_portClient a comandes:
# km0/serveis/comandes/client_inventari.py (fragment)
credencials = grpc.ssl_channel_credentials(root_certificates=_llegir("certs/km0-ca.crt"))
canal = grpc.secure_channel("inventari:50051", credencials) # el nom ha de ser al SAN
stub = inventari_pb2_grpc.InventariStub(canal)
# El JWT continua viatjant a les metadades, ara xifrat per TLS:
stub.ReservarEstoc(req, metadata=(("authorization", f"Bearer {token}"),), timeout=2)Al client només se li dona la CA, no el certificat d'inventari: així, quan el certificat d'inventari es renovi d'aquí a 90 dies, comandes continuarà confiant-hi sense canviar res. Si comandes es connecta per localhost:50051 des del host, funciona perquè localhost és al SAN; si es connectés per IP, fallaria (Ssl handshake failed ... Hostname mismatch).
Al docker-compose.yml, inventari i comandes munten ./certs en només lectura; el fitxer km0-ca.key no es munta a cap servei (només cal per signar):
inventari:
build: ./serveis/inventari
volumes:
- ./certs/inventari.crt:/app/certs/inventari.crt:ro
- ./certs/inventari.key:/app/certs/inventari.key:ro
comandes:
build: ./serveis/comandes
volumes:
- ./certs/km0-ca.crt:/app/certs/km0-ca.crt:roPots comprovar que el canal va xifrat capturant el trànsit: docker compose exec comandes tcpdump -A port 50051 mostrava abans formatge-curat i el JWT en clar; ara només bytes il·legibles després del ClientHello.
- TLS a PostgreSQL, Kafka, Redis i MinIO
Cada magatzem dels Mòduls 2 i 4 té la seva manera d'activar TLS. No la desenvolupem aquí, però sí que la deixem localitzada, perquè tots els clients de Quilòmetre Zero hauran de canviar la seva cadena de connexió:
| Sistema | Costat servidor | Costat client (Python) | Nota |
|---|---|---|---|
PostgreSQL (km0_analitica, km0_inventari) |
ssl = on, ssl_cert_file, ssl_key_file a postgresql.conf; hostssl a pg_hba.conf per obligar |
psycopg.connect(..., sslmode="verify-full", sslrootcert="certs/km0-ca.crt") |
sslmode=require xifra però no verifica el servidor; verify-full sí |
Kafka (comandes.esdeveniments, repartiment.posicions...) |
Listener SSL://:9093, ssl.keystore.location, ssl.truststore.location; SASL_SSL si a més hi ha autenticació |
KafkaProducer(security_protocol="SSL", ssl_cafile="certs/km0-ca.crt") |
Kafka fa servir keystores JKS/PKCS12; converteix-los amb keytool/openssl pkcs12 |
| Redis (memòria cau, sessions, revocats) | tls-port 6379, port 0, tls-cert-file, tls-key-file, tls-ca-cert-file |
redis.Redis(ssl=True, ssl_ca_certs="certs/km0-ca.crt") o rediss:// |
Redis ≥ 6; els clients de Redis Cluster de 04-05 també ho suporten |
Cassandra (km0_comandes) |
client_encryption_options: enabled: true a cassandra.yaml |
Cluster(ssl_context=ctx) amb un ssl.SSLContext carregant la CA |
La comunicació entre nodes es xifra a part (server_encryption_options) |
MinIO (km0-fotos...) |
Certificats a ~/.minio/certs/public.crt i private.key |
boto3.client("s3", endpoint_url="https://minio:9000", verify="certs/km0-ca.crt") |
Els URL presignats de 04-03 passen a https:// sense més canvis |
HDFS (/km0/esdeveniments/...) |
dfs.http.policy=HTTPS_ONLY, dfs.encrypt.data.transfer=true |
WebHDFS per https:// |
Kerberos per a l'autenticació és un món a part, fora de l'abast del curs |
La regla comuna: verificar sempre el certificat del servidor contra la CA interna. Un client que xifra sense verificar (sslmode=require, verify=False) protegeix contra escoltes passives però no contra un servidor impostor, que és l'atac d'"home al mig" que TLS existeix per impedir.
- Dades en repòs: disc, base de dades, columna
Les dades passen molt més temps en repòs que en trànsit, i allà les amenaces són unes altres: un disc retirat sense esborrar, un volum de núvol clonat, una còpia de seguretat en un bucket accessible, un administrador de base de dades curiós, o un SELECT * d'un servei compromès. Hi ha tres nivells, que s'acumulen:
| Nivell | Què protegeix | Contra què | Contra què no | Cost |
|---|---|---|---|---|
| Disc / volum (LUKS, dm-crypt, xifratge de volums EBS/PD) | Tot el que hi ha al disc | Disc robat o reciclat, volum clonat, accés físic | Qualsevol amb accés al sistema en marxa: la base de dades veu les dades en clar | Gairebé nul (AES-NI); transparent |
| Base de dades (TDE: transparent data encryption, xifratge dels fitxers de dades i del WAL) | Els fitxers de la BD i els seus backups | Fitxers copiats, backups filtrats | Administradors de la BD, qualsevol SELECT autoritzat |
Baix; cal gestionar la clau fora de la BD |
Columna / camp (l'aplicació xifra abans de l'INSERT) |
Camps concrets: telèfon, adreça, IBAN | DBA, dumps, un servei compromès que llegeix la taula, filtracions de rèpliques i analítica | Un compromís del servei que posseeix la clau | Alt: no es pot indexar ni cercar pel camp; cal gestionar claus per servei |
Quilòmetre Zero aplica els tres: volums xifrats a tota la infraestructura (una línia de configuració al núvol o a la instal·lació), TDE on el motor ho ofereixi (Cassandra i PostgreSQL amb extensions o xifratge de fitxers), i xifratge de camp per a les dades personals sensibles que només un servei necessita en clar. El telèfon de l'Anna el necessita repartiment perquè el repartidor truqui en arribar; analitica no l'hauria de veure mai. Amb xifratge de camp i clau pròpia de repartiment, encara que analitica llegeixi la taula, hi veu bytes.
- Envelope encryption: el telèfon de l'Anna
Si cada servei xifra els seus camps amb una clau, on viu aquesta clau? Desar-la al costat de les dades anul·la el xifratge; desar-la a cada procés fa la rotació impossible (caldria rexifrar tota la taula). El patró estàndard és el xifratge per sobres (envelope encryption):
- Existeix una clau mestra (KEK, key encryption key) que mai no surt d'un KMS (key management service): AWS KMS, Google Cloud KMS, Azure Key Vault, o el motor
transitde HashiCorp Vault. El KMS ofereix dues operacions:xifrar(bytes)idesxifrar(bytes), amb control d'accés i auditoria. - Per a cada dada (o cada lot, o cada client) es genera una clau de dades (DEK) aleatòria, es xifra la dada amb AES-GCM i la DEK, i es xifra la DEK amb la KEK al KMS.
- Es desen juntes la dada xifrada i la DEK xifrada. La DEK en clar es descarta.
- Per llegir: es demana al KMS que desxifri la DEK, i amb ella es desxifra la dada.
flowchart LR
T[Telèfon de l'Anna<br/>+34 600 123 456] -->|AES-GCM amb DEK| TC[Telèfon xifrat]
DEK[DEK aleatòria<br/>32 bytes] -->|KMS.xifrar amb KEK| DEKC[DEK xifrada]
TC --> BD[(km0_comandes<br/>telefon_xif, dek_xif, kek_id)]
DEKC --> BD
KMS[KMS / Vault transit<br/>KEK 'km0-comandes-2026' no surt mai]
DEK -.generada localment.-> DEK
L'avantatge decisiu: rotar la KEK no obliga a rexifrar les dades, només a rexifrar les DEK (32 bytes cadascuna, i es pot fer de manera mandrosa), i revocar l'accés d'un servei a la KEK deixa totes les seves dades il·legibles de cop, encara que tingui la taula sencera. A Quilòmetre Zero no tenim un KMS de núvol; a 06-04 Vault ocuparà aquest paper amb el seu motor transit. Per a aquesta lliçó, simulem el KMS amb una classe que exposa només xifrar_dek/desxifrar_dek i guarda la KEK fora de l'abast del codi de negoci:
# km0/serveis/comandes/xifratge_camps.py
import os, base64, json
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
class KMSLocal:
"""Substitut de desenvolupament d'un KMS: la KEK viu aquí i no s'exposa mai.
A 06-04 aquesta classe se substitueix pel motor 'transit' de Vault."""
def __init__(self, kek_per_id: dict[str, bytes], kek_activa: str):
self._keks = kek_per_id
self.kek_activa = kek_activa
def xifrar_dek(self, dek: bytes) -> tuple[str, bytes]:
nonce = os.urandom(12)
return self.kek_activa, nonce + AESGCM(self._keks[self.kek_activa]).encrypt(nonce, dek, b"dek")
def desxifrar_dek(self, kek_id: str, dek_xifrada: bytes) -> bytes:
kek = self._keks[kek_id] # KeyError si la KEK s'ha retirat: crypto-shredding
return AESGCM(kek).decrypt(dek_xifrada[:12], dek_xifrada[12:], b"dek")
class XifradorCamps:
"""Xifra camps d'un registre amb envelope encryption i AAD = id del registre."""
def __init__(self, kms: KMSLocal):
self._kms = kms
def xifrar(self, valor: str, registre_id: str) -> str:
dek = AESGCM.generate_key(bit_length=256)
nonce = os.urandom(12)
xifrat = AESGCM(dek).encrypt(nonce, valor.encode(), registre_id.encode())
kek_id, dek_xifrada = self._kms.xifrar_dek(dek)
sobre = {"v": 1, "kek": kek_id,
"dek": base64.b64encode(dek_xifrada).decode(),
"n": base64.b64encode(nonce).decode(),
"c": base64.b64encode(xifrat).decode()}
return json.dumps(sobre, separators=(",", ":")) # es desa com a TEXT/BLOB
def desxifrar(self, sobre_json: str, registre_id: str) -> str:
s = json.loads(sobre_json)
dek = self._kms.desxifrar_dek(s["kek"], base64.b64decode(s["dek"]))
return AESGCM(dek).decrypt(base64.b64decode(s["n"]), base64.b64decode(s["c"]),
registre_id.encode()).decode()
if __name__ == "__main__":
# En desenvolupament, la KEK ve d'una variable d'entorn carregada des del gestor de secrets (06-04)
kms = KMSLocal({"km0-comandes-2026": base64.b64decode(os.environ["KM0_KEK_COMANDES"])},
kek_activa="km0-comandes-2026")
xif = XifradorCamps(kms)
sobre = xif.xifrar("+34 600 123 456", "P-2026-000123")
print(sobre[:80], "...")
print(xif.desxifrar(sobre, "P-2026-000123")) # +34 600 123 456
# Copiar el sobre a la comanda del Marc no serveix: l'AAD el delata
try:
xif.desxifrar(sobre, "P-2026-000124")
except Exception as e:
print("Rebutjat:", type(e).__name__) # InvalidTagA Cassandra (km0_comandes), la columna telefon passa a ser telefon_xif text, i a serveis/comandes només el codi que atén repartiment crida desxifrar. Els requisits de consulta canvien: no es pot fer WHERE telefon = ... sobre un camp xifrat amb nonce aleatori. Si cal cercar per telèfon (per exemple, perquè el suport localitzi una comanda), s'afegeix una columna telefon_hmac amb HMAC(clau_index, telefon normalitzat): determinista, cercable, i no reversible. És el mateix truc de la pseudonimització de l'apartat 12.
Per als camps de pagaments, la regla és no tenir-los: el número de targeta (PAN) no arriba mai a Quilòmetre Zero; el navegador el lliura directament a la passarel·la de pagament, que retorna un token de pagament que pagaments emmagatzema. Xifrar un PAN propi és assumir PCI DSS complet; delegar és l'única opció sensata per a un marketplace.
- Gestió de claus: rotació, entorns i on no van
El xifratge desplaça el problema: de protegir dades a protegir claus, que són poques i petites. Principis:
- Mai al codi ni al repositori. Ni en un
.py, ni en undocker-compose.ymlversionat, ni en una imatge de contenidor. Un secret a Git ho és per sempre a l'historial. La variable d'entorn de l'exemple anterior és un pas intermedi que 06-04 substitueix per injecció des de Vault. - Una clau per entorn: desenvolupament, proves i producció no comparteixen cap clau. Un bolcat de la base de proves no ha de ser mai desxifrable amb la clau de producció.
- Una clau per servei i per propòsit:
comandesno pot desxifrar els camps derepartiment, i la clau d'HMAC d'esdeveniments no és la de xifratge de camps. Amb HKDF se'n deriven diverses d'una arrel si cal, però les KEK de producció viuen al KMS. - Rotació programada: la KEK anualment (o abans si hi ha sospita), les DEK per registre (no cal rotar-les: es regeneren en reescriure), els certificats TLS cada 90 dies o menys, la clau de signatura de JWT cada pocs mesos amb
kid. Tot xifrat guarda l'identificador de la clau amb què es va fer, per poder desxifrar durant la transició. - Separació de funcions: qui administra la base de dades no administra el KMS; qui desplega no veu les claus de producció.
- Còpia de seguretat de les claus, xifrades i en un lloc diferent de les dades: unes dades xifrades la clau de les quals s'ha perdut són dades esborrades.
El que aquí és principi, a 06-04 serà operació: Vault, credencials dinàmiques, leases i auditoria d'accessos a cada clau.
- Dades personals: minimització, pseudonimització, anonimització i dret a l'oblit
L'Anna, el Marc i la Llúcia són persones, i els seus noms, adreces, telèfons, correus, historial de compres i (a repartiment.posicions) la posició dels repartidors són dades personals subjectes al RGPD. La criptografia és una de les eines, no l'única, i les decisions de disseny importen més que els algorismes:
- Minimització: no recollir ni conservar el que no cal. El llac de dades de 04-02 desava a
/km0/esdeveniments/<dia>/comandes.jsonll'esdeveniment complet, amb correu i adreça.analiticanecessita saber que un mateix client va comprar tres vegades, no qui és. Ivendes_diariesno necessita ni això. - Pseudonimització: substituir els identificadors directes per un pseudònim estable que només es pot revertir amb informació guardada a part.
client_iden lloc del correu és el mínim; encara millor, un HMAC delclient_idamb una clau queanaliticano té, de manera que ni tan sols unJOINaccidental ambkm0_comandesreidentifiqui. Les dades pseudonimitzades continuen sent personals, però el risc d'una fuita del llac baixa radicalment. - Anonimització: transformar les dades de manera que la reidentificació sigui raonablement impossible: agregar (vendes per producte i dia, no per client), generalitzar (ciutat en lloc d'adreça; franja horària en lloc d'instant), suprimir valors rars (una única comanda de
vi-criancaa Lleida un dimarts identifica algú encara que no hi hagi nom). Els agregats dekm0_analiticasón anònims; els esdeveniments pseudonimitzats no. - Tokenització: reemplaçar un valor per un token sense cap relació matemàtica amb ell, amb una cambra cuirassada que guarda la correspondència; és el que fa la passarel·la de pagament amb la targeta.
- Dret de supressió ("a l'oblit"): quan la Llúcia demana que s'esborrin les seves dades, cal esborrar-les de
km0_comandes, de les rèpliques, dels backups, del llac i dels tòpics de Kafka amb retenció llarga. Esborrar d'un backup immutable o d'un log de Kafka és impracticable. El crypto-shredding ho resol: si les dades personals de la Llúcia es van xifrar amb una DEK pròpia de la Llúcia (una per client, desada xifrada per la KEK), destruir aquesta DEK converteix totes les seves dades, a tot arreu, en soroll irrecuperable, sense tocar els backups. És la raó de pes per triar "una DEK per client" en lloc d'"una per registre" a l'apartat 10.
La pseudonimització d'esdeveniments per al llac, en codi:
# km0/serveis/analitica/pseudonimitzar.py
# S'executa al consumidor que escriu /km0/esdeveniments/<dia>/comandes.jsonl (04-02),
# de manera que el llac no rebi mai identificadors directes.
import hmac, hashlib
CAMPS_DIRECTES = {"email", "nom", "telefon", "adreca"} # s'eliminen
CAMPS_PSEUDONIM = {"client_id"} # se substitueixen
def pseudonimitzar(esdeveniment: dict, clau_pseudonims: bytes) -> dict:
dades = dict(esdeveniment["dades"])
for camp in CAMPS_DIRECTES:
dades.pop(camp, None)
for camp in CAMPS_PSEUDONIM:
if camp in dades:
dades[camp] = hmac.new(clau_pseudonims, dades[camp].encode(),
hashlib.sha256).hexdigest()[:24]
if "adreca_lliurament" in dades: # generalitzar: només el mercat
dades["mercat"] = dades.pop("adreca_lliurament").get("mercat") # 'girona'
return {**esdeveniment, "dades": dades}
# Entrada: {"tipus": "comanda.creada", "dades": {"comanda_id": "P-2026-000125", "client_id": "u-llucia",
# "email": "[email protected]", "telefon": "+34 6...", "linies": [...],
# "adreca_lliurament": {"carrer": "...", "mercat": "lleida"}}}
# Sortida: {"tipus": "comanda.creada", "dades": {"comanda_id": "P-2026-000125",
# "client_id": "9f1c2a...b7", "linies": [...], "mercat": "lleida"}}clau_pseudonims la custodia identitat (o el KMS), no analitica: així l'equip de dades pot comptar clients recurrents i calcular recomanacions (05-03) sense poder saber qui és 9f1c2a...b7. Si un dia cal contactar amb aquest client (per exemple, per a una retirada de producte), la petició passa per identitat, que sí que pot recalcular l'HMAC de cada client i trobar-ne la correspondència, amb auditoria de qui ho ha demanat (06-05).
Advertència de compliment normatiu. El RGPD (i la LOPDGDD a Espanya) exigeix molt més que xifratge: base jurídica, informació a l'interessat, registre d'activitats de tractament, avaluació d'impacte quan hi ha seguiment de posicions, contractes amb encarregats (la passarel·la de pagament, el proveïdor de núvol), i notificació de bretxes en 72 hores. PCI DSS regula qualsevol contacte amb dades de targeta. El que es descriu aquí és el disseny tècnic; la política de retenció, l'abast de la pseudonimització i el procediment de supressió s'han de definir i revisar amb un professional de protecció de dades i seguretat.
- Mapa de protecció de Quilòmetre Zero
Amb tot l'anterior, cada dada de la plataforma queda localitzada i protegida:
| Dada | On viu | En trànsit | En repòs | Personal |
|---|---|---|---|---|
| Contrasenyes | identitat (PostgreSQL) |
TLS | Argon2id (irreversible) | Sí |
| JWT d'accés | Memòria de l'app; metadades gRPC / Authorization |
TLS (gRPC i HTTP) | No es persisteix | Conté sub |
| Catàleg, preus, fotos | cataleg (PostgreSQL), Redis, km0-fotos |
TLS; URL presignats per HTTPS | Volum xifrat; SSE a MinIO | No |
| Comandes (línies, imports) | km0_comandes (Cassandra) |
TLS client-node i entre nodes | Volum xifrat + TDE | Sí (vinculades a client_id) |
| Telèfon i adreça de lliurament | km0_comandes |
TLS | Xifratge de camp (envelope, DEK per client, KEK al KMS) | Sí, sensible |
| Dades de targeta | No s'emmagatzemen: token de la passarel·la a pagaments |
TLS navegador → passarel·la | Token opac | PCI DSS delegat |
| Estoc | km0_inventari (inv-bcn, inv-vlc) |
TLS | Volum xifrat | No |
Esdeveniments (comandes.esdeveniments, inventari.alertes) |
Kafka | TLS + HMAC de l'embolcall | Volum xifrat; retenció limitada | Sí fins a pseudonimitzar |
Posicions (repartiment.posicions, repartiment.panell) |
Kafka, Flink | TLS + HMAC | Retenció curta (hores) | Sí (repartidors): minimitzar i agregar |
| Llac d'esdeveniments | HDFS /km0/esdeveniments/... |
HTTPS / transferència xifrada | Volum xifrat; pseudonimitzat en escriure | Pseudònims |
| Agregats | km0_analitica (PostgreSQL) |
TLS | Volum xifrat | Anònims |
| Factures PDF | km0-factures (MinIO) |
HTTPS presignat | SSE-S3 amb clau de MinIO + object lock | Sí |
| Backups | km0-backups (MinIO) |
HTTPS | Xifrats pel procés de backup (AES-GCM, clau del KMS) abans de pujar-los, a més de SSE | Sí |
| Configuració d'etcd | etcd | TLS entre parells i clients | Volum xifrat; sense secrets a dins (06-04) | No |
Sobre el xifratge del costat del servidor a MinIO/S3 (SSE): el magatzem xifra cada objecte en escriure'l i el desxifra en servir-lo. SSE-S3 (clau gestionada pel magatzem) protegeix contra discos i volums; SSE-KMS (clau al KMS, amb auditoria per accés) protegeix a més contra administradors del magatzem; SSE-C (el client envia la clau a cada petició) deixa la custòdia al client. Per a km0-fotos n'hi ha prou amb SSE-S3; per a km0-factures i km0-backups, SSE-KMS i, en el cas dels backups, xifratge addicional al client perquè un compromís de MinIO no els exposi.
Errors Comuns i Consells
- Xifrar sense autenticar. AES-CBC o AES-CTR "a pèl" permeten manipular el text xifrat. Fes servir sempre un mode AEAD (GCM, ChaCha20-Poly1305) o xifratge + HMAC (encrypt-then-MAC).
- Reutilitzar nonces. Un nonce fix, o derivat d'alguna cosa repetible (l'id de la comanda), destrueix AES-GCM.
os.urandom(12)per xifratge, o un comptador que es persisteix. - Inventar criptografia. Ni algorismes propis ni combinacions creatives de primitives.
cryptographyamb la seva API d'alt nivell (AESGCM,Fernet) i protocols estàndard (TLS) són el límit del que un equip de producte ha de tocar. verify=Falseen desenvolupament que arriba a producció. Un client TLS que no verifica el certificat és una escolta passiva més. Fes servir la CA interna també en desenvolupament.- Certificat sense el SAN correcte. "Hostname mismatch" és l'error de TLS més freqüent; el SAN ha de contenir tots els noms pels quals s'accedeix al servei.
- Desar la clau al costat de les dades (a la mateixa base de dades, al mateix bucket, al
docker-compose.yml). Envelope encryption amb la KEK a fora, al KMS. - Xifrar un camp i després cercar-hi. No és possible amb nonce aleatori; planifica una columna HMAC determinista si necessites cercar.
- Comparar HMAC amb
==. La comparació normal s'atura al primer byte diferent i filtra informació pel temps;hmac.compare_digestsempre. - Confondre pseudonimitzar amb anonimitzar. Substituir el correu per un hash continua sent una dada personal; només l'agregació o la generalització suficient anonimitza.
- Backups en clar "perquè el bucket és privat". Els buckets es fan públics per error amb una freqüència sorprenent. Xifra abans de pujar.
- Consell: desa amb cada dada xifrada l'identificador de la clau i la versió del format (
{"v":1,"kek":"km0-comandes-2026",...}). Sense això, la primera rotació de clau és una migració a cegues. - Consell: prova la recuperació: un backup xifrat que mai no s'ha restaurat amb la clau d'un altre entorn és un backup que no existeix.
Exercicis
Exercici 1: Un esdeveniment manipulat
Un procés desconegut dins de la xarxa de Quilòmetre Zero publica a comandes.esdeveniments un esdeveniment comanda.creada per a P-2026-000126 amb una línia de 200 unitats de vi-crianca a 0,01 €. Explica què passa a inventari i analitica (a) amb el disseny de 02-04 sense canvis, (b) amb TLS a Kafka però sense HMAC, (c) amb l'HMAC de l'apartat 4. Després, indica què protegeix l'HMAC que TLS no protegeix, i viceversa, i per què l'HMAC no impedeix que el procés continuï publicant esdeveniments brossa al tòpic (quina lliçó ho resol).
Exercici 2: La Llúcia exerceix el seu dret de supressió
La Llúcia demana que s'esborrin les seves dades. Enumera on són (fes servir la taula de l'apartat 13) i, per a cada lloc, si es poden esborrar directament, si cal crypto-shredding, o si la dada ja no és personal. Explica què s'hauria d'haver decidit a l'apartat 10 perquè el crypto-shredding funcioni, i què queda a km0_analitica després.
Exercici 3: Rotar la KEK
L'equip de seguretat decideix rotar km0-comandes-2026 a km0-comandes-2027. Descriu pas a pas, fent servir les classes KMSLocal i XifradorCamps, què cal canviar perquè (1) les dades noves es xifrin amb la nova KEK, (2) les antigues es continuïn llegint, (3) les antigues es migrin a la nova sense rexifrar els telèfons, i (4) l'antiga es retiri. Quin camp del sobre fa possible el pas 2? Quants bytes cal rexifrar per registre al pas 3?
Solucions
Exercici 1.
(a) L'esdeveniment arriba, inventari el consumeix i reserva 200 unitats de vi-crianca de l'estoc del Celler Roure Alt (o esgota l'estoc i dispara inventari.alertes), analitica l'escriu al llac i vendes_diaries comptabilitza 2 € de vendes de vi; ningú no detecta res perquè l'esdeveniment és sintàcticament correcte. (b) TLS xifra el canal entre cada client i el broker i autentica el broker, però no el productor: el procés desconegut obre la seva pròpia connexió TLS vàlida i publica igualment; només protegeix contra algú que escolti o modifiqui el trànsit d'un altre client. (c) Tots dos consumidors calculen HMAC(clau, embolcall canònic), no coincideix amb el camp signatura (l'atacant no té la clau) i descarten l'esdeveniment amb una alerta; l'estoc no es toca i el llac no el rep. L'HMAC protegeix la integritat i l'autenticitat del missatge d'extrem a extrem, fins i tot davant del broker, d'un reproductor des del llac o d'un productor no autoritzat; TLS protegeix la connexió (confidencialitat i que el broker és l'autèntic), cosa que l'HMAC no fa (l'esdeveniment continua sent llegible al tòpic). L'HMAC no impedeix publicar perquè Kafka no exigeix que el productor s'identifiqui: això requereix autenticació del client al broker (SASL o certificats de client, és a dir, mTLS i ACL), que és la identitat de càrrega de treball de 06-04.
Exercici 2.
identitat (compte, hash de contrasenya): esborrat directe. km0_comandes: línies i imports vinculats a client_id: es poden esborrar o dissociar (substituir client_id per un marcador), segons el que exigeixi l'obligació fiscal de conservar factures; telèfon i adreça xifrats: crypto-shredding destruint la DEK de la Llúcia, cosa que també invalida les còpies a les rèpliques i als backups de km0-backups, on l'esborrat directe és impracticable. km0-factures: les factures tenen retenció legal d'anys amb object lock; no s'esborren, es justifica la conservació per obligació legal (i les seves dades de contacte poden estar xifrades amb la mateixa DEK). Kafka comandes.esdeveniments: els esdeveniments amb dades directes dins de la retenció no es poden editar; si la retenció és curta caduquen sols; si és llarga, és un motiu més perquè els esdeveniments portin els camps personals xifrats amb la DEK del client o pseudonimitzats. Llac HDFS: només conté el pseudònim HMAC; continua sent una dada personal mentre identitat conservi la clau i el client_id; en esborrar el compte a identitat, el pseudònim deixa de ser vinculable i es pot argumentar que queda anonimitzat (a revisar amb el professional de protecció de dades). km0_analitica: agregats anònims, no es toca. La decisió necessària a l'apartat 10 és una DEK per client (no per registre), desada xifrada per la KEK en un lloc central: destruir aquesta única DEK és l'esborrat.
Exercici 3.
(1) Afegir "km0-comandes-2027": nova_kek al diccionari de KMSLocal i posar kek_activa = "km0-comandes-2027": xifrar_dek farà servir la nova per a tot sobre nou. (2) Res més: desxifrar_dek rep s["kek"] del sobre i cerca aquesta KEK al diccionari, que continua contenint la 2026. El camp "kek" del sobre és el que ho fa possible. (3) Un procés de migració que, per a cada registre, fa dek = kms.desxifrar_dek(s["kek"], s["dek"]) i s["kek"], s["dek"] = kms.xifrar_dek(dek), i desa el sobre; el telèfon xifrat ("c") i el seu nonce no es toquen. Es rexifren 32 bytes (la DEK) més els 12 del nonce i 16 del tag del sobre de la DEK: uns 60 bytes per registre, davant de rexifrar el camp sencer i, sobretot, sense necessitat que el procés de migració vegi els telèfons en clar. (4) Quan cap sobre a la base de dades ni als backups que es vulguin mantenir llegibles no tingui "kek": "km0-comandes-2026", eliminar l'entrada del diccionari (a Vault: deshabilitar la versió). Qualsevol sobre oblidat donarà KeyError, que és exactament el comportament del crypto-shredding: per això es verifica amb una consulta abans de retirar-la.
Conclusió
Protegir una dada és garantir tres coses diferents, confidencialitat, integritat i autenticitat, i cadascuna té la seva eina: AES-GCM per xifrar volum amb nonce únic i etiqueta d'autenticació; RSA i corbes el·líptiques per acordar claus i signar, sempre en mode híbrid; hashes per a empremtes, HMAC per a integritat amb secret compartit, signatures per a autoria verificable per tothom. TLS les reuneix totes per al trànsit, amb certificats, cadena de confiança, SAN i TLS 1.3, i Quilòmetre Zero el té ara entre comandes i inventari amb una CA interna, i localitzat a la configuració de PostgreSQL, Kafka, Redis, Cassandra, MinIO i HDFS. En repòs, el xifratge s'acumula per capes (volum, base de dades, camp) i l'envelope encryption amb una KEK en un KMS fa possible rotar claus, revocar accessos i esborrar per destrucció de clau. Les dades personals de l'Anna, el Marc i la Llúcia es minimitzen, es pseudonimitzen abans d'entrar al llac, s'agreguen per a l'analítica, i es xifren amb una DEK per client que converteix el dret de supressió en una operació de 32 bytes. Les dades de targeta, senzillament, no hi entren. I tot això sota la revisió d'un professional de compliment normatiu, perquè el RGPD i PCI DSS són força més que criptografia.
Queda una peça que aquesta lliçó ha donat per resolta amb un fitxer .pub copiat a mà i un formulari de login propi: d'on surten les identitats. Els clients es registren a la web, els empleats tenen comptes corporatius, els productors volen entrar amb el seu compte de Google, l'app de repartidors necessita un flux sense contrasenya visible, i comandes necessita un token de màquina per parlar amb inventari. Gestionar tot això servei per servei és inviable. La següent lliçó centralitza la identitat en un proveïdor (Keycloak), connecta els empleats amb LDAP, i explica els protocols que fan que un token emès allà valgui a tota la plataforma: SAML, OAuth 2.0 i OpenID Connect.
Curs d'Arquitectures Distribuïdes
Mòdul 1: Introducció als Sistemes Distribuïts
- Conceptes Bàsics de Sistemes Distribuïts
- Models de Sistemes Distribuïts
- Avantatges i Desafiaments dels Sistemes Distribuïts
- Les Fal·làcies de la Computació Distribuïda
- Temps, Rellotges i Ordenació d'Esdeveniments
- Del Monòlit a la Plataforma Distribuïda: el Cas Quilòmetre Zero
Mòdul 2: Comunicació en Sistemes Distribuïts
- Protocols de Comunicació
- RPC i RMI
- gRPC i Serialització de Dades
- Missatgeria i Cues de Missatges
- Patrons de Comunicació Asíncrona
Mòdul 3: Consistència i Replicació
- Models de Consistència
- El Teorema CAP i PACELC
- Algorismes de Consens
- Replicació de Dades
- Transaccions Distribuïdes i Sagues
Mòdul 4: Emmagatzematge Distribuït
- Particionament de Dades i Hashing Consistent
- Sistemes de Fitxers Distribuïts
- Emmagatzematge d'Objectes
- Bases de Dades Distribuïdes
- Memòries Cau Distribuïdes
Mòdul 5: Computació Distribuïda
- Models de Computació Distribuïda
- MapReduce i Hadoop
- Spark i Computació en Memòria
- Processament de Fluxos de Dades
- Planificació de Treballs i Pipelines de Dades
Mòdul 6: Seguretat en Sistemes Distribuïts
- Autenticació i Autorització
- Xifratge i Protecció de Dades
- Gestió d'Identitats
- Seguretat entre Serveis: mTLS i Gestió de Secrets
- Passarel·les d'API, Limitació de Taxa i Auditoria
Mòdul 7: Monitoratge i Manteniment
- Monitoratge de Sistemes Distribuïts
- Logs Centralitzats i Traçabilitat Distribuïda
- Gestió de Fallades i Recuperació
- Patrons de Resiliència: Timeouts, Reintents i Circuit Breaker
- Automatització i Orquestració
- Proves en Sistemes Distribuïts i Enginyeria del Caos
