Hi ha un problema que arrossegues des de 06-03 sense resoldre'l del tot. PostgreSQL escolta a 10.0.2.15:5432 i ufw només ho permet des de 10.0.2.0/24, cosa que està bé mentre siguis dins d'aquella xarxa. El tauler d'estadístiques d'HAProxy de 07-07 té la mateixa restricció. I quan la Marta necessita mirar un informe des de casa, o quan tu has de diagnosticar alguna cosa un diumenge, l'única via és SSH al servidor i treballar des d'allà — cosa que funciona, però no escala i no serveix per a una eina gràfica.

La temptació evident és obrir el 5432 al món amb una contrasenya forta. És exactament el que no es fa: exposaries el motor de base de dades als escàners automàtics que recorren Internet, i el dia que aparegui una vulnerabilitat d'autenticació a PostgreSQL —n'han aparegut— la teva base de dades sencera estaria en joc mentre apliques el pedaç.

Aquesta lliçó munta l'alternativa correcta: una VPN amb WireGuard. En acabar, la xarxa interna serà abastable des de qualsevol lloc amb un sol port UDP obert, PostgreSQL i el tauler d'estadístiques quedaran tancats a l'exterior, i tindràs el mateix mecanisme a punt per arribar al servidor multimèdia de 08-03 des d'un hotel.

Contingut

  1. Què resol una VPN i què no
  2. Casos d'ús i quin necessita Tramontana
  3. WireGuard enfront d'OpenVPN i IPsec
  4. Com funciona WireGuard: interfície, parells i encaminament criptogràfic
  5. AllowedIPs: encaminament i control d'accés alhora
  6. Instal·lació i generació de claus
  7. Configuració del servidor
  8. Configuració dels clients: portàtil i mòbil
  9. Encaminament, NAT i túnel dividit enfront de túnel complet
  10. DNS dins del túnel
  11. Integració amb el tallafocs i tancament de ports
  12. Verificació
  13. Gestió de clients: alta, baixa i rotació
  14. Seguretat: el que protegeix i el que no
  15. Automatització amb Ansible
  16. Operació i problemes habituals

Què resol una VPN i què no

Una xarxa privada virtual crea un túnel xifrat entre dos punts de manera que el trànsit que hi circula per dins és indesxifrable i inalterable per a qui l'observi des de fora, i els extrems apareixen com si estiguessin a la mateixa xarxa local.

Això és tot el que fa. I convé enunciar-ho amb precisió perquè el màrqueting de les VPN comercials ha creat un núvol d'expectatives falses:

Una VPN sí Una VPN no
Xifra el trànsit entre el client i el servidor VPN Et fa anònim
Permet arribar a serveis interns sense publicar-los Protegeix d'un lloc web maliciós
Autentica el client abans de donar-li accés a la xarxa Protegeix de programari maliciós al teu equip
Amaga el contingut en una xarxa Wi-Fi pública Xifra més enllà del servidor VPN
Redueix a un els ports que cal exposar Converteix una xarxa interna en segura
Permet polítiques d'accés per origen Substitueix l'autenticació de cada servei

Les dues files més importants de la dreta:

«No xifra més enllà del servidor VPN.» El trànsit va xifrat del portàtil de la Marta al servidor. A partir d'aquí surt com qualsevol altre trànsit. Si va a PostgreSQL sense TLS, viatja en clar per la xarxa interna. La VPN trasllada el punt de confiança, no l'elimina — i per això a 08-02 vas configurar hostssl per a la rèplica.

«No converteix una xarxa interna en segura.» És l'error conceptual amb pitjors conseqüències, i hi tornarem a l'apartat 14.

Casos d'ús i quin necessita Tramontana

Cas Topologia Qui inicia Exemple
Accés remot Client → xarxa interna Persones La Marta consulta el tauler des de casa
Unir dues seus Xarxa ↔ xarxa Els routers, permanent Oficina i magatzem com una sola xarxa
Sortida per una altra IP Client → Internet Persones Veure contingut d'un altre país; amagar la IP
Accés remot Dues seus Sortida per una altra IP
Túnel Dividit normalment Dividit sempre Complet
Qui és «client» Un dispositiu Tota una xarxa Un dispositiu
Persistent No: es connecta en fer-lo servir Sí, sempre Mentre es faci servir
NAT necessari Només si el túnel és complet No Sí
Risc principal Un dispositiu compromès Una seu compromet l'altra Confiar en el proveïdor

Tramontana necessita el primer: accés remot. L'objectiu concret i acotat:

  • Que la Marta arribi al tauler d'estadístiques d'HAProxy i als informes des de casa.
  • Que tu arribis a PostgreSQL a 10.0.2.15:5432 amb una eina gràfica, sense exposar-lo.
  • Que l'accés SSH deixi de dependre de tenir el port 22 publicat a Internet.
  • Que, en conseqüència, es puguin tancar el 5432 i el 8404 a l'exterior.

El que no cal: unir seus (només n'hi ha una), ni sortida per una altra IP (ningú no ho necessita, i muntar-ho obligaria a encaminar tot el trànsit personal de la Marta pel servidor de l'empresa, cosa que a més té implicacions de privadesa que no volem).

WireGuard enfront d'OpenVPN i IPsec

WireGuard OpenVPN IPsec (strongSwan)
Línies de codi ~4.000 ~100.000 ~400.000 (+ IKE)
On corre Al nucli Espai d'usuari Nucli + dimoni IKE
Rendiment típic Molt alt, a prop del cable Moderat Alt
Criptografia Fixa, sense opcions: ChaCha20, Poly1305, Curve25519, BLAKE2s Configurable (i mal configurable) Molt configurable
Configuració Un fitxer de 15 línies Desenes de directives + PKI Complexa
Transport Només UDP UDP o TCP (pot passar per servidors intermediaris) UDP 500/4500 + ESP
Travessa NAT Sí, molt bé: keepalive Sí Amb NAT-T, de vegades problemàtic
Canvi de xarxa sense tallar (mòbil) Sí, transparent Reconnexió visible Amb MOBIKE
Autenticació Claus públiques per parell Certificats, usuari/contrasenya, TOTP Certificats, PSK, EAP
Usuari i contrasenya, MFA No de sèrie Sí Sí
Auditabilitat Alta: es pot llegir sencer Baixa per la mida Molt baixa
Estàndard del sector Creixent, dins del nucli Linux Molt estès El dels equips de xarxa

L'elecció és WireGuard, i les raons per ordre de pes:

  1. Les 4.000 línies de codi no són una curiositat: són la raó que es pugui auditar de debò i que la superfície d'atac sigui minúscula. OpenVPN i IPsec han tingut vulnerabilitats greus; amb vint-i-cinc vegades menys codi, hi ha vint-i-cinc vegades menys llocs on amagar-les.
  2. La criptografia no és configurable, i això és un avantatge. No hi ha negociació d'algorismes, no hi ha conjunts febles per desactivar, no hi ha atacs de degradació. La majoria de les VPN insegures del món no ho són per una fallada del programari, sinó per una configuració mal feta; WireGuard elimina aquella categoria sencera d'errors.
  3. Corre al nucli, sense copiar cada paquet a espai d'usuari. En un servidor de 2 vCPU, la diferència es nota.
  4. La configuració cap en una pantalla i s'entén sencera, cosa que fa que es pugui revisar.

I les dues raons honestes per triar una altra cosa:

  • Si necessites usuari, contrasenya i segon factor, WireGuard no ho fa: autentica per clau pública, punt. Existeixen capes per sobre, però no és natiu. En una empresa amb cent empleats i rotació, OpenVPN amb LDAP i TOTP pot ser millor.
  • Si la xarxa de destinació bloqueja UDP —algunes xarxes corporatives i d'hotels només deixen sortir el 80 i el 443 per TCP—, WireGuard senzillament no connectarà. OpenVPN sobre TCP/443 passa per gairebé qualsevol lloc disfressat d'HTTPS.

Per a Tramontana, amb dues o tres persones de confiança i control sobre els dispositius, WireGuard és clarament l'opció correcta.

Com funciona WireGuard: interfície, parells i encaminament criptogràfic

Aquí hi ha els tres conceptes que fan que WireGuard s'entengui o no s'entengui. Mereixen llegir-se a poc a poc.

  1. És una interfície de xarxa, no un servei

OpenVPN és un dimoni que corre, es configura, arrenca, s'atura i manté connexions. WireGuard no és un dimoni: és un tipus d'interfície de xarxa del nucli, com eth0 o com una interfície VLAN.

$ ip -brief link show
lo            UNKNOWN  00:00:00:00:00:00
enp0s3        UP       08:00:27:a1:b2:c3
wg0           UNKNOWN  # <- no te adreca MAC: es de capa 3

Les conseqüències pràctiques són grans:

  • Es configura amb ip i amb wg, les eines de xarxa de sempre.
  • L'encaminament és l'encaminament normal de Linux: ip route, taules, mètriques. El de 06-01 s'hi aplica tal qual.
  • El tallafocs la tracta com qualsevol altra interfície: les regles de nftables de 06-03 hi funcionen igual.
  • No hi ha «servidor» ni «client» al protocol. Tots dos extrems són parells (peers) idèntics. El que anomenem servidor és simplement el parell que té IP pública fixa i no especifica l'Endpoint dels altres.

  1. Un parell de claus per dispositiu, i res més

No hi ha autoritat certificadora, ni certificats, ni llistes de revocació, ni PKI. Cada dispositiu genera un parell de claus Curve25519. Perquè dos dispositius parlin, cadascun necessita la clau pública de l'altre. Això és tota la gestió d'identitat.

Element On viu Es comparteix
Clau privada Només al dispositiu que la va generar Mai
Clau pública Al dispositiu i a la configuració de l'altre parell Sí, lliurement
Clau precompartida (opcional) A tots dos extrems Només entre aquells dos

  1. Encaminament criptogràfic: el concepte central

