Les dues lliçons anteriors han suposat que l'atacant entra per la porta: que intenta autenticar-se, que abusa d'una regla de sudo, que fa servir una credencial que no li correspon. Aquesta lliçó s'ocupa del contrari: del que passa quan ningú no truca a la porta perquè l'errada és a dins, al codi que ja s'executa, i n'hi ha prou d'enviar-li uns bytes ben triats perquè faci alguna cosa que ningú no havia previst.

L'escenari de Meteora és realista. meteo-api accepta peticions HTTP d'Internet; l'ingestor accepta connexions d'estacions meteorològiques en un port obert; els tres processos depenen de biblioteques escrites per tercers que s'actualitzen soles cada poques setmanes. Cap credencial no protegeix aquestes tres superfícies: estan obertes per definició, perquè la seva feina és acceptar entrada de fora. La pregunta ja no és "qui pot entrar?", sinó "què passa quan l'entrada és maliciosa i el programa que la processa té una errada?".

La lliçó té tres parts. Primer, conèixer l'adversari: un panorama ordenat de les amenaces reals d'un servidor, cadascuna amb l'indici observable que deixaria a meteo-01, i el model d'amenaces de Meteora com a exercici estructurat. Segon, entendre l'errada: les classes de vulnerabilitat de programa explicades conceptualment —què es corromp i per què—, sempre tancades amb la manera correcta d'escriure el codi, i les defenses que el sistema operatiu posa per sota (ASLR, NX, canaris, PIE, RELRO) amb l'ordre exacta per comprovar que estan actives. I tercer, enfortir: la llista completa i ordenada de mesures que converteixen un Debian acabat d'instal·lar en meteo-01, cadascuna amb la seva ordre i la seva justificació, des de minimitzar paquets fins a la prova de restauració d'una còpia de seguretat.

Advertència prèvia, que val per a tota la lliçó. Els mecanismes d'atac es descriuen a nivell conceptual i amb finalitats defensives: no hi trobaràs instruccions d'explotació ni codi funcional d'atac, perquè no calen per defensar-se. Qualsevol prova de seguretat —un escaneig de ports, una auditoria amb lynis, una comprovació de configuració— s'ha de fer exclusivament sobre sistemes propis o amb autorització expressa i per escrit del responsable. I les decisions amb implicacions legals o regulatòries (RGPD, retenció de dades, notificació de bretxes) s'han de revisar amb un professional de compliment normatiu o amb assessoria jurídica.

Contingut

  1. Panorama d'amenaces i el seu indici observable
  2. El model d'amenaces de Meteora
  3. Classes de vulnerabilitat de programa
  4. Les defenses del sistema operatiu i com comprovar-les
  5. Minimitzar la superfície: paquets, serveis i ports
  6. Actualitzacions de seguretat i la seva automatització
  7. Tallafocs amb nftables
  8. Enfortiment de l'accés remot
  9. Aïllament del servei amb systemd
  10. Opcions de muntatge i límits de recursos
  11. Xifratge en repòs, en trànsit i gestió de secrets
  12. Còpies de seguretat com a control de seguretat
  13. Marcs de referència i verificació
  14. Llista de comprovació de meteo-01

Panorama d'amenaces i el seu indici observable

Conèixer la llista d'amenaces serveix de poc si no saps quin aspecte té cadascuna des de dins del servidor. Per això cada fila porta el seu indici: el que veuries a meteo-01 si estigués passant.

Amenaça En què consisteix Indici observable a meteo-01
Programari maliciós Codi que executa accions no desitjades Binari desconegut a /tmp, /dev/shm o /var/tmp; procés sense cap paquet que el justifiqui
Segrest de dades (ransomware) Xifra les dades i exigeix un rescat Pic d'escriptures a /var/lib/meteora; fitxers .dat amb extensió nova; nota de rescat; còpies esborrades
Rootkit d'usuari Substitueix binaris (ps, ls, netstat) per amagar activitat ps no mostra un procés que sí que apareix a /proc; dpkg --verify marca binaris alterats
Rootkit de nucli Mòdul que manipula el mateix nucli Mòdul no signat a lsmod; discrepàncies entre eines; molt difícil de detectar des del mateix sistema
Porta del darrere Accés persistent al marge de l'autenticació Port a l'escolta inesperat; clau nova a authorized_keys; usuari amb UID 0; unitat de systemd desconeguda
Criptomineria Fa servir la teva CPU per minar criptomonedes CPU al 100 % de manera sostinguda sense càrrega que ho expliqui; connexions sortints a ports de pools
Botnet El servidor passa a formar part d'una xarxa d'atac Trànsit sortint massiu; connexions a servidors de control; pics de xarxa sense origen conegut
Denegació de servei Esgota un recurs fins que el servei deixa de respondre Descriptors esgotats (EMFILE); memòria al límit; cua de connexions plena; meteo-api sense respondre
Robatori de dades Extracció de la informació Transferència sortint gran i inusual; lectures massives de .dat; connexió a una destinació nova
Cadena de subministrament El codi maliciós arriba per una dependència legítima Cap d'evident: per això és la més perillosa. Només es veu fixant versions i verificant signatures

Tres observacions que ordenen la taula i que convé tenir clares abans de continuar.

Gairebé totes les amenaces comparteixen quatre indicis. Un procés que no hauries de tenir, un port que no hauries de tenir obert, un fitxer que no hauries de tenir i una connexió sortint que no hauries de tenir. Això simplifica enormement la detecció: en lloc de buscar cada amenaça per separat, es vigilen aquests quatre eixos contra una línia base coneguda. És exactament la mateixa estratègia de "detecció per diferència" de 05-02.

El rootkit de nucli trenca la lògica de la resta. Si l'atacant controla el nucli, controla també el que veuen ps, ls, netstat i qualsevol eina que executis: no pots confiar en les respostes del sistema que estàs investigant. És l'argument decisiu per a la telemetria externa —enviar els registres fora de la màquina— i per analitzar en fred des d'un mitjà d'arrencada net. Ho desenvolupem a Auditoria, Registres i Resposta a Incidents.

La cadena de subministrament és la que no deixa indici. Quan el codi maliciós arriba dins d'una actualització legítima d'una biblioteca que fas servir, signada correctament pel seu repositori, cap vigilància de l'amfitrió no la distingeix d'una actualització normal. Les defenses són d'un altre tipus: fixar versions exactes, verificar les signatures del repositori, revisar quines dependències entren realment, minimitzar-ne el nombre, i reduir el dany possible amb el confinament de l'apartat 9.

El model d'amenaces de Meteora

Un model d'amenaces és un exercici estructurat de mitja hora que respon a tres preguntes: què protegeixes, de qui i per on pot entrar. Sense ell, l'enfortiment es converteix en una llista de receptes sense criteri per prioritzar.

Actius —el que cal protegir, amb la raó—:

