TL;DR něco k nastavení Nginx
Ahoj,
když se tady poslední dobou probírá dost Nginx, tak jsem si říkal, že zkusím napsat nějaké poznámky, které jsem jinde uceleně nenašel, k věcem, co mi přišly špatné. Snad to někomu pomůže. Přeci jen je prima, pokud ty služby máme nastaveny správně. Omlouvám se všem, pro které to byly sovy v Aténách.
Předně musím říct, že já mám Nginx nesmírně rád; myslím si, že Igor Sysojev odvedl výbornou práci a trošku mu závidím. Již jsem nějaký ten Nginx nastavil (včetně vlastních modulů), takže vím, co mne také na něm hněvá.
1) tip: dejte si pozor na to, že Nginx v upstreamu nepoužívá při HTTPS SNI. `proxy_ssl_server_name on`, defaultně je to off;
2) Nginx neumí ještě ani v roce 2020 volbu ekvivalentní `strict-sni` v HAProxy, bug je na to veden 8 let a z vývojářů je cítit lehká arogance a nepochopení stavu věci [https://trac.nginx.org/nginx/ticket/195]. Někteří to řeší tak, že dávají HAProxy před Nginx, což je IMO overkill. Pokud mi na HTTPS přistupuje klient a neuvede SNI pole v TLS, Nginx použije defaultně první TLS certifikát, který má v konfiguraci uvedený. Mi se to nelíbí a nedává mi to smysl. Je radost se podívat, jak mají weby vedené u Cloudflare ve Qualys SSL reportu pouze jeden certifikát. (Tady je vidět rozdíl: nějaký náhodný Cloudflare hostovaný web: https://www.ssllabs.com/ssltest/analyze.html?d=oops.ml&s=2606%3a4700%3a3... a tady se omlouvám Janovi, že jsem vzal jeho web, ale nic jiného mne nenapadlo: https://www.ssllabs.com/ssltest/analyze.html?d=www.zazen-nudu.cz&s=37.20... (certificate 2 pro No SNI)) Z vlastních zkušeností mohu říci, že bez SNI a s chybným SNI, mám výhradně závadný provoz z Ruska a Číny a takový, kde si jakýsi člen vpsFree, co měl "mé" IPv4 a IPv6 adresy předemnou, zapomenul zrušit záznamy v DNS u svých klientů. Možnost je snadno opatchovat Nginx, nebo trochu zneužít nastavení TLS. Pokud nepoužijeme TLSv1.3, nýbrž pouze TLSv1.2(!), je možné Nginx jako první TLS http/server nastavit tak, že bude podporovat pouze aNULL cipher suite (tj. bez autentizace, takže nebude nabízet certifikát pro Server Hello) a takovéto spojení budeme zavírat bez odpovědi: ``` $ less /etc/nginx/nginx.conf
http { ssl_protocols TLSv1.2; # we do not want TLSv1.3 because of "strict sni" workaround ssl_ciphers "ECDHE-ECDSA-AES128-GCM-SHA256"; ssl_ecdh_curve "prime256v1";
# aka "strict sni" workaround # https://trac.nginx.org/nginx/ticket/195 server { listen *:443 ssl http2; listen [::]:443 ssl http2; server_name --; ssl_ciphers "ADH-AES128-SHA256"; # aNULL cipher, no authentication, no cert :) ssl_stapling off; ssl_certificate certs/dummy.pem; # it could be sneak-oil etc. ssl_certificate_key certs/dummy.key;
return 444; # non-standard code that closes the connection }
server { listen *:443 ssl http2; listen [::]:443 ssl http2; server_name example.com; root /srv/http/example.com; index index.txt index.html; ssl_certificate certs/example.com.pem; ssl_certificate_key certs/example.com.key;
// … }
server { listen *:443 ssl http2; listen [::]:443 ssl http2; server_name sample.cz; root /srv/http/sample.cz; index index.txt index.html; ssl_certificate certs/sample.cz.pem; ssl_certificate_key certs/sample.cz.key;
// … } } ``` OK, funguje to pěkně, funguje to dokonce správně, ale s použitím TLSv1.3 to padá. Ovšem zdá se, že to vývojáři nakonec řešit budou. :)
(Dalo by se to asi i řešit přes iptables, https://github.com/Lochnair/xt_tls, ale do toho jsem se pouštět nechtěl.)
3) Nginx chybně pracuje s OCSP Must-Staple certifikáty: Pokud máte v certifikátu "OCSP Must-Staple" assertion (OID 1.3.6.1.5.5.7.1.24), což tedy doporučuji, podporuje je i Let's Encrypt, Nginx jako první odpověď po restartu OCSP nepošle(!), teprve si jej získá a bude jej již běžně posílat až s druhou a další odpovědí. Ovšem to není košér, protože první odpověď je bez OCSP, ovšem s cert. příznakem Must-Staple. Vyřešil jsem to tak, že jsem si na nginx.service v systemd (protože jej mám rád) nabindoval druhou service (spouštěnou/zastavenou automaticky spolu s nginx), která mi provede první dotazy přes s_client v openssl: ``` $ less /etc/systemd/system/nginx-ocspfix.service
[Unit] Description=nginx OCSP fix Wants=network-online.target After=network-online.target BindsTo=nginx.service After=nginx.service
[Service] Type=oneshot User=nobody Group=nobody ExecStart=/usr/local/bin/nginx-ocspfix.sh RemainAfterExit=true
[Install] WantedBy=nginx.service ```
a
``` $ less /usr/local/bin/nginx-ocspfix.sh
#!/bin/bash
for c in /etc/nginx/certs/*.pem; do domains=$(openssl x509 -noout -text -in $c | grep DNS: | perl -l -0777 -ne '@names=/\bDNS:([^\s,]+)/g; print join("\n", sort @names);' | sort -u) for d in $domains; do openssl s_client -connect 127.0.0.1:443 -servername $d -status < /dev/null > /dev/null 2> /dev/null done done ```
4) Pikoška – malé tiskací "P", které mne stálo hromadu času, ale spolu s Googlem jsem to dohromady dal (vize přílohu): Ve své konfiguraci preferuji své pořadí cipher suites (AES-GCM), ovšem s tím, že umožňuji využít CHACHA20-POLY1305, pokud klient preferuje ji, protože je energeticky méně náročná, typicky nemá-li klient v CPU akcelerované AES-GCM (AES-NI instrukce pro AES a PCLMULQDQ pro Galois Field operace v x86), jedná se většinou o nějaké Androidy. Nginx to nastavit umožňuje s novým openssl, ovšem je mu trošku pomoci s konfigurací. Opět využívám systemd – v proměnných prostředí mu předávám vlastní konfiguraci openssl.conf:
```$ less /etc/nginx/openssl.conf
openssl_conf = default_conf
[default_conf] ssl_conf = ssl_sect
[ssl_sect] system_default = system_default_sect
[system_default_sect] Options = ServerPreference,PrioritizeChaCha ```
a
``` $ less /etc/systemd/system/nginx.service.d/override.conf
[Unit] Wants=network-online.target After=network-online.target
[Service] Environment=OPENSSL_CONF=/etc/nginx/openssl.conf ```
A jde to! :)
5) QUIC (HTTP/3) netřeba ani vzpomínat, Cloudflare dokonce připravilo patche a nic…
OK, dost stesku nad Nginx, přeji všem pěkně nastavený Nginx, ať už dělá proxy nebo servíruje obsah! :)
Petr