Aquí hi ha la idea que fa WireGuard diferent de tota la resta. En una VPN clàssica hi ha dos mecanismes separats: un decideix per on va un paquet (encaminament) i un altre decideix si es permet (control d'accés). WireGuard els fusiona en un de sol.

Cada parell té associada una llista d'adreces IP: AllowedIPs. I aquella llista es fa servir en totes dues direccions, amb dos significats diferents:

graph LR
    subgraph SORTIDA["Paquet SORTINT (a quin parell l'envio?)"]
        A["Paquet amb<br/>destinació 10.8.0.3"] --> B{"Cercar a les taules<br/>AllowedIPs dels parells"}
        B -->|"coincideix amb<br/>el parell luis"| C["Xifrar amb la clau<br/>pública de luis<br/>i enviar al seu Endpoint"]
        B -->|"no coincideix<br/>amb cap"| D["DESCARTAR<br/>(no hi ha ruta)"]
    end
    subgraph ENTRADA["Paquet ENTRANT (l'accepto?)"]
        E["Paquet xifrat<br/>amb la clau de luis"] --> F["Desxifrar i mirar<br/>la IP d'ORIGEN"]
        F --> G{"La IP d'origen és<br/>als AllowedIPs de luis?"}
        G -->|Sí| H["Acceptar i lliurar<br/>al sistema"]
        G -->|"No"| I["DESCARTAR<br/>(suplantació)"]
    end

Llegit en paraules:

  • En enviar, AllowedIPs funciona com una taula de rutes: el paquet es xifra amb la clau del parell la llista del qual contingui la IP de destinació.
  • En rebre, AllowedIPs funciona com un filtre antisuplantació: un paquet desxifrat correctament amb la clau de luis la IP d'origen del qual no sigui als AllowedIPs de luis es descarta.

Aquella segona part és el que fa WireGuard segur per disseny. Encara que el portàtil del Luis estigui compromès i la seva clau privada robada, l'atacant no pot enviar paquets fingint ser 10.8.0.99 ni 10.0.2.15: la criptografia i l'encaminament estan lligats, i el nucli descarta el paquet abans que arribi enlloc.

  1. No hi ha estat de connexió

WireGuard no té «connectar» ni «desconnectar». La interfície existeix o no existeix. Quan hi ha un paquet per enviar, WireGuard fa una salutació d'un viatge d'anada i tornada —si no l'ha feta en els últims dos minuts— i l'envia. Si no hi ha trànsit, no hi ha paquets.

Això té tres conseqüències molt pràctiques:

  • Canviar de xarxa no talla res. Un mòbil que passa de Wi-Fi a 5G continua funcionant: WireGuard detecta el canvi d'origen al següent paquet vàlid i actualitza l'extrem automàticament (roaming).
  • Un parell inactiu és indistingible d'un d'apagat. wg show indica quan va ser l'última salutació, no si està «connectat».
  • WireGuard és silenciós davant de qui no té claus. Un paquet que no es desxifra correctament es descarta sense respondre res. Per a un escàner de ports, el port UDP de WireGuard és indistingible d'un port tancat. Aquella propietat, per si sola, ja justifica preferir-lo a exposar serveis.

Instal·lació i generació de claus

$ sudo apt install wireguard wireguard-tools qrencode
$ modinfo wireguard | head -3
filename:       /lib/modules/6.8.0-41-generic/kernel/drivers/net/wireguard/wireguard.ko.zst
license:        GPL v2
description:    WireGuard secure network tunnel

El mòdul és dins del nucli des de la versió 5.6, així que a Ubuntu 24.04 amb el nucli 6.8 no hi ha res per compilar.

Generar les claus amb els permisos correctes

# umask 077 ABANS de generar: si no, la clau privada neix amb
# permisos 644 durant un instant, i aquell instant ja basta.
$ umask 077
$ sudo mkdir -p /etc/wireguard/claus
$ cd /etc/wireguard

# Servidor
$ wg genkey | sudo tee claus/servidor.key | wg pubkey | sudo tee claus/servidor.pub
kLm3Xq7pR2vN8sT4uY9wA1bC5dE6fG0hI2jK3lM4nO8=

# Clau precompartida per client (apartat 14)
$ wg genpsk | sudo tee claus/marta.psk >/dev/null

$ sudo chmod 0600 claus/*.key claus/*.psk
$ sudo chmod 0644 claus/*.pub
$ sudo ls -l claus/
-rw------- 1 root root 45 Aug 18 10:12 marta.psk
-rw------- 1 root root 45 Aug 18 10:11 servidor.key
-rw-r--r-- 1 root root 45 Aug 18 10:11 servidor.pub

Les tres ordres mereixen una nota: wg genkey genera una clau privada Curve25519 en base64; wg pubkey deriva la pública llegint la privada de l'entrada estàndard; wg genpsk genera 32 bytes aleatoris per a la clau precompartida. La derivació és d'un sol sentit: de la pública no es pot tornar a la privada.

La clau privada del servidor no surt mai del servidor. La clau privada de cada client no surt mai del client. Això significa que, idealment, és el client qui genera el seu parell i t'envia només la pública. A la pràctica, per a un mòbil, se sol generar al servidor i transferir-se per QR; és un compromís acceptable si la transferència és directa i el fitxer s'esborra després.

El pla d'adreçament

Abans d'escriure res, cal decidir el rang de la VPN. Ha de no encavalcar-se amb cap xarxa que els clients puguin fer servir:

Xarxa Rang Comentari
Interna de Tramontana 10.0.2.0/24 La que cal abastar
VPN 10.8.0.0/24 Triada: poc habitual en routers domèstics
Casa de la Marta 192.168.1.0/24 Molt habitual: evitar aquell rang
Hotels i cafeteries 192.168.0.0/24, 10.0.0.0/24 Evitar també

Aquell 10.0.0.0/24 de l'última fila és una trampa real: és el rang per defecte de molts routers, i si un client és en una xarxa 10.0.2.0/24 —la mateixa que la interna de Tramontana— tindrà un conflicte de rutes irresoluble. Triar rangs poc freqüents per a la infraestructura pròpia evita aquell problema per sempre.

Parell IP a la VPN Dispositiu
servidor 10.8.0.1 srv-tramontana
operador-portatil 10.8.0.2 El teu portàtil
marta-portatil 10.8.0.3 Portàtil de la Marta
marta-mobil 10.8.0.4 Mòbil de la Marta
luis-portatil 10.8.0.5 Portàtil del Luis

Configuració del servidor

# /etc/wireguard/wg0.conf
# Permisos 0600, propietat de root: conte la clau privada.

[Interface]
# Adreca d AQUEST parell dins de la VPN. La mascara /24 fa que el
# nucli afegeixi automaticament una ruta a tota la xarxa 10.8.0.0/24
# per wg0 en aixecar la interficie.
Address = 10.8.0.1/24

# Port UDP d escolta. Triar-ne un d alt i poc habitual redueix el
# soroll dels escaners, encara que NO es una mesura de seguretat real:
# WireGuard no respon a qui no te clau, aixi que el port es
# indistingible d un de tancat en qualsevol cas.
ListenPort = 51820

# La clau privada. S injecta des d un fitxer per no tenir-la en clar
# aqui: PostUp la llegeix. Alternativa mes simple pero menys neta:
#   PrivateKey = <contingut de servidor.key>
PostUp = wg set %i private-key /etc/wireguard/claus/servidor.key

# --- Regles de xarxa en aixecar la interficie ---
# %i se substitueix pel nom de la interficie (wg0).
#
# 1. Permetre el reenviament de paquets que entren o surten per wg0
# 2. Emmascarar el transit que surt cap a la xarxa interna, perque les
#    respostes tornin al servidor VPN i no es perdin cercant 10.8.0.x
PostUp = nft add table inet wg
PostUp = nft 'add chain inet wg forward { type filter hook forward priority 0; }'
PostUp = nft add rule inet wg forward iifname "%i" oifname "enp0s3" accept
PostUp = nft add rule inet wg forward iifname "enp0s3" oifname "%i" ct state related,established accept
PostUp = nft 'add chain inet wg postrouting { type nat hook postrouting priority 100; }'
PostUp = nft add rule inet wg postrouting oifname "enp0s3" ip saddr 10.8.0.0/24 masquerade

# En abaixar la interficie s elimina la taula sencera: una sola ordre,
# idempotent, sense deixar regles orfes acumulades.
PostDown = nft delete table inet wg

# Comptabilitat: qui es connecta i des d on queda al journal
PostUp = logger -t wireguard "interficie %i aixecada"
PostDown = logger -t wireguard "interficie %i abaixada"

# ==========================================================
#                     P A R E L L S
# ==========================================================

# --- operador (portatil d administracio) ---
[Peer]
# PublicKey identifica el parell. Es el seu nom criptografic.
PublicKey = aB3cD4eF5gH6iJ7kL8mN9oP0qR1sT2uV3wX4yZ5aB6c=
# Clau precompartida: capa addicional simetrica (apartat 14)
PresharedKey = zY9xW8vU7tS6rQ5pO4nM3lK2jI1hG0fE9dC8bA7zY6x=
# AllowedIPs per a un CLIENT: nomes la seva propia IP, amb mascara /32.
# Significa dues coses: (1) el transit cap a 10.8.0.2 va a aquest parell;
# (2) aquest parell NOMES pot enviar paquets amb origen 10.8.0.2.
AllowedIPs = 10.8.0.2/32

# --- Marta, portatil ---
[Peer]
PublicKey = cD5eF6gH7iJ8kL9mN0oP1qR2sT3uV4wX5yZ6aB7cD8e=
PresharedKey = xW7vU6tS5rQ4pO3nM2lK1jI0hG9fE8dC7bA6zY5xW4v=
AllowedIPs = 10.8.0.3/32

# --- Marta, mobil ---
[Peer]
PublicKey = eF7gH8iJ9kL0mN1oP2qR3sT4uV5wX6yZ7aB8cD9eF0g=
PresharedKey = vU5tS4rQ3pO2nM1lK0jI9hG8fE7dC6bA5zY4xW3vU2t=
AllowedIPs = 10.8.0.4/32
# El mobil es darrere de NAT i canvia de xarxa constantment. El
# keepalive el posa el CLIENT, no el servidor: es qui ha de mantenir
# viva l associacio NAT de la seva operadora.

# --- Luis, portatil (acces restringit) ---
[Peer]
PublicKey = gH9iJ0kL1mN2oP3qR4sT5uV6wX7yZ8aB9cD0eF1gH2i=
PresharedKey = tS3rQ2pO1nM0lK9jI8hG7fE6dC5bA4zY3xW2vU1tS0r=
AllowedIPs = 10.8.0.5/32
$ sudo chmod 0600 /etc/wireguard/wg0.conf
$ sudo chown root:root /etc/wireguard/wg0.conf

Quatre detalls d'aquella configuració que convé entendre:

Address = 10.8.0.1/24, amb /24 i no /32. La màscara determina la ruta que wg-quick afegeix automàticament: amb /24, tot 10.8.0.0/24 s'encamina per wg0. Amb /32 caldria afegir les rutes a mà.

Les regles van en una taula inet wg pròpia. És l'aplicació del que vas aprendre a 06-03: una taula separada s'elimina d'un cop amb nft delete table i no interfereix amb les regles que gestiona ufw. L'alternativa —afegir regles a la taula existent— deixa restes acumulades cada vegada que es reinicia la interfície.

La clau privada es llegeix d'un fitxer amb PostUp. Posar-la directament a wg0.conf funciona igual, però barreja secret i configuració en un sol fitxer, cosa que complica gestionar-lo amb Ansible sense no_log a tot arreu. Separar-los permet versionar la configuració i tractar la clau a part.

AllowedIPs = 10.8.0.2/32 per a cada client, amb /32. És el correcte i és on més gent s'equivoca. Vegeu l'apartat 9.

Configuració dels clients: portàtil i mòbil

Portàtil Linux

# /etc/wireguard/wg0.conf al portatil d operador
[Interface]
Address = 10.8.0.2/32
PrivateKey = <clau privada del portatil, generada AQUI>
# DNS intern mentre el tunel esta actiu (apartat 10)
DNS = 10.0.2.15

[Peer]
# La clau PUBLICA del servidor
PublicKey = kLm3Xq7pR2vN8sT4uY9wA1bC5dE6fG0hI2jK3lM4nO8=
PresharedKey = zY9xW8vU7tS6rQ5pO4nM3lK2jI1hG0fE9dC8bA7zY6x=

# On es el servidor. Es pot fer servir un nom DNS: WireGuard el
# resol en aixecar la interficie, cosa que permet IP dinamica.
Endpoint = vpn.tramontana.example:51820

# --- TUNEL DIVIDIT: nomes la xarxa interna i la VPN van pel tunel ---
# La resta del transit del portatil (navegacio, correu, videotrucades)
# surt per la seva connexio normal.
AllowedIPs = 10.0.2.0/24, 10.8.0.0/24

# Mantenir viva l associacio de NAT del router del client. 25 s es
# el valor recomanat: per sota del temps d expiracio tipic de les
# taules NAT (30 s en molts routers domestics).
PersistentKeepalive = 25
$ sudo systemctl enable --now wg-quick@wg0
$ sudo wg show
interface: wg0
  public key: aB3cD4eF5gH6iJ7kL8mN9oP0qR1sT2uV3wX4yZ5aB6c=
  private key: (hidden)
  listening port: 45182

peer: kLm3Xq7pR2vN8sT4uY9wA1bC5dE6fG0hI2jK3lM4nO8=
  preshared key: (hidden)
  endpoint: 203.0.113.45:51820
  allowed ips: 10.0.2.0/24, 10.8.0.0/24
  latest handshake: 8 seconds ago
  transfer: 12.41 KiB received, 18.02 KiB sent
  persistent keepalive: every 25 seconds

Mòbil, amb codi QR

Les aplicacions oficials de WireGuard per a Android i iOS llegeixen la configuració des d'un QR, cosa que evita teclejar quaranta-quatre caràcters en base64 tres vegades:

$ sudo qrencode -t ansiutf8 < /etc/wireguard/clients/marta-mobil.conf
█████████████████████████████████████
██ ▄▄▄▄▄ █▀ █▀▀██▀▄ ▀█ ▄█ ▄▄▄▄▄ ██
██ █   █ █▄  ▀▄█▄▀▄█▄ ▀█ █   █ ██
██ █▄▄▄█ █ ▀▄ ▄▀▄ █▀▄▀██ █▄▄▄█ ██
...

# I per enviar-lo per un canal segur, com a PNG
$ sudo qrencode -t png -o /tmp/marta-mobil.png \
      < /etc/wireguard/clients/marta-mobil.conf

Advertiment important sobre el QR: conté la clau privada en clar. No s'envia per correu, ni per WhatsApp, ni es deixa a /tmp. Es mostra al terminal, s'escaneja davant del mòbil, i s'esborra:

$ shred -u /tmp/marta-mobil.png

Un QR de WireGuard fotografiat per damunt de l'espatlla és accés complet a la xarxa interna.

# marta-mobil.conf
[Interface]
Address = 10.8.0.4/32
PrivateKey = <generada per a aquest mobil>
DNS = 10.0.2.15

[Peer]
PublicKey = kLm3Xq7pR2vN8sT4uY9wA1bC5dE6fG0hI2jK3lM4nO8=
PresharedKey = vU5tS4rQ3pO2nM1lK0jI9hG8fE7dC6bA5zY4xW3vU2t=
Endpoint = vpn.tramontana.example:51820
AllowedIPs = 10.0.2.0/24, 10.8.0.0/24
# En mobil, el keepalive es imprescindible: sense ell, l associacio NAT
# de l operadora caduca i el servidor no pot iniciar transit cap al
# telefon fins que aquest enviï alguna cosa.
PersistentKeepalive = 25

A l'aplicació mòbil convé activar a més «VPN a demanda» excloent-hi la xarxa Wi-Fi de l'oficina: així el túnel s'aixeca automàticament fora i no molesta a dins.

Encaminament, NAT i túnel dividit enfront de túnel complet

El reenviament de paquets

Per defecte, Linux no reenvia paquets entre interfícies: és una màquina, no un router. Perquè el trànsit que arriba per wg0 surti cap a 10.0.2.0/24 cal activar-ho:

$ cat /proc/sys/net/ipv4/ip_forward
0

# Persistent, al fitxer d enduriment que ja existeix (06-06)
$ echo 'net.ipv4.ip_forward = 1' | \
      sudo tee /etc/sysctl.d/72-wireguard.conf
$ echo 'net.ipv6.conf.all.forwarding = 0' | \
      sudo tee -a /etc/sysctl.d/72-wireguard.conf
$ sudo sysctl --system | grep forward
net.ipv4.ip_forward = 1

L'IPv6 es deixa desactivat deliberadament: si no el fas servir, activar-lo obre un camí que no estàs vigilant. És la mateixa lògica de 06-06.

Per què cal l'emmascarament

La Marta, des de 10.8.0.3, consulta PostgreSQL a 10.0.2.15. Sense NAT, el paquet arriba a PostgreSQL amb origen 10.8.0.3, PostgreSQL respon a 10.8.0.3... i la seva taula de rutes no sap on és aquella xarxa, així que envia la resposta a la passarel·la per defecte i es perd.

Hi ha dues solucions:

Emmascarament (NAT) Ruta estàtica a cada destinació
Què fa El servidor VPN reescriu l'origen Cada màquina interna aprèn a arribar a 10.8.0.0/24
Configuració Una regla, en un lloc Una ruta a cada màquina o al router
Els serveis interns veuen La IP del servidor VPN La IP real del client VPN
Registres i control per IP Es perd la granularitat Es conserva
Quan fer-lo servir Xarxes que no controles Xarxes pròpies, i és preferible

A Tramontana, amb tot dins de 10.0.2.0/24 i el servidor VPN sent la mateixa màquina, l'emmascarament simplifica i el cost és acceptable. En una xarxa més gran, la ruta estàtica al router és millor: permet que pg_hba.conf distingeixi la Marta del Luis per la seva IP de VPN, i que els registres d'accés siguin útils.

Túnel dividit enfront de túnel complet

És la decisió de disseny amb més conseqüències de tota la lliçó, i es pren amb una sola línia: AllowedIPs al client.

Túnel dividit Túnel complet
AllowedIPs del client 10.0.2.0/24, 10.8.0.0/24 0.0.0.0/0, ::/0
Què va pel túnel Només l'intern Tot
Amplada de banda del servidor Mínima Tot el trànsit de tothom
Latència de la navegació normal Sense canvis Pitjor: fa la volta pel servidor
Videotrucades i reproducció en flux Directes Pel servidor: poden anar malament
Protegeix en Wi-Fi públic Només el trànsit intern Tot el trànsit
Privadesa de l'usuari Es respecta L'empresa veu tota la seva navegació
Permet forçar polítiques de sortida No Sí: filtratge, registre
Fuita de DNS possible Sí, cal vigilar-ho No

Per a Tramontana la decisió és el túnel dividit, i amb tres arguments:

  1. Amplada de banda. Tot el trànsit de tres persones passant per un servidor de 2 vCPU amb una connexió compartida degradaria la navegació de tothom i competiria amb l'aplicació de reserves, que és el que dona de menjar.
  2. Privadesa. Amb túnel complet, tota la navegació personal de la Marta —inclosa la que fa fora d'hores al seu portàtil d'empresa— passa per un servidor de l'empresa i apareix als seus registres. Això té implicacions de protecció de dades i, sobretot, no és necessari per a l'objectiu.
  3. L'objectiu és accedir al que és intern, no protegir la navegació general. Per a això hi ha HTTPS i el sentit comú a les xarxes públiques.

Quan sí el túnel complet: un portàtil corporatiu que ha de complir la política de filtratge de l'empresa sigui on sigui, o algú que treballa habitualment des de xarxes que no són de fiar i necessita que tot vagi xifrat. En aquell cas, AllowedIPs = 0.0.0.0/0 al client, i les regles d'emmascarament del servidor ja ho admeten tal com estan escrites.

DNS dins del túnel

Amb túnel dividit apareix un detall fàcil de passar per alt: els noms interns.

# Sense DNS intern, amb el tunel aixecat
$ dig +short srv-tramontana.intern
# (buit: el DNS de l hotel no coneix aquell nom)

La directiva DNS = 10.0.2.15 del client fa que wg-quick configuri el resolutor mentre el túnel està actiu. A Ubuntu amb systemd-resolved, wg-quick fa servir resolvectl i fa una cosa elegant:

$ resolvectl status wg0
Link 5 (wg0)
    Current Scopes: DNS
         Protocols: +DefaultRoute
Current DNS Server: 10.0.2.15
       DNS Servers: 10.0.2.15

Amb ~tramontana.example com a domini de cerca, només les consultes d'aquell domini anirien al DNS intern i la resta continuaria pel DNS normal — el que s'anomena DNS dividit i és l'ideal amb túnel dividit:

# Al client, per a un DNS dividit de debo
PostUp = resolvectl dns %i 10.0.2.15; resolvectl domain %i '~tramontana.example'

La fuita de DNS és el problema simètric: si el client continua fent servir el DNS de l'hotel per a tot, aquell servidor veu quins noms interns consultes, cosa que revela informació sobre la teva infraestructura. Comprovar-ho:

$ resolvectl query panel.tramontana.example
panel.tramontana.example: 10.0.2.20 -- link: wg0

$ sudo tcpdump -ni enp0s3 port 53 -c 5
# Amb el DNS dividit ben configurat, aqui NO han d apareixer
# consultes de noms interns.

Requisit previ, és clar: que hi hagi un DNS intern que resolgui aquells noms. Amb tres màquines, dnsmasq a srv-tramontana basta i sobra.

Integració amb el tallafocs i tancament de ports

Aquest és l'apartat on la VPN paga el seu cost, tancant ports que feia temps que estaven oberts des del Mòdul 6.

# 1. Obrir el port de WireGuard. UDP, i nomes aquell.
$ sudo ufw allow 51820/udp comment 'WireGuard'

# 2. Permetre el transit que arriba PEL tunel
$ sudo ufw allow in on wg0 comment 'Transit intern de la VPN'

# 3. Permetre el reenviament, que per defecte ufw denega
$ sudo sed -i 's/^DEFAULT_FORWARD_POLICY=.*/DEFAULT_FORWARD_POLICY="ACCEPT"/' \
      /etc/default/ufw
$ sudo ufw reload

I ara la part important. Abans de tancar res, cal verificar que la VPN funciona — és literalment el cas de «no tanquis mai la porta per la qual estàs entrant»:

# --- VERIFICAR PRIMER, des del client amb el tunel aixecat ---
$ psql -h 10.0.2.15 -U operador -d tramontana -c 'SELECT 1;' >/dev/null && echo OK
OK
$ curl -sI http://10.0.2.20:8404/ | head -1
HTTP/1.1 200 OK
$ ssh [email protected] 'hostname'
srv-tramontana

# --- NOMES ALESHORES, tancar ---
$ sudo ufw status numbered | grep -E '5432|8404|22'
[ 3] 5432/tcp    ALLOW IN    10.0.2.0/24
[ 5] 8404/tcp    ALLOW IN    10.0.2.0/24
[ 7] 22/tcp      LIMIT IN    Anywhere

# PostgreSQL: nomes des de la VPN i des de la propia xarxa interna
$ sudo ufw delete 5
$ sudo ufw delete 3
$ sudo ufw allow from 10.8.0.0/24 to any port 5432 proto tcp comment 'PostgreSQL via VPN'
$ sudo ufw allow from 10.8.0.0/24 to any port 8404 proto tcp comment 'Tauler via VPN'

# SSH: es mante obert amb LIMIT com a xarxa de seguretat. Tancar-lo
# del tot significa que una fallada de WireGuard et deixa sense acces
# a la maquina. Es tanca quan hi hagi una segona via provada (consola
# del proveidor o KVM), no abans.

Aquella última decisió mereix defensar-se, perquè la temptació de tancar el 22 és forta. No es tanca SSH fins a tenir una segona via d'accés provada. Si WireGuard falla després d'una actualització del nucli, o si la configuració es corromp, o si el mòdul no carrega, el 22 obert amb LIMIT i fail2ban és la diferència entre un ensurt i un desplaçament físic al centre de dades. És exactament la lliçó de 06-02.

Resultat final:

$ sudo ufw status verbose
Status: active
Default: deny (incoming), allow (outgoing), allow (routed)

To                          Action      From
--                          ------      ----
22/tcp                      LIMIT IN    Anywhere
80,443/tcp                  ALLOW IN    Anywhere
51820/udp (WireGuard)       ALLOW IN    Anywhere
Anywhere on wg0             ALLOW IN    Anywhere
5432/tcp (PostgreSQL VPN)   ALLOW IN    10.8.0.0/24
8404/tcp (Tauler VPN)       ALLOW IN    10.8.0.0/24

Què ha canviat, i és el resum del projecte:

Servei Abans Després
PostgreSQL 5432 Obert a 10.0.2.0/24; inabastable des de fora Només per VPN, abastable des de qualsevol lloc
Tauler 8404 Igual Només per VPN
Ports TCP exposats a Internet 22, 80, 443 22, 80, 443 (sense canvis)
Ports UDP exposats 0 1, que no respon a qui no té clau
Accés remot de la Marta SSH i treballar al servidor Directe, amb les seves eines

Verificació

# 1. La interficie existeix i te la seva adreca
$ ip -brief addr show wg0
wg0   UNKNOWN   10.8.0.1/24

# 2. Les rutes que ha creat wg-quick
$ ip route show dev wg0
10.8.0.0/24 proto kernel scope link src 10.8.0.1

# 3. Estat dels parells (des del servidor)
$ sudo wg show
interface: wg0
  public key: kLm3Xq7pR2vN8sT4uY9wA1bC5dE6fG0hI2jK3lM4nO8=
  private key: (hidden)
  listening port: 51820

peer: cD5eF6gH7iJ8kL9mN0oP1qR2sT3uV4wX5yZ6aB7cD8e=
  preshared key: (hidden)
  endpoint: 198.51.100.87:41922
  allowed ips: 10.8.0.3/32
  latest handshake: 41 seconds ago
  transfer: 4.18 MiB received, 28.44 MiB sent

peer: eF7gH8iJ9kL0mN1oP2qR3sT4uV5wX6yZ7aB8cD9eF0g=
  allowed ips: 10.8.0.4/32
  # sense 'latest handshake': aquest parell no s ha connectat MAI

Com es llegeix wg show, que és l'eina principal de diagnòstic:

Camp Què significa
endpoint On és el parell ara. Absent = no s'ha vist mai
latest handshake Quan. Més de 3 min = inactiu o desconnectat
transfer Bytes xifrats intercanviats
Sense latest handshake El parell no ha connectat mai: revisar claus o xarxa
# 4. Connectivitat basica (des del client)
$ ping -c3 10.8.0.1
64 bytes from 10.8.0.1: icmp_seq=1 ttl=64 time=24.1 ms

# 5. Abastar la xarxa interna
$ ping -c2 10.0.2.15 && nc -z -v 10.0.2.15 5432
Connection to 10.0.2.15 5432 port [tcp/postgresql] succeeded!

# 6. COMPROVAR QUE EL TRANSIT VA XIFRAT
# A enp0s3 (la interficie fisica) nomes es veu UDP opac:
$ sudo tcpdump -ni enp0s3 udp port 51820 -c 3 -X | head -12
14:02:11.482 IP 198.51.100.87.41922 > 10.0.2.15.51820: UDP, length 128
	0x0000:  4500 009c 0000 4000 3611 ...
	0x0020:  0400 0000 9f2c 1a4b 8e73 d012 4a91 ...
# No hi ha ni un byte llegible: ni "SELECT", ni capcaleres, res.

# A wg0 (dins del tunel) es veu el transit REAL, ja desxifrat:
$ sudo tcpdump -ni wg0 -c 3
14:02:14.118 IP 10.8.0.3.51422 > 10.0.2.15.5432: Flags [P.], length 34
14:02:14.121 IP 10.0.2.15.5432 > 10.8.0.3.51422: Flags [P.], length 8

# 7. Amb tunel dividit, la IP de sortida NO canvia
$ curl -s https://ifconfig.me; echo
198.51.100.87        # <- la de l hotel, no la del servidor

# Amb tunel complet (AllowedIPs = 0.0.0.0/0) seria:
# 203.0.113.45       # <- la del servidor VPN

# 8. Comprovar que no hi ha fuita de DNS
$ resolvectl query srv-tramontana.intern
srv-tramontana.intern: 10.0.2.15 -- link: wg0

La comprovació 6 és la que demostra el valor de tot el muntatge: dues captures de la mateixa comunicació, una d'il·legible a la xarxa pública i una altra de llegible dins del túnel. És el que cal ensenyar quan algú pregunta què fa exactament una VPN.

I la 7 és la que confirma la decisió de túnel dividit: la navegació normal de la Marta continua sortint per la seva connexió, com ha de ser.

Gestió de clients: alta, baixa i rotació

Alta d'un client

#!/usr/bin/env bash
#
# alta_vpn.sh - Dona d alta un client de WireGuard
#
# Us: alta_vpn.sh <nom> [ip]
#
set -euo pipefail

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

readonly WG_DIR=/etc/wireguard
readonly WG_IF=wg0
readonly WG_XARXA=10.8.0
readonly WG_ENDPOINT="vpn.tramontana.example:51820"
readonly WG_ALLOWED="10.0.2.0/24, 10.8.0.0/24"

umask 077

seguent_ip() {
    # La primera lliure a partir de .2, mirant la configuracio real
    local usades i
    usades="$(grep -oP 'AllowedIPs\s*=\s*'"${WG_XARXA}"'\.\K[0-9]+' \
        "${WG_DIR}/${WG_IF}.conf" | sort -n)"
    for i in $(seq 2 254); do
        grep -qx "$i" <<<"$usades" || { echo "${WG_XARXA}.${i}"; return 0; }
    done
    morir 73 "no queden adreces lliures a ${WG_XARXA}.0/24"
}

main() {
    local nom="${1:?us: alta_vpn.sh <nom> [ip]}"
    [[ "$nom" =~ ^[a-z0-9-]+$ ]] || morir 2 "nom invalid: nomes [a-z0-9-]"
    [[ -f "${WG_DIR}/clients/${nom}.conf" ]] && morir 2 "el client $nom ja existeix"

    requereix_comanda wg
    requereix_comanda qrencode

    local ip; ip="${2:-$(seguent_ip)}"
    log "donant d alta '$nom' amb IP $ip"

    mkdir -p "${WG_DIR}/clients" "${WG_DIR}/claus"

    local priv pub psk
    priv="$(wg genkey)"
    pub="$(wg pubkey <<<"$priv")"
    psk="$(wg genpsk)"

    # 1. Configuracio del client
    cat > "${WG_DIR}/clients/${nom}.conf" <<EOF
[Interface]
Address = ${ip}/32
PrivateKey = ${priv}
DNS = 10.0.2.15

[Peer]
PublicKey = $(wg pubkey < "${WG_DIR}/claus/servidor.key")
PresharedKey = ${psk}
Endpoint = ${WG_ENDPOINT}
AllowedIPs = ${WG_ALLOWED}
PersistentKeepalive = 25
EOF
    chmod 0600 "${WG_DIR}/clients/${nom}.conf"

    # 2. Afegir el parell a la configuracio persistent
    cat >> "${WG_DIR}/${WG_IF}.conf" <<EOF

# --- ${nom} (alta: $(date +%F)) ---
[Peer]
PublicKey = ${pub}
PresharedKey = ${psk}
AllowedIPs = ${ip}/32
EOF

    # 3. I en CALENT, sense tallar els altres. 'wg syncconf' aplica els
    #    canvis sense abaixar la interficie; 'wg-quick down/up' tallaria
    #    tots els clients connectats.
    wg syncconf "$WG_IF" <(wg-quick strip "$WG_IF")

    log "client '$nom' donat d alta"
    printf '\nEscaneja aquest codi amb l aplicacio de WireGuard:\n\n'
    qrencode -t ansiutf8 < "${WG_DIR}/clients/${nom}.conf"
    printf '\nConfiguracio a: %s/clients/%s.conf\n' "$WG_DIR" "$nom"
    printf 'ESBORRA-LA del servidor despres de lliurar-la a l usuari.\n'
}

main "$@"
$ sudo ~/scripts/alta_vpn.sh marta-tauleta
[2026-08-18 14:22:01] donant d alta 'marta-tauleta' amb IP 10.8.0.6
[2026-08-18 14:22:01] client 'marta-tauleta' donat d alta

Escaneja aquest codi amb l aplicacio de WireGuard:
█████████████████████████
...

La línia clau és wg syncconf: aplica els canvis sense abaixar la interfície, de manera que donar d'alta un client nou no talla els que estan connectats. Amb wg-quick down && wg-quick up es tallarien tots, i és un error freqüent.

Baixa d'un client

# Immediata, en calent: n hi ha prou amb treure la clau publica
$ sudo wg set wg0 peer 'cD5eF6gH7iJ8kL9mN0oP1qR2sT3uV4wX5yZ6aB7cD8e=' remove

# I de la configuracio persistent, perque no torni en reiniciar
$ sudo sed -i '/--- marta-portatil/,/^$/d' /etc/wireguard/wg0.conf
$ sudo wg-quick strip wg0 | sudo wg syncconf wg0 /dev/stdin
$ sudo shred -u /etc/wireguard/clients/marta-portatil.conf

Per què n'hi ha prou amb treure el parell, i és un avantatge arquitectònic de WireGuard: no hi ha llista de revocació, ni certificats que continuïn sent vàlids, ni finestra de caducitat. El servidor només accepta paquets de claus públiques que siguin a la seva configuració. Així que la clau desapareix, aquell dispositiu és indistingible de qualsevol desconegut d'Internet: els seus paquets es descarten sense resposta.

Compara-ho amb OpenVPN, on revocar un certificat exigeix actualitzar la CRL, assegurar-se que el servidor la llegeix, i que no hi hagi sessions actives. Aquí és una ordre que fa efecte al següent paquet.

Rotació de claus

Situació Acció Urgència
Robatori o pèrdua d'un dispositiu Treure el parell immediatament Minuts
Algú deixa l'empresa Treure el parell El mateix dia
Rotació preventiva Regenerar el parell del client Anual
Compromís del servidor Regenerar-HO TOT: servidor i tots els clients Immediata

La rotació de la clau del servidor és l'operació disruptiva, perquè la seva clau pública és a la configuració de tots els clients. El procediment sense talls: aixecar una segona interfície wg1 en un altre port amb la clau nova, migrar els clients un a un, i retirar wg0 quan l'últim hagi migrat.

Seguretat: el que protegeix i el que no

La clau privada no surt mai del dispositiu

És el principi bàsic, i té una implicació operativa concreta: el correcte és que el client generi el seu propi parell i t'enviï únicament la clau pública. La clau pública es pot enviar per correu sense cap problema.

# Al portatil del Luis
$ umask 077 && wg genkey | tee privada.key | wg pubkey
gH9iJ0kL1mN2oP3qR4sT5uV6wX7yZ8aB9cD0eF1gH2i=
# El Luis envia NOMES aquella linia. La privada no surt del seu portatil.

L'script alta_vpn.sh genera el parell al servidor perquè per a un mòbil és el pràctic. És un compromís conscient, i per això l'script insisteix a esborrar el fitxer després de lliurar-lo.

La clau precompartida

PresharedKey = zY9xW8vU7tS6rQ5pO4nM3lK2jI1hG0fE9dC8bA7zY6x=

És una capa de xifratge simètric de 256 bits que es barreja en la derivació de claus, a més de l'intercanvi Curve25519.

Contra què protegeix, que és una amenaça futura i no actual: un ordinador quàntic prou gran podria trencar Curve25519 mitjançant l'algorisme de Shor. No existeix avui, ni hi és a prop, però existeix un model d'amenaça anomenat «collir ara, desxifrar després»: un adversari amb recursos captura i emmagatzema trànsit xifrat avui per desxifrar-lo d'aquí a quinze anys. La clau precompartida, en ser simètrica, és resistent a aquell atac —Grover només redueix la seguretat efectiva de 256 a 128 bits, cosa que continua sent inabastable.

Contra què no protegeix: res del que passi avui. No afegeix seguretat davant d'atacants clàssics. És una pòlissa barata contra un risc llunyà, i per això és a la configuració: costa una línia.

El que cal dir en veu alta

Una VPN no converteix una xarxa interna en una xarxa segura.

És la conclusió més important de la lliçó, i la més ignorada. Amb la VPN muntada, qualsevol que arribi pel túnel és dins de 10.0.2.0/24 i veu tot el que veu una màquina d'aquella xarxa. Si el portàtil del Luis s'infecta amb un troià, aquell troià té accés directe a PostgreSQL, al tauler d'estadístiques i a qualsevol servei que confiï en la xarxa interna.

Amenaça La VPN en protegeix?
Espionatge del trànsit en una xarxa pública Sí
Escaneig de ports des d'Internet Sí: no hi ha ports TCP nous
Força bruta contra PostgreSQL des d'Internet Sí: ja no és abastable
Portàtil d'un usuari infectat No: el troià entra pel túnel
Contrasenya feble en un servei intern No
Vulnerabilitat d'un servei intern No, si l'atacant arriba per VPN
Un empleat descontent No
Moviment lateral un cop a dins No

El que se'n segueix, d'aquella taula, és l'arquitectura de confiança zero: cada servei autentica i xifra pel seu compte, sense confiar en la xarxa. A la pràctica, per a Tramontana, significa que la VPN no substitueix res del que ja tens: pg_hba.conf continua exigint scram-sha-256, el tauler d'estadístiques continua necessitant la seva restricció, SSH continua amb claus i fail2ban, i TLS continua fent falta a dins. La VPN és una capa més, no un perímetre que permeti relaxar les altres.

I una restricció addicional que la VPN sí que permet fer bé, aprofitant AllowedIPs com a control d'accés:

# El Luis es desenvolupador: no necessita PostgreSQL de produccio.
# La seva IP de VPN es fixa (10.8.0.5), aixi que es pot filtrar.
$ sudo ufw deny from 10.8.0.5 to any port 5432 proto tcp \
      comment 'Luis: sense acces a la BD de produccio'
$ sudo ufw allow from 10.8.0.5 to any port 8404 proto tcp

Com que cada parell té una IP fixa garantida pel mateix protocol —recorda: AllowedIPs impedeix suplantar-la—, aquesta regla és fiable d'una manera que no ho seria en una xarxa normal.

Automatització amb Ansible

# ~/tramontana-infra/roles/vpn/defaults/main.yml
---
vpn_interficie: wg0
vpn_port: 51820
vpn_xarxa: 10.8.0.0/24
vpn_ip_servidor: 10.8.0.1
vpn_interficie_sortida: enp0s3
vpn_endpoint: vpn.tramontana.example
vpn_dns_intern: 10.0.2.15
vpn_xarxes_accessibles: "10.0.2.0/24, 10.8.0.0/24"
vpn_clients:
  - { nom: operador-portatil, ip: 10.8.0.2, acces_bd: true }
  - { nom: marta-portatil,    ip: 10.8.0.3, acces_bd: true }
  - { nom: marta-mobil,       ip: 10.8.0.4, acces_bd: false }
  - { nom: luis-portatil,     ip: 10.8.0.5, acces_bd: false }

Les claus públiques i precompartides viuen a Vault, integrat amb pass segons 07-06:

# group_vars/all/vault.yml (xifrat amb ansible-vault)
vault_vpn_clients:
  operador-portatil:
    pubkey: "aB3cD4eF5gH6iJ7kL8mN9oP0qR1sT2uV3wX4yZ5aB6c="
    psk: "zY9xW8vU7tS6rQ5pO4nM3lK2jI1hG0fE9dC8bA7zY6x="
  marta-portatil:
    pubkey: "cD5eF6gH7iJ8kL9mN0oP1qR2sT3uV4wX5yZ6aB7cD8e="
    psk: "xW7vU6tS5rQ4pO3nM2lK1jI0hG9fE8dC7bA6zY5xW4v="
# ~/tramontana-infra/roles/vpn/tasks/main.yml
---
- name: Instal·lar WireGuard
  ansible.builtin.apt:
    name: [wireguard, wireguard-tools, qrencode]
    state: present
  tags: [paquets]

- name: Comprovar que el modul del nucli esta disponible
  ansible.builtin.command: modinfo wireguard
  register: modwg
  changed_when: false
  failed_when: modwg.rc != 0

- name: Directoris de WireGuard
  ansible.builtin.file:
    path: "{{ item }}"
    state: directory
    owner: root
    group: root
    mode: '0700'
  loop:
    - /etc/wireguard
    - /etc/wireguard/claus

- name: Generar la clau privada del servidor si no existeix
  ansible.builtin.shell:
    cmd: "umask 077 && wg genkey > /etc/wireguard/claus/servidor.key"
    creates: /etc/wireguard/claus/servidor.key
  # 'creates' fa la tasca idempotent: NO regenera la clau a cada
  # execucio, cosa que deixaria fora tots els clients (07-06).

- name: Llegir la clau publica del servidor
  ansible.builtin.shell:
    cmd: "wg pubkey < /etc/wireguard/claus/servidor.key"
  register: wg_pub
  changed_when: false

- name: Activar el reenviament d IPv4
  ansible.posix.sysctl:
    name: net.ipv4.ip_forward
    value: '1'
    sysctl_file: /etc/sysctl.d/72-wireguard.conf
    reload: true

- name: Desplegar la configuracio del servidor
  ansible.builtin.template:
    src: wg0.conf.j2
    dest: "/etc/wireguard/{{ vpn_interficie }}.conf"
    owner: root
    group: root
    mode: '0600'
    backup: true
  no_log: true                    # conte claus precompartides
  notify: Sincronitzar wireguard

- name: Generar la configuracio de cada client
  ansible.builtin.template:
    src: client.conf.j2
    dest: "/etc/wireguard/clients/{{ item.nom }}.conf"
    owner: root
    group: root
    mode: '0600'
  loop: "{{ vpn_clients }}"
  loop_control:
    label: "{{ item.nom }}"
  no_log: true
  tags: [clients]

- name: Obrir el port de WireGuard
  community.general.ufw:
    rule: allow
    port: "{{ vpn_port }}"
    proto: udp
    comment: WireGuard
  tags: [firewall]

- name: Permetre el transit entrant per la interficie del tunel
  community.general.ufw:
    rule: allow
    interface: "{{ vpn_interficie }}"
    direction: in
  tags: [firewall]

- name: Acces a PostgreSQL nomes per als clients autoritzats
  community.general.ufw:
    rule: "{{ 'allow' if item.acces_bd else 'deny' }}"
    src: "{{ item.ip }}"
    port: '5432'
    proto: tcp
    comment: "VPN {{ item.nom }}"
  loop: "{{ vpn_clients }}"
  loop_control:
    label: "{{ item.nom }}"
  tags: [firewall]

- name: Activar la interficie
  ansible.builtin.systemd:
    name: "wg-quick@{{ vpn_interficie }}"
    enabled: true
    state: started

- name: Forcar els handlers abans de verificar
  ansible.builtin.meta: flush_handlers

# --- Verificacio ---
- name: Comprovar que la interficie esta aixecada amb la seva adreca
  ansible.builtin.command: "ip -brief addr show {{ vpn_interficie }}"
  register: ipwg
  changed_when: false
  failed_when: vpn_ip_servidor not in ipwg.stdout
  tags: [verificar]

- name: Comprovar que tots els parells configurats hi son
  ansible.builtin.command: "wg show {{ vpn_interficie }} peers"
  register: parells
  changed_when: false
  failed_when: parells.stdout_lines | length != vpn_clients | length
  tags: [verificar]
# roles/vpn/handlers/main.yml
---
- name: Sincronitzar wireguard
  # 'wg syncconf' aplica els canvis SENSE ABAIXAR la interficie: no talla
  # els clients connectats. 'wg-quick down/up' els tallaria tots,
  # i a tu mateix si estas administrant PER la VPN.
  ansible.builtin.shell:
    cmd: "wg syncconf {{ vpn_interficie }} <(wg-quick strip {{ vpn_interficie }})"
    executable: /bin/bash
  changed_when: true

Dos detalls d'aquest rol que resumeixen el que vas aprendre a 07-06: el creates: que impedeix regenerar la clau del servidor a cada execució —cosa que deixaria fora tots els clients de cop—, i el handler amb wg syncconf en lloc de reiniciar, que és la versió WireGuard del reload en comptes de restart de 08-01.

Operació i problemes habituals

Què mirar

# Parells i ultima salutacio, en una linia per parell
$ sudo wg show wg0 dump | awk 'NR>1 {
    printf "%-12s %-22s %s\n", substr($1,1,10)"...", $3,
    ($5==0 ? "MAI" : strftime("%F %T", $5)) }'