Actiu Per què importa Impacte si es perd o es filtra
Les lectures històriques (.dat) És el producte de l'empresa Pèrdua irrecuperable si no hi ha còpies; avantatge per a un competidor
secrets.conf (claus d'API) Dona accés a serveis de tercers Suplantació de Meteora davant del proveïdor; cost econòmic
Disponibilitat de meteo-api Els clients hi paguen Incompliment de contracte; pèrdua de clients
Integritat de les dades Una dada falsa és pitjor que cap dada Decisions errònies dels clients; pèrdua de credibilitat
El servidor mateix Recurs de còmput i xarxa Ús per a mineria o com a origen d'atacs a tercers, amb responsabilitat legal

Adversaris —qui ataca, amb quina motivació i quins recursos—:

Adversari Motivació Recursos Probabilitat
Automatitzat (bots) Qualsevol màquina val Escaneig massiu, errades conegudes Contínua: és el 99 % del soroll
Oportunista Benefici ràpid Eines públiques Alta
Dirigit Les dades de Meteora en concret Temps, diners, potser errades desconegudes Baixa, alt impacte
Intern Descontentament, error, negligència Accés legítim Mitjana, i la més difícil de detectar

Superfícies d'entrada —per on s'hi arriba—:

Superfície Exposada a Què la protegeix avui Risc residual
Port d'estacions (9010/TCP) Xarxa d'estacions Tallafocs per origen, TLS mutu Estació compromesa enviant dades falses
API HTTP (443/TCP) Internet TLS, autenticació, límit de peticions Errada en l'anàlisi de peticions
SSH (22/TCP) Xarxa de gestió Claus, AllowGroups, sense contrasenyes Clau privada robada del portàtil d'un administrador
Dependències El repositori i els seus proveïdors Signatures, versions fixades Cadena de subministrament
Accés físic i consola Centre de dades Control d'accés, LUKS Robatori del disc

Aquesta taula és la que decideix on invertir l'esforç. Contra l'adversari automatitzat, que és el 99 % del trànsit hostil, la defensa efectiva i barata és no tenir res obvi obert i estar al dia d'actualitzacions: tallafocs, sense contrasenyes a SSH, pedaços. Contra el dirigit, calen confinament i detecció. I contra l'intern, privilegi mínim, separació de funcions i auditoria, que cap altra capa no cobreix.

Classes de vulnerabilitat de programa

Aquí expliquem què es corromp i per què, perquè sense entendre el mecanisme no s'entén la defensa. No hi ha codi explotable: cada apartat acaba amb la manera correcta d'escriure el programa.

Desbordament de memòria intermèdia

Recorda de 02-03 com està organitzada la pila d'una funció: les variables locals, el punter de marc desat i l'adreça de retorn, que és la posició del codi a la qual es tornarà quan la funció acabi. Són contigus a la memòria, i aquesta contigüitat és tot el problema.

/* ❌ VULNERABLE: l'ingestor processant el nom d'una estació */
void registrar_estacio(const char *entrada) {
    char nom[32];                 /* 32 bytes a la pila */
    strcpy(nom, entrada);         /* copia FINS AL '\0': no mira la mida */
    ...
}   /* en tornar, la CPU salta a l'adreça de retorn desada */

Si entrada fa 200 bytes, strcpy escriu 200 bytes on només en caben 32. Els 168 sobrants no desapareixen: sobreescriuen el que segueix a la pila, inclosa l'adreça de retorn. Quan la funció acaba, la CPU salta on digui aquella adreça, que ara conté el que l'atacant hi va posar. Ha aconseguit desviar el flux d'execució sense cap credencial. La causa arrel és sempre la mateixa: una còpia la mida de la qual la decideix l'atacant i no el programa.

/* ✔ CORRECTE: la mida la decideix el programa, no l'entrada */
void registrar_estacio(const char *entrada) {
    char nom[32];
    if (snprintf(nom, sizeof nom, "%s", entrada) >= (int) sizeof nom) {
        registrar_error("nom d'estacio massa llarg");            /* rebutjar */
        return;
    }
    ...
}

Les tres regles que eviten tota la família: fer servir funcions amb límit (snprintf, strncat, memcpy amb mida calculada) en comptes de strcpy, strcat, sprintf o gets; derivar el límit de sizeof de la destinació, mai de la longitud de l'entrada; i comprovar el valor de retorn, perquè truncar en silenci és una errada diferent però també una errada. En Python, Go, Rust o Java aquesta classe no existeix perquè el llenguatge comprova els límits; en C i C++ és responsabilitat del programador, i per això és la vulnerabilitat més rendible de la història del programari.

Cadenes de format

printf(missatge_usuari);          /* ❌ l'entrada ÉS la cadena de format */
printf("%s", missatge_usuari);    /* ✔ l'entrada és un ARGUMENT */

La diferència sembla cosmètica i és abismal. Si el text de l'usuari conté especificadors com ara %x o %n, la primera versió els interpreta: printf llegeix arguments que ningú no ha passat —revelant contingut de la pila, punters i dades inclosos— i %n arriba a escriure a la memòria. La regla és absoluta i fàcil d'auditar: la cadena de format sempre és una constant del programa; les dades externes van sempre com a argument. Compila amb -Wformat -Wformat-security i el compilador t'avisa.

Injecció d'ordres

És la classe més freqüent avui, i la que més apareix en codi d'administració.

# ❌ VULNERABLE: construir una ordre de shell concatenant dades externes
estacio = request.args.get("estacio")
os.system(f"grep {estacio} /var/lib/meteora/lectures/2026-08-31.dat")

El problema no és grep: és que el text s'entrega a una shell, i la shell interpreta ;, |, &&, $(...) i comodins abans d'executar res. Un valor com ara x; ordre-arbitraria es converteix en dues ordres, la segona triada per qui va enviar la petició, executada amb la identitat de meteora. La causa arrel: barrejar codi i dades a la mateixa cadena, exactament igual que a la injecció SQL.

# ✔ CORRECTE: sense shell, i amb els arguments SEPARATS
import subprocess, re
if not re.fullmatch(r"[A-Za-z0-9_-]{1,32}", estacio):
    return "estacio no valida", 400
subprocess.run(["grep", "--", estacio, "/var/lib/meteora/lectures/2026-08-31.dat"],
               shell=False, check=False, timeout=10)

Tres defenses independents, i cadascuna tapa una cosa diferent. La llista d'arguments fa que no existeixi cap shell: el sistema executa execve("/usr/bin/grep", ["grep", "--", "PATIO-01", ...]) i el contingut d'estacio és un argument, no codi, per molts ; que porti. El -- impedeix que un valor que comenci per - s'interpreti com a opció de grep. I la validació per llista blanca rebutja el que no tingui la forma esperada, en comptes d'intentar enumerar el que és prohibit —que sempre oblida alguna cosa—. La regla general: mai shell=True, mai os.system, mai concatenar; i en C, execve amb argv separat en lloc de system().

TOCTOU: temps de comprovació davant de temps d'ús

Ja va aparèixer a 04-04 i a 05-01 com l'enemic de la mediació completa. El patró vulnerable és sempre el mateix: comprovar alguna cosa sobre un nom de fitxer i després actuar sobre aquell nom, perquè entre les dues operacions l'atacant pot canviar cap a on apunta el nom.

# ❌ VULNERABLE: comprova el NOM, actua sobre el NOM
if os.access(ruta, os.W_OK):          # 1. comprovació
    with open(ruta, "w") as f:        # 2. ús  ← la finestra és entre 1 i 2
        f.write(dades)

# ✔ CORRECTE: obrir primer, i actuar sobre el DESCRIPTOR
fd = os.open(ruta, os.O_WRONLY | os.O_CREAT | os.O_EXCL | os.O_NOFOLLOW, 0o640)
with os.fdopen(fd, "w") as f:
    f.write(dades)

La versió correcta aplica la lliçó de 05-01: un descriptor de fitxer és una capacitat, una clau sobre un objecte concret. Un cop obert, el descriptor continua apuntant al mateix inode encara que algú reanomeni el fitxer o substitueixi el nom per un enllaç, així que no hi ha finestra. O_EXCL fa fallar l'operació si la destinació ja existeix i O_NOFOLLOW impedeix seguir un enllaç simbòlic. És especialment crític en directoris compartits i escrivibles per diversos, que és la raó de PrivateTmp=yes de l'apartat 9.

Desreferència després d'alliberar

En C i C++, alliberar memòria amb free() no esborra el punter: continua apuntant al mateix lloc, però aquell lloc ja no és teu. Si el programa el fa servir després, llegeix o escriu en una zona que l'assignador pot haver reutilitzat per a una altra cosa —i si l'atacant aconsegueix que la seva dada acabi allà, el programa opera sobre contingut que ell controla. La disciplina que ho evita és senzilla i cal aplicar-la sempre: posar el punter a NULL immediatament després d'alliberar, no alliberar dues vegades, i deixar clar a cada estructura qui és l'amo de cada bloc de memòria. Eines com ara valgrind i els sanitizers del compilador (-fsanitize=address) detecten aquestes errades durant les proves, i fer-les servir en integració contínua és de les inversions més rendibles que existeixen.

Les defenses del sistema operatiu i com comprovar-les

El sistema operatiu no pot impedir que un programa tingui errades, però sí que en pot encarir molt l'aprofitament. Aquestes són les capes que aporta, i com comprovar que estan actives a la teva màquina.

Defensa Què fa Quin atac encareix
ASLR Col·loca pila, monticle i biblioteques en adreces aleatòries a cada execució L'atacant no sap a quina adreça saltar
KASLR El mateix per al nucli mateix Atacs contra el nucli
NX / W^X Marca la pila i les dades com a no executables Executar codi dipositat a la pila
Canari de pila Valor aleatori entre les variables i l'adreça de retorn; es comprova en sortir Detecta el desbordament abans de saltar
PIE Binari reubicable, perquè ASLR s'apliqui també al programa Reutilitzar el codi del binari mateix
RELRO Marca de només lectura les taules d'enllaç després de carregar-les Sobreescriure punters de funcions
seccomp Redueix les crides al sistema disponibles (05-01) Tot el que vingui després del control del flux
# ASLR del sistema: 2 = complet (valor correcte)
cat /proc/sys/kernel/randomize_va_space          # → 2

# Comprovar que les adreces canvien entre execucions
for i in 1 2 3; do ldd /usr/bin/meteo-api | grep libc; done | awk '{print $4}'

# Proteccions compilades en un binari concret
checksec --file=/usr/bin/meteo-api
# RELRO: Full RELRO | Canary: found | NX: enabled | PIE: PIE enabled

Com es llegeix aquella sortida. Full RELRO significa que les taules d'enllaç queden de només lectura després de la càrrega; Partial en deixa una part escrivible i No RELRO és un senyal que el binari es va compilar sense les proteccions habituals. Canary: found indica que el compilador va inserir la comprovació de pila —s'obté amb -fstack-protector-strong, actiu per defecte a Debian—. NX: enabled és la pila no executable. I PIE enabled permet que ASLR aleatoritzi també la posició del codi del programa; sense PIE, el binari sempre és a la mateixa adreça i bona part d'ASLR es perd. Si compiles programari propi per a meteo-01, les banderes mínimes són:

gcc -O2 -fstack-protector-strong -D_FORTIFY_SOURCE=2 \
    -fPIE -pie -Wl,-z,relro,-z,now -Wformat -Wformat-security \
    -o meteo-api meteo-api.c

-D_FORTIFY_SOURCE=2 substitueix funcions perilloses per versions que comproven mides quan el compilador les coneix; -Wl,-z,now força la resolució completa de símbols en carregar, que és el que fa possible el RELRO total.

I l'advertència que impedeix un excés de confiança: són capes, no solucions. Cadascuna encareix un pas, i les tècniques per esquivar-les una a una són conegudes des de fa anys. El seu valor és a la combinació —i en el fet que, juntament amb seccomp i AppArmor, converteixen una errada que abans donava el control de la màquina en, amb sort, un servei que cau i es reinicia—. Cap no substitueix corregir l'errada, i per això l'apartat següent comença per les actualitzacions.

Minimitzar la superfície: paquets, serveis i ports

El principi és de 05-01: el codi que no està instal·lat no té vulnerabilitats. Un Debian mínim porta uns 400 paquets; una instal·lació amb "entorn d'escriptori" marcat per descuit, més de 1.500. Cada paquet de més és codi que pot tenir errades, que cal actualitzar i que amplia el que un atacant troba en arribar.

# Què hi ha instal·lat i què ocupa? (el més gran primer)
dpkg-query -Wf '${Installed-Size}\t${Package}\n' | sort -rn | head -25

# Quins serveis s'executen realment?
systemctl list-units --type=service --state=running

# Què està a l'escolta, en quina interfície i quin procés ho va obrir?
sudo ss -tulnp
# LISTEN 0 128 0.0.0.0:22    users:(("sshd",pid=812,fd=3))
# LISTEN 0 511 0.0.0.0:443   users:(("meteo-api",pid=1481,fd=6))
# LISTEN 0 128 10.0.5.11:9010 users:(("ingestor",pid=1402,fd=4))
# LISTEN 0 128 127.0.0.1:5432 users:(("postgres",pid=901,fd=5))

La sortida d'ss -tulnp és la superfície de xarxa real del servidor, i cal saber llegir la columna d'adreça, que és on hi ha la informació important. 0.0.0.0:443 significa "escolta a totes les interfícies", correcte per a una API pública. 10.0.5.11:9010 escolta només a la interfície de la xarxa d'estacions, de manera que l'API pública no pot assolir aquell port: és una restricció gratuïta i molt efectiva. I 127.0.0.1:5432 escolta només en local, així que la base de dades és inabastable des de fora encara que el tallafocs fallés. La regla que se'n dedueix: cada servei ha d'escoltar a la interfície mínima que necessiti, i aquesta configuració és una capa independent del tallafocs.

# Desactivar un servei que no es necessita (i que no torni en reiniciar)
sudo systemctl disable --now avahi-daemon
sudo apt purge avahi-daemon                       # millor: treure'l del disc
sudo apt autoremove --purge                       # dependències òrfenes

disable --now atura el servei i evita que arrenqui; purge va més enllà i elimina el programari i la seva configuració. La distinció importa: un servei desactivat continua al disc i es pot reactivar per una actualització o per un error de configuració. I apt autoremove --purge neteja dependències que ja ningú no fa servir, que solen ser el gruix de l'excés.

Actualitzacions de seguretat i la seva automatització

És, amb diferència, la mesura amb millor relació entre esforç i protecció. La immensa majoria dels compromisos reals de servidors no aprofiten errades desconegudes, sinó errades publicades i corregides fa setmanes o mesos en sistemes que ningú no va actualitzar.

sudo apt install unattended-upgrades apt-listchanges
sudo dpkg-reconfigure -plow unattended-upgrades

# /etc/apt/apt.conf.d/50unattended-upgrades
Unattended-Upgrade::Origins-Pattern {
    "origin=Debian,codename=${distro_codename}-security";   # NOMÉS seguretat
};
Unattended-Upgrade::Mail "[email protected]";
Unattended-Upgrade::Automatic-Reboot "false";               # decidir a mà
Unattended-Upgrade::Remove-Unused-Dependencies "true";

# Verificar que funciona de debò
sudo unattended-upgrade --dry-run --debug
grep -c . /var/log/unattended-upgrades/unattended-upgrades.log

Les tres decisions d'aquella configuració. Només l'origen -security, no totes les actualitzacions: els pedaços de seguretat de Debian estable són mínims i molt conservadors, mentre que actualitzar-ho tot automàticament introdueix canvis funcionals que poden trencar el servei de matinada. Automatic-Reboot "false" perquè un reinici no anunciat de meteo-01 és una interrupció del servei; el reinici es planifica, i /var/run/reboot-required indica quan cal. I el correu d'avís, perquè una automatització que ningú no vigila acaba fallant en silenci: l'errada més comuna és un apt bloquejat per una actualització a mitges que fa mesos que no s'aplica.

Per al nucli, a més, existeix l'aplicació de pedaços en calent (kpatch, livepatch), que evita reinicis; és útil en entorns on la disponibilitat mana, i no substitueix reiniciar de tant en tant amb un nucli nou.

Tallafocs amb nftables

El tallafocs aplica denegar per defecte al trànsit de xarxa: s'enumera el que és permès i es rebutja la resta, inclòs el que encara no existeix. Al Debian actual el motor és nftables; ufw és una interfície senzilla sobre el mateix i és perfectament vàlida per a casos simples.

#!/usr/sbin/nft -f
# /etc/nftables.conf — tallafocs de meteo-01
flush ruleset

table inet filter {
  chain entrada {
    type filter hook input priority 0; policy drop;      # ← DENEGAR PER DEFECTE

    ct state established,related accept                  # respostes al que demanem
    ct state invalid drop
    iif lo accept                                        # trànsit local

    ip protocol icmp icmp type echo-request limit rate 5/second accept

    # SSH: NOMÉS des de la xarxa de gestió, amb límit d'intents nous
    ip saddr 10.0.1.0/24 tcp dport 22 ct state new limit rate 6/minute accept

    # API pública: oberta a Internet, amb límit per origen
    tcp dport 443 ct state new limit rate 30/second accept

    # Estacions: només la seva xarxa, i només cap al port de l'ingestor
    ip saddr 10.0.5.0/24 tcp dport 9010 ct state new accept

    counter comment "denegat per defecte"                # compta el descartat
  }

  chain sortida {
    type filter hook output priority 0; policy accept;   # vegeu el comentari de sota
  }

  chain reenviament {
    type filter hook forward priority 0; policy drop;    # no som un encaminador
  }
}

Cada regla respon a una decisió del model d'amenaces. policy drop a la cadena d'entrada és el principi de valors per defecte segurs: si demà algú arrenca un servei nou, no queda exposat per accident. ct state established,related accept permet les respostes al trànsit que nosaltres iniciem, i va la primera perquè és la regla que casa més paquets —l'ordre importa per al rendiment—. SSH restringit per origen elimina de cop el soroll dels escaneigs d'Internet, i el límit de 6 connexions noves per minut encareix qualsevol intent repetitiu. El limit del 443 és una defensa bàsica davant de la saturació. La regla de les estacions aplica la superfície de la taula d'amenaces: només aquella xarxa, només aquell port. I el counter final és un detall de diagnòstic molt útil: et diu quant trànsit s'està descartant, que és el primer que voldràs saber quan alguna cosa "no connecta".

La cadena de sortida mereix un comentari a part. Deixar-la en accept és l'habitual i el còmode. Posar-la en drop i enumerar les destinacions permeses és notablement més segur —talla l'exfiltració de dades, les connexions a servidors de control i la descàrrega d'eines per part d'un procés compromès, tres files senceres de la taula d'amenaces—, i és també bastant més feina de mantenir: cal permetre DNS, NTP, el repositori de paquets i les API de què depenguis. Per a un servidor amb dades valuoses com meteo-01, val la pena.

sudo nft -c -f /etc/nftables.conf      # validar SENSE aplicar
sudo systemctl enable --now nftables
sudo nft list ruleset                  # veure les regles actives
sudo nft list ruleset | grep counter   # quant s'està descartant?

Abans d'aplicar regles en un servidor remot, valida amb nft -c, i protegeix-te d'un error amb un mecanisme de reversió automàtica: per exemple, sudo sh -c 'sleep 300 && nft flush ruleset' & abans d'aplicar, cancel·lant-lo si tot va bé. Bloquejar-te a tu mateix amb el teu propi tallafocs és un clàssic.

Enfortiment de l'accés remot

La configuració de SSH ja es va justificar línia a línia a Usuaris, Autenticació i Escalada de Privilegis, així que aquí només en recollim el conjunt mínim i el que afegeix al que ja hem vist:

PermitRootLogin no                  # traçabilitat: cada acció, amb nom propi
PasswordAuthentication no           # elimina TOTA la família d'atacs de contrasenya
KbdInteractiveAuthentication no
AllowGroups admins                  # llista blanca d'accés
MaxAuthTries 3
AllowAgentForwarding no
X11Forwarding no

El que convé afegir al nivell d'enfortiment del sistema són dues capes complementàries, perquè protegeixen de coses diferents: la restricció per origen al tallafocs de l'apartat anterior, que fa que el servei ni tan sols sigui abastable des d'Internet; i un bloqueig dinàmic com ara fail2ban, que observa els registres i bloqueja temporalment els orígens amb moltes errades. Amb PasswordAuthentication no, fail2ban aporta poc contra el compromís —no hi ha contrasenya per provar— però continua sent útil per reduir el soroll als registres, que és un benefici real: un registre ple de milers d'intents automatitzats amaga l'intent que sí que importa.

Aïllament del servei amb systemd

Aquí és on es materialitza tot el de 05-01. Systemd permet confinar un servei amb una dotzena de directives, sense tocar el codi de l'aplicació i sense escriure una política de MAC completa. Aquesta és la unitat de meteo-api, comentada:

# /etc/systemd/system/meteo-api.service
[Unit]
Description=API de consultes meteorològiques de Meteora
After=network-online.target

[Service]
Type=notify
ExecStart=/usr/bin/meteo-api --config /etc/meteora/meteora.conf
Restart=on-failure
RestartSec=5

# --- IDENTITAT (05-02) ---
User=meteora
Group=meteora
UMask=0027                          # els fitxers neixen 0640 (04-06)

# --- PRIVILEGI (05-01) ---
NoNewPrivileges=yes                 # MAI no pot guanyar privilegi, ni per setuid
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
AmbientCapabilities=CAP_NET_BIND_SERVICE    # port 443 sense ser root

# --- SISTEMA DE FITXERS ---
ProtectSystem=strict                # TOT el sistema, de només lectura
ProtectHome=yes                     # /home, /root i /run/user: invisibles
PrivateTmp=yes                      # /tmp propi i aïllat (mecanisme comú mínim)
ReadWritePaths=/var/log/meteora /run/meteora
ReadOnlyPaths=/var/lib/meteora/lectures /etc/meteora
ProtectProc=invisible               # no veu els processos d'altres usuaris
PrivateDevices=yes                  # sense accés a dispositius físics

# --- NUCLI I MEMÒRIA ---
ProtectKernelTunables=yes           # /proc/sys i /sys, de només lectura
ProtectKernelModules=yes            # no pot carregar ni descarregar mòduls
ProtectKernelLogs=yes
ProtectControlGroups=yes
MemoryDenyWriteExecute=yes          # cap pàgina escrivible I executable (W^X)
LockPersonality=yes
RestrictRealtime=yes
RestrictSUIDSGID=yes                # no pot crear fitxers setuid

# --- XARXA ---
PrivateNetwork=no                   # necessita xarxa
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX
IPAddressDeny=any
IPAddressAllow=10.0.0.0/8 127.0.0.0/8    # i el que l'API hagi d'assolir

# --- CRIDES AL SISTEMA (seccomp, 05-01) ---
SystemCallFilter=@system-service
SystemCallFilter=~@privileged @mount @module @debug @reboot @swap @obsolete
SystemCallArchitectures=native
SystemCallErrorNumber=EPERM

# --- RECURSOS (defensa davant de l'esgotament) ---
LimitNOFILE=8192
LimitNPROC=64
MemoryMax=2G
TasksMax=128

[Install]
WantedBy=multi-user.target

Les directives que més valor aporten, i quin atac concret atura cadascuna. ProtectSystem=strict deixa tot el sistema de fitxers en només lectura i obliga a declarar a ReadWritePaths exactament on s'escriu: és una llista blanca del sistema de fitxers que cap permís DAC no pot contradir, així que un procés compromès no pot modificar binaris ni deixar res persistent fora de dues rutes. NoNewPrivileges=yes impedeix tot guany de privilegi posterior, inclòs el d'executar un binari setuid del sistema; talla l'escalada per aquella via encara que existeixi un setuid vulnerable. MemoryDenyWriteExecute=yes impedeix que existeixi una pàgina alhora escrivible i executable, cosa que bloqueja la tècnica habitual de generar codi a la memòria i saltar-hi. PrivateTmp=yes dona un /tmp propi, eliminant d'un cop els atacs per fitxers temporals i el TOCTOU en directoris compartits. IPAddressDeny=any amb llista blanca és el tallafocs de sortida aplicat a un sol servei: encara que el procés caigui, no pot trucar a casa. I SystemCallFilter és seccomp sense escriure una línia de C.

sudo systemd-analyze security meteo-api      # puntuació 0 (millor) a 10 (pitjor)
sudo systemctl daemon-reload && sudo systemctl restart meteo-api
sudo journalctl -u meteo-api -p err -n 50    # alguna cosa ha deixat de funcionar?

systemd-analyze security és l'eina clau d'aquest apartat: analitza la unitat, en puntua l'exposició i enumera exactament quines directives falten, ordenades per impacte. Una unitat sense enfortir treu al voltant de 9,5; amb les directives anteriors baixa a la zona d'1,5 a 2,5. Aplica-les de manera incremental, comprovant el servei després de cada bloc: si meteo-api deixa d'arrencar, gairebé sempre és perquè necessita escriure en una ruta que falta a ReadWritePaths, i journalctl -p err ho diu.

Opcions de muntatge i límits de recursos

Les opcions de muntatge de 04-03 són enfortiment pur, i aquí encaixen al seu lloc:

# /etc/fstab
UUID=a1b2...  /var/lib/meteora  ext4  defaults,noatime,nosuid,nodev,data=ordered  0 2
UUID=c3d4...  /var/log          ext4  defaults,nosuid,nodev,noexec               0 2
tmpfs         /dev/shm          tmpfs defaults,nosuid,nodev,noexec,size=512M     0 0
tmpfs         /tmp              tmpfs defaults,nosuid,nodev,noexec,size=1G       0 0

Les tres opcions tanquen tres vectors pel preu de tres paraules. nosuid fa que el nucli ignori els bits setuid i setgid a tot el volum, de manera que un binari setuid dipositat allà no serveix de res. nodev ignora els fitxers de dispositiu, impedint que algú creï un /var/lib/meteora/disc que doni accés directe al disc cru. I noexec impedeix executar qualsevol binari del volum: és exactament el que vols a /tmp, /dev/shm i /var/log, perquè són els llocs on un procés compromès pot escriure. Fixa't que /var/lib/meteora no porta noexec per coherència amb el seu ús —només hi guarda dades—, encara que afegir-l'hi també seria raonable; el que no pot portar és noatime fora, perquè aquella opció és de rendiment i ja està justificada des de 04-03.

Els límits de recursos són la defensa davant de l'esgotament, que és la fila de "denegació de servei" de la taula d'amenaces:

# Límits per servei (preferible): a la unitat, com a dalt
LimitNOFILE=8192 ; LimitNPROC=64 ; MemoryMax=2G ; TasksMax=128

# Límits per usuari: /etc/security/limits.conf (els aplica pam_limits, 05-02)
meteora  hard  nproc   128
meteora  hard  nofile  8192
*        hard  core    0        # sense bolcats de memòria: poden contenir secrets

Per què importen. Sense LimitNOFILE, una errada que obri descriptors sense tancar-los esgota el límit global del sistema i deixa la resta de serveis sense descriptors: una errada local es converteix en una caiguda general. LimitNPROC i TasksMax frenen la creació descontrolada de processos. MemoryMax evita que un consum desbocat activi l'OOM killer de 02-04, que podria matar un altre procés diferent del culpable. I core 0 mereix atenció especial: un bolcat de memòria conté tot el que el procés tenia a la RAM, incloses les claus de secrets.conf acabades de llegir, i sol acabar en un directori poc protegit o en un sistema d'anàlisi d'errades.

Xifratge en repòs, en trànsit i gestió de secrets

En repòs: LUKS. El xifratge de disc protegeix d'un escenari molt concret i molt real: que algú obtingui el disc físic —robatori, retirada de maquinari, un disc defectuós que es torna al fabricant sense esborrar—.

sudo cryptsetup luksFormat --type luks2 /dev/md0
sudo cryptsetup open /dev/md0 meteora-dades
sudo mkfs.ext4 /dev/mapper/meteora-dades
sudo cryptsetup luksDump /dev/md0 | head -20      # veure capçalera i ranures de clau

L'important és saber de què no protegeix, per no crear falsa seguretat: amb el sistema arrencat i el volum obert, les dades són accessibles com qualsevol altre fitxer, així que LUKS no protegeix en absolut davant d'un meteo-api compromès ni davant d'un atacant amb accés al sistema en marxa. Protegeix el disc apagat. I porta una conseqüència operativa que cal decidir per endavant: un volum xifrat necessita la clau a cada arrencada, així que o algú la teclegi —i el servidor no arrenca sol després d'un tall de llum— o es guarda en un TPM o en un servidor de claus, amb les seves pròpies implicacions.

En trànsit: TLS. Tot el que surti o entri per la xarxa ha d'anar xifrat i autenticat: l'API amb TLS 1.2 com a mínim —preferiblement 1.3—, certificats renovats automàticament, i TLS mutu al port de les estacions, que a més autentica l'estació i talla la fila "estació compromesa" del model d'amenaces. Els protocols en clar —telnet, FTP, HTTP per a dades, SNMP v1 i v2— no tenen lloc en un servidor modern.

Gestió de secrets. /etc/meteora/secrets.conf amb mode 0600 (o 0640 amb grup meteora, com vam fixar a 04-06) és el mínim acceptable, no una bona solució. Els seus límits són concrets: el secret és en clar al disc, entra a les còpies de seguretat, qualsevol amb root el llegeix, i rotar-lo exigeix tocar el servidor.

Enfocament Avantatge Inconvenient
Fitxer 0600 Simple, sense dependències En clar al disc i a les còpies; rotació manual
LoadCredential= de systemd El secret només és visible per a aquell servei Continua estant al disc
Gestor de secrets (Vault i similars) Rotació, auditoria d'accessos, secrets temporals Infraestructura addicional; s'ha de protegir igual
TPM o mòdul de seguretat La clau no surt mai del maquinari Cost, complexitat, dependència de l'equip

I tres regles que valen sempre, sigui quin sigui l'enfocament: mai al codi ni al repositori —quedaria a l'historial de Git per sempre, encara que després l'esborris—; mai en variables d'entorn, perquè són visibles a /proc/<pid>/environ i es filtren als processos fills i als bolcats d'error; i rotables, perquè un secret que no es pot rotar sense una aturada planificada acaba no rotant-se mai.

Còpies de seguretat com a control de seguretat

Les còpies no solen aparèixer a les llistes de seguretat, i són l'única defensa real contra el segrest de dades i contra l'esborrat destructiu. La regla clàssica continua sent la millor guia:

Regla 3-2-1: 3 còpies de les dades, en 2 mitjans diferents, amb 1 fora de l'emplaçament.

Aplicada a Meteora: l'original al RAID 1 de /var/lib/meteora; una còpia diària en un emmagatzematge diferent del mateix centre, per restaurar de pressa; i una còpia remota, per al cas d'incendi, inundació o compromís de tot el centre. I el matís que l'actualitza a l'era del segrest de dades:

Còpies immutables. Si l'atacant arriba amb privilegis, buscarà les còpies abans de xifrar res, perquè unes còpies accessibles fan inútil la seva extorsió. D'aquí les tres propietats que ha de tenir la destinació: credencials diferents de les del servidor —mai les mateixes claus, ni un muntatge permanent escrivible—; model de només afegir, de manera que el servidor pugui crear còpies però no esborrar ni sobreescriure les anteriors; i retenció forçada pel sistema de destinació durant un termini fix, no per una política que l'atacant pugui canviar.

# Còpia amb verificació i retenció (exemple amb restic)
export RESTIC_REPOSITORY="s3:s3.example.com/meteora-backups"
restic backup /var/lib/meteora /etc/meteora --tag diaria
restic forget --keep-daily 14 --keep-weekly 8 --keep-monthly 12 --prune
restic check --read-data-subset=5%       # verifica que les dades són llegibles

# LA PROVA QUE GAIREBÉ NINGÚ NO FA: restaurar de debò
restic restore latest --target /tmp/prova-restauracio
diff <(sha256sum /var/lib/meteora/lectures/2026-08-31.dat | cut -d' ' -f1) \
     <(sha256sum /tmp/prova-restauracio/var/lib/meteora/lectures/2026-08-31.dat | cut -d' ' -f1)

La prova de restauració és la part no negociable de tot això. Una còpia que no s'ha restaurat mai no és una còpia: és una suposició. Les errades que apareixen a la primera restauració real són sempre les mateixes i sempre en el pitjor moment —la clau de xifratge del repositori no està documentada, faltava un directori a la llista de rutes, el procés triga catorze hores i ningú no ho sabia, o els permisos i les ACL es van perdre perquè l'eina no els desava (04-06)—. Programa una restauració de prova trimestral, amb el seu temps mesurat i la seva verificació de sumes, i documenta el procediment pas a pas.

Marcs de referència i verificació

No cal inventar l'enfortiment des de zero: existeixen guies públiques i revisades per molta gent.

Marc o eina Què aporta Com es fa servir
CIS Benchmarks Guia detallada per sistema operatiu, amb justificació i comprovació de cada punt Com a línia base; s'adapta, no s'aplica a cegues
STIG (DISA) Equivalent d'àmbit governamental, molt estricte En entorns regulats
lynis Auditoria automàtica de l'amfitrió amb puntuació i suggeriments sudo lynis audit system
debsecan Vulnerabilitats conegudes dels paquets instal·lats debsecan --suite bookworm --format detail
systemd-analyze security Exposició de cada unitat, amb les directives que falten Per servei
OpenSCAP Comprovació automatitzada de perfils de compliment En entorns amb auditoria formal
sudo apt install lynis debsecan
sudo lynis audit system            # informe amb "warnings" i "suggestions"
sudo debsecan --suite bookworm --only-fixed --format detail

Dues advertències sobre aquestes eines. Aplicar un benchmark complet a cegues trenca serveis: els CIS Benchmarks inclouen recomanacions pensades per a perfils molt diferents, i algunes són incompatibles amb el que meteo-01 necessita fer. Es llegeixen, es decideix punt per punt, i es documenten les excepcions amb la seva justificació, que és el que després permet defensar la configuració davant d'una auditoria. I una puntuació alta de lynis no és seguretat: mesura la conformitat amb una llista, no la resistència a un adversari; serveix per trobar el que t'has oblidat, no per declarar el sistema segur.

Autorització prèvia. Executar lynis o qualsevol escàner sobre el teu servidor és normal i recomanable. Executar qualsevol eina d'anàlisi —inclòs un simple escaneig de ports— contra un sistema que no és teu requereix autorització expressa i per escrit del responsable, amb un abast i unes dates definits. Sense aquell document, l'actuació pot constituir un delicte amb independència de la intenció, i la bona fe no és una defensa.

Llista de comprovació de meteo-01

# Mesura Comprovació
1 Actualitzacions de seguretat automàtiques unattended-upgrade --dry-run
2 Paquets i serveis innecessaris eliminats systemctl list-units --state=running
3 Només els ports previstos a l'escolta, a la interfície mínima ss -tulnp
4 Tallafocs amb policy drop d'entrada nft list ruleset
5 SSH sense contrasenyes, sense root, amb AllowGroups sshd -T | grep -E 'permitroot|password'
6 Unitats enfortides systemd-analyze security
7 nosuid,nodev,noexec on pertoca findmnt -o TARGET,OPTIONS
8 ASLR complet i binaris amb les proteccions checksec, /proc/sys/kernel/randomize_va_space
9 Permisos i propietat correctes (04-06) Auditoria amb find
10 Inventari de setuid i de capabilities al dia find -perm -4000, getcap -r /
11 Secrets fora del codi i del repositori Revisió de l'historial de Git
12 TLS en tot el que surt i entra ss -tulnp, revisió de configuració
13 Còpies 3-2-1 amb destinació immutable Inventari de còpies
14 Restauració provada en els últims 3 mesos Registre de la prova
15 MAC actiu (AppArmor) en mode obligatori aa-status
16 Registre centralitzat i auditoria Mòdul 05-04

Errors Habituals i Consells

Enfortir sense haver fet el model d'amenaces. S'acaba invertint en el que un sap fer en lloc d'en el que protegeix. Mitja hora enumerant actius, adversaris i superfícies canvia l'ordre de la llista de tasques.

Aplicar un benchmark complet de cop en producció. Trenca serveis i genera desconfiança cap a tot l'enfortiment. Aplica per blocs, verifica el servei després de cadascun i documenta les excepcions.

Confiar en ASLR, NX i canaris com si fossin una solució. Són capes que encareixen passos concrets, i cadascuna té tècniques conegudes per esquivar-la. No substitueixen corregir l'errada ni actualitzar.

Creure que LUKS protegeix d'un servidor compromès. Protegeix el disc apagat. Amb el sistema arrencat i el volum obert, les dades són fitxers normals per a qualsevol procés amb permisos.

Posar secrets en variables d'entorn. Són visibles a /proc/<pid>/environ, s'hereten als fills i apareixen en bolcats i en traces d'error. Fitxer amb permisos restrictius, LoadCredential= o un gestor de secrets.

Tenir còpies i no haver-les restaurat mai. Una còpia no verificada és una suposició. Programa la restauració de prova trimestral, mesura quant triga i comprova les sumes.

Deixar un servei escoltant a 0.0.0.0 quan només el fa servir localhost. És superfície gratuïta. Mira la columna d'adreça a ss -tulnp i restringeix la interfície: és una capa independent del tallafocs i sobreviu a un error en ell.

Automatitzar actualitzacions i no vigilar que funcionin. L'errada típica és un apt bloquejat per una actualització a mitges que fa mesos que no s'aplica. Configura l'avís per correu i revisa'l.

Aplicar regles de tallafocs en un servidor remot sense xarxa de seguretat. Valida amb nft -c i deixa programada una reversió automàtica que cancel·lis si tot va bé.

Consell: mesura abans i després. systemd-analyze security, lynis i l'inventari de ports donen números concrets. Desa l'estat inicial, aplica i compara: converteix l'enfortiment en una cosa verificable i et permet justificar el temps invertit.

Exercicis

Exercici 1: model d'amenaces i superfície real

Sobre una màquina pròpia o una màquina virtual de proves: (a) enumera la seva superfície de xarxa real amb ss -tulnp, i indica per a cada port qui el va obrir, en quina interfície escolta i si aquella exposició està justificada; (b) llista els serveis en execució i decideix quins podries desactivar, amb la justificació; (c) construeix la taula d'actius, adversaris i superfícies del sistema; i (d) per a les tres amenaces de la taula de l'apartat 1 que consideris més probables en el teu cas, indica quin indici concret buscaries i amb quina ordre.

Exercici 2: enfortir una unitat de systemd

Parteix d'aquesta unitat sense enfortir i porta-la al nivell de l'apartat 9, afegint les directives per blocs i verificant el servei després de cada bloc. Per a cada directiva que afegeixis, explica quin atac concret atura. Mesura la puntuació de systemd-analyze security abans i després, i explica per què cada bloc la redueix. Indica també quin error veuries a journalctl si ReadWritePaths es quedés curt i com ho diagnosticaries.

[Service]
ExecStart=/usr/bin/meteo-api --config /etc/meteora/meteora.conf
Restart=on-failure

Exercici 3: revisió defensiva de codi

Per a cada fragment, identifica la classe de vulnerabilitat, explica què es corromp o què s'executa de més i per què, escriu la versió correcta i digues quina defensa del sistema operatiu (apartat 4) o directiva de systemd (apartat 9) reduiria el dany si l'errada arribés a producció.

/* A */ void desar(const char *nom) { char ruta[64]; strcpy(ruta, nom); }
# B
os.system("gzip /var/lib/meteora/lectures/" + request.args.get("fitxer"))
# C
if os.path.exists(desti) == False:
    with open(desti, "w") as f: f.write(informe)
/* D */ fprintf(log, missatge_rebut_estacio);

Solucions

Solució 1

sudo ss -tulnp                                        # (a) superfície de xarxa
systemctl list-units --type=service --state=running   # (b) serveis actius
sudo systemd-analyze security                         # exposició de cada unitat

(a) Cada línia d'ss -tulnp s'interpreta per la columna d'adreça local. 127.0.0.1:port és local i no suposa exposició externa; 0.0.0.0:port o [::]:port escolta a totes les interfícies i és exposició real; una IP concreta ho limita a aquella xarxa. Per a cada port cal respondre tres preguntes: quin procés el va obrir (columna users:), qui necessita assolir-lo i si la interfície és la mínima possible. Una troballa habitual i perfectament evitable és una base de dades o un servidor de desenvolupament escoltant a 0.0.0.0 quan només el fa servir la mateixa màquina; es corregeix a la configuració del servei, i aquella correcció és una capa independent del tallafocs que continua protegint si les regles fallen.

(b) Candidats habituals a desactivar en un servidor: descobriment de xarxa (avahi-daemon), impressió (cups), Bluetooth, servidors gràfics i serveis de gestió que ja no es fan servir. El criteri és doble: si no hi ha resposta clara a "qui ho fa servir i per a què?", es desactiva; i purge és preferible a disable, perquè un servei desactivat continua al disc i es pot reactivar amb una actualització.

(c) La taula ha de recollir, com a mínim: els actius amb el seu impacte en cas de pèrdua o filtració; els adversaris, sense oblidar que l'automatitzat és continu i l'intern és el més difícil de detectar; i les superfícies amb la seva protecció actual i el seu risc residual. El resultat útil és la priorització: contra l'adversari automatitzat, que és gairebé tot el trànsit hostil, la defensa barata i efectiva és tallafocs, sense contrasenyes a SSH i actualitzacions al dia.

(d) Indicis i ordres:

# Criptomineria: CPU sostinguda al 100 % sense càrrega que ho expliqui
top -b -n1 -o %CPU | head -12
# Porta del darrere: ports i connexions inesperats, i claus noves
sudo ss -tulnp ; sudo ss -tp state established
sudo find /home /root -name authorized_keys -newermt '-30 days' -ls
# Robatori de dades: transferència sortint inusual i lectures massives
sudo ss -tp state established ; vnstat -h 2>/dev/null || cat /proc/net/dev

El que converteix això en detecció de debò no és executar les ordres una vegada, sinó comparar-les amb una línia base coneguda: la llista de ports, la de processos, la de fitxers setuid i la de claus autoritzades. Els quatre eixos de què parlava l'apartat 1.

Solució 2

systemd-analyze security meteo-api     # ABANS: ~9,5 de 10 ("UNSAFE")

Bloc 1 — identitat i privilegi. User=meteora, Group=meteora, UMask=0027, NoNewPrivileges=yes, CapabilityBoundingSet=CAP_NET_BIND_SERVICE. Atura el més greu: sense User=, el servei s'executa com a root i qualsevol errada en l'anàlisi de peticions dona la màquina sencera. NoNewPrivileges=yes talla l'escalada per binaris setuid encara que n'existeixi un de vulnerable al sistema, i el CapabilityBoundingSet impedeix per sempre que el procés o els seus fills obtinguin capabilities de muntatge, de càrrega de mòduls o de depuració.

Bloc 2 — sistema de fitxers. ProtectSystem=strict, ProtectHome=yes, PrivateTmp=yes, ReadWritePaths=, ReadOnlyPaths=, PrivateDevices=yes, ProtectProc=invisible. ProtectSystem=strict deixa tot en només lectura tret del que es declara, així que un procés compromès no pot modificar binaris ni deixar persistència; ProtectHome li amaga /home i /root, on viuen claus SSH i credencials; i PrivateTmp elimina els atacs per fitxers temporals compartits i el TOCTOU a /tmp.

Bloc 3 — nucli, memòria i xarxa. ProtectKernelTunables/Modules/Logs, MemoryDenyWriteExecute=yes, RestrictSUIDSGID=yes, RestrictAddressFamilies=, IPAddressDeny=any amb IPAddressAllow=. MemoryDenyWriteExecute bloqueja la generació de codi a la memòria; RestrictAddressFamilies talla famílies de sòcols que l'API no necessita; i IPAddressDeny=any impedeix l'exfiltració i la connexió a servidors de control, que són dues files completes de la taula d'amenaces.

Bloc 4 — crides al sistema i recursos. SystemCallFilter=@system-service amb les restes de @privileged @mount @module @debug, SystemCallArchitectures=native, i els límits LimitNOFILE, LimitNPROC, MemoryMax, TasksMax. El filtre redueix la superfície de ~350 crides a les que el servei fa servir realment (05-01); SystemCallArchitectures=native tanca el forat d'invocar per l'ABI de 32 bits per esquivar el filtre; i els límits eviten que una errada del servei esgoti els descriptors o la memòria de tot el sistema.

systemd-analyze security meteo-api     # DESPRÉS: ~1,5–2,5 ("OK"/"MEDIUM")

Per què baixa la puntuació: l'eina pondera cada exposició no mitigada, i els dos blocs amb més pes són el de privilegi i el de sistema de fitxers, precisament perquè són els que determinen si un compromís queda contingut o es propaga.

Si ReadWritePaths es queda curt, el servei arrenca i falla en escriure. A journalctl -u meteo-api -p err hi apareixerà un error del tipus "Read-only file system" (EROFS) sobre una ruta concreta, que és exactament la que falta declarar. La manera sistemàtica de diagnosticar-ho és llançar el binari sota el mateix confinament amb systemd-run i les mateixes directives, o revisar amb strace -f -e trace=openat,write en un entorn de proves —mai en producció— per veure quines rutes obre en mode escriptura.

Solució 3

A — Desbordament de memòria intermèdia. strcpy copia fins al \0 sense mirar que ruta fa 64 bytes. Un nom més llarg sobreescriu el que segueix a la pila, inclosa l'adreça de retorn, i en acabar la funció la CPU salta on digui l'atacant. Correcció: if (snprintf(ruta, sizeof ruta, "%s", nom) >= (int) sizeof ruta) return -1;, amb el límit derivat de sizeof de la destinació i el retorn comprovat. Mitigació del sistema: canari de pila (ho detecta i avorta), ASLR + PIE (l'atacant no sap on saltar), NX (no pot executar codi a la pila) i MemoryDenyWriteExecute=yes a la unitat.

B — Injecció d'ordres. El nom de fitxer arriba d'una petició HTTP i es concatena en una ordre que executa una shell, i la shell interpreta ;, |, && i $(...) abans d'executar. Un valor amb ; produeix una segona ordre triada per qui va enviar la petició, amb la identitat de meteora. Correcció: validar per llista blanca i executar sense shell amb arguments separats, subprocess.run(["gzip", "--", ruta], shell=False), després de comprovar a més amb realpath que la ruta continua dins del directori previst —si no, també és un delegat confús (05-01)—. Mitigació: SystemCallFilter sense execve, perfil d'AppArmor sense regles d'execució, i ProtectSystem=strict.

C — TOCTOU. Entre os.path.exists(desti) i open(desti, "w") hi ha una finestra en què l'atacant hi pot crear un enllaç simbòlic a un altre fitxer, i l'escriptura acabaria a la destinació que ell triï, amb els permisos del servei. Correcció: no comprovar el nom i després fer-lo servir, sinó obrir directament amb os.open(desti, O_WRONLY|O_CREAT|O_EXCL|O_NOFOLLOW, 0o640), que falla si ja existeix o si és un enllaç, i operar sobre el descriptor. Mitigació: PrivateTmp=yes si és un directori temporal, ReadWritePaths acotat, i els permisos de directori de 04-06.

D — Cadena de format. missatge_rebut_estacio es fa servir com a cadena de format, així que fprintf interpreta els especificadors que contingui: %x revela contingut de la pila i %n permet escriure a la memòria. Correcció: fprintf(log, "%s", missatge_rebut_estacio);, amb la cadena de format sempre constant. Mitigació: compilar amb -Wformat -Wformat-security —el compilador ho detecta en temps de compilació, que és on cal caçar-ho—, -D_FORTIFY_SOURCE=2, i RELRO total, que impedeix sobreescriure les taules d'enllaç.

La lliçó comuna als quatre: les mitigacions del sistema encareixen l'aprofitament, no arreglen l'errada. Són valuoses perquè converteixen un compromís total en una caiguda del servei, però la correcció és sempre al codi, i per això les revisions, els sanitizers i les actualitzacions són la primera línia i no l'última.

Conclusió

Un servidor real s'enfronta a un catàleg acotat d'amenaces —programari maliciós, segrest de dades, rootkits d'usuari i de nucli, portes del darrere, criptomineria, botnets, denegació de servei, robatori de dades i cadena de subministrament— i gairebé totes deixen quatre indicis comuns: un procés, un port, un fitxer o una connexió sortint que no hauries de tenir. Això simplifica la detecció a vigilar aquests quatre eixos contra una línia base. Dues amenaces trenquen l'esquema i convé tenir-les presents: el rootkit de nucli, que corromp les respostes de les mateixes eines amb què investigues —d'aquí la necessitat de telemetria externa—, i la cadena de subministrament, que no deixa indici perquè arriba signada dins d'una actualització legítima. El model d'amenaces —actius, adversaris, superfícies— és el que dona criteri per prioritzar: contra l'adversari automatitzat, que és el 99 % del soroll, n'hi ha prou de no tenir res obvi obert i estar al dia de pedaços; contra el dirigit calen confinament i detecció; i contra l'intern, privilegi mínim i auditoria.

Les classes de vulnerabilitat de programa s'entenen millor per la seva causa arrel que pel seu nom. El desbordament de memòria intermèdia passa quan la mida d'una còpia la decideix l'atacant i no el programa, i el que corromp és l'adreça de retorn que és contigua a la pila. Les cadenes de format apareixen quan dades externes ocupen el lloc de la cadena de format, que sempre ha de ser una constant. La injecció d'ordres neix de barrejar codi i dades en una cadena que interpreta una shell, i desapareix en executar amb execve i arguments separats, sense shell, amb validació per llista blanca. El TOCTOU viu a la finestra entre comprovar un nom i fer-lo servir, i es tanca operant sobre el descriptor, que és una capacitat. I la desreferència després d'alliberar s'evita amb disciplina de propietat de la memòria i eines de detecció a les proves. El sistema aporta capes que encareixen l'aprofitament —ASLR i KASLR, NX, canaris, PIE, RELRO, seccomp—, comprovables amb checksec i /proc/sys/kernel/randomize_va_space; són capes, no solucions, i cap no substitueix corregir l'errada.

L'enfortiment de meteo-01 té un ordre que reflecteix la relació entre esforç i protecció. Primer, actualitzacions automàtiques de seguretat, perquè la majoria dels compromisos reals aprofiten errades ja corregides. Després, minimitzar la superfície: purgar paquets, apagar serveis i fer que cadascun escolti a la interfície mínima, cosa que ss -tulnp revela en un segon. Després el tallafocs amb policy drop, que aplica denegar per defecte també al que s'instal·li demà, amb la cadena de sortida restringida si les dades s'ho mereixen. L'accés remot ja enfortit a 05-02, amb restricció per origen i reducció de soroll als registres. I el bloc de més valor per línia escrita: l'aïllament amb systemd, on NoNewPrivileges, ProtectSystem=strict, PrivateTmp, CapabilityBoundingSet, MemoryDenyWriteExecute, IPAddressDeny i SystemCallFilter implementen sense tocar el codi tot el que 05-01 plantejava en teoria —i systemd-analyze security ho mesura—. A això s'hi sumen les opcions de muntatge nosuid,nodev,noexec, els límits de recursos com a defensa davant de l'esgotament, el xifratge en repòs amb LUKS —que protegeix el disc apagat, no un sistema compromès— i en trànsit amb TLS, i una gestió de secrets en què el fitxer 0600 és el mínim i mai l'ideal. Tanquen el conjunt les còpies 3-2-1 amb destinació immutable i credencials separades, única defensa real contra el segrest de dades, la part no negociable de les quals és la prova de restauració; i els marcs de verificació —CIS, lynis, debsecan—, que s'adapten i es documenten, mai s'apliquen a cegues, i l'ús dels quals sobre sistemes aliens exigeix autorització expressa per escrit.

Amb això, meteo-01 està raonablement protegit. Però fixa't en el que continua faltant, i és el més incòmode: tot l'anterior és prevenció. Cap d'aquestes mesures no et diu si alguna cosa ha passat. Si demà a les 03:12 hi ha un pic d'escriptures a /var/lib/meteora i apareix un procés que ningú no reconeix, com ho saps? Quins registres existeixen, on són i què contenen? Com s'investiga en l'ordre correcte, sense destruir l'evidència pel camí? I quin valor té un registre escrit per un sistema que l'atacant controla?

Això és Auditoria, Registres i Resposta a Incidents, on veurem syslog i journald amb els seus filtres, com es dissenya el registre d'una aplicació i què no ha de contenir mai, la rotació i la retenció, el subsistema auditd del nucli, la integritat dels registres davant d'un atacant amb privilegis, la detecció de canvis amb AIDE, i el cicle complet de resposta a incidents aplicat a aquell pic de les 03:12.

Fonaments de Sistemes Operatius

Mòdul 1: Introducció als Sistemes Operatius

Mòdul 2: Gestió de Recursos

Mòdul 3: Concurrència

Mòdul 4: Estructures de Fitxers

Mòdul 5: Protecció i Seguretat del Sistema

Mòdul 6: Virtualització i Contenidors

Mòdul 7: Administració i Diagnòstic a la Pràctica

© Copyright 2026. Tots els drets reservats