La lliçó anterior va acabar assenyalant dos deutes que arrossegues des del Mòdul 5, i tots dos són criptogràfics. El primer: db_password està escrita en clar dins de /etc/tramontana/app.conf, i ja has vist que qualsevol fallada de permisos converteix això en una fuita de credencials que cap vigilància no evita. El segon: el trànsit de reserves —amb els noms dels hostes que tanta cura has posat a protegir dins de les còpies— viatja sense xifrar pel port 8080. Aquesta lliçó resol totes dues, perquè són el mateix problema vist des de dos angles: guardar un secret i demostrar una identitat.

Són dues parts clarament separades. A la primera aprendràs a treure un secret d'un fitxer de configuració i a gestionar-lo amb les eines que el sistema ja et dona: permisos estrictes, EnvironmentFile i LoadCredential de systemd, GPG, pass i LUKS. A la segona, què és realment un certificat, com es genera, es llegeix i es verifica, com se n'obté un de gratuït i renovat automàticament de Let's Encrypt, i com vigilar-ne la caducitat — que és la fallada més ximple i més freqüent de producció.

Avís d'abast. Aquí s'obté, s'instal·la i es verifica el material criptogràfic de reserves.tramontana.example. El servidor web que el servirà —Nginx com a servidor intermediari invers davant de l'aplicació del port 8080— es munta al projecte 08-01. És deliberat: el xifratge en trànsit es prepara i es prova ara perquè al Mòdul 8 sigui un detall de configuració i no un tema nou.

Advertiment de compliment. El xifratge de dades personals, en trànsit i en repòs, no és una millora opcional: el RGPD l'exigeix com a mesura tècnica apropiada (art. 32). reserves.csv conté noms d'hostes. En un entorn real, el disseny de la gestió de claus i de la seva custòdia l'ha de revisar el responsable de seguretat, i les decisions sobre dades personals el responsable de protecció de dades.

Contingut

  1. Què és un secret i on no va mai
  2. El mínim decent: permisos, EnvironmentFile i LoadCredential
  3. Xifratge en repòs de secrets: GPG i pass
  4. Xifratge de disc amb LUKS
  5. Gestors centralitzats de secrets
  6. Rotació de credencials
  7. Què garanteix TLS i què conté un certificat
  8. Generar, llegir i verificar material criptogràfic amb OpenSSL
  9. Let's Encrypt i la renovació automàtica
  10. Bones pràctiques de TLS, custòdia de la clau i vigilància de la caducitat

Què és un secret i on no va mai

Un secret és una dada el valor de la qual depèn que només la conegui qui ha de fer-ho: una contrasenya, una clau d'API, un testimoni, una clau privada, la frase de pas d'un dipòsit de còpies. Es distingeix d'una dada sensible qualsevol perquè el seu compromís no és un problema de privacitat sinó de control: qui el té pot actuar en nom teu.

La primera regla és negativa, i convé entendre el perquè de cada punt perquè cadascun correspon a una fuita real i freqüent:

On NO va un secret Per què
Al codi font Acaba al dipòsit, i a git l'historial és permanent: esborrar la línia no esborra el commit
A la línia d'ordres Qualsevol usuari del sistema la veu amb ps aux mentre el procés corre
En una variable d'entorn d'un procés Llegible a /proc/<pid>/environ pel seu propietari i per root; i s'hereta als processos fills
Als registres Un set -x o un log "connectant amb $PASS" l'escriu al journal, que es conserva 30 dies
En una còpia de seguretat sense xifrar És l'incident que ja vas viure: la còpia amb permisos 644
En un fitxer de configuració llegible És l'estat actual d'app.conf, i és el que es corregeix en aquesta lliçó

Els dos primers mereixen una demostració, perquè la gent els subestima:

# Un secret a la linia d ordres es public mentre el proces viu
$ ps aux | grep -m1 'psql'
luis    3417  0.0  0.1  ... psql -h 10.0.2.15 -U tramontana --password=Zx9K2pQ

# I l entorn d un proces es llegible pel seu propietari
$ sudo tr '\0' '\n' < /proc/1284/environ | grep -i pass
TRAMONTANA_DB_PASSWORD=Zx9K2pQ

Sobre el segon cas convé ser precís, perquè hi ha un matís que sovint es malinterpreta: /proc/<pid>/environ té permisos 0400 i només el poden llegir el propietari del procés i root. Això fa que les variables d'entorn siguin molt millors que la línia d'ordres, però no les converteix en un bon lloc: s'hereten a tot procés fill, apareixen en abocaments de memòria i en traces d'error de molts entorns de treball, i qualsevol escalada a root les exposa totes de cop.

El mínim decent: permisos, EnvironmentFile i LoadCredential

No cal infraestructura per millorar substancialment. Amb el que ja saps de permisos (02-07), usuaris de servei (05-01) i unitats de systemd (05-05), hi ha tres nivells, cadascun millor que l'anterior.

Nivell 1: fitxer de secrets amb permisos estrictes

La idea és separar el secret de la configuració: app.conf deixa de contenir la contrasenya i passa a ser un fitxer llegible pel grup, mentre el secret viu en un fitxer a part amb permisos mínims.

# El fitxer de secrets: propietat de l usuari del servei, nomes ell pot llegir-lo
$ sudo install -o svc-tramontana -g svc-tramontana -m 600 /dev/null \
       /etc/tramontana/secrets.env
$ sudo tee /etc/tramontana/secrets.env >/dev/null <<'EOF'
TRAMONTANA_DB_PASSWORD=Zx9K2pQ7vLm4RtWn
EOF
$ sudo ls -l /etc/tramontana/
total 8
-rw-r----- 1 root            tramontana        312 ago 18 12:04 app.conf
-rw------- 1 svc-tramontana  svc-tramontana     42 ago 18 12:06 secrets.env

I la unitat el carrega a l'entorn del servei, sense que aparegui mai a la línia d'ordres:

# /etc/systemd/system/tramontana.service.d/override.conf
[Service]
EnvironmentFile=/etc/tramontana/secrets.env
$ sudo systemctl daemon-reload && sudo systemctl restart tramontana.service

Recorda la convenció del curs: app.conf està chattr +i, així que per editar-lo cal treure l'atribut, canviar, i tornar-lo a posar — amb la seva còpia prèvia:

$ sudo cp -p /etc/tramontana/app.conf /etc/tramontana/app.conf.bak-$(date +%F)
$ sudo chattr -i /etc/tramontana/app.conf
$ sudo sed -i.bak-$(date +%F) '/^db_password/d' /etc/tramontana/app.conf
$ sudo chattr +i /etc/tramontana/app.conf
$ sudo diff -u /etc/tramontana/app.conf.bak-$(date +%F) /etc/tramontana/app.conf
--- /etc/tramontana/app.conf.bak-2026-08-18
+++ /etc/tramontana/app.conf
@@ -3,7 +3,6 @@
 db_name=tramontana_reserves