cD5eF6gH7i... 198.51.100.87:41922    2026-08-18 14:02:11
eF7gH8iJ9k... (none)                 MAI

# Volum per parell (util per detectar un client descontrolat)
$ sudo wg show wg0 transfer
cD5eF6gH7iJ8kL9mN0oP1qR2sT3uV4wX5yZ6aB7cD8e=	4382144	29827072
Freqüència Què es mira Senyal d'alarma
Continu (08-06) Interfície wg0 aixecada Absent
Continu Parells amb salutació recent Cap en horari laboral
Setmanal Parells que no han connectat mai Alta que no es va completar, o clau mal lliurada
Mensual Parells sense activitat en 90 dies Candidats a baixa
Mensual Trànsit anòmal per parell Un client compromès

Els cinc problemes que apareixen de debò

1. AllowedIPs encavalcats — l'error més comú i el més desconcertant.

# MALAMENT: dos parells reclamen la mateixa adreca
[Peer]   # marta
AllowedIPs = 10.8.0.3/32
[Peer]   # luis
AllowedIPs = 10.8.0.0/24     # <-- inclou 10.8.0.3

WireGuard resol l'encaminament pel prefix més específic, igual que la taula de rutes. Amb aquella configuració, el trànsit a 10.8.0.3 va a la Marta (més específic), però el Luis pot enviar paquets falsificant qualsevol IP de 10.8.0.0/24, perquè el seu filtre d'entrada l'hi permet. És una bretxa de seguretat, no només un problema d'encaminament.

