Zapomněl jste citovat zbytek. Ten hlavní proces nic nedělá, jen spawnuje workery pod jiným UID. Třeba v Apache je defaultní konfigurák, kde je non-root uživatel (apache, www-data nebo tak něco). Majoritní distribuce dělají žádné nebo jen minimální zásahy do upstream konfiguráků (je to best-practise), takže pokud není tvůrce distribuce úplný mimoň, tak se prostě stát nemůže, že by web běžel pod rootem (ale donutit ho nejspíš můžete).
Kromě toho jsou oprávnění dále osekána (SELinux, AppArmor).
Mohl byste se prosím rozepsat, jak si přesně představujete ten útok pomocí OpenSSL? klíč je generovaný lokálně, jediná 3rd party komponenta je certifikát (navíc uložený v CT logu) - nějak tu nesedí poměr ztráty (pro CA, na kterou se provalí že dala exploit do certifikátu) vs možný benefit, takže celý scénář se jeví býti značně akademickým.
Vůbec nemusí jít o cílený útok na konkrétní instanci. Stačí chyba buffer overflow v OpenSSL, která někde něco přepíše. Vzhledem k tomu, že nginx typicky servíruje jen statické soubory a aplikační kód je v jiném procesu, který může běžet pod jiným uživatelem, radši zpřístupním privátní klíč na disku té části nginxu, která servíruje statické soubory (a která k němu má stejně přístup – pokud se zase nedozvím, že ten kód pod rootem i navazuje TLS a workeru předá jen klíč spojení), než spouštět cokoli (a zejména kód OpenSSL) pod rootem.
Fedora/Red Hat pro mod_ssl používá modifikovaný OpenSSL s certifikací FIPS, což jiné SSL/TLS balíky nemají a nikdo se pro certifikaci a následnou údržbu nehlásí a pro komerční/průmyslové nasazení to je nutné.
Pokud byste chtěl zaútočit na httpd přes klíče, musel byste být root, protože nikdo jiný k nim nemůže. Když už jste root, můžete cokoliv, takže tento vektor útoku je maximálně teoretický a bez benefitu.
Pokud budete útočit přes HTTPS stream, tak se dostanete jenom do workeru a ne na roota. Pokud máte dojem/víte, že v OpenSSL jsou takové excesy v buffer overflow, asi se o tom bude brzo psát, ale kritických zranitelností je v OpenSSL málo (2002, 2002, 2013, 2016). Důležité zranitelnosti jsou také, ale ty ze sítě nejdou využít.
Pokud vás trápí, že Apache HTTPD neodděluje SSL/TLS od workeru, můžete (v RHEL/CentOS) použít i ten nginx, v distribucích je.
Mně na tom vadí to, že se začne tím, že část procesu běžící pod rootem jenom spawnuje workery, na tom přece není co zkazit. No, dobře. Jenže pak si někdo vzpomene, že se pod rootem také načítají privátní klíče. Přes OpenSSL, které nemá dobrou pověst co se týče kvality kódu a kvality vývoje. Jasně, v poslední době se to výrazně zlepšilo, když vybublalo na povrch, jaký průšvih to je. Certifikace FIPS řeší kryptografii – ale řeší i bezpečnost kódu? Pokud je privátní klíč někde na tokenu, tak v OpenSSL může být třeba chyba umožňující spuštění kódu, a z hlediska kryptografie je to celé v pořádku, privátní klíč je v bezpečí – ale ta knihovna rozhodně bezpečná není. No a pak se vrátím k tomu hlavnímu procesu, který tedy má načtený ten privátní klíč, jenže TLS pak dělá worker. Takže buď hlavní proces předá privátní klíč workeru (běžícímu pod neprivilegovaným uživatelem – jenže to se ten privátní klíč dostane do spárů toho neprivilegovaného uživatele). A nebo nebo, pokud má privátní klíč zůstat opravdu jen v moci roota, musí ten hlavní proces řešit i navázání spojení a TLS až do okamžiku, kdy už není privátní klíč potřeba. Takže se z jednoduchého masteru, který „jenom spawnuje workery“ (a také např. parsuje konfiguraci, to tu nezaznělo) najednou stává docela komplikovaný proces, který má docela dost vstupů. A tohle všechno běží pod rootem.
Pro mne je tohle stopka, nechci, aby všechno tohle běželo pod rootem. Ochrana privátního klíče serveru mi za tohle nestojí, dokážu ho ochránit jinak. Navíc k privátnímu klíči nemá přístup jenom webserver, ale také ten, kdo řeší obnovu certifikátu. CSR musí být privátním klíčem podepsána. Budu také CSR podepisovat pod rootem? Navíc spousta běžných aplikací vytváří nový CSR při každé obnově certifikátu a dělají vše pod jedním uživetelem, včetně komunikace ACME protokolem. To všechno také budu spouštět pod rootem? Zkrátka ten přístup „k privátnímu klíči může jen root“má také spoustu negativ, a oprávnění roota jsou strašně silná a zbytečná pro práci s privátním klíčem – vždyť jde jen o přečtení souboru a nějaké kryptografické operace. K tomu nejsou potřeba žádná administrátorská oprávnění.
Takže budu mnohem radši řešit to, aby web server nikdy žádná práva roota neměl, a vedle toho budu řešit to, aby uživatel, který má přístup k privátnímu klíči, nespouštěl žádný „uživatelský“ kód ve smyslu CGI, PHP nebo jiných server-side webových aplikací. Připadá mi takový přístup mnohem bezpečnější.
Zapomínáte na SELinux apod - root už není absolutní root a impakt master procesu httpd je v podstatě nula. Je zde též riziko komplexnosti kódu - pokud něco vylepšíte tím, že to bude složitější, není jisté zda si celkově nepohoršíte (a nenaděláte problémy chybnou konfigurací). FIPS (a jiné certifikace) komplikují přijímání záplat a refaktoring kódu - jenže o to jde (omezit zbrklé změny, jako kdysi třeba v Debianu).
Poslední odstavec - asi jste myslel oddělit krypto od uživatelského kódu - to se řeší přes php-fpm a fastcgi.
Obecně OpenSSL rozhodně není takový bezpečnostní průšvih, jak naznačujete - viz můj odkaz výše. Jsou tam obskurní části/moduly, ale ty se právě do solidních distribucí nedostanou, případně jde o chyby s nízkými dopady na bezpečnost. To že nějaký projekt má více CVE, ještě neznamená, že je ten kód tak špatný (případně mluvíte o 10+ let staré historii).
Pořád jde o to, že je zbytečné dávat webserveru práva roota. Jediné, k čemu je skutečně potřebuje, je naslouchání na nízkém portu – a to je řešitelné přes capabilities. To, že privátní klíč bude číst jenom root, je pak čistě rozhodnutí správce.
Problém komplexnosti kódu je právě to zbavování se oprávnění. Pokud se server nastartuje rovnou pod uživatelem s omezenými oprávněními, nepotřebuju vůbec kód řešící zbavení se oprávnění, ani nemusím řešit, kdy se má provést kód s vysokými oprávněními a co odložit až do části s nízkými oprávněními.
Oddělení uživatelského kódu znamená nejen oddělení kryptografie, ale oddělení všech zbytných věcí – loadbalancing, alespoň část síťové komunikace a parsování protokolu. Jasně, s FastCGI zase komunikuji síťovým protokolem, ale ten je trochu kompaktnější než HTTP a na druhé straně je můj server, ne internet.
Nemluvím o 10+ let staré historii, jenom o cca 5+ let staré historii. A nejde mi tolik ani o CVE, ale spíš o jakousi kulturu toho kódu a projektu. To jsou věci, které jsou v kódu vidět i po pěti letech, i když se nové věci budou psát dobře a i když se zjevné chyby opraví.
Navíc pověst OpenSSL je jen část problému. Druhou částí je obecně TLS – potřebujete tam dělat různé triky kvůli bezpečnosti, další triky kvůli rychlosti. Pro provoz nejsou potřeba žádná speciální oprávnění. Za téhle konstelace to provozovat pod rootem je pro mne zbytečné riziko – podle mne je to učebnicový příklad kódu, který by pod rootem běžet neměl. Argument, že je potřeba načíst privátní klíč z disku a neumožnit stejnému procesu ten klíč načíst znova podle mne v žádném případě neobstojí – k tomu nejsou vůbec potřeba oprávnění roota.
Ještě jednou - se SELinuxem sice vidíte root, ale ten root u httpd démona nemůže skoro nic. Můžete použít ještě capabilities (od RHEL7), ale systémově to použito není, protože SELinux je mnohem silnější nástroj. Jistě, můžete nosit pásek i kšandy, ale...
OpenSSL se používá kvůli FIPS. Do alternativ se nikomu nechce, protože to stojí peníze, a protože se rychle přišlo na to, že jedna věc je RFC/norma a druhá věc realita, takže uchodit cokoliv jiného, aby to fungovalo se vším, je oříšek a dojdete nejspíš ke stejně nepřehlednému kódu.
Navíc pověst OpenSSL je jen část problému. Druhou částí je obecně TLS – potřebujete tam dělat různé triky kvůli bezpečnosti, další triky kvůli rychlosti. Pro provoz nejsou potřeba žádná speciální oprávnění. Za téhle konstelace to provozovat pod rootem je pro mne zbytečné riziko – podle mne je to učebnicový příklad kódu, který by pod rootem běžet neměl. Argument, že je potřeba načíst privátní klíč z disku a neumožnit stejnému procesu ten klíč načíst znova podle mne v žádném případě neobstojí – k tomu nejsou vůbec potřeba oprávnění roota.
Já si myslím, že se na to nedíváte úplně správně. OpenSSL i Nginx se snaží, aby tam nebyly bezpečnostní problémy a k tomu cíli se to bude přibližovat. Určité riziko zůstane, ale ne moc vysoké.
Proti tomu stavíte řešení, které naruší standardy (či zvyklosti) v jiných ohledech. Např. klíče mimo keystore, speciální oprávnění, oprávnění k logům, ... Tímto řešením se to bude od ideálu zase vzdalovat.
Pokud mám zvolit nestandardní řešení, měl bych k tomu mít opravdu dobré důvody. Rizika zrovna z nginx a openssl jsou okrajová proti rozvrtání dalších věcí.
Řešením je, jak psal pan Keršláger, využít SElinux. Není to 100% řešení, stejně jako Vaše řešení není 100%. První navrhované je však správně i systematicky, druhé už je custom. Já, pro případ strachu z dopadů rizik doporučuju předřadit reverzní proxy zajišťující SSL offloading. To je třetí řešení a taky není 100%.
OpenSSL i Nginx se snaží, aby tam nebyly bezpečnostní problémy
Ano, snaží. Přesto je snaha omezit běh aplikací pod rootem na minimum. Nejdřív se do webového serveru implementovalo to, že se připojí na port a pak se vzdá rootovských oprávnění. Pak se implementovaly capabilities, aby se aplikace vůbec nemusel spouštět pod rootem, když jenom potřebuje naslouchat na nízkém portu. Pak se přidal SElinux.
Všichni se smaží, to nikomu neupírám, ale pořád nejjednodušší a nejspolehlivější řešení je vůbec procesu roota nedávat.
Proti tomu stavíte řešení, které naruší standardy (či zvyklosti) v jiných ohledech.
Stejné narušení zvyklostí bylo to, že se Apache zbavil rootovských oprávnění po startu. Před tím standardně běžel pořád pod rootem.
klíče mimo keystore
Nevím, čemu říkáte keystore – nginx načítá klíče ze souboru na disku.
speciální oprávnění
K tomu přece oprávnění slouží. SElinux je daleko větší specialita, než normální oprávnění na souboru. Navíc běžný nginx běží pod speciálním uživatelem, servíruje statické soubory a pak přes síť komunikuje s jinými aplikacemi, které poskytují dynamické stránky. Tyhle aplikace často běží i na jiném zařízení. Takže jediné, co se po správci chce, je to, aby nebyl líný, a vytvořil třeba pro php-fpm jiného uživatele, než pod jakým běží nginx. Což je v každém případě dobrý nápad. Co je na tom speciálního? Navíc by to měla řešit rovnou distribuce.
Tímto řešením se to bude od ideálu zase vzdalovat.
Ne, řešení, kdy se server vůbec nespouští s právy roota, když je nepotřebuje, je přiblížení se k ideálu.
Řešením je, jak psal pan Keršláger, využít SElinux.
Nakonfigurovat správně SElinux je podstatně složitější, než nakonfigurovat v systemd capabilities a vytvořit pro nginx speciálního uživatele. Navíc je trochu na hlavu dát nginxu úplně zbytečně roota a pak ho všemi možnými způsoby omezovat, když mu toho roota vůbec nemusím dávat.
První navrhované je však správně i systematicky, druhé už je custom.
Systematicky správně je nespouštět server pod rootem, když nepotřebuje rootovská oprávnění. Webový server potřebuje rootovská oprávnění jenom pro naslouchání na nízkém portu, a k tomu stačí použít capability.
Já vidím následující rizika – proces, který jenom servíruje statické soubory a odpovědi, které dostane od jiných procesů, bude mít přístup k privátnímu klíči serveru a k webovým logům. Za mne jsou tahle rizika minimální. Pokud by útočník přes to dokázal server napadnout tak, aby mohl modifikovat odpovědi nebo soubory, bude prakticky jedno, že získal privátní klíč k certifikátu – může podvrhovat provoz přímo na serveru. Takže skutečné riziko je to, že by útočník mohl jenom přečíst soubor s privátním klíčem. A to je velmi málo pravděpodobné, nebyla by to žádná nevratná katastrofa, a kdybych se toho opravdu bál, můžu ten privátní klíč po startu server znepřístupnit.
Jaká další rizika vidíte vy?
@Filip Jirsák
O tomhle by se dala vést sáhodlouhá diskuse. Protože to už je o poměřování rizik a jejich dopadů. OpenSSL chyby obsahovat samozřejmě může, stejně jako hlavní proces nginxu. Otázkou, kterou bych si kladl je, jestli při ručním přenastavení práv k logům, přenastavení logrotate, přístupu ke klíčům etc. nemůžete udělat sérii dalších chyb (nemluvě o tom, že takové přenastavení může vyvolat kaskádu minoritních problémů, které zase budete řešit custom úpravami v jiných částech systému).
Takže tu proti sobě stojí:
a) výchozí nastavení, poměrně dobře zkoumané a provozované, laděné + riziko 0day zranitelností
b) ruční nastavení, se snížením rizika 0day zranitelností, ale se zvýšením rizika udělání jiné chyby, kterou ale můžete zjistit jen Vy sám (nedá se předpokládat, že stejné custom řešení zvolí moc lidí, každý už si to udělá po svém).
Takovou situaci bych spíš řešil předřazením reverzní proxy na jiném serveru, případně v jailu, který by se staral o SSL offloading. Na něm už můžete zvolit libovolnou z obou dobrých variant. Rizika nesnížíte, ale odstraníte 90 % dopadů.
Např Nextcloud nainstalovaný jako snap baliček. I když tam je to prý omezeno na jiné úrovni, podobně jako docker.
root 614 0.0 0.0 4636 1596 ? Ss 03:28 0:00 /bin/sh /snap/nextcloud/21521/bin/run-httpd -k start -DFOREGROUND root 1911 0.0 0.0 4636 1720 ? S 03:29 0:00 \_ /bin/sh /snap/nextcloud/21521/bin/httpd-wrapper -k start -DFOREGROUND root 1946 0.0 0.0 51092 4732 ? S 03:29 0:01 \_ httpd -d /snap/nextcloud/21521 -k start -DFOREGROUND root 1947 0.0 0.0 1266220 4260 ? Sl 03:29 0:00 \_ httpd -d /snap/nextcloud/21521 -k start -DFOREGROUND root 1948 0.0 0.0 1266220 4260 ? Sl 03:29 0:00 \_ httpd -d /snap/nextcloud/21521 -k start -DFOREGROUND root 1949 0.0 0.0 1266220 4260 ? Sl 03:29 0:00 \_ httpd -d /snap/nextcloud/21521 -k start -DFOREGROUND
Ano, snap je v sandboxu. Flatpak taky. Ovšem záleží na tvůrci konkrétních balíčků, někteří totiž nepoužívají žádná omezení. V každém případě je vždy na uvážení uživatele, co si pustí do systému (a za jakou cenu). Snapy/Flatpaky přímo od distribuce by mohly být nastaveny slušně.
Distribuce jsou historicky vycepované (a ty lepší mají bugtracking), ale na netu (bohužel) najdete ledacos a často nemáte ani kam chybu nahlásit.