-db_password=Zx9K2pQ7vLm4RtWn
 max_connexions=200

Això ja tanca l'exposició de l'últim exercici de 06-04: un chmod 644 accidental sobre app.conf ja no filtra res. Però el secret continua en clar al disc i a l'entorn del procés.

Nivell 2: LoadCredential

Ubuntu 24.04 porta la mecànica de credencials de systemd, que és clarament superior: el secret es lliura al servei en un fitxer dins d'un tmpfs privat accessible només per aquell servei, no s'hereta als fills i no apareix a l'entorn.

# /etc/systemd/system/tramontana.service.d/override.conf
[Service]
LoadCredential=db_password:/etc/tramontana/secrets/db_password
$ sudo install -d -o root -g root -m 700 /etc/tramontana/secrets
$ printf '%s' 'Zx9K2pQ7vLm4RtWn' | sudo tee /etc/tramontana/secrets/db_password >/dev/null
$ sudo chmod 600 /etc/tramontana/secrets/db_password

El servei rep la ruta a la variable CREDENTIALS_DIRECTORY i llegeix el fitxer:

# Dins del servei, el secret es a:
#   ${CREDENTIALS_DIRECTORY}/db_password
$ systemd-run --property=LoadCredential=prova:/etc/tramontana/secrets/db_password \
    --pty bash -c 'ls -l "$CREDENTIALS_DIRECTORY"; cat "$CREDENTIALS_DIRECTORY/prova"'
total 0
-r--r----- 1 root root 16 ago 18 12:22 prova
Zx9K2pQ7vLm4RtWn

La diferència amb EnvironmentFile en una taula, perquè decideix l'elecció:

EnvironmentFile= LoadCredential=
Visible a /proc/<pid>/environ Sí No
S'hereta als processos fills Sí No
Apareix a systemctl show Sí (el nom del fitxer) Només la ruta d'origen
Funciona amb ProtectSystem=strict Sí Sí, i el tmpfs és privat
Compatible amb qualsevol aplicació Sí, gairebé totes llegeixen de l'entorn Requereix que l'aplicació llegeixi d'un fitxer

Nivell 3: systemd-creds, secret xifrat al disc

El pas que tanca el cercle: systemd-creds xifra el secret amb una clau derivada del sistema (i del TPM si existeix), de manera que el fitxer al disc ja no conté el secret en clar.

$ printf '%s' 'Zx9K2pQ7vLm4RtWn' \
    | sudo systemd-creds encrypt --name=db_password - /etc/tramontana/secrets/db_password.cred
$ sudo cat /etc/tramontana/secrets/db_password.cred | head -c 80
-----BEGIN CREDENTIAL-----
CqiVzUCu5c1TF4TBTdxr4hK+3XPqmn9lBTJmMWk0YjMwNzY2MzQ0ZTk...
# /etc/systemd/system/tramontana.service.d/override.conf
[Service]
LoadCredentialEncrypted=db_password:/etc/tramontana/secrets/db_password.cred
$ sudo systemctl daemon-reload && sudo systemctl restart tramontana.service
$ sudo systemctl status tramontana.service --no-pager | head -5
● tramontana.service - Tramontana Reserves
     Loaded: loaded (/etc/systemd/system/tramontana.service; enabled)
     Active: active (running) since Tue 2026-08-18 12:31:08 CEST; 4s ago
   Main PID: 4102 (tramontana)

# Verificacio: el secret ja no es a l entorn del proces
$ sudo tr '\0' '\n' < /proc/4102/environ | grep -ci pass
0

Aquell 0 és el resultat que buscaves. El tercer incident obert queda tancat: la contrasenya ja no està en clar en cap fitxer de configuració, no és a l'entorn del procés, no és llegible per cap usuari del sistema, i —conseqüència important— tampoc no viatja en clar dins de la còpia de restic, perquè el que es copia és el fitxer xifrat.

Una limitació honesta: la clau de xifratge de systemd-creds està lligada a la màquina (/var/lib/systemd/credential.secret). Això és exactament el que vols davant de la fuita d'un fitxer, però significa que el fitxer .cred no es pot restaurar en un servidor reconstruït. El secret original ha d'estar també en un lloc del qual el puguis recuperar: això és la secció següent, i és el que ha de constar al manual d'operació de 05-08.

Xifratge en repòs de secrets: GPG i pass

Necessites un lloc on el secret original visqui xifrat, recuperable per una persona autoritzada, amb historial de canvis i fora del servidor. La resposta clàssica a Linux és GPG, i pass construïda al damunt.

GPG en dos modes

# Simetric: una frase de pas, sense claus. Simple i suficient per a un fitxer puntual
$ gpg -c --cipher-algo AES256 manual-credencials.txt
$ ls manual-credencials.txt.gpg
$ gpg -d manual-credencials.txt.gpg > /dev/shm/recuperat.txt

# Asimetric: xifrat per a destinataris concrets, sense compartir cap frase de pas
$ gpg --quick-generate-key "operador (Tramontana) <[email protected]>" ed25519 cert 2y
$ gpg -e -r [email protected] -r [email protected] secrets.txt
$ gpg -d secrets.txt.gpg

La diferència pràctica és de gestió: el mode simètric obliga a transmetre la frase de pas per un altre canal i a canviar-la quan algú deixa l'equip; l'asimètric permet xifrar per a diverses persones i revocar l'accés d'una sense tornar a xifrar per a les altres. En un equip, sempre asimètric.

Fixa't en el > /dev/shm/recuperat.txt del desxifratge: /dev/shm és memòria, no disc. Escriure un secret desxifrat al sistema de fitxers deixa restes recuperables fins i tot després d'esborrar-lo.

pass: el gestor construït sobre GPG

pass guarda cada secret en un fitxer xifrat amb GPG dins d'un dipòsit git. És simple, auditable i no depèn de cap servei.

$ sudo apt install pass
$ pass init [email protected]
mkdir: created directory '/home/operador/.password-store/'
Password store initialized for [email protected]

$ pass git init
$ pass insert tramontana/produccio/db_password
Enter password for tramontana/produccio/db_password:
Retype password for tramontana/produccio/db_password:
[master 8f2c1a4] Add given password for tramontana/produccio/db_password to store.

$ pass insert -m tramontana/produccio/restic
# (-m permet diverses linies: contrasenya, diposit, notes)

$ pass ls
Password Store
└── tramontana
    └── produccio
        ├── db_password
        └── restic

$ pass tramontana/produccio/db_password
Zx9K2pQ7vLm4RtWn

$ pass -c tramontana/produccio/db_password
Copied tramontana/produccio/db_password to clipboard. Will clear in 45 seconds.