Pitjor encara: AllowedIPs = 0.0.0.0/0 en un parell del servidor significa «tot el trànsit d'Internet va per aquest parell». És correcte a la configuració d'un client amb túnel complet i catastròfic a la del servidor: en trenca tota la connectivitat.

Regla: al servidor, cada client porta només el seu /32. 0.0.0.0/0 només apareix a la configuració del client, mai a la del servidor.

# Detectar encavalcaments abans que mosseguin
$ sudo wg show wg0 allowed-ips | awk '{$1=""; print}' | tr ' ' '\n' | \
      grep -v '^$' | sort | uniq -d
# (buit: sense encavalcaments)

2. La salutació no passa mai.

$ sudo wg show wg0 | grep -A2 'peer:'
peer: eF7gH8iJ9kL0mN1oP2qR3sT4uV5wX6yZ7aB8cD9eF0g=
  allowed ips: 10.8.0.4/32
  # sense 'latest handshake'

Per ordre de probabilitat: el port UDP no hi arriba (tallafocs del servidor, del router, o del proveïdor del client); les claus no es corresponen (la pública del client al servidor no deriva de la seva privada); l'Endpoint és incorrecte o el DNS no resol; o hi ha una PresharedKey en un costat i no a l'altre.

# Comprovar que els paquets hi arriben, ni que sigui
$ sudo tcpdump -ni enp0s3 udp port 51820
# Si no apareix RES amb el client intentant connectar, el problema
# es abans del servidor: xarxa, router o tallafocs intermedi.

