V prvních dvou článcích jste se dozvěděli, z jakých komponent můžete CDN postavit a jak nastavit servery a reverzní proxy (CDN cache).
Ve třetím a zároveň posledním článku z této série, bychom rádi doplnili tipy a doporučení, jak si vlastní CDN zabezpečit, ochránit před útoky, jak ji monitorovat či jak ji rozvíjet dál.
V úplném závěru najdete různé zajímavosti či zkušenosti, které nás implementace vlastní CDN naučila i pár off-topic informací. Je to sice dlouhé, ale věřím, že přínosné. Přeji příjemné čtení :)
Bezpečnost
- Protože bude vaše reverzní proxy na Origin servery přeposílat i požadavky útočníků, nasaďte nějaký z dostupných WAF (Web Application Firewall), abyste evidentně útoční požadavky rovnou zamítly a neposílaly je zbytečně na originy. V řadě situací tím můžete zabránit i tzv. útoku otrávení cache (cache poisoning). V případě, že se rozhodnete pro Nginx, doporučujeme ModSecurity, nebo Nemesida WAF. Už i jejich základní sady pravidel podle OWASP TOP 10 udělají dobrou službu. U Nemesidy je nevýhoda, že k provozu potřebuje i RabbitMQ, ale zase výhoda, že má proces na pozadí, který průběžně pravidla aktualizuje podle udržované databáze známých zranitelností.
- Chcete-li z CDN posílat i typy souborů, které podléhají CORS (např. fonty), tak je nutné, aby vaše CDN vracela správně hlavičku Access-Control-Allow-Origin pro CORS požadavky s hlavičkou Origin v požadavku. My to máme konfigurovatelné per-origin a ve výchozím stavu povolujeme načítání pouze z domény originu. Hodnotu
*
nedoporučujeme. Správně máte mít ve webserverové či aplikační konfiguraci sadu trusted origins a hlavičku vrátit pouze pro důvěryhodný origin. Je dobré si taky uvědomit, co může případné kešování CORS hlaviček způsobit a proto zvážit i použití hlavičky Vary: Origin. - U CDN, stejně jako u běžných aplikačních serverů, doporučujeme nastavit security hlavičky. Pokud máte CDN hlavně pro statický obsah, tak zejména hlavičku X-Content-Type-Options: nosniff, případně i X-Frame-Options či X-XSS-Protection, které mají smysl hlavně u HTML, ale případně i SVG či XML. Nezapomeňte ani na HSTS a hlavičku Strict-Transport-Security, aby prohlížeč už i interně vynucoval HTTPS a neumožnil downgrade na HTTP.
- Aby vaše CDN nebyla zranitelná otrávením cache, doporučujeme nastavit různé buffery a limitní hodnoty výrazně striktněji, než to mají obvykle zdrojové origin servery. Zároveň, pokud si to můžete dovolit, je lepší ignorovat příchozí HTTP hlavičky a na originy přeposílat pouze pár relevantních (např. Accept, Accept-Encoding, Origin, Referer, User-Agent). Ke zvážení je taky nekešovat HTTP kód 400 Bad Request a určitě do cache neukládat např. 413 Request Entity Too Large.
- Když nasadíte TLS v1.3 s 0-RTT (early data), musíte zohlednit riziko Replay útoku. Vzhledem k tomu, že je naše CDN optimalizovaná a striktní pro statický obsah a blokuje POST/PUT/PATCH/DELETE požadavky, riziko reálného výsledného zneužití je téměř nulové. Modifikace dat v aplikaci by navíc neměl nikdy realizovat GET požadavek, ale alespoň POST s CSRF tokenem, který by měl mít navíc jednorázovou platnost (nonce).
- Proti DNS spoofingu se můžete bránit tím, že Nginx upstreamy k originům máte nastavené na IP adresy, nikoliv na hostnamy. My pro většinu klientů hostujeme projekty na vlastních clusterových řešeních, které umožňují k webům přistupovat skrz primární, ale i sekundární datacentrum a skrz více různých IP adres. Takže i CDN upstream k jednomu originu load-balancuje načítání přes 2–3 IP adresy u různých ISP. Pokud už musíte použít hostnamy, doporučujeme využít alespoň Dnsmasq jako lokální DNS cache.
- Před DDoS útokem vás to neochrání, ale DoS útoku z jedné IP adresy se můžete ubránit nastavením rate-limitingu (maximální počet požadavků za vteřinu či minutu z jedné IP) a connection-limitingu (maximální počet otevřených souběžných spojení z jedné IP). Doporučujeme si nastudovat a pochopit parametry burst a delay či nodelay, které zásadně ovlivňují chování v případě, že nějaká IP adresa začíná překračovat limity. My obvykle u aplikačních serverů používáme více úrovní rate-limitingu. Také v případě POST/PUT/PATCH/DELETE požadavků zásadně omezujeme počet požadavků za 1 minutu – tím efektivně zabraňujeme brute-force útokům.
- Kromě HSTS hlavičky si vynucujte i okamžité přesměrování z HTTP na HTTPS.
- Pokud požadavek přichází na URL, kde je místo domény IP adresa nebo jiná nepodporovaná doména, použijte
return 444;
– Nginx takové spojení okamžitě ukončí. - Uvědomte si riziko a implementujte alespoň základní ochranu proti zacyklení – například odmítejte zpracovat URL, která v cestě obsahuje i některou z domén, na které CDN “poslouchá”.
- Pokud nechcete, aby někdo mohl vkládat obsah z konkrétního originu na cizí stránky (a čerpat tak vaše data), můžete použít direktivu
valid_referers
, která podle vašich pravidel nastaví proměnnou$invalid_referer
. - Správnost konfigurace HTTPS otestujte na SSLLabs.com – měli byste bez problémů dosáhnout na známku A+. Bezpečnostní hlavičky zase můžete zkontrolovat na SecurityHeaders.com.
V případě, že před servery nemáte router, který by na váš server přeposílal pouze vybrané porty, nezapomeňte nastavit iptables/nftables firewall. Ve výchozím stavu by mělo být všechno zakázané a explicitně povolené pouze TCP porty 80 a 443. Dále si ze svých IP adres můžete povolit IPsec, SSH, atp. Nám se u bezpečnosti dlouhodobě osvědčilo všechny služby u kterých to jde, bindovat pouze na loopback rozhraní a formou DNATu ve firewallu směrovat zvenku pouze vybrané porty. Nějaký vysoký per-ip rate-limiting můžete nastavit právě i u DNATu již na síťové úrovni, hezky to popisuje např. článek Per-IP rate limiting with iptables. Doporučujeme úplně zakázat i ICMP. Pravděpodobně ale nejspíš povolíte alespoň echo-request kvůli různým online nástrojům pro měření latencí v různých částech světa, jako např CDN Latency Benchmark.
Ochrana proti DDoS útoku
Jako nejdražší a nejúčinnější DDoS ochrana pro vaší CDN, by bylo využívání anycastových IP adres (které nemá ani většina komerčních CDN poskytovatelů) a využití robustní DDoS ochrany od komerčních poskytovatelů, kteří mají velmi výkonná zařízení na páteřní síti, která “chrání”, a v případě detekce útoku aktivují mitigaci a “čištění” provozu vašich IP rozsahů (scrubbing). Některá tato řešení zvládají čištění i největších DDoS útoků o síle až stovek Gbps. Tato řešení ale stojí i desítky tisíc Kč měsíčně a rozhodně si je nedovolíte u všech PoPů ve světě.
Jenom pro zajímavost uvedu, že od 25. do 27. února 2018 byl jeden z námi hostovaných českých klientů cílem 230Gbps DDoS útoku, postaveného na memcrashed (umožňuje zesílení UDP útoku až o desítky tisíc násobku, nikoliv desítky/stovky jako u DNS či NTP amplification útoků). První velký memcrashed útok přišel na Cloudflare a jako na první v ČR, hned o 1 den nato, jsme se s tím museli poprat my. Pokud si robustní DDoS ochranu neplatíte, počítejte s tím, že v případě masivního útoku vám jenom ISP zavolá, že kvůli ochraně celého datacentra a všech jejích klientů, musí do konce útoku vaše IP subnety zcela zablokovat již na páteřní síti (blackhole).
Pokud to tedy s CDN a její vysokou dostupností myslíte vážně, je potřeba mít alespoň u hlavních PoPů DDoS ochranu domluvenou. Když bude nejhůř, alespoň pár z nich by mělo i ten největší útok ustát. Pokud využijete GeoDNS s auto-failoverem, jak jsem to popsal v prvním článku a dodržíte pravidlo vracení vždy alespoň 2 IP adres nezávislých poskytovatelů v každé světové lokalitě, uživatelé CDN by si nutně některé DDoS útoky ani nemuseli všimnout.
Z čeho jsme při návrhu DDoS ochrany vycházeli my:
- Máme velmi bohaté statistiky ze všech našich PoPů, případně u některých i z routerů. Máme tedy detailní přehled a trendy legitimním provozu – počty otevřených spojení, pakety, unikátní IP adresy i GEO informace o nich. U některých PoPů sbíráme a sledujeme i NetFlow data. Mít detailní informace o legitimním provozu a jeho špičkách je klíčové – jedině na základě nich je možné se správně rozhodovat a navrhnout optimální limity pro aktivaci mitigace.
- Ze všech DDoS útoků, které jsme v minulosti absolvovali víme, že více než 99% zapojených zdrojových IP adres bylo mimo ČR – to znamená mimo země naši majoritní návštěvnosti.
- Anycastové IP adresy si pro naše PoPy určitě dovolit nemůžeme. Na trhu se ale najde pár poskytovatelů, kteří fyzické či virtuální servery s anycastovými IP adresami nabízí.
- Anycastové IP adresy i robustní DDoS ochrany mají naši DNS poskytovatelé (Constellix, Cloudflare a ClouDNS). Tí mají natolik robustnou infrastrukturu, že by DDoS útoky na jejich NS servery měli ustát.
- Robustní DDoS ochranu schopnou zvládnout i stovky Gbps, máme u některých PoPů v ČR. Ostatní PoPy si musí vystačit s případnou robustní DDoS ochranou celé sítě konkrétního ISP (většina z nich ji má alespoň za příplatek).
- Z povahy CDN (různé resolvované IP adresy v různých částech světa) může zdánlivě plynout vyšší odolnost proti DDoS útokům. Je to pravda, ale jenom částečně. Zjistit všechny IP adresy, na které se resolvuje vaše CDN doména/hostname v různých koutech světa, je práce na pár minut. Útočník tedy potřebuje směrovat útok na více IP adres (které navíc nejsou anycastové), takže zaútočit na celou CDN síť, je pouze několikanásobně těžší (resp. je potřeba větší síly), ale ne nemožné. Pokud ale útočí pouze na doménu, tak útoční zdroje např. z Asie opravdu postihnou pouze PoPy v Asii, takže v našem případě je dopad na legitimní primární návštěvníky, téměř nulový.
- Až na pár výjimek s 10 Gbps, máme všude max. 1 či 2×1 Gbps linku (bonding). To je docela tenká trubka, nicméně, většinu světových DDoS útoků tvoří statisticky menší útoky o síle kolem 2–5 Gbps, takže když máme firewally na routerech či v linuxu nastavené optimálně, můžeme to celkem slušně ustát.
- Máme k dispozici GeoDNS s minutovými healthchecky a automatickým failoverem, takže v případě úspěšného útoku (nedostupnost nějakých IP adres/portů) můžeme do CDN sítě zapojit záložní PoPy, o kterých doteď útočník nevěděl (DNS překlad je doposud nikdy neukázal), nebo jde o známé PoPy, ale s robustní DDoS ochranou.
- Víme, že legitimní provoz u některých PoPů z 90% tvoří provoz z IP adres konkrétní země/kontinentu. To můžeme zohlednit pro nastavení geo-based limitingu.
Par tipů, jak DDoS ochranu uchopit na úrovni koncových serverů:
- Když používáte firewall na linuxu, či máte linux-based routery, veškerý nežádoucí provoz dropujte již přímo v RAW tabulce (UDP, ICMP či TCP porty mimo 80/443). U UDP povolte jenom responses z IP whitelistu vámi používaných DNS serverů. Tím v rámci možností nejefektivněji ochráníte koncová zařízení (servery či routery) před UDP amplification útoky i ICMP floodem. Pokud to budete dělat až ve standardním filtru, který je až za connection-trackingem, je už pozdě. Procesor se musel s každým spojením či paketem potrápit (prerouting, connection tracking, mangle, nat, filter) a každé otevřené spojení alokuje i paměť.
- TCP SYN floodu na port 80/443 se můžete bránit použitím rate limitingu (v iptables limit či dst-limit), kde říkáte, kolik nových spojení se SYN příznakem za čas (typicky vteřinu, či minutu) akceptujete (globálně, nebo s ohledem na src/dest IP adresu či port). Podobně jako u Nginxu, i zde je klíčové správně pochopit význam nastavení burst a rozumět algoritmu leaky bucket. Určitě nezapomeňte aktivovat SYN cookies.
- Samotnou L7 (HTTP/HTTPS provoz) můžete chránit rate i connection limitingem na firewallu a sekundárně i na Nginxu (nicméně, ten nikdy nebude tak efektivní jako firewall).
- U PoPů, kde víte, že drtivou většinu legitimního provozu tvoří lokální provoz, si stáhněte IP subnety dané země/zemí (např. z ip2location.com). Např. na PoPech v ČR můžete mít benevolentnější rate-limiting pro ČR, ale pro ostatní zahraničí můžete být výrazně přísnější. Když pak velikost útoku řádově nepřevyšuje hrdlo vaší trubky (konektivity), velmi pravděpodobně majorita návštěvníků z ČR výpadek ani nezaznamená a zahraniční útočné IP adresy odfiltrujete. S dobrými routery toto zajistíte hravě, včetně dynamické tvorby IP blacklistu (který pak rovnou filtrujete v RAW tabulce). Pokud použijete pouze firewall v Linuxu, můžete pro správu těchto IP seznamů použít ipset, o kterém napsal Petr Krčmář hezký článek ipset: odlehčete přetíženým iptables. Ať už použijete jakýkoliv firewall, nastudujte význam definice tzv. “chainů”, abyste minimalizovali počet firewall pravidel, kterými spojení/pakety musí projít k jejich finálnímu schválení, či zamítnutí. K zamítnutí používejte DROP, nikoliv REJECT. Pokud to váš firewall umožňuje a máte hodně paměti, můžete u některých TCP situací použít i TARPIT a útočníka tím zpomalit.
- Extra tip (naše nestandardní, ale funkční řešení pro středně velké DDoS útoky na L7): DDoS útok na L7 je situace, kdy na vaše servery útočník vychrlí vysoké tisíce HTTP či HTTPS požadavků za vteřinu z tisíců zcela unikátních IP adres různě po světě. Obvykle jsou tyto L7 útoky „pouze“ o síle stovek Mbps či jednotek Gbps, takže to můžete zvládnout ustát. Pro představu – pokud má útočník vygenerovat na vstupu 1 Gbps provozu pomocí 500B (bajtových) HTTP/HTTPS požadavků, potřebuje generovat konzistentně 250 000 požadavků za vteřinu. Navrhované řešení je optimální implementovat na routeru, případně na SW firewallu vašeho serveru (iptables/nftables a ipset). Řešení spočívá v tom, že si definujete několik úrovní connection-limit pravidel s různě vysokými limity pro různě velké IP subnety (např.
/3
,/8
,/16
,/24
) a když počet otevřených spojení z daného IP subnetu překročí limity, tak IP adresu (případně u extrémního překročení rovnou celý IP subnet) přidáte do dočasného blacklistu (technicky v případě linuxu ipset s nastaveným timeoutem), který zajišťuje DROP veškerého zdrojového provozu již přímo na vstupu, v RAW tabulce. Obvykle totiž i v případě DDoS útoku přichází z každé zdrojové IP adresy více požadavků současně. Ve finále se pak u DDoS útoku stane, že např. díky pravidlu na IP subnet/3
zablokujete na chvíli osminu celosvětových IP adres, nebo klidně všech 8/3
IP subnetů, když jde o opravdu rozsáhlý DDoS útok. Když to ale nastavíte v kombinaci s předchozím doporučením a provozu z ČR/SK IP adres (resp. tuzemských IP adres daného PoPu) povolíte vyšší limity a tato pravidla se zpracují dřív, majoritní návštěvníci se na reverzní proxy (cache) dostanou a CDN jim bude fungovat, i když bude obsah posílat pomaleji kvůli saturované konektivitě. Z jiných koutů světa (některým high-traffic IP subnetům) ale budete provoz na daném PoPu dočasně zahazovat a útočník bude mít pocit, že vaše servery shodil (= úspěšný DDoS útok), protože bude mít porty 80 a 443 nedostupné. Pochopitelně pak ale potřebujete mít na IP whitelistu vaše origin servery, IP adresy skrz které se na servery připojují monitoringy, IPsecy, DNS, atd. Toto řešení je trošku zvláštní a vymysleli jsme si ho sami, ale funguje velmi dobře i při reálném DDoS útoku. Je ale potřeba nastavit jednotlivé úrovně limitů s rozvahou a na základě max. počtu otevřených TCP spojení ve špičkách, co vám ukáže monitoring. Pak třeba můžete pro celý obrovský/3
IP subnet nastavit limit počtu otevřených TCP spojení na 5–10 násobek předchozího maxima ve špičkách. Neomezíte tím legitimní provoz a DDoS útok možná ustojíte. - Pokud máte možnosti, svoje DoS i DDoS ochrany testujte, analyzujte chování, sledujte související zátěž. Existují i online nástroje, které za poplatek umí generovat dost objemný provoz z velkého počtu unikátních IP adres a nejde přitom o něco nemorálního z dark netu.
- Navrhněte si nějaké aktivní mechanizmy, které vás okamžitě upozorní na probíhající útok – např. tím, že budete sledovat velikost blacklist fronty.
- V každém případě je při návrhu těchto ochran dobré vědět, jak/proč a jak dlouho jsou otevřená TCP spojení, čím se to řídí a jak se to chová z TCP pohledu v případě, dnes již většinového HTTP2 provozu. V případě reakce na aktivní útok pak můžete automaticky dočasně snížit různé timeouty v TCP stacku či na webserveru, začít posílat Connection: close hlavičku, atp.
- Za zmínku zde stojí i možnost použití Fail2Ban, nicméně, způsob jeho detekce i fungování vám v případě rozsáhlého DDoS útoku, kde se v logu začnou objevovat desítky/stovky tisíc řádků za vteřinu, moc nepomůže. Jenom logování pak dělá klidně 10 MB/s zápisu na disk a kdybyste neměli zapnutý buffering access logů, tak i extrémní IOPS.
Monitoring
Bez ohledu na to z kolika serverů máte CDN složenou, potřebujete je aktivně i pasivně monitorovat.
My pro aktivní monitoring používáme Nagios a pro rychlé základní grafy životních funkcí Munin. V Muninu můžeme rychle prohlížet i trendové grafy za několik let. To s níže uvedenou Kibanou (součást ELK stacku) není jednoduše možné, kvůli velikosti indexů, případně je nutné používat transformace/rollup do archivních indexů.
Pro více živé statistiky používáme 2 další nástroje:
- Pomocí collectd sbíráme metriky všech životní funkcí (CPU, RAM, IOPS, storage, network, Nginx) – vše posíláme do Kibany.
- Pomocí filebeat zase do jiné Kibany posíláme všechny access a error logy. Z Ansible generujeme Nginx vhosty tak, aby měl každý origin svůj vlastní access i error log.
V jednotlivých Kibanách pak máme dashboardy shrnující provoz CDN jako celku i rozpady po jednotlivých serverech (PoPech). Díky vyhodnocování naprosto všech metrik z access logů máme detailní informace například o:
- cache hit-ratio
- statistiky IP adres a vykreslení provozu do GEO mapy světa
- statistiky HTTP kódů
- statistiky datových přenosů (sbíráme velikosti požadavků i odpovědí)
- statistiky časů odezvy
- rozpad podle serverů (PoPů) či jednotlivých GEO lokalit
- rozpad podle origin domén
- rozpad podle typů obsahu (JS/CSS/obrázky/fonty/audio/video)
- rozpad podle konkrétních URL.
Doporučujeme monitorovat i DNS resolving vašich CDN domén, abyste měli neustále pod kontrolou, zda GeoDNS poskytovatelé vždy vrací očekávané sady IP adres. My jsme tento monitoring implementovali následovně:
- Nagios každou minutu hlídá níže uvedené kontroly a okamžitě nám formou e-mailu a SMS notifikuje neočekávané stavy, nebo pomalé reakce NS (name serverů).
- Napsali jsme si Nagios plugin, který v argumentech dostává NS server (např. ns11.constellix.com, nebo třeba 8.8.8.8), testovanou doménu (např. moje.cdn.cz), sadu očekávaných IP adres, min. počet IP adres, kolik ze sady se musí vyskytovat v resolvingu a samozřejmě max. čas a timeout odezvy NS serveru. V případě, že DNS resolve neobsahuje očekávanou sadu IP adres v min. počtu, nebo se doména resolvuje na jinou IP adresu/adresy, případně resolving trvá dlouho, odesílají se notifikace.
- Takto každou minutu testujeme naprosto všechny autoritativní NS servery našich GeoDNS poskytovatelů (6× NS Constellix a 4× NS ClouDNS).
- Každou minutu taky kontrolujeme správnou funkčnost DNS resolvingu u oblíbených rekurzivních cache NS serverů Google (8.8.8.8) a Cloudflare (1.1.1.1), abychom měli jistotu, že není nějaký zádrhel i na cestě mezi autoritativními a rekurzivními DNS servery.
- Tento monitoring provádíme jak z našich serverů v ČR tak v jiných zemích skrz NRPE agenty, přičemž např. u pluginu spuštěného na německém serveru se hlídá, že se DNS přeložilo na IP adresy našich německých POPů.
- Výsledek všech těchto kontrol zaznamenáváme do daily-rorated logů a v případě potřeby nám slouží jako podklad při zpětné analýze problémů či anomálií.
Další tipy:
- Pro rychlé statistiky síťového provozu na jednotlivých serverech doporučujeme vnstat. Příkazy jako
vnstat -l
pro live info, či statistickévnstat -h
,vnstat -d
nebovnstat -m
se také často hodí. Pro detailní analýzu aktuálního provozu zase iptraf-ng. Pro přehled o TCP spojeních použijtess -s
či např.ss -at
. - Pro rychlý live přehled, co server zrovna dělá ve všech důležitých oblastech, máme nejradši dstat, konkrétně s přepínači
dstat -ta
. A samozřejmě htop. - Pokud zatím s Kibanou nemáte zkušenosti, podívejte se i na Grafanu s InfluxDB. Kibanu používáme léta a máme v ní stovky vlastních vizualizací a dashboardů (proto to byla naše první volba), ale poslední naše zkušenost je taková, že Grafana s InfluxDB je celkově rychlejší a to obzvlášť pro long-term dashboardy. Koncept práce s daty, tvorba vizualizací a dashboardů je ale o dost jiná.
Tipy a zajímavosti z implementace
- Při realizaci některých funkčností určitě narazíte na jednu nepříjemnost v Nginxu – direktiva add_header se nechová dědičně. Pokud nastavíte add_header v úrovni server a pak i uvnitř location, tak se ve finále odešlou pouze hlavičky nastavené v location, ale ty nastavené o úroveň výš v server se ignorují. Z toho důvodu je lepší používat modul more-headers a jeho funkce, které se chovají dědičně (more_set_headers, more_clear_headers, more_set_input_headers, more_clear_input_headers).
- Pokud používáte Debian, doporučuji používat repo od Ondřeje Surého (packages.sury.org) (tímto Ondrovi moc děkujeme za údržbu tohoto repo), který kromě posledních verzi Nginxu obsahuje i kompatibilní verze more-headers modulu.
- Etalonem spolehlivosti je pro nás dlouhodobě HAProxy, kterou dlouhé roky využíváme pro load balancing a různé auto-failover scénáře. Od verze 2 již má navíc kompletně předělaný a vylepšený handling HTTP requestů i robustnější podporu HTTP/2. Zkoušeli jsme nejdřív místo Nginxu použít právě HAProxy, ale bohužel má pouze velmi omezené možnosti kešování, co je u CDN kritické. HAProxy bychom ale určitě využili jako load-balancer v případě, že bychom za jedním PoPem měli více serverů.
- V případě, že chcete maximální výkon, doporučujeme vyzkoušet místo Nginxu i H2O – https://h2o.examp1e.net/. S Nginxem máme dlouholeté zkušenosti, takže i složitější scénáře již máme plně zautomatizované v Ansible. Přepis do H2O by byl rozhodně zajímavý, ale taky časově dost náročný. Navíc poměr 500 otevřených a 650 zavřených ticketů na GitHubu je zvěstí toho, že to ještě není úplně production ready.
- Pokud máte na fungování cache ještě větší nároky, doporučujeme místo Nginxu spíše Varnish. Nginx je super a podle našich měření o něco výkonnější, ale s Varnishem můžete získat např. podporu cache tagů skrz HTTP hlavičky, kdy pak umíte selektivně invalidovat cache všech URL s požadovaným tagem. To se může velmi hodit např. v kombinaci s kešováním POST požadavků (např. na GraphQL API), kde byste pak po detekci změny v nějaké entitě na BE, mohli invalidovat veškerou relevantní cache na API vrstvě. Takto to kešujeme i invalidujeme u nás na aplikační vrstvě a naším budoucím cílem je to takto kešovat i na úrovni dat v CDN. U budoucích webových projektů se chceme držet filozofie JAMStack, kde taková CDN s chytrými možnostmi selektivní invalidace cache, hraje klíčovou roli. U naší CDN proto budeme v budoucnu Varnish určitě používat, pravděpodobně v kombinaci s Nginxem.
- Když chcete podporovat HTTP/3 (QUIC), doporučujeme quiche od Cloudflare, nebo lsquic, který je součástí OpenLiteSpeed webserveru. My zatím s HTTP/3 jenom experimentujeme. Vyžaduje totiž BoringSSL místo OpenSSL a navíc Nginx starší verze 1.16. Od léta 2020 se již taky pracuje na oficiální podpoře přímo v Nginxu (k 11/2020 je ale stále experimentální a ve vývoji).
- Pokud používáte virtualizované servery a máte tu HW možnost, použijte SR-IOV a ovladač ixgbevf s nastavením InterruptThrottleRate=1. Fronta příchozích požadavků se bude zpracovávat efektivněji a sníží se i zátěž CPU.
- Pokud máte hodně jader CPU a optimalizujete i pro statisíce požadavků za vteřinu, zaměřte se i na RPS (Receive Packet Steering), protože obvykle příchozí frontu zpracovává pouze jedno jádro CPU.
- Pro ty, kteří se zajímají i o různé síťové detaily ohledně požadavků v prohlížeči, HTTP/2 streamů či DNS resolvingu, doporučujeme nastudovat nástroje kolem Google Chromu. Konkrétně chrome://net-internals/, chrome://net-export/ a související nástroj https://netlog-viewer.appspot.com/. Nám to pomáhá pochopit vliv chování HTTPs požadavků na samotné renderování stránky, také odhalit hluchá místa kde se na něco čeká, atp.
- Když chcete opravdu rozumět HTTP/2 a optimalizovat rychlost načítání vašich stránek “na krev”, nainstalujte si nghttp2 a pochopte jak u HTTP/2 probíhá komunikace přímo u vašeho webu. Vyzkoušet můžete např. příkaz
nghttp -nv https://www.root.cz/
. - Výkon serveru i jeho konektivitu je možné jednoduše otestovat např. pomocí one-line nench benchmarku.
- V případě hostování velkých souborů je nutné si uvědomit, že i když klient položí byte-range požadavek, tak vaše CDN z originu nejdřív musí načíst celý soubor (ten případně zakešuje) a až poté z něj vrátí požadovaný chunk. I proto může být lepší, když máte možnost tato videa a další objemné soubory na CDN pushnout ještě předtím než na ně začnou návštěvníci přistupovat. Pomoct si ale můžete i použitím slice modulu Nginxu, který umí z Originu stahovat a kešovat pouze konfigurovatelné “chunky”.
- Pozor na oblíbené a občas trošku zrádní rekurzivní cache DNS servery Google (8.8.8.8, 8.8.4.4) či Cloudflare (1.1.1.1). Není totiž ojedinělé, že i českým návštěvníkům občas přeloží požadavky na nějaké zahraniční IP adresy. Stává se to ale jenom jednou za pár dní či hodin a trvá to obvykle jenom pár minut.
- CDN PoPy jako takové jsou sice funkčně plně autonomní a nezávislé, ale stejně budete potřebovat spojení na nějaké centrální místo kvůli jejich správě, monitoringu nebo např.: distribuci cache-purge požadavků. Zřiďte si proto IPsec tunely pomocí strongSwan nebo WireGuard, kterých konfigurace se dá velmi hezky zautomatizovat.
- Při implementaci mazání cache si můžete pomoci např. skriptem nginx-cache-purge, který znázorňuje, jak se dají efektivně najít cache soubory podle URL či masky. Doporučuji i články Purging cached items from Nginx with Lua a Improving NGINX LUA cache purges. My jsme se rozhodli vycházet z tohoto Lua skriptu, akorát jsme si tam přidali pár našich úprav. V případě, že si to naskriptujete v Lua, doporučujeme udělat si k tomu vhosta naslouchajícího na nestandardním portu, který budete mít dostupný pouze skrz IPsec tunel. Pokud implementujete i statickou brotli/gzip kompresi, tak nezapomeňte mazat i vaše .br/.gz soubory či .webp/.avif.
- U naší CDN máme podporu AVIF zatím vypnutou, protože implementace ve Firefoxu zatím nepodporuje průhlednost (alpha kanály). Google již zároveň pracuje na WebP2, který by měl datově ještě o 30% úspornější, takže ± dorovnává AVIF.
- V případě, že nasazujete vlastní či komerční CDN před celou svojí doménu, dávejte pozor na jednu potenciální zranitelnost, kterou můžete rychle přehlédnout. Klientskou IP adresu z hlavičky X-Forwarded-For respektujte pouze v případě, že k vám požadavek síťově přichází pouze z konkrétních známých veřejných IP adres CDN serverů. V Nginxu se důvěryhodné zdroje definují přes realip modul a direktivu set_real_ip_from. V žádném případě nepoužijte něco jako
set_real_ip_from 0.0.0.0/0
. Pokud máte nějakou část domény či aplikační funkčnost omezenou pouze na IP whitelist, tak by útočník mohl HTTP hlavičkou podhotit jinou IP adresu. - V případě, že se rozhodnete využít komerční CDN, doporučujeme tuzemskou CDN77, protože jejich podpora pro vás umí na požádání zajistit, že veškeré požadavky na vaší zdrojovou Origin doménu budou přicházet pouze z pár fixních IP adres v ČR (jejich CDN proxy servery), a ty můžete nastavit jako důvěryhodné. Většinou vám totiž CDN poskytovatele neřeknou celý seznam možných IP adres svých PoPů a nemůžete se spoléhat na to, že v požadavcích posílají hlavičku např. Via: cdn-provider. To prostě není bezpečné a lze to jednoduše podhodit, přitom podpora CDN poskytovatelů vám často takové nebezpečné řešení doporučí.
Závěr a zkušenosti z praxe
Věříme, že vám série těchto 3 článků pomohla a ukázala, jakým způsobem si můžete CDN postavit i sami. Popsali jsme vám z čeho všeho se skládá, a jak můžete konkrétní komponenty vyskládat a nastavit i svépomoci. Dobře ale zvažte, zda se vám to skutečně pro vaše potřeby vyplatí. Myslete také na to, že vám poběží několik serverů po světě, které je potřeba platit a taky o ně pečovat i je patchovat.
Naše CDN, která je postavená tak, jak popisuje tento článek, funguje skvěle. Máme ji pod drobnohledem a pečlivě aktivně i pasivně monitorujeme provoz na všech serverech. Její výkon a rychlost v prohlížečích je ještě vyšší než u komerčních CDN (díky statické kompresi a taky tomu, že většina obsahu je v RAM, protože nemáme tisíce klientů). Postupně ji nasazujeme do projektů, které pro naše klienty vyvíjíme. Velmi dobře a vlastní silou díky tomu pokrýváme zejména Evropu. Abychom stejně dobře pokryli i odlehlé kouty světa, tak si pomáháme v těchto sekundárních lokalitách jinou komerční CDN. Víme, že našim klientům dodáváme kvalitní službu za dobrou cenu. A technicky máme v portfoliu další zajímavý projekt, který „žije“ a přináší reálnou hodnotu.
Od produkčního nasazení v 09/2019 se navíc neobjevil jediný problém – všechny komponenty fungují bezchybně. Snažili jsme se nic nepodcenit – produkčnímu nasazení předcházely zátěžové i penetrační testy. Hledali jsme post-mortem různých úspěšných útoků na komerční CDN a snažili se podle nich odladit i naše konfigurace. Funkcionalitu jsme nejdříve testovali na různých neprodukčních prostředích našich klientských projektů. Vyhledávače využití naší CDN detekují správně – přesto, že jsou obrázky načítané z CDN domény, tak jsou indexované správně pod doménou originu.
Do budoucna ještě zvážíme, zda nerozdělíme CDN na dvě části – jednu optimalizovanou zejména pro mnoho malých, často načítaných souborů (např. JS/CSS/ikony/fonty) a druhou pro větší soubory (např. audio/video či velké obrázky). Takové řešení může mít 2 výhody – prohlížeč si při renderování stránky dovolí ještě více paralelizace (assety bude podle typu načítat z více různých CDN domén/IP adres) a taky nám to umožní fine-tuning ještě přesněji na míru provozu, efektivnější využití cache, či výběr HW.
V hlavách máme pořád i variantu, že bychom naší CDN používali jako reverzní proxy před celou klientskou doménou, tedy na všechny požadavky, včetně POST/PUT/DELETE. Tím bychom získali benefit další úrovně DDoS ochrany před Origin servery, ale připravili bychom se tím zase o jiné benefity – zejména mířenou optimalizaci pro statický obsah a taky využití vyšší paralelizace v prohlížečích, díky načítání obsahu z více různých domén, resp. IP adres. Velmi lákavá by zároveň byla u každého PoPu možnost využít více serverů pro různé typy obsahu s load balancingem mezi tyto servery např. podle přípony v URL. Takových možných vylepšení ale máme v šuplíku spoustu a možná nám dají v příštích letech smysl i návratnost.
Implementace a ladění CDN nám taky ukázala, že všechny technologie mají chyby. Čím více super-vychytávek někdo přináší, tím více chyb u toho vzniká. A to bez ohledu na to, zda to vyvíjí a testuje jeden, nebo tisíce lidí. Proto mám jednu osobní off-topic prosbu: nebuďme prosím laxní a když narazíme na problém, reportujme ho autorům a nečekejme, že to někdo udělá za nás. Rychleji tím vyřešíme komunitní, ale i náš problém a zároveň se u toho hodně věčí naučíme, protože u toho musíme jít často do hloubky. Taky nás to učí sdělovat věci druhé straně ve srozumitelné podobě. Kdysi jsem to ale sám nedělal a říkal jsem si, že to „určitě rychle zjistí a opraví i sami“. Omyl a chybná úvaha, co jsem si přiznal až časem… Za poslední roky jsem už ale sám nahlásil či se podílel na opravě různých chyb a problémů. Pro příklad v prohlížeči Firefox (chyby v chování a hlavičkách kolem AVIF), Google Chrome (problémy s CORS vs. cache vs. prefetching), webserveru Nginx (HTTP/2), PHP (OPcache), ELK Stack (UI/UX chyby v Kibaně a Grok v Logstashi) v Mikrotik RouterOS či GlusterFS. 13 ticketů mám i u MariaDB a MaxScale proxy. U těchto technologií jsem sice jako vývojář pomoct nemohl, ale dodal jsem alespoň dostatek srozumitelných informací, aby vývojáři dokázali problémy rychle pochopit, nasimulovat a opravit. Pokud si náhodou dáváte nějaká předsevzetí, ochota zakládat tickety či posílat PR, může být jedno z nich.
Pokud by vás zajímaly nějaké další související detaily k CDN, zeptejte se v diskuzi, nebo se nám do SiteOne ozvěte a stavte se na osobní pokec. Rádi se o know-how v této oblasti podělíme.