Tres detalls que el fan apte per a producció:

  • Historial complet amb git. pass git log --oneline mostra cada alta, canvi i baixa, amb data i autor. És la traçabilitat de la rotació.

  • Els fitxers estan xifrats, així que el dipòsit es pot sincronitzar a un remot sense exposar res: pass git remote add origin ... i pass git push. Això resol el «fora del servidor».

  • Composició amb scripts. pass escriu a stdout, així que encaixa amb el del Mòdul 4:

    # Generar el fitxer .cred des de pass, sense que el secret toqui el disc en clar
    $ pass tramontana/produccio/db_password \\
        | sudo systemd-creds encrypt --name=db_password - \\
               /etc/tramontana/secrets/db_password.cred
    

    Aquella canonada és la resposta al problema de recuperabilitat de l'apartat anterior: pass és la font de veritat, systemd-creds la còpia operativa lligada a la màquina, i reconstruir el servidor consisteix a tornar a executar aquesta línia.

I l'advertiment obvi: la clau GPG privada i la seva frase de pas són ara la clau mestra. La seva còpia de seguretat (gpg --export-secret-keys, xifrada, en un mitjà físic separat) i la seva custòdia són part del manual d'operació, i la seva pèrdua equival a perdre tots els secrets.

Xifratge de disc amb LUKS

Els secrets ja estan xifrats, però /srv/tramontana/backups conté reserves.csv amb noms d'hostes. Si algú s'endú el disc —o la imatge de la VM, o el disc d'un servidor retirat sense esborrar—, els permisos del sistema de fitxers no protegeixen res: es munten en una altra màquina i es llegeix tot.

LUKS és l'estàndard de xifratge de bloc a Linux. Xifra el dispositiu complet, per sota del sistema de fitxers.

# Sobre un dispositiu NOU o el contingut del qual puguis perdre: luksFormat DESTRUEIX les dades
$ sudo cryptsetup luksFormat --type luks2 /dev/vg-dades/lv-backups
WARNING!
========
This will overwrite data on /dev/vg-dades/lv-backups irrevocably.
Are you sure? (Type 'yes' in capital letters): YES
Enter passphrase for /dev/vg-dades/lv-backups:

$ sudo cryptsetup luksOpen /dev/vg-dades/lv-backups backups-xifrat
$ sudo mkfs.ext4 -L backups /dev/mapper/backups-xifrat
$ sudo mount /dev/mapper/backups-xifrat /srv/tramontana/backups

$ sudo cryptsetup luksDump /dev/vg-dades/lv-backups | head -8
LUKS header information
Version:        2
Epoch:          3
Metadata area:  16384 [bytes]
Keyslots:
  0: luks2
        Key:        512 bits
        Cipher:     aes-xts-plain64

I aquí hi ha el problema real, que cal plantejar sense adorns: un volum LUKS necessita una frase de pas per obrir-se, i un servidor ha d'arrencar tot sol a les 3 de la matinada. Les opcions, amb el seu compromís:

Opció Com Cost
Frase de pas manual Algú la teclegi en cada arrencada Protecció màxima; el servidor no arrenca sense intervenció
Fitxer de clau al disc arrel /etc/crypttab amb keyfile Automàtic; protegeix només contra el robatori del disc de dades, no de l'arrel
TPM systemd-cryptenrol --tpm2-device=auto Automàtic i lligat al maquinari; requereix TPM i una configuració acurada
Desbloqueig remot en l'arrencada dropbear-initramfs Automàtic amb supervisió; més peces per mantenir

Per al laboratori, l'opció del fitxer de clau amb nofail és raonable, i és important entendre exactament què protegeix:

# /etc/crypttab
# nom            dispositiu (per UUID)                           clau                   opcions
backups-xifrat   UUID=8f4c2a19-7d3e-4b1a-9c85-2e6f0a4d7b31      /etc/luks/backups.key  luks,nofail
$ sudo install -d -m 700 /etc/luks
$ sudo dd if=/dev/urandom of=/etc/luks/backups.key bs=512 count=1
$ sudo chmod 400 /etc/luks/backups.key
$ sudo cryptsetup luksAddKey /dev/vg-dades/lv-backups /etc/luks/backups.key
# /etc/fstab (la linia de backups passa a apuntar al dispositiu desbloquejat)
/dev/mapper/backups-xifrat  /srv/tramontana/backups  ext4  defaults,noatime,nodev,nosuid,nofail  0  2
# La xarxa de seguretat obligatoria abans de reiniciar, com a 05-04
$ sudo systemctl daemon-reload && sudo mount -a && findmnt /srv/tramontana/backups

Què protegeix i què no protegeix, dit amb precisió: protegeix contra el robatori o la retirada del disc de dades, contra l'accés a la imatge de la VM aturada, i contra la recuperació de dades d'un disc llençat. No protegeix contra un atacant que compromet el servidor en marxa, perquè per a ell el volum està muntat i llegible com per a qualsevol. És xifratge en repòs, no xifratge en ús, i presentar-ho com una altra cosa davant de la Marta seria enganyar-la.

Fixa't a més en la composició amb l'anterior: restic ja xifra el seu dipòsit amb la seva pròpia contrasenya —que ara viu a pass—, així que les còpies tenen dues capes independents. I per a la destinació externa de la regla 3-2-1, el xifratge de restic és el que de debò importa, perquè el mitjà remot no el controles tu.

Gestors centralitzats de secrets

Tot l'anterior és correcte per a una màquina. Quan n'hi ha diverses, apareix un problema nou: distribuir i rotar secrets en N servidors sense copiar-los a mà.

Solució Model Quan compensa
pass + git Fitxers GPG en un dipòsit 1-5 màquines, un equip petit, sense dependències
sops + age Xifra només els valors d'un YAML/JSON, versionable a git Configuració com a codi; encaixa molt bé amb Ansible (07-06)
HashiCorp Vault Servei amb API, polítiques, auditoria i secrets dinàmics de vida curta Desenes de màquines, diversos equips, requisits d'auditoria
Secrets Manager del núvol (AWS/GCP/Azure) Servei gestionat integrat amb IAM Infraestructura ja en aquell proveïdor

El salt de rendibilitat té un llindar bastant identificable: quan rotar una credencial deixa de ser una tasca de cinc minuts. Amb un servidor, rotar és canviar pass, regenerar el .cred i reiniciar el servei. Amb vint, sense un gestor central, és una operació manual propensa a deixar màquines a mitges — que és el pitjor dels mons, perquè tens el cost de la rotació sense la garantia.

sops mereix una menció concreta perquè serà rellevant a 07-06: permet tenir a git un fitxer de variables d'Ansible en què les claus són llegibles i els valors estan xifrats, de manera que el diff d'un canvi de configuració continua sent revisable sense exposar els secrets.

L'altre avantatge de Vault que no tenen les altres són els secrets dinàmics: en lloc d'una contrasenya de base de dades fixa, genera credencials amb validesa d'una hora, específiques de cada procés. Un secret que caduca sol no necessita rotació, i una fuita té una finestra d'explotació mínima. És el model cap al qual tendeix la indústria.

Rotació de credencials