# Verificar que una clau publica es correspon amb una privada
$ wg pubkey < /etc/wireguard/claus/servidor.key
kLm3Xq7pR2vN8sT4uY9wA1bC5dE6fG0hI2jK3lM4nO8=

3. Salutació correcta però no hi ha trànsit. El túnel està muntat i no passa res. Gairebé sempre és una de tres: falta net.ipv4.ip_forward=1, falta la regla d'emmascarament, o falta la xarxa de destinació als AllowedIPs del client.

$ sysctl net.ipv4.ip_forward
$ sudo nft list table inet wg | grep masquerade
$ sudo wg show wg0 allowed-ips

4. Es talla després d'uns minuts d'inactivitat. És NAT: el router del client o l'operadora mòbil oblida l'associació. Solució: PersistentKeepalive = 25 al client.

5. Fragmentació i MTU. El símptoma és característic i enganyós: ping funciona, SSH connecta i es penja en llistar un directori gran, les pàgines web carreguen a mitges.

# La MTU per defecte de WireGuard es 1420 (1500 - 80 de capcaleres).
# Amb PPPoE o altres encapsulacions pot continuar sent massa gran.
$ ping -M do -s 1372 -c2 10.0.2.15      # 1372 + 28 = 1400
$ ping -M do -s 1392 -c2 10.0.2.15      # 1392 + 28 = 1420
ping: local error: message too long, mtu=1420
# Al client, si hi ha fragmentacio
[Interface]
MTU = 1380

