Salta el contingut

Nginx i Proxy Invers

Introducció a Nginx

Nginx (es pronuncia "engine x") va ser creat per Igor Sysoev el 2004 amb un objectiu concret: resoldre el problema C10k (atendre deu mil connexions simultànies) que els servidors basats en un procés o fil per connexió, com Apache amb el MPM prefork, no gestionaven bé. Nginx utilitza una arquitectura asíncrona i orientada a esdeveniments, amb un nombre reduït de processos worker que gestionen milers de connexions cadascun.

Avui Nginx s'utilitza sobretot per a tres papers, sovint combinats:

  • Servidor web de contingut estàtic (HTML, imatges, CSS, JS) amb molt bon rendiment.
  • Proxy invers (reverse proxy) davant d'una o més aplicacions backend (Node.js, PHP-FPM, Java, Python...).
  • Balancejador de càrrega entre diverses instàncies d'un mateix backend.

Instal·lació a Ubuntu/Debian

# Actualitzar repositoris
sudo apt update

# Instal·lar Nginx
sudo apt install nginx

# Verificar estat
sudo systemctl status nginx

# Habilitar a l'arrencada
sudo systemctl enable nginx

Estructura de Directoris

/etc/nginx/
├── nginx.conf            # Configuració principal
├── conf.d/                # Configuracions addicionals globals
├── sites-available/       # Llocs disponibles
├── sites-enabled/         # Llocs habilitats (enllaços simbòlics)
├── snippets/               # Fragments de configuració reutilitzables
└── modules-enabled/       # Mòduls dinàmics carregats
# Comprovar sintaxi abans de recarregar
sudo nginx -t

# Recarregar configuració (sense tallar connexions actives)
sudo systemctl reload nginx

Bloc server: lloc web bàsic

# /etc/nginx/sites-available/exemple.cat
server {
    listen 80;
    listen [::]:80;
    server_name www.exemple.cat exemple.cat;

    root /var/www/exemple;
    index index.html;

    location / {
        try_files $uri $uri/ =404;
    }

    error_log /var/log/nginx/exemple-error.log;
    access_log /var/log/nginx/exemple-access.log;
}
# Habilitar el lloc
sudo ln -s /etc/nginx/sites-available/exemple.cat /etc/nginx/sites-enabled/

# Desactivar el lloc per defecte
sudo rm /etc/nginx/sites-enabled/default

sudo nginx -t && sudo systemctl reload nginx

AC0375/03/09 — Miniactivitat

RA3 · CA3b, CA3d

Instal·la Nginx en una màquina virtual o contenidor Linux i configura un server que serveixi un lloc estàtic senzill amb server_name propi. Comprova amb curl -H "Host: ..." que respon correctament i revisa access_log per veure com queda registrada la petició.

Nginx com a Proxy Invers

Un proxy invers rep les peticions dels clients en nom d'un o més servidors backend, les hi reenvia, i torna la resposta al client com si l'hagués generat ell mateix. El client mai parla directament amb el backend.

sequenceDiagram
    participant C as Client
    participant N as Nginx (proxy invers)
    participant B as Backend (app:3000)

    C->>N: GET https://app.exemple.cat/
    N->>B: GET http://127.0.0.1:3000/
    B-->>N: Resposta HTTP
    N-->>C: Resposta HTTP (com si fos ell)

Avantatges d'usar un proxy invers davant de l'aplicació:

  • Terminació SSL/TLS: el certificat es gestiona en un sol lloc; el backend pot parlar HTTP en clar dins la xarxa interna.
  • Balanceig de càrrega entre diverses instàncies del backend.
  • Cache i compressió de respostes sense tocar el codi de l'aplicació.
  • Aïllament: el backend no cal que estigui exposat directament a Internet.
  • Un sol punt per aplicar límits de peticions, capçaleres de seguretat i registres.

Configuració bàsica