Rotar és canviar un secret per un altre de nou. És obligatori perquè la probabilitat que un secret estigui compromès només creix amb el temps: cada còpia, cada registre, cada persona que el va veure, cada màquina on va estar. Rotar limita la finestra d'explotació d'una fuita que potser ja va passar i no ho saps.

Quan rotar, en ordre d'urgència:

  1. Immediatament, davant de qualsevol sospita d'exposició. És el que vas fer amb db_password quan va aparèixer a la còpia amb permisos 644, i el que faria el desenllaç B de l'últim exercici de 06-04.
  2. Quan algú amb accés deixa l'equip. No és desconfiança: és que el control sobre qui coneix el secret s'ha perdut.
  3. Periòdicament, amb un termini escrit. Per a una credencial de servei com aquesta, entre 90 dies i un any és l'habitual, segons l'exposició.

El procediment sense tallar el servei és la part amb contingut tècnic, i depèn d'una propietat del sistema de destinació: que accepti dues credencials vàlides alhora. PostgreSQL no ho fa amb el mateix compte, així que el patró és de dos usuaris:

# 1. Crear la credencial nova al costat de la vella (totes dues valides)
$ sudo -u postgres psql -c \
    "CREATE ROLE tramontana_app2 LOGIN PASSWORD 'nova-clau-generada';"
$ sudo -u postgres psql -c \
    "GRANT ALL PRIVILEGES ON DATABASE tramontana_reserves TO tramontana_app2;"

# 2. Guardar-la a la font de veritat, amb historial
$ pass insert tramontana/produccio/db_password
$ pass git log --oneline -1
a3f1c88 Add given password for tramontana/produccio/db_password to store.

# 3. Actualitzar la copia operativa i aplicar
$ pass tramontana/produccio/db_password \
    | sudo systemd-creds encrypt --name=db_password - \
           /etc/tramontana/secrets/db_password.cred
$ sudo systemctl restart tramontana.service

# 4. VERIFICAR abans de retirar la vella
$ ~/scripts/revisio_salut.sh; echo "estat: $?"
estat: 0

# 5. Nomes ara, retirar la credencial antiga
$ sudo -u postgres psql -c "DROP ROLE tramontana_app;"

L'ordre és el que importa: crear, aplicar, verificar, retirar. Invertir els dos últims passos deixa el servei caigut fins que algú se n'adoni, i a les 3 de la matinada això són hores. És la mateixa disciplina de «mesurar abans i després» que vas aplicar a 05-07.

Una generació de contrasenyes decent, per no cedir a la temptació d'inventar-les:

$ pass generate -n tramontana/produccio/db_password 32
$ openssl rand -base64 24     # alternativa sense pass
Kj8mQ2vX9pLnR4tW7cYbF3sZ6dHa

Què garanteix TLS i què conté un certificat

