ťařina
Obsah Soubory
TLS a reverse proxy

Nginx

Aktualizováno 3 min čtení 444 slov

Nginx

Nejrozšířenější reverse proxy a webserver. Upovídanější než Caddy, ale najdeš k němu návod na cokoliv a při vysoké zátěži je nejrychlejší.

Koncepty za tím jsou v reverse proxy a TLS.

Struktura konfigurace

/etc/nginx/
├── nginx.conf              # hlavní, globální nastavení
├── conf.d/*.conf           # sem se dávají vlastní konfigurace
├── sites-available/        # Debian a Ubuntu: dostupné weby
└── sites-enabled/          # symlinky na aktivní

Debianovský systém sites-available a sites-enabled je jen konvence. Aktivace se dělá symlinkem:

ln -s /etc/nginx/sites-available/mujweb /etc/nginx/sites-enabled/
nginx -t          # kontrola syntaxe, VŽDYCKY před reload
systemctl reload nginx

To nginx -t je návyk, který stojí za vypěstování. Reload s chybnou konfigurací nechá běžet starou, ale restart shodí celý server.

Reverse proxy, minimální funkční verze

server {
    listen 80;
    server_name nas.example.cz;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl;
    http2 on;
    server_name nas.example.cz;

    ssl_certificate     /etc/letsencrypt/live/nas.example.cz/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/nas.example.cz/privkey.pem;
    ssl_protocols TLSv1.2 TLSv1.3;

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

        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;
        proxy_set_header X-Forwarded-Host  $host;
    }
}

Těch pět řádků s proxy_set_header je povinná výbava. Bez nich aplikace nevidí skutečného klienta ani neví, že běží pod HTTPS. Proč, je vysvětlené v reverse proxy.

Nginx je nenastavuje sám a to je nejčastější důvod, proč „aplikace za nginxem zlobí".

Sdílený blok hlaviček

Ať to nepíšeš dvacetkrát, dej si to do souboru /etc/nginx/snippets/proxy.conf:

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;
proxy_set_header X-Forwarded-Host  $host;

proxy_http_version 1.1;
proxy_set_header Upgrade    $http_upgrade;
proxy_set_header Connection $connection_upgrade;

proxy_read_timeout    300s;
proxy_send_timeout    300s;
proxy_connect_timeout 30s;

a v konfiguraci pak jen:

location / {
    proxy_pass http://127.0.0.1:5000;
    include snippets/proxy.conf;
}

WebSockety

Ty tři řádky s Upgrade výše jsou pro WebSockety, ale samy nestačí. V nginx.conf v bloku http musíš přidat:

map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

Bez toho tě čeká Connection: upgrade i u běžných požadavků a některé aplikace se z toho zblázní.

Pozor taky na proxy_read_timeout — ve výchozím stavu je 60 sekund a nečinný WebSocket po nich tiše spadne. Souvislosti v HTTP, QUIC a WebSocketu.

Tohle je řešení problému „Home Assistant se načte, ale nic se neaktualizuje".

Lomítko v proxy_pass

Nejzáludnější detail v celém nginxu. Chování se liší podle toho, jestli je za portem lomítko:

location /app/ {
    proxy_pass http://127.0.0.1:3000;      # BEZ lomítka
    # /app/foo  →  /app/foo
}

location /app/ {
    proxy_pass http://127.0.0.1:3000/;     # S lomítkem
    # /app/foo  →  /foo
}

Bez lomítka se cesta předá celá. S lomítkem se prefix z location odřízne.

Když ti aplikace pod podcestou vrací 404 na všechny statické soubory, je to skoro vždycky tohle. Většina aplikací navíc pod podcestou nefunguje vůbec, ať děláš co děláš — pokud to nemají výslovně podporované, dej jim vlastní subdoménu.

Velké soubory

client_max_body_size 10G;        # výchozí je 1 MB!
proxy_request_buffering off;     # neukládat celý upload na disk proxy

Výchozí megabajt je důvod, proč ti na Nextcloud nejde nahrát nic většího než dokument. Chyba se hlásí jako 413 Request Entity Too Large.

Kompletní příklad se vším

map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

server {
    listen 80;
    listen [::]:80;
    server_name foto.example.cz;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl;
    listen [::]:443 ssl;
    http2 on;
    server_name foto.example.cz;

    ssl_certificate     /etc/letsencrypt/live/foto.example.cz/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/foto.example.cz/privkey.pem;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_session_cache shared:SSL:10m;

    add_header Strict-Transport-Security "max-age=31536000" always;
    add_header X-Content-Type-Options nosniff always;
    add_header X-Frame-Options SAMEORIGIN always;

    client_max_body_size 50G;
    access_log /var/log/nginx/foto.access.log;

    location / {
        proxy_pass http://127.0.0.1:2283;
        include snippets/proxy.conf;
    }
}

Poslouchání na [::]:443 je pro IPv6. Zapomíná se to a pak se na tebe přes IPv6 nikdo nedostane, i když DNS má AAAA.

Omezení rychlosti

# v bloku http
limit_req_zone $binary_remote_addr zone=obecne:10m rate=10r/s;

# v bloku location
limit_req zone=obecne burst=20 nodelay;

Ochrana proti robotům, kteří ti perou do přihlašovacího formuláře. Doplněk k fail2ban, ne náhrada.

Skutečné IP za další proxy

Když je před nginxem ještě Cloudflare nebo jiná proxy, vidí nginx její adresu. Náprava:

set_real_ip_from 103.21.244.0/22;    # rozsahy Cloudflare
real_ip_header CF-Connecting-IP;
real_ip_recursive on;

Bez toho ti fail2ban zablokuje Cloudflare a s ním všechny návštěvníky.

Diagnostika

nginx -t                             # syntaxe
nginx -T                             # kompletní výsledná konfigurace včetně include
systemctl reload nginx
tail -f /var/log/nginx/error.log

nginx -T je nedoceněný. Ukáže ti složeninu všech souborů, takže hned vidíš, co se doopravdy načetlo — včetně toho, že máš dvě konfigurace pro stejnou doménu a použije se ta první.

Chyba Příčina
502 Bad Gateway aplikace za proxy neběží nebo je na jiném portu
504 Gateway Timeout aplikace odpovídá moc dlouho, zvyš proxy_read_timeout
413 Request Entity Too Large client_max_body_size
404 na statické soubory pod podcestou lomítko v proxy_pass
smyčka přesměrování chybí X-Forwarded-Proto