# /etc/nginx/sites-available/app.exemple.cat
server {
    listen 80;
    server_name app.exemple.cat;

    location / {
        proxy_pass http://127.0.0.1:3000;

        # Capçaleres perquè el backend conegui l'origen real
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

Suport per a WebSockets

Aplicacions que utilitzen WebSocket (xats, notificacions en temps real) necessiten que Nginx reenviï també les capçaleres Upgrade/Connection:

location /ws/ {
    proxy_pass http://127.0.0.1:3000;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_set_header Host $host;
}

Balanceig de càrrega amb upstream

upstream backend {
    least_conn;                       # mètode de balanceig
    server 192.168.1.10:8080 weight=3;
    server 192.168.1.11:8080;
    server 192.168.1.12:8080 backup;  # només s'utilitza si les altres cauen

    keepalive 32;
}

server {
    listen 80;
    server_name app.exemple.cat;

    location / {
        proxy_pass http://backend;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

Mètodes de balanceig disponibles: round-robin (per defecte), least_conn (envia al servidor amb menys connexions actives), ip_hash (la mateixa IP client sempre va al mateix backend, útil per a sessions sense compartir estat).

AC0375/03/10 — Miniactivitat

RA3 · CA3b, CA3d, CA3h

Desplega dues instàncies d'una mateixa aplicació senzilla (per exemple dos contenidors amb la mateixa imatge en ports diferents) i configura un bloc upstream a Nginx perquè hi balancegi peticions. Comprova amb diverses peticions seguides (curl en un bucle) que la resposta prové alternativament de cada backend.

Nginx amb SSL/TLS (Let's Encrypt)

# Instal·lar Certbot
sudo apt install certbot python3-certbot-nginx

# Obtenir certificat i configurar Nginx automàticament
sudo certbot --nginx -d app.exemple.cat

# Renovació automàtica (Certbot ja instal·la un temporitzador systemd)
sudo systemctl status certbot.timer
sudo certbot renew --dry-run
server {
    listen 443 ssl http2;
    server_name app.exemple.cat;

    ssl_certificate /etc/letsencrypt/live/app.exemple.cat/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/app.exemple.cat/privkey.pem;

    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
    ssl_prefer_server_ciphers off;

    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

# Redirigir tot el trànsit HTTP a HTTPS
server {
    listen 80;
    server_name app.exemple.cat;
    return 301 https://$host$request_uri;
}

Ciberseguretat: protegir el proxy invers d'escanejos i atacs

Un servidor exposat a Internet rep constantment escanejos automatitzats (bots que busquen versions vulnerables, rutes d'administració oblidades, certificats mal configurats...). L'objectiu del hardening és reduir la superfície d'atac i dificultar el reconeixement (fingerprinting) sense complicar el manteniment legítim.

Ocultar la versió i la identitat del servidor

Per defecte Nginx envia la seva versió a la capçalera Server, informació que un atacant fa servir per buscar vulnerabilitats conegudes d'aquella versió concreta.

# /etc/nginx/nginx.conf (dins del bloc http {})
http {
    server_tokens off;   # elimina la versió de la capçalera Server
    ...
}
# Abans:  Server: nginx/1.18.0 (Ubuntu)
# Després: Server: nginx

Per eliminar completament la capçalera Server (no només la versió) cal el mòdul de tercers headers-more-nginx-module o la imatge nginx compilada amb aquest mòdul:

more_clear_headers Server;

Bloquejar peticions sense Host vàlid (escanejos per IP)

Molts bots ataquen directament l'adreça IP del servidor abans de conèixer cap nom de domini, provant capçaleres Host genèriques o buides. Un server "de captura" (catch-all) que talla la connexió sense resposta evita respondre a aquestes peticions:

# Bloc catch-all: primer server que troba Nginx si cap altre coincideix
server {
    listen 80 default_server;
    listen 443 ssl default_server;
    server_name _;

    # Certificat "fals" només perquè el handshake TLS es completi
    ssl_certificate /etc/nginx/ssl/snakeoil.pem;
    ssl_certificate_key /etc/nginx/ssl/snakeoil.key;

    return 444;   # codi especial de Nginx: tanca la connexió sense resposta
}

Limitar el ritme de peticions (rate limiting)

Protegeix contra força bruta d'inici de sessió i contra escanejos massius de rutes:

http {
    # Zona de memòria compartida "login": 10 MB, màxim 5 peticions/segon per IP
    limit_req_zone $binary_remote_addr zone=login:10m rate=5r/s;
    limit_req_zone $binary_remote_addr zone=general:10m rate=20r/s;

    # Limitar connexions simultànies per IP
    limit_conn_zone $binary_remote_addr zone=addr:10m;
}

server {
    location /login {
        limit_req zone=login burst=10 nodelay;
        proxy_pass http://127.0.0.1:3000;
    }

    location / {
        limit_req zone=general burst=40 nodelay;
        limit_conn addr 20;
        proxy_pass http://127.0.0.1:3000;
    }
}

Bloquejar rutes típiques d'escaneig

Els bots proven sistemàticament rutes conegudes (panells d'administració, fitxers de configuració, backups oblidats...). Tot i que la millor defensa és que aquestes rutes no existeixin, es poden tallar explícitament:

# Fitxers i directoris que mai s'haurien de servir
location ~ /\. {              # .env, .git, .htaccess...
    deny all;
    return 404;
}

location ~* \.(bak|old|sql|log|swp)$ {
    deny all;
    return 404;
}

location ~* ^/(wp-admin|wp-login\.php|phpmyadmin|\.git|\.env) {
    deny all;
    return 404;
}

Restringir per IP les rutes sensibles

location /admin/ {
    allow 192.168.1.0/24;
    allow 10.0.0.5;
    deny all;

    proxy_pass http://127.0.0.1:3000;
}

Capçaleres de seguretat al proxy invers

Com que totes les peticions passen per Nginx, és el lloc més eficient per afegir capçaleres de seguretat un sol cop, encara que hi hagi diversos backends:

add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Content-Security-Policy "default-src 'self'" always;

Timeouts i límits de mida per evitar exhauriment de recursos

http {
    client_body_timeout 10s;
    client_header_timeout 10s;
    keepalive_timeout 15s;
    send_timeout 10s;

    client_max_body_size 10m;      # evita pujades enormes (DoS)
    large_client_header_buffers 4 8k;
}

Fail2ban: bloqueig automàtic d'IPs abusives

Fail2ban llegeix els logs de Nginx i, quan detecta patrons d'abús (molts 401/403/404 en poc temps, intents de força bruta...), bloqueja la IP a nivell de tallafocs (iptables/nftables) durant un temps determinat.

sudo apt install fail2ban
# /etc/fail2ban/jail.local
[nginx-limit-req]
enabled = true
filter = nginx-limit-req
logpath = /var/log/nginx/*error.log
maxretry = 10
findtime = 60
bantime = 3600

[nginx-botsearch]
enabled = true
filter = nginx-botsearch
logpath = /var/log/nginx/*access.log
maxretry = 5
findtime = 60
bantime = 86400
sudo systemctl restart fail2ban
sudo fail2ban-client status nginx-botsearch

WAF (Web Application Firewall) amb ModSecurity

Per a una protecció més completa contra atacs a nivell d'aplicació (SQL injection, XSS, escaneig de vulnerabilitats), es pot afegir el mòdul ModSecurity amb el conjunt de regles OWASP Core Rule Set (CRS), que ja inclou detecció de bots i escanejos coneguts.

load_module modules/ngx_http_modsecurity_module.so;

http {
    modsecurity on;
    modsecurity_rules_file /etc/nginx/modsec/main.conf;
}

AC0375/03/11 — Miniactivitat

RA3 · CA3e, CA3g, CA3h

Sobre el proxy invers muntat a l'activitat anterior, aplica com a mínim: server_tokens off, un bloc default_server que respongui 444 a peticions sense Host conegut, i una zona limit_req sobre la ruta d'inici de sessió. Simula un escaneig senzill (ab, curl en bucle o nmap --script http-*) i comprova als logs que les peticions es limiten o es tallen.


Checklist ràpid d'endurellament (hardening)

Mesura Efecte
server_tokens off Amaga la versió de Nginx
default_server amb return 444 Ignora escanejos directes per IP sense Host vàlid
limit_req_zone / limit_conn_zone Limita força bruta i escanejos massius
Bloqueig de rutes (.git, .env, wp-admin...) Talla escanejos de rutes conegudes
allow/deny per IP en rutes sensibles Restringeix l'accés administratiu
Capçaleres de seguretat (HSTS, CSP, X-Frame-Options...) Mitiga XSS, clickjacking, MITM
client_max_body_size, timeouts Evita exhauriment de recursos (DoS bàsic)
Fail2ban sobre els logs Bloqueig automàtic d'IPs abusives al tallafocs
ModSecurity + OWASP CRS WAF: detecció d'atacs i bots coneguts a nivell d'aplicació

Referències

Documentació oficial de Nginx, Certbot, Fail2ban i ModSecurity: consulta la secció Serveis Web a la pàgina d'Annexos · Recursos.