Segona meitat de la lliçó. TLS (Transport Layer Security, el successor d'SSL) dona tres garanties, i convé separar-les perquè la tercera és la que gairebé tothom oblida:

Garantia Què significa Sense ella
Confidencialitat Ningú del camí no pot llegir el contingut Qui sigui a la xarxa veu les dades dels hostes
Integritat Ningú no pot modificar el contingut sense que es detecti Es pot alterar una reserva en trànsit
Autenticació Estàs parlant amb qui creus Xifres perfectament... amb l'atacant

La tercera és la raó que existeixin els certificats. El xifratge sense autenticació és inútil davant d'un intermediari: si un atacant s'hi interposa, negocia una connexió xifrada amb tu i una altra amb el servidor real, ho llegeix tot. És el mateix raonament de l'avís d'empremta d'SSH a 06-02.

I el que TLS no garanteix: que el servidor sigui honest, que l'aplicació sigui segura, que les dades estiguin xifrades en repòs, ni que qui es connecta sigui qui diu (per a això cal autenticar el client).

La cadena de confiança

El problema a resoldre és d'arrencada: com confiar en la clau pública d'un servidor que no havies vist mai. La solució és delegar en un tercer en qui ja confies.

graph TD
    R["CA arrel<br/>(al magatzem del sistema<br/>i del navegador)"] -->|signa| I["CA intermedia<br/>(p. ex. Let's Encrypt E6)"]
    I -->|signa| H["Certificat fulla<br/>reserves.tramontana.example"]
    H -.->|conte| K["Clau publica del servidor"]
    S["Clau privada<br/>(mai surt del servidor)"] -.->|parella de| K

La verificació consisteix a comprovar, cap amunt, que cada certificat està signat pel següent, fins a arribar a una arrel que ja és al magatzem local (/etc/ssl/certs/, del paquet ca-certificates). L'autoritat de certificació arrel signa poc i viu molt protegida; les intermèdies fan la feina del dia a dia i es poden revocar sense invalidar l'arrel.

Què hi ha dins d'un certificat

Un certificat és la clau pública del servidor més un conjunt d'afirmacions, tot signat per l'autoritat de certificació:

Camp Contingut Nota
Subject A qui identifica El CN està obsolet per a noms d'amfitrió
SAN (Subject Alternative Name) Els noms vàlids És el camp que es valida avui
Issuer Qui el va signar Enllaça amb la cadena
Not Before / Not After Finestra de validesa La causa de la fallada més comuna
Public Key Clau pública i algorisme ECDSA P-256 o RSA 2048+
Key Usage / Extended Key Usage Per a què es pot fer servir serverAuth, clientAuth

El punt sobre el SAN és pràctic, no anecdòtic: des de fa anys els navegadors i les biblioteques TLS ignoren el CN i validen exclusivament contra el SAN. Un certificat amb CN=reserves.tramontana.example i sense SAN és rebutjat. És una font clàssica d'hores perdudes en generar certificats a mà.

L'encaixada de mans de TLS 1.3, en cinc línies i sense entrar en criptografia:

  1. El client envia els algorismes que admet i la seva aportació a l'intercanvi de claus.
  2. El servidor tria, envia la seva i el seu certificat.
  3. El client verifica el certificat contra el seu magatzem d'autoritats de certificació i comprova que el nom és al SAN.
  4. Tots dos deriven la mateixa clau de sessió sense haver-la transmès mai.
  5. A partir d'aquí, tot va xifrat amb aquella clau. Un sol viatge d'anada i tornada.

Generar, llegir i verificar material criptogràfic amb OpenSSL

openssl és la navalla suïssa de l'àrea. Quatre operacions cobreixen el 90 % de la feina.

Generar una clau privada i una petició de signatura

# Clau privada ECDSA P-256: mes curta i rapida que RSA, amb seguretat equivalent a RSA 3072
$ openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-256 \
    -out reserves.key
$ chmod 600 reserves.key

# CSR amb el SAN correctament posat (la part que s oblida)
$ openssl req -new -key reserves.key -out reserves.csr \
    -subj "/CN=reserves.tramontana.example/O=Tramontana S.L./C=ES" \
    -addext "subjectAltName=DNS:reserves.tramontana.example,DNS:www.reserves.tramontana.example"

$ openssl req -in reserves.csr -noout -text | grep -A2 'Alternative'
            X509v3 Subject Alternative Name:
                DNS:reserves.tramontana.example, DNS:www.reserves.tramontana.example

La CSR (Certificate Signing Request) conté la clau pública i els noms sol·licitats, i va signada amb la clau privada per demostrar que la posseeixes. La clau privada no surt mai del servidor.

Autosignat per a proves

$ openssl x509 -req -in reserves.csr -signkey reserves.key -days 365 \
    -copy_extensions copyext -out reserves-autosignat.crt

Aquell -copy_extensions copyext és imprescindible: sense ell, el SAN de la CSR no es copia al certificat i n'obtens un d'inservible. És l'error més freqüent en generar certificats de prova a mà.

Un autosignat serveix per provar la configuració del servidor, i només per a això: cap autoritat de certificació no el respatlla, així que tot client avisarà. Mai en producció.

Llegir i verificar

$ openssl x509 -in /etc/letsencrypt/live/reserves.tramontana.example/cert.pem \
    -noout -text | grep -A1 -E 'Issuer:|Not Before|Not After|Alternative'
        Issuer: C = US, O = Let's Encrypt, CN = E6
            Not Before: Aug 18 09:12:41 2026 GMT
            Not After : Nov 16 09:12:40 2026 GMT
            X509v3 Subject Alternative Name:
                DNS:reserves.tramontana.example

# Verificar la cadena completa contra el magatzem del sistema
$ openssl verify -untrusted /etc/letsencrypt/live/reserves.tramontana.example/chain.pem \
    /etc/letsencrypt/live/reserves.tramontana.example/cert.pem
/etc/letsencrypt/live/reserves.tramontana.example/cert.pem: OK

# Comprovar que la clau privada correspon al certificat (els moduls han de coincidir)
$ openssl pkey -in privkey.pem -pubout -outform DER | sha256sum
$ openssl x509 -in cert.pem -pubkey -noout -outform DER | sha256sum

Aquella última comprovació resol una fallada desconcertant i freqüent: el servidor no arrenca o rebutja les connexions perquè la clau i el certificat no són parella, típicament després de regenerar-ne una i oblidar l'altra. Si les dues sumes coincideixen, són parella.

I la inspecció d'un servidor en marxa:

$ openssl s_client -connect reserves.tramontana.example:443 \
    -servername reserves.tramontana.example </dev/null 2>/dev/null \
    | openssl x509 -noout -dates -subject
notBefore=Aug 18 09:12:41 2026 GMT
notAfter=Nov 16 09:12:40 2026 GMT
subject=CN = reserves.tramontana.example

El -servername és obligatori quan hi ha diversos llocs a la mateixa IP: és l'extensió SNI, que indica quin certificat vols. Sense ell rebràs el certificat per defecte i creuràs que hi ha un problema on no n'hi ha.

Let's Encrypt i la renovació automàtica

Let's Encrypt és una autoritat de certificació gratuïta i automatitzada. El seu protocol, ACME, funciona sobre una idea simple: per demostrar que controles un domini, l'autoritat de certificació et demana que facis una cosa que només el seu titular podria fer.

Desafiament Què exigeix Quan fer-lo servir
HTTP-01 Servir un fitxer a http://domini/.well-known/acme-challenge/<token> Per defecte; necessita el port 80 accessible des d'Internet
DNS-01 Publicar un registre TXT _acme-challenge.domini Quan el 80 no és accessible, i obligatori per a comodins
TLS-ALPN-01 Respondre al 443 amb una extensió ALPN Quan el 80 està tancat però el 443 no

El DNS-01 és imprescindible en dos casos: certificats comodí (*.tramontana.example) i servidors que no exposen el port 80 — i requereix que el proveïdor de DNS tingui API, o fer-ho a mà en cada renovació, que no és viable.

$ sudo apt install certbot python3-certbot-nginx

I aquí s'aplica l'avís d'abast del principi: Nginx es munta a 08-01, així que ara es fa servir el mode --standalone, en què certbot aixeca el seu propi servidor temporal al port 80. Recorda que a 06-03 vas deixar el 443 obert però el 80 no: cal obrir-lo abans.

$ sudo ufw allow 80/tcp comment 'ACME HTTP-01'
$ sudo certbot certonly --standalone \
    -d reserves.tramontana.example \
    --email [email protected] \
    --agree-tos --no-eff-email

Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/reserves.tramontana.example/fullchain.pem
Key is saved at:         /etc/letsencrypt/live/reserves.tramontana.example/privkey.pem
This certificate expires on 2026-11-16.

Els quatre fitxers que genera, perquè cada servidor en demana un de diferent:

Fitxer Contingut Qui el fa servir
privkey.pem Clau privada Sempre; permisos estrictes
cert.pem Només el certificat fulla Configuracions antigues
chain.pem Només les intermèdies Grapat OCSP
fullchain.pem Fulla + intermèdies L'habitual: Nginx, Apache modern

Servir cert.pem en lloc de fullchain.pem és un error clàssic: funciona al navegador d'escriptori, que sol tenir la intermèdia a la memòria cau, i falla en clients mòbils i a curl. Es detecta amb openssl s_client mirant si la cadena està completa.

La renovació, i per què provar-la

Els certificats de Let's Encrypt duren 90 dies. Això obliga a automatitzar, que és intencionat: una renovació automatitzada i provada falla menys que una d'anual feta a mà.

$ systemctl list-timers certbot.timer --no-pager
NEXT                        LEFT      LAST  PASSED  UNIT           ACTIVATES
Tue 2026-08-18 22:47:19 CEST 10h left  -     -       certbot.timer  certbot.service

# La prova, que fa TOT el proces real contra l entorn de proves de la CA
$ sudo certbot renew --dry-run
Simulating renewal of an existing certificate for reserves.tramontana.example
Congratulations, all simulated renewals succeeded.

--dry-run és la línia més important d'aquesta secció. La renovació falla, típicament, pel port 80 tancat per una regla nova del tallafocs, per un servei ocupant aquell port, o per un --deploy-hook trencat. I falla en silenci, 60 dies després que introduïssis el canvi, quan ja ningú no recorda haver-lo fet. Provar en sec després de cada canvi de xarxa o de tallafocs és la disciplina que evita aquell escenari.

El ganxo de desplegament és el que recarrega el servei després de renovar:

$ sudo tee /etc/letsencrypt/renewal-hooks/deploy/10-recarregar >/dev/null <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
systemctl is-active --quiet nginx && systemctl reload nginx
logger -t certbot -p local0.notice "certificat renovat i servei recarregat"
EOF
$ sudo chmod 700 /etc/letsencrypt/renewal-hooks/deploy/10-recarregar

Un certificat renovat que el servei no ha recarregat no serveix: el procés continua presentant el vell fins que es reiniciï. És una altra fallada silenciosa clàssica.

Sobre mTLS (autenticació mútua), breument: el servidor exigeix també un certificat de client. És excel·lent per a la comunicació entre serveis —molt millor que una clau d'API— i desaconsellable per a persones, pel cost de distribuir i renovar certificats a cada dispositiu.

Bones pràctiques de TLS, custòdia de la clau i vigilància de la caducitat

Configuració

Els criteris vigents, que s'aplicaran a 08-01 en configurar Nginx:

  • Només TLS 1.2 i TLS 1.3. SSL 3.0, TLS 1.0 i 1.1 estan retirats per vulnerabilitats conegudes i cap client actual no els necessita.
  • Suites amb confidencialitat persistent (ECDHE), perquè el compromís futur de la clau privada no permeti desxifrar trànsit capturat en el passat.
  • HSTS, la capçalera Strict-Transport-Security, que diu al navegador que faci servir HTTPS sempre per a aquest domini. Compte: és difícil de revertir, així que primer amb max-age curt.
  • No inventis la llista de suites. Fes servir el generador de ssl-config.mozilla.org i el seu perfil intermediate, que és l'equilibri raonable entre seguretat i compatibilitat, i revisa'l periòdicament.

Custòdia de la clau privada

$ sudo ls -l /etc/letsencrypt/live/reserves.tramontana.example/privkey.pem
-rw-r----- 1 root ssl-cert 241 ago 18 11:12 privkey.pem

640 root:ssl-cert és el patró: root escriu, el grup ssl-cert llegeix, ningú més. El procés que necessita la clau s'afegeix a aquell grup — mai no es fa la clau llegible per tothom.

Si la clau privada es filtra, no n'hi ha prou de generar-ne una de nova: cal revocar l'anterior, perquè continua sent vàlida fins a la seva caducitat i permetria a qui la tingui suplantar el teu servei:

$ sudo certbot revoke --cert-path /etc/letsencrypt/live/reserves.tramontana.example/cert.pem \
    --reason keyCompromise
$ sudo certbot certonly --standalone -d reserves.tramontana.example --force-renewal

Amb honestedat sobre la seva eficàcia: la revocació depèn que el client la comprovi, i molts no ho fan o fallen de manera permissiva. És necessària però no suficient; el pla de resposta ha d'assumir que la clau filtrada continua sent utilitzable un temps.

Vigilar la caducitat

La fallada més ximple i més freqüent de producció és un certificat caducat. Passa fins i tot amb renovació automàtica, perquè l'automatització també es trenca. La mesura és un control independent que no confiï en el mecanisme de renovació, en la línia de comprovar_copia.sh de 05-08:

$ cat ~/scripts/revisar_certificat.sh
#!/usr/bin/env bash
# revisar_certificat.sh - Avisa si un certificat TLS caduca aviat.
# Us: revisar_certificat.sh [-d dies] [-H host] [-p port]
# Sortida: 0 correcte | 1 avis (caduca aviat) | 2 critic (caducat o inabastable)
set -euo pipefail

readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=lib/comuns.sh
source "${SCRIPT_DIR}/lib/comuns.sh"

readonly ETIQUETA_LOG="revisar-certificat"

main() {
    local dies_avis="${TRAMONTANA_CERT_DIES:-20}"
    local host="${TRAMONTANA_CERT_HOST:-reserves.tramontana.example}"
    local port="${TRAMONTANA_CERT_PORT:-443}"

    while getopts ":d:H:p:h" opcio; do
        case "$opcio" in
            d) dies_avis="$OPTARG" ;;
            H) host="$OPTARG" ;;
            p) port="$OPTARG" ;;
            h) sed -n '2,4s/^# \?//p' "$0"; return 0 ;;
            *) morir 64 "opcio no valida: -$OPTARG" ;;
        esac
    done

    requereix_comanda openssl
    es_numero "$dies_avis" || morir 64 "els dies han de ser un numero: $dies_avis"

    local fi_text
    if ! fi_text="$(timeout 10 openssl s_client -connect "${host}:${port}" \
            -servername "$host" </dev/null 2>/dev/null \
            | openssl x509 -noout -enddate 2>/dev/null | cut -d= -f2)"; then
        error "no s ha pogut obtenir el certificat de ${host}:${port}"
        return 2
    fi
    [[ -n "$fi_text" ]] || { error "resposta buida de ${host}:${port}"; return 2; }

    local fi_epoch ara_epoch dies_restants
    fi_epoch="$(date -d "$fi_text" +%s)"
    ara_epoch="$(date +%s)"
    dies_restants=$(( (fi_epoch - ara_epoch) / 86400 ))

    if (( dies_restants < 0 )); then
        error "CADUCAT fa $(( -dies_restants )) dies: $host"
        return 2
    fi
    if (( dies_restants <= dies_avis )); then
        error "caduca en ${dies_restants} dies (llindar ${dies_avis}): $host"
        return 1
    fi

    log "certificat de $host correcte, caduca en ${dies_restants} dies"
    return 0
}