Errors Comuns i Consells

  • Posar AllowedIPs = 0.0.0.0/0 en un parell del servidor. En trenca tota la connectivitat. Allà hi va només el /32 del client.
  • AllowedIPs encavalcats entre parells. No és només un problema de rutes: permet que un client suplanti les IP d'un altre.
  • Triar 192.168.1.0/24 o 10.0.0.0/24 per a la VPN. Xocarà amb la xarxa de casa d'algú o d'un hotel, i el conflicte no té arranjament des del teu costat.
  • Enviar el QR o el fitxer de configuració per correu o missatgeria. Conté la clau privada en clar: és accés complet a la xarxa interna.
  • Generar les claus sense umask 077. Neixen amb permisos 644, encara que sigui un instant.
  • Tancar el port SSH el mateix dia que muntes la VPN. Si WireGuard falla després d'actualitzar el nucli, et quedes fora. Segona via provada primer.
  • Oblidar net.ipv4.ip_forward=1. El túnel es munta perfectament i no passa ni un sol paquet cap a la xarxa interna.
  • Oblidar PersistentKeepalive en mòbils. El túnel «cau» al cap de pocs minuts d'inactivitat.
  • Fer servir wg-quick down && up per afegir un client. Talla tots els connectats, inclòs tu si administres per la VPN. Fes servir wg syncconf.
  • Regenerar la clau del servidor a cada execució d'Ansible. Deixa fora tots els clients de cop. creates: o stat abans.
  • Creure que la VPN fa segura la xarxa interna. Un portàtil infectat entra pel túnel amb tots els permisos. Cada servei autentica pel seu compte.
  • Treure el TLS dels serveis interns «perquè ja va per la VPN». El xifratge acaba al servidor VPN, no al servei.
  • Diagnosticar sense tcpdump. Veure si els paquets UDP arriben, ni que sigui, al servidor separa en un minut els problemes de xarxa dels de configuració.
  • Ignorar la MTU. El símptoma —connecta i després es penja amb transferències grans— sembla qualsevol cosa menys fragmentació.
  • Consell de mètode. Abans de tancar un port, verifica des del client que ja hi arribes per la VPN. La regla del curs no admet excepcions: no tanquis mai la porta per la qual estàs entrant.

Exercicis

Exercici 1

La Marta et truca des d'un hotel: «la VPN diu que està connectada però no puc entrar al tauler». Diagnostica el problema de manera sistemàtica, del més probable al menys, i resol-lo.

Exercici 2

Dissenya el procediment complet de resposta davant la pèrdua del mòbil de la Marta amb la VPN configurada, en forma de manual d'operació, i inclou-hi el que cal fer després perquè l'incident no es repeteixi igual.

Exercici 3

La Marta pregunta si amb la VPN «ja estem segurs» i si es poden simplificar altres mesures ara que la xarxa està protegida. Redacta la resposta.

Solucions

Solució 1

Mètode: de fora cap a dins, i del més probable al menys. Cada pas descarta una capa sencera, i aquell ordre estalvia la major part del temps.

