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
- Què resol una VPN i què no
- Casos d'ús i quin necessita Tramontana
- WireGuard enfront d'OpenVPN i IPsec
- Com funciona WireGuard: interfície, parells i encaminament criptogràfic
- AllowedIPs: encaminament i control d'accés alhora
- Instal·lació i generació de claus
- Configuració del servidor
- Configuració dels clients: portàtil i mòbil
- Encaminament, NAT i túnel dividit enfront de túnel complet
- DNS dins del túnel
- Integració amb el tallafocs i tancament de ports
- Verificació
- Gestió de clients: alta, baixa i rotació
- Seguretat: el que protegeix i el que no
- Automatització amb Ansible
- 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:
- 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.
- 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.
- Corre al nucli, sense copiar cada paquet a espai d'usuari. En un servidor de 2 vCPU, la diferència es nota.
- 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.
- É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 3Les conseqüències pràctiques són grans:
- Es configura amb
ipi ambwg, 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
nftablesde 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'
Endpointdels altres.
- 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 |
- 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,
AllowedIPsfunciona 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,
AllowedIPsfunciona com un filtre antisuplantació: un paquet desxifrat correctament amb la clau deluisla IP d'origen del qual no sigui alsAllowedIPsdeluises 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.
- 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 showindica 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 tunnelEl 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.pubLes 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/32Quatre 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 secondsMò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.confAdvertiment 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:
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 = 25A 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 = 1L'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:
- 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.
- 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.
- 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.15Amb ~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 reloadI 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/24Què 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 MAICom 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: wg0La 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.confPer 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
É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 tcpCom 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: trueDos 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.3WireGuard 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/0nomé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-ips4. 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=1420Errors Comuns i Consells
- Posar
AllowedIPs = 0.0.0.0/0en un parell del servidor. En trenca tota la connectivitat. Allà hi va només el/32del client. AllowedIPsencavalcats 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
PersistentKeepaliveen mòbils. El túnel «cau» al cap de pocs minuts d'inactivitat. - Fer servir
wg-quick down && upper afegir un client. Talla tots els connectats, inclòs tu si administres per la VPN. Fes servirwg syncconf. - Regenerar la clau del servidor a cada execució d'Ansible. Deixa fora tots els clients de cop.
creates:ostatabans. - 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 sentHi 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 0Tres 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=8404Aquí 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 wg0L'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 sentL'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:
wg showrespon en un segon la pregunta més important: si hi ha salutació, el problema no és la VPN. És la primera ordre, sempre.- 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. - 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 statusde manera evident. Els missatgesUFW BLOCKdel 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.confI al repositori d'Ansible, perquè si no es treu d'allà, el següent
ansible-playbookel 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 --diff3. 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_keysa tots els amfitrionsOperacions Contrasenya de PostgreSQL Rotar-la a passi ambALTER ROLEOperacions 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.7El 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.confper saber-hoMí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:
- 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.
- 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
- Què és Linux?
- Història de Linux
- Distribucions de Linux
- Instal·lant Linux
- Primer Contacte amb el Sistema
- Estructura del Sistema de Fitxers de Linux
Mòdul 2: Comandes Bàsiques de Linux
- Introducció a la Línia de Comandes
- Obtenir Ajuda i Documentació del Sistema
- Navegant pel Sistema de Fitxers
- Operacions amb Fitxers i Directoris
- Visualització i Edició de Fitxers
- Enllaços Durs i Simbòlics
- Permisos i Propietat dels Fitxers
Mòdul 3: Habilitats Avançades en la Línia de Comandes
- L'Entorn del Shell: Variables, Àlies i Historial
- Ús de Comodins i Expressions Regulars
- Cerca de Fitxers i Contingut: find, locate i grep
- Canonades i Redirecció
- Processament de Text: cut, sort, uniq, sed i awk
- Gestió de Processos
- Programació de Tasques amb Cron
- Comandes de Xarxa
Mòdul 4: Scripting en Shell
- Introducció al Scripting en Shell
- Variables i Tipus de Dades
- Entrada, Sortida i Arguments d'un Script
- Estructures de Control
- Funcions i Biblioteques
- Depuració i Gestió d'Errors
- Scripts de Producció: Bones Pràctiques
Mòdul 5: Administració del Sistema
- Gestió d'Usuaris i Grups
- sudo i Permisos Especials
- Gestió de Paquets
- Gestió de Discs
- systemd i la Gestió de Serveis
- Registres del Sistema: journald i syslog
- Monitoratge del Sistema i Optimització del Rendiment
- Còpies de Seguretat i Restauració
Mòdul 6: Xarxes i Seguretat
- Configuració de Xarxes
- SSH i Accés Remot
- Tallafocs i Seguretat Perimetral
- Sistemes de Detecció d'Intrusions
- Gestió de Secrets i Certificats TLS
- Assegurant Sistemes Linux
Mòdul 7: Temes Avançats
- El Procés d'Arrencada i la Recuperació del Sistema
- Diagnòstic Avançat: strace, perf i eBPF
- Optimització del Nucli de Linux
- Virtualització amb Linux
- Contenidors de Linux i Docker
- Automatització amb Ansible
- Alta Disponibilitat i Balanceig de Càrrega