main "$@"
$ chmod +x ~/scripts/revisar_certificat.sh
$ shellcheck ~/scripts/revisar_certificat.sh && echo "sense avisos"
sense avisos
$ ~/scripts/revisar_certificat.sh; echo "estat: $?"
certificat de reserves.tramontana.example correcte, caduca en 90 dies
estat: 0

Els 20 dies de llindar no són arbitraris: amb certificats de 90 dies, certbot renova als 60, així que queden 30 dies de marge. Un avís als 20 significa que la renovació automàtica ha fallat dues vegades i encara hi ha gairebé tres setmanes per arreglar-ho amb calma.

I com que l'script retorna 0/1/2 igual que revisio_salut.sh, s'integra sense fricció amb la comprovació general i amb el monitoratge.

Errors Comuns i Consells

  • Generar un certificat sense SAN. El CN ja no es valida. Sense -addext "subjectAltName=..." a la CSR i sense -copy_extensions copyext en autosignar, obtens un certificat que tot client rebutja.
  • Servir cert.pem en lloc de fullchain.pem. Funciona al teu navegador —té la intermèdia a la memòria cau— i falla en mòbils i a curl. Verifica sempre amb openssl s_client des d'una altra màquina.
  • No provar la renovació en sec. certbot renew --dry-run després de cada canvi de tallafocs o de servidor web. Si no, te n'assabentes 60 dies després, quan el certificat caduca de debò.
  • Oblidar el ganxo de desplegament. Un certificat renovat que el servei no ha recarregat no serveix de res: el procés continua presentant el vell.
  • Passar un secret per la línia d'ordres. --password=X és visible a ps per a tot el sistema. Fes servir un fitxer, l'entrada estàndard o el mecanisme de credencials del programa.
  • Desxifrar un secret a un fitxer al disc. Deixa restes recuperables. Fes servir /dev/shm, una canonada, o pass -c amb porta-retalls temporal.
  • Creure que LUKS protegeix un servidor en marxa. Protegeix el disc robat o llençat, no el sistema compromès, on el volum ja està muntat. Dir-ho malament en un informe és pitjor que no posar-ho.
  • Rotar retirant la credencial antiga abans de verificar la nova. Crear, aplicar, verificar, retirar. Aquell ordre, sempre.
  • Perdre la clau GPG de pass sense còpia. És la clau mestra de tots els teus secrets. La seva còpia xifrada i la seva custòdia són part del manual d'operació, no un detall.
  • Consell de mètode. Anota al manual d'operació, per a cada secret: on és la font de veritat, qui el pot recuperar, quan es va rotar per última vegada i cada quant toca. Un inventari de secrets sense dates és un inventari que ningú no manté.