# ===== PAS 1: hi ha salutacio? (separa la xarxa de tota la resta) =====
# Des del servidor:
$ sudo wg show wg0 | grep -A4 'cD5eF6gH7iJ8'
peer: cD5eF6gH7iJ8kL9mN0oP1qR2sT3uV4wX5yZ6aB7cD8e=
  endpoint: 198.51.100.87:41922
  latest handshake: 14 seconds ago
  transfer: 892 B received, 0 B sent

Hi ha salutació recent: el túnel està criptogràficament establert, la clau és correcta, el port UDP hi arriba i el Wi-Fi de l'hotel deixa passar. Això descarta d'un cop les quatre causes més freqüents.

Però fixa't en 0 B sent: el servidor rep de la Marta i no li envia res. Aquell detall apunta directament a encaminament o filtratge, no a la VPN.

# ===== PAS 2: arriben els paquets al servidor pel tunel? =====
$ sudo tcpdump -ni wg0 -c 5
14:31:02 IP 10.8.0.3.52118 > 10.0.2.20.8404: Flags [S], seq 118..., length 0
14:31:03 IP 10.8.0.3.52118 > 10.0.2.20.8404: Flags [S], seq 118..., length 0
14:31:05 IP 10.8.0.3.52118 > 10.0.2.20.8404: Flags [S], seq 118..., length 0

Tres SYN sense resposta. El paquet de la Marta travessa el túnel correctament i arriba al servidor. El problema és després de la VPN: alguna cosa entre el servidor i el port 8404 no respon. La VPN queda descartada per complet.

# ===== PAS 3: es el tallafocs? =====
$ sudo ufw status numbered | grep 8404
[ 9] 8404/tcp   ALLOW IN   10.8.0.0/24

# La regla existeix. S esta aplicant? Comptar encerts:
$ sudo nft list ruleset | grep -B2 -A2 8404
$ sudo journalctl -k --since "5 min ago" | grep -i 'UFW BLOCK' | tail -3
[UFW BLOCK] IN=wg0 OUT=enp0s3 SRC=10.8.0.3 DST=10.0.2.20 PROTO=TCP DPT=8404

Aquí està. IN=wg0 OUT=enp0s3 significa que el paquet està sent reenviat, no lliurat localment: el tauler d'HAProxy viu a 10.0.2.20, que és una altra màquina. I ufw denega el reenviament per defecte — la regla allow ... to any port 8404 s'aplica a la cadena d'entrada, no a la de reenviament.

# ===== PAS 4: confirmar la causa arrel =====
$ grep DEFAULT_FORWARD_POLICY /etc/default/ufw
DEFAULT_FORWARD_POLICY="DROP"

Confirmat. El pas 11 de la lliçó es va aplicar a mitges: es va permetre el trànsit cap al servidor però no el reenviament a través seu.

Resolució:

# Opcio A: permetre el reenviament en general (mes simple)
$ sudo cp /etc/default/ufw /etc/default/ufw.bak-$(date +%F)
$ sudo sed -i 's/^DEFAULT_FORWARD_POLICY=.*/DEFAULT_FORWARD_POLICY="ACCEPT"/' \
      /etc/default/ufw
$ sudo diff -u /etc/default/ufw.bak-$(date +%F) /etc/default/ufw
-DEFAULT_FORWARD_POLICY="DROP"
+DEFAULT_FORWARD_POLICY="ACCEPT"
$ sudo ufw reload

# Opcio B (preferible): reenviar NOMES el que cal, mantenint DROP
$ sudo ufw route allow in on wg0 out on enp0s3 to 10.0.2.20 port 8404 proto tcp
$ sudo ufw route allow in on wg0 out on enp0s3 to 10.0.2.15 port 5432 proto tcp
$ sudo ufw route allow in on wg0 out on enp0s3 to 10.0.2.0/24 port 22 proto tcp
$ sudo ufw reload

$ sudo ufw status | grep ROUTE
Anywhere on enp0s3         ALLOW FWD    Anywhere on wg0
10.0.2.20 8404/tcp         ALLOW FWD    Anywhere on wg0

L'opció B és la correcta i mereix defensar-se: manté la política de llista blanca de 06-03, on el reenviament per defecte continua denegat i només es permet explícitament el que cal. L'opció A obre el reenviament de qualsevol cosa a qualsevol lloc a través del servidor VPN, que és precisament el tipus de permís ampli que el model d'amenaces de 06-06 desaconsella.

Verificació:

# Des del portatil de la Marta
$ curl -sI http://10.0.2.20:8404/ | head -1
HTTP/1.1 200 OK

# I des del servidor, ara hi ha transit en tots dos sentits
$ sudo wg show wg0 | grep -A3 'cD5eF6gH7iJ8' | tail -1
  transfer: 128.42 KiB received, 891.20 KiB sent

L'arbre de decisió resumit, que va al manual d'operació per a la propera vegada:

Símptoma Què descarta Pas següent
Sense salutació Res encara Xarxa, port UDP, claus, Endpoint
Salutació, 0 B sent Xarxa i criptografia correctes Encaminament o tallafocs del servidor
Salutació, trànsit simètric, no funciona Túnel completament sa El servei de destinació, o el DNS
Va, però es penja amb dades grans Tot l'anterior MTU

I les tres lliçons d'aquest incident:

  1. wg show respon en un segon la pregunta més important: si hi ha salutació, el problema no és la VPN. És la primera ordre, sempre.
  2. L'asimetria received / sent és un indicador molt potent que gairebé ningú no mira: diu si la fallada és al camí d'anada o al de tornada.
  3. Un tallafocs amb el reenviament denegat és la causa número u de «la VPN connecta però no arribo enlloc», i no apareix a ufw status de manera evident. Els missatges UFW BLOCK del nucli el delaten en deu segons.

Solució 2

Manual d'operació: Pèrdua o robatori d'un dispositiu amb VPN

Document: RB-SEG-04 · Versió: 1.0 · Data: 2026-08-18 Severitat: Alta · Temps objectiu de contenció: 15 minuts des de l'avís Ubicació: arxivador d'operacions i ~/tramontana-infra/docs/. Còpia impresa: la resposta pot haver-se de donar des d'un mòbil, sense accés al servidor.

0. Avaluació inicial (2 min)

Tres preguntes, i no s'espera a tenir les respostes per començar el pas 1:

Pregunta Per què importa
El dispositiu té bloqueig de pantalla i xifratge? Determina si la clau és accessible
Hi tenia desades altres credencials (correu, pass, SSH)? Amplia l'abast molt més enllà de la VPN
Perdut o robat? Un robatori dirigit implica un adversari amb intenció

S'assumeix el pitjor cas fins a demostrar el contrari. Retirar l'accés d'un dispositiu que després apareix costa cinc minuts de reconfiguració; no retirar-lo pot costar la base de dades.

1. Contenció immediata (5 min) — fer això ABANS d'investigar

# 1.1 Identificar la clau publica del dispositiu
$ grep -B3 '10.8.0.4/32' /etc/wireguard/wg0.conf
# --- marta-mobil (alta: 2026-08-18) ---
[Peer]
PublicKey = eF7gH8iJ9kL0mN1oP2qR3sT4uV5wX6yZ7aB8cD9eF0g=

# 1.2 RETIRAR EL PARELL EN CALENT. Efecte immediat, sense tallar ningu mes.
$ sudo wg set wg0 peer 'eF7gH8iJ9kL0mN1oP2qR3sT4uV5wX6yZ7aB8cD9eF0g=' remove

# 1.3 Confirmar que ha desaparegut
$ sudo wg show wg0 peers | grep -c 'eF7gH8iJ9k'
0

# 1.4 Bloquejar tambe la IP, per si de cas (defensa en profunditat)
$ sudo ufw insert 1 deny from 10.8.0.4 comment 'INCIDENT 2026-08-18'

A partir d'1.2, aquell dispositiu és indistingible d'un desconegut d'Internet. No hi ha llista de revocació que propagar ni finestra de caducitat: WireGuard descarta sense respondre els paquets de claus que no coneix.

2. Persistència del canvi (3 min)

# Sense aixo, el parell torna en reiniciar la interficie o el servidor
$ sudo cp /etc/wireguard/wg0.conf /etc/wireguard/wg0.conf.bak-$(date +%F)
$ sudo sed -i '/--- marta-mobil/,/^$/d' /etc/wireguard/wg0.conf
$ sudo diff -u /etc/wireguard/wg0.conf.bak-$(date +%F) /etc/wireguard/wg0.conf
$ sudo shred -u /etc/wireguard/clients/marta-mobil.conf

I al repositori d'Ansible, perquè si no es treu d'allà, el següent ansible-playbook el torna a donar d'alta:

$ cd ~/tramontana-infra
$ vim roles/vpn/defaults/main.yml     # eliminar l entrada marta-mobil
$ ansible-vault edit group_vars/all/vault.yml   # eliminar les seves claus
$ git commit -am "Baixa de marta-mobil: dispositiu perdut 2026-08-18"
$ ansible-playbook lloc.yml --limit srv-tramontana --tags vpn --check --diff

3. Avaluació d'abast (15 min)

# 3.1 Hi va haver activitat des del moment de la perdua?
$ sudo journalctl -t wireguard --since "2026-08-18 09:00" | tail -20

# 3.2 Accessos a la base de dades des de la seva IP de VPN
$ sudo journalctl -u postgresql@16-main --since "2026-08-18 09:00" | \\
      grep '10.8.0.4'

# 3.3 Accessos al tauler d estadistiques
$ journalctl -t nginx_acces --since "2026-08-18 09:00" | grep '10.8.0.4'

# 3.4 Sessions SSH
$ sudo last -i | grep '10.8.0.4'
$ sudo journalctl -u ssh --since "2026-08-18 09:00" | grep 'Accepted'
Troballa Conclusió Acció addicional
Sense activitat després de la pèrdua Contenció a temps Cap
Activitat compatible amb l'ús normal de la Marta Probablement ella Confirmar-ho amb ella
Activitat anòmala (hores estranyes, consultes massives) Compromís probable Escalar al pas 6

4. Altres credencials del dispositiu (20 min)

La VPN gairebé mai no és l'única cosa que hi ha en un mòbil. Es revisa i es rota tot el que hi hagués:

Credencial Acció Responsable
Correu corporatiu Tancar sessions remotes i canviar la contrasenya Marta
Clau SSH (si en tenia) Treure-la d'authorized_keys a tots els amfitrions Operacions
Contrasenya de PostgreSQL Rotar-la a pass i amb ALTER ROLE Operacions
Sessions de Tramontana Reserves Invalidar totes les d'aquell compte Luis
Gestor de contrasenyes Canviar la clau mestra Marta
Segon factor (TOTP) Regenerar i revisar els codis de recuperació Marta
# Exemple: rotar la contrasenya de la base de dades
$ pass generate -f tramontana/db 32
$ sudo -u postgres psql -c "ALTER ROLE operador PASSWORD '$(pass tramontana/db)';"

5. Restabliment del servei (10 min)

# Quan la Marta tingui dispositiu nou: parell NOU, IP NOVA.
# No es reutilitza ni la clau ni l adreca: reutilitzar la IP
# confondria els registres de l incident amb l activitat legitima
# posterior.
$ sudo ~/scripts/alta_vpn.sh marta-mobil2
[..] donant d alta 'marta-mobil2' amb IP 10.8.0.7

El QR s'escaneja presencialment o en videotrucada amb la Marta ensenyant la pantalla del mòbil nou. Mai per missatgeria.

6. Escalat

Es declara incident de seguretat complet —i se segueix el procediment de 06-04— si es compleix qualsevol d'aquestes condicions:

  • Hi ha activitat des de la IP del dispositiu posterior al moment de la pèrdua.
  • El dispositiu no tenia bloqueig de pantalla ni xifratge.
  • Contenia claus SSH o credencials d'administració.
  • Va ser un robatori dirigit, no un extraviament.

7. Prevenció: què es canvia per a la propera vegada

Aquesta és la part que converteix un incident en una millora, i la que gairebé tots els procediments ometen.

Mesura Estat Justificació
Xifratge i bloqueig obligatoris a tot dispositiu amb VPN Pendent: política per escriure Sense xifratge, la clau privada és llegible extraient-ne la memòria
Registrar qui té quin dispositiu i amb quin accés Pendent: inventari Avui cal buscar-ho a wg0.conf per saber-ho
Mínim privilegi per parell: el mòbil no necessita PostgreSQL Aplicar ja Redueix l'abast de la propera pèrdua
Alerta automàtica si un parell nou o inactiu connecta Pendent (08-06) Detecció, no només prevenció
Revisió trimestral de parells i baixes Aplicar ja Els parells s'acumulen i ningú no els treu
Assaig anual d'aquest manual d'operació Aplicar ja Un procediment no assajat és una suposició
# Minim privilegi per a dispositius mobils, aplicat avui mateix
$ sudo ufw deny from 10.8.0.7 to any port 5432 proto tcp \\
      comment 'mobil: sense acces a la BD'
$ sudo ufw deny from 10.8.0.7 to any port 22 proto tcp \\
      comment 'mobil: sense SSH'

8. Registre

S'anota al quadern de guàrdia (08-06): data i hora de l'avís, hora de la contenció, abast avaluat, credencials rotades i mesures preventives acordades. Sense culpables: perdre un mòbil no és una falta, i tractar-ho com a tal garanteix que la propera vegada triguin dos dies a avisar — que és l'única cosa que convertiria aquest incident en un desastre.

Solució 3

Sobre la VPN: què hi hem guanyat i què no canvia Per a: Marta Vidal · De: Operacions de sistemes · 18 d'agost de 2026

Resposta breu: hi hem guanyat força, i no, no podem simplificar res. De fet, la VPN funciona precisament perquè la resta de mesures continuen al seu lloc.


Què hem muntat. Un túnel xifrat que permet al teu portàtil i al teu mòbil comportar-se, des de qualsevol lloc del món, com si estiguessin endollats a la xarxa de l'oficina. Necessites una clau digital única del teu dispositiu; sense ella, el servidor ni tan sols respon: per a qualsevol que l'escanegi des d'Internet, aquell punt d'entrada és indistingible d'un que no existeix.

Què hi hem guanyat, en concret:

Abans Ara
Per veure els informes des de casa calia demanar-m'ho Hi entres directament amb les teves eines
La base de dades era inabastable des de fora Abastable només pel túnel
El tauler d'estat, igual Igual
Al Wi-Fi d'un hotel, el trànsit intern aniria en clar Va xifrat d'extrem a extrem
Qualsevol servei nou que volguessis fer servir des de fora calia publicar-lo Ja és accessible, sense publicar res

L'important: hem tancat portes, no obert. Abans, perquè poguessis treballar des de fora, l'única opció hauria estat publicar la base de dades a Internet. Amb la VPN hem fet el contrari: hi ha menys coses exposades que ahir, i tot i així pots arribar a més.

Ara la pregunta que em fas, i la resposta és que no. Entenc per què la fas: si la xarxa està protegida, sembla raonable relaxar el de dins. És un raonament molt estès i és la causa d'una part important de les bretxes de seguretat que surten a les notícies.

El motiu, amb un exemple concret. Imagina't que el teu portàtil s'infecta amb un programa maliciós —per un correu, per un web, per una memòria USB—. Aquell programa és dins del teu portàtil, i el teu portàtil és dins del túnel. Per a ell, la VPN no és cap obstacle: és una autopista directa a la base de dades de reserves. La VPN comprova que el dispositiu és teu; no comprova què està fent el dispositiu.

Per això tota la resta continua sent necessari:

Mesura Es pot relaxar? Per què
Contrasenya de la base de dades No És l'única cosa que atura un programa maliciós ja dins del túnel
Xifratge de les connexions internes No El túnel acaba al servidor; d'allà endavant no protegeix
Bloqueig d'intents d'accés repetits No Algú pot arribar per altres vies
Còpies de seguretat No La VPN no protegeix d'errors ni d'esborrats
Actualitzacions de seguretat No Una fallada en un programa s'explota igual des de dins
Registre de qui accedeix a què No, al contrari Ara hi ha més maneres d'arribar-hi: cal vigilar més

La manera correcta de veure-ho, i és com es treballa avui al sector: no existeix «dins segur» i «fora perillós». Cada servei comprova qui ets i xifra el seu, sigui qui sigui a l'altre costat. La VPN és una capa més, no un mur que permeti abaixar la guàrdia al darrere.

El que sí que hem pogut fer gràcies a la VPN, i això sí que és una simplificació real: restringir per persona. Com que cada dispositiu té una adreça fixa garantida pel mateix sistema —no es pot falsificar—, he pogut deixar que tu arribis a la base de dades i que el Luis, que és desenvolupador i no la necessita en producció, no hi pugui. Abans això no era possible de manera fiable. És menys accés, més ben repartit.

Dues coses que necessito de la teva part:

  1. Que el mòbil i el portàtil tinguin bloqueig de pantalla i xifratge activats. La clau digital viu al dispositiu; si algú l'encén i hi entra sense contrasenya, té la clau. És l'única mesura que depèn de tu i és la més important de totes.
  2. Que m'avisis immediatament si perds un dispositiu, a qualsevol hora. Retirar-li l'accés em porta dos minuts i és instantani: no hi ha finestres ni esperes. Perdre un mòbil no és un problema; trigar un dia a dir-ho, sí. Tinc el procediment escrit i assajat.

I una cosa més, per al registre. He aprofitat per tancar a l'exterior la base de dades i el tauler d'estat, que fins ara depenien d'estar físicament a la xarxa de l'oficina. He deixat obert l'accés d'administració habitual, i no per descuit: si la VPN fallés després d'una actualització, sense aquella segona via hauria de desplaçar-me físicament al servidor per arreglar-la. El tancaré quan tinguem una alternativa provada, no abans. És la regla que aplico sempre: no es tanca mai la porta per la qual estàs entrant.

Conclusió

La VPN està muntada i ha pagat el seu cost immediatament: PostgreSQL i el tauler d'estadístiques estan tancats a l'exterior i, alhora, són accessibles des de qualsevol lloc. El servidor exposa un port UDP més que ahir, i aquell port no respon a qui no té clau, cosa que el fa indistingible d'un de tancat per a qualsevol escàner d'Internet. És l'intercanvi que buscaves: menys superfície exposada i més abast.

Entens WireGuard per dins, que era l'objectiu real. Saps que és una interfície de xarxa i no un dimoni, i que per això les eines de 06-01 i les regles de 06-03 s'hi apliquen tal qual. Saps que no hi ha servidor ni client al protocol, només parells amb claus públiques. I sobretot entens AllowedIPs, que és el concepte que separa qui ha copiat una configuració de qui sap què fa: en enviar és una taula de rutes, en rebre és un filtre antisuplantació, i per això al servidor cada client porta el seu /32 i 0.0.0.0/0 només apareix al costat del client. Aquell doble paper és el que fa que la IP de VPN de cada persona sigui un identificador fiable, i el que t'ha permès donar accés a la base de dades a uns dispositius i negar-lo a d'altres.

Has decidit el túnel dividit amb arguments —amplada de banda, privadesa de les persones i l'objectiu real, que és arribar al que és intern— en lloc de per costum. Has verificat el xifratge capturant els mateixos paquets a les dues interfícies: opacs a enp0s3, llegibles a wg0. Has automatitzat l'alta de clients amb wg syncconf, que aplica canvis sense tallar ningú, i amb un creates: a Ansible que impedeix el desastre de regenerar la clau del servidor. I no has tancat SSH, perquè una segona via d'accés provada va abans que l'elegància.

I t'endus la frase que més importa de tota la lliçó: una VPN no converteix una xarxa interna en una xarxa segura. Trasllada el punt de confiança, redueix l'exposició i dona un identificador fiable per dispositiu. No protegeix d'un portàtil infectat, ni d'una contrasenya feble, ni del moviment lateral un cop a dins. Tot el que vas muntar al Mòdul 6 continua sent igual de necessari avui que ahir.

A 08-05 arriba el projecte més gran del mòdul i el que exigeix més honestedat: un clúster de Kubernetes. La lliçó et dirà des del primer paràgraf que per a Tramontana és desproporcionat, i t'explicarà exactament per què, amb números. I tot i així la faràs sencera, perquè és la tecnologia dominant del sector i perquè te la trobaràs a la teva feina. Muntaràs k3s sobre srv-tramontana-proves, entendràs el pla de control peça a peça, desplegaràs Tramontana Reserves amb Deployments, Services, Ingress, ConfigMaps i Secrets —i veuràs que un Secret és base64, no xifrat, cosa que enllaça directament amb 06-05—, i aprendràs a llegir un CrashLoopBackOff en lloc de témer-lo. Amb el tancament honest sobre la complexitat operativa real de mantenir un clúster viu.

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