Exercicis

Exercici 1

restic necessita la seva contrasenya de dipòsit per executar la còpia nocturna sense intervenció humana. Avui és en un fitxer 600 propietat de root llegit per copia_tramontana.sh. Redissenya la solució fent servir pass com a font de veritat i el mecanisme de credencials de systemd, i explica el problema de recuperabilitat que apareix i com el resols.

Exercici 2

Reps aquest avís i l'has de diagnosticar:

$ ~/scripts/revisar_certificat.sh
[2026-11-10 08:00:03] ERROR revisar-certificat: caduca en 6 dies (llindar 20): reserves.tramontana.example

certbot.timer està actiu i certbot renew no ha donat cap error visible al journal. Enumera les causes possibles en ordre de probabilitat, amb l'ordre que confirma o descarta cadascuna.

Exercici 3

La Marta et demana per escrit «que el xifratge protegeixi les dades dels hostes». Redacta l'informe amb les tres capes de xifratge que has muntat —secrets amb systemd-creds i pass, disc amb LUKS, trànsit amb TLS—, indicant per a cadascuna què protegeix i què no protegeix, i quin risc sobre les dades d'hostes continua obert malgrat les tres.

Solucions

Solució 1

# 1. La font de veritat, amb historial i sincronitzable fora del servidor
$ pass insert -m tramontana/produccio/restic
Enter contents of tramontana/produccio/restic and press Ctrl+D when finished:

<contrasenya-del-diposit>
diposit: /srv/tramontana/backups/restic
rotada: 2026-08-18

# 2. La copia operativa, xifrada i lligada a la maquina
$ pass tramontana/produccio/restic | head -1 \
    | sudo systemd-creds encrypt --name=restic_pass - \
           /etc/tramontana/secrets/restic_pass.cred
$ sudo chmod 600 /etc/tramontana/secrets/restic_pass.cred
# /etc/systemd/system/tramontana-copia.service.d/override.conf
[Service]
LoadCredentialEncrypted=restic_pass:/etc/tramontana/secrets/restic_pass.cred

I a l'script, restic llegeix la contrasenya d'un fitxer, que és just el que la credencial li lliura:

# A copia_tramontana.sh
readonly RESTIC_PASSWORD_FILE="${CREDENTIALS_DIRECTORY:?aquest script s executa via systemd}/restic_pass"
export RESTIC_PASSWORD_FILE
restic -r "$TRAMONTANA_REPO" backup /home/operador/dades /etc/tramontana

Tres decisions que convé justificar:

  • RESTIC_PASSWORD_FILE en lloc de RESTIC_PASSWORD: la contrasenya no passa per l'entorn, només la ruta al fitxer.
  • ${CREDENTIALS_DIRECTORY:?...} amb missatge: si l'script s'executa a mà en lloc de per systemd, falla immediatament i amb un missatge clar, en comptes d'intentar copiar sense credencial. És l'expansió de 04-02 aplicada a una cosa real.
  • La credencial la munta systemd en un tmpfs privat del servei: ni el luis, ni el becari, ni el mateix svc-tramontana des d'un altre procés no la poden llegir.

El problema de recuperabilitat, que és el fons de l'exercici: el fitxer .cred està xifrat amb una clau derivada d'aquesta màquina (/var/lib/systemd/credential.secret). Si el servidor es perd —l'escenari (c) de restauració de 05-08, o una reinstal·lació després d'un compromís—, aquell fitxer és indesxifrable. I sense la contrasenya de restic no hi ha manera de llegir les còpies: tindries les còpies i no les podries restaurar, que és el pitjor dels fracassos possibles.

Es resol en dos fronts:

  1. pass és la font de veritat, i viu fora. El dipòsit de pass se sincronitza a un remot (pass git push), i la clau GPG privada té còpia xifrada en un mitjà físic separat. Reconstruir el servidor és: instal·lar, restaurar pass, i tornar a executar la canonada del pas 2.
  2. El manual d'operació ho documenta explícitament, amb aquella canonada escrita literalment i la ubicació de la clau GPG. El manual d'operació ja és fora del servidor per la disciplina de 05-08.

La regla general que se n'extreu: un secret lligat a la màquina no pot ser mai l'única còpia. La credencial de systemd és una memòria cau operativa; la font de veritat és en un altre lloc i és recuperable per una persona autoritzada.

Solució 2

Que certbot renew no donés error és la pista principal: cal distingir «va renovar i alguna cosa posterior va fallar» de «no va renovar i ningú no se'n va assabentar». La primera comprovació separa les dues branques:

$ sudo openssl x509 -in /etc/letsencrypt/live/reserves.tramontana.example/cert.pem \
    -noout -enddate

Branca A — el certificat al disc és nou (caduca en ~90 dies). La renovació va funcionar; el problema és després.

  1. El servei no es va recarregar (el més probable). El procés continua a memòria amb el certificat vell:
    $ ls -l /etc/letsencrypt/renewal-hooks/deploy/
    $ sudo journalctl -u certbot --since "40 days ago" | grep -iE 'hook|deploy|reload'
    $ systemctl show nginx -p ActiveEnterTimestamp
    
    Si la marca d'arrencada del servei és anterior a la renovació, és això. S'arregla recarregant, i es corregeix d'arrel revisant el ganxo de desplegament.
  2. El servidor apunta al fitxer equivocat: a un cert.pem copiat a una altra ruta en el seu dia, en lloc de a l'enllaç de live/ que certbot actualitza.
    $ grep -rE 'ssl_certificate' /etc/nginx/
    
  3. Estàs mirant un altre certificat: sense -servername, openssl s_client retorna el lloc per defecte. El mateix script el passa, però comprova-ho si ho has provat a mà.

Branca B — el certificat al disc també caduca en 6 dies. No s'ha renovat res. certbot renew no falla sorollosament quan el desafiament no es pot completar en una execució no interactiva, així que cal provocar l'error:

$ sudo certbot renew --dry-run     # l ordre que revela la causa real

Causes en ordre de probabilitat:

  1. El port 80 està tancat. És la causa número u, perquè qualsevol revisió del tallafocs s'endú per davant aquella regla «que no semblava necessària»:
    $ sudo ufw status verbose | grep -E '^80|http'
    $ sudo nft list ruleset | grep -E 'dport 80|dport { .*80'
    
  2. Un altre procés ocupa el 80 i --standalone no pot aixecar el seu servidor:
    $ sudo ss -tulpn | grep ':80 '
    
  3. El temporitzador no s'executa de debò, encara que estigui active:
    $ systemctl list-timers certbot.timer --no-pager
    $ sudo journalctl -u certbot.service --since "70 days ago" | tail -20
    
    Un LAST buit amb el temporitzador actiu significa que mai no s'ha disparat.
  4. DNS: el nom ja no resol a aquesta màquina, així que el desafiament HTTP-01 arriba a un altre lloc:
    $ dig +short reserves.tramontana.example
    $ getent hosts reserves.tramontana.example
    
  5. Límit de peticions de l'autoritat de certificació, si hi va haver molts intents fallits. La sortida de --dry-run ho diu, i es distingeix perquè fa servir l'entorn de proves i no consumeix quota.

La lliçó de mètode: l'avís va arribar amb 6 dies de marge perquè el llindar era 20 i el control és independent del mecanisme de renovació. Si la vigilància hagués consistit a confiar en certbot, te n'hauries assabentat amb el servei caigut. Per això els controls de verificació no han de compartir mecanisme amb allò que verifiquen.

Solució 3

Informe per a Marta Vidal — Xifratge de les dades d'hostes a srv-tramontana 18 d'agost de 2026

S'han implantat tres capes de xifratge. Cadascuna cobreix un risc diferent i cap no substitueix les altres. Detallo què protegeix i què no protegeix cadascuna, i el risc que continua obert.

Capa 1 — Secrets (systemd-creds + pass) La contrasenya de la base de dades i la del dipòsit de còpies ja no estan en clar en cap fitxer de configuració ni a l'entorn de cap procés. Estan xifrades al disc i systemd les lliura al servei en un espai privat a memòria. La font de veritat és en un gestor amb historial, fora del servidor. Protegeix: la fuita de credencials per un error de permisos, per una còpia mal feta, per un registre o per la lectura de l'entorn d'un procés. Tanca l'incident obert des de la còpia amb permisos 644. No protegeix: contra un atacant amb privilegis de root a la màquina en marxa, que pot llegir la credencial de la mateixa manera que el servei.

Capa 2 — Disc de còpies (LUKS) El volum /srv/tramontana/backups està xifrat a nivell de bloc. A més, el dipòsit de restic té el seu propi xifratge, així que les còpies tenen dues capes independents. Protegeix: el robatori o la pèrdua física del disc, l'accés a la imatge de la màquina apagada, i la recuperació de dades d'un disc retirat sense esborrat segur. No protegeix: contra un atacant que compromet el servidor en marxa. Per a ell, el volum està muntat i llegible. És xifratge en repòs, no en ús.

Capa 3 — Trànsit (TLS) El material criptogràfic de reserves.tramontana.example està emès per Let's Encrypt, verificat, amb renovació automàtica provada i vigilància independent de la caducitat amb avís a 20 dies. Protegeix: la lectura i la modificació de les dades de reserva pel seu camí per la xarxa, i la suplantació del servei. No protegeix: les dades un cop arriben al servidor, ni la seguretat de la mateixa aplicació.

Risc obert, i és important que hi consti Les dades d'hostes viatgen avui sense xifrar: l'aplicació escolta en HTTP al port 8080. El certificat està a punt i verificat, però el component que el presentarà —el servidor intermediari invers davant de l'aplicació— està previst a la fase següent del projecte. Fins que es desplegui, el xifratge en trànsit no està en vigor, encara que el material estigui preparat. Mitigació provisional: el 8080 no és accessible des d'Internet, així que l'exposició es limita a la xarxa interna.

Risc residual comú a les tres capes Cap capa no protegeix contra el compromís del servidor en marxa amb privilegis de root. Contra aquell escenari actuen els controls de la fase anterior: detecció de canvis amb AIDE, auditoria d'accessos amb auditd, i el procediment de resposta a incidents amb reconstrucció.

Compliment. El xifratge de dades personals en trànsit i en repòs és una obligació del RGPD (art. 32), no una millora opcional, i el risc obert del punt anterior és rellevant a aquests efectes. Recomano que el disseny de la custòdia de claus i la decisió sobre el termini de desplegament del servidor intermediari invers els revisi el responsable de protecció de dades.

Conclusió

Els dos deutes criptogràfics del curs estan saldats, cadascun a la seva manera. La db_password ja no existeix com a text en cap fitxer de configuració: viu xifrada en un .cred que només systemd pot desxifrar i que només el servei pot llegir, amb pass com a font de veritat versionada i sincronitzada fora del servidor. El volum de còpies està xifrat amb LUKS, sabent amb precisió què protegeix això i què no. I el material de reserves.tramontana.example està emès, verificat amb openssl, amb renovació automàtica provada en sec i amb un control de caducitat independent que avisa amb vint dies de marge. El tercer incident obert queda tancat, i saps per què LoadCredentialEncrypted és millor que EnvironmentFile, per què el SAN mana sobre el CN, i per què l'ordre d'una rotació és crear, aplicar, verificar i retirar.

En queda un. El 18 d'agost a les 06:12, unattended-upgrades va aplicar la correcció d'un CVE de TLS sobre openssl i libssl3t64 — la biblioteca que sosté tot el que acabes de muntar a la segona meitat d'aquesta lliçó — i va deixar serveis en execució que continuen fent servir la versió antiga carregada a memòria. Un pedaç instal·lat que ningú no ha aplicat de debò és una vulnerabilitat amb paperassa. A la lliçó 06-06: Assegurant Sistemes Linux es tanca aquell incident amb needrestart i es fa una cosa més ambiciosa: reunir tot el que has après en un model d'amenaces explícit i una postura de seguretat coherent. Reduiràs la superfície d'atac, enfortiràs l'autenticació amb PAM —el deute que vas deixar pendent a 05-01—, escriuràs un perfil d'AppArmor per a l'aplicació, aplicaràs els sysctl d'enfortiment del nucli i els muntatges amb noexec, apujaràs la puntuació de systemd-analyze security amb criteri, i tancaràs amb una llista de comprovació d'enfortiment de srv-tramontana que digui amb honestedat què està fet, què s'ha decidit assumir i què excedeix l'abast d'un administrador.

Curs de Linux: De Principiant a Administrador de Sistemes

Mòdul 1: Introducció a Linux

Mòdul 2: Comandes Bàsiques de Linux

Mòdul 3: Habilitats Avançades en la Línia de Comandes

Mòdul 4: Scripting en Shell

Mòdul 5: Administració del Sistema

Mòdul 6: Xarxes i Seguretat

Mòdul 7: Temes Avançats

Mòdul 8: Projectes Pràctics

© Copyright 2026. Tots els drets reservats