Zabezpečování SSH

Článků a blogů s podobným tématem je po internetu mnoho. Proč tedy další takový? Jak jsem v poslední době zjistil, SSH nabízí i několik dalších zajímavých možností, které celkové zabezpečení zvednou, jen se o nich až tak moc nemluví, což si myslím, že je škoda. Chtěl bych tedy napsat nějaký celkový přehled (ač jistě také spoustu věcí opomenu), čím vším lze zvýšit zabezpečení SSH démona.

Co je SSH?

Program SSH asi není potřeba představovat - ve zkratce se jedná o velmi bezpečný nástroj pro správu serverů. Lze jím zpřístupnit vzdálený shell a dělat také spoustu jiných zajímavých věcí (přenášet soubory, tunelovat porty a další). Dnes již je standardní součástí skoro každých unixových a unix like systémů. Když už jsem se tak rozepsal, tak bych rád upozornil, že následující text vychází z implementace OpenSSH. Nicméně veškeré tyto vlastnosti by měly mít i původní implementace SSH a snad i jiné, komerční (byť konfigurace některých věcí se bude zapisovat nejspíše jinak, ale možnost tam je).

Síťová omezení

Na začátek věci, které souvisejí přímo se sítí. Nástroje jako firewall jsou dnes snad samozřejmé pro cokoliv, ale pro jistotu je uvedu.

Firewall, TCP wrappers

Každý stroj na síti by dnes měl mít firewall. Pokud máte tu možnost, určitě se hodí SSH z firewallu povolit jen z rozsahů, ze kterých to bude potřeba. Pokud ta možnost není, je vhodné použít nástroj typu fail2ban - konkrétně fail2ban funguje tak, že prochází logy na serveru a pokud zaregistruje z jedné IP adresy větší počet nezdařených pokusů o přihlášení, pak IP adresu na firewallu zablokuje.

SSH podporuje i použití TCP wrappers - tj. to, co se konfiguruje soubory /etc/hosts.allow a /etc/hosts.denny. Osobně je nepoužívám a ani moc nemám rád. V zásadě ta věc neumí nic víc, než firewall, takže jediný případ, kdy může být užitečná je, pokud máte díru ve firewallu. Naopak někteří lidé (jako já) na ní s oblibou zapomínají, takže se pak dlouhé hodiny vztekají, proč to připojení nefunguje...

TCP port

Trochu pomoci může také změnit standardní TCP port, na kterém SSH poslouchá (22) na nějaký jiný - ideálně něco vysokého (přes 1024). Nelze tomu říkat zabezpečení - pokud po vás někdo půjde, najde běžící SSH na jakémkoliv portu. Nicméně to zabrání tomu, aby si démon dával práci odpovídat různým pitomečkům, kteří to jen tak (a obvykle dost horlivě zkouší) - tedy krom toho, že se na tom ušetří procesorový čas s odpovídáním denny, tak to taky ušetří nějaké místo na disku, když v logu nebudete mít 3x do hodiny celý anglický slovník...

V souvislosti se změnou portu stojí také za zvážení, zda nenechat SSH naslouchat jen na tom rozhraní, na kterém ho potřebujete. Pokud už věci jako firewall padnou, tak ať alespoň neposlouchá tam, kde je to zbytečné.

Omezení uživatelů

V konfiguraci SSH lze určit, které uživatelé (nebo skupiny) se smí připojit anebo naopak nesmí. Dělá se to v sshd_config kouzelnými slovíčky AllowUsers, AllowGroups, DenyUsers, DenyGroups. DenyUsers (a Groups) odmítne přihlášení těm uživatelům (skupinám), kteří jsou za touto direktivou uvedeni (jednotlivé hodnoty se oddělují mezerami). Naopak direktivy allow to povolí jen těm, co tam jsou uvedeni. Řečeno stručně: do konfigurace dávat jen allow nebo deny - mít tam obojí postrádá smysl (neboť to, co není v allow se stejně zakáže a nemá cenu to vyjmenovávat). Vzhledem k tomu, že unix like systémy mají obvykle lokálních uživatelů vytvořeno mraky, přikláněl bych se k použití AllowUsers jen na ty uživatele, co se doopravdy přes SSH budou přihlašovat.

AllowUsers a DenyUsers mají ještě jednu zajímavou možnost - krom zápisu uživatelského jména lze ještě zapsat něco ve stylu ivatel@hostitel - za hostitele se dosadí DNS jméno nebo IP adresa. V tom případě má tento uživatel povolení se připojit, ale jenom z této adresy (zákaz funguje analogicky a ano - při rozvedení těchto možností může za určitých okolností dávat smysl použití AllowUsers a DenyUsers zároveň - například při povolení přístupu uživateli pepa odkudkoliv, krom sítě oskliva-zla-sit.cz). Pokud tedy víte, že někteří uživatelé se budou připojovat vždy jen z nějaké sítě, je určitě lepší jim ten přístup povolit jen z této sítě.

Autentizační metody

(Open)SSH umí autentizačních metod hned několik. Pokud vynecháme ty méně časté a ty dnes již nepoužitelné, skončíme u dvou - heslo a klíče (pak ještě certifikáty, ty také proberu). A u hesla si rovnou řekneme, že ne.

Heslo ne!

Heslo je v SSH dnes již téměř nejslabší autentizační metodou (horší už je snad jen ta na základě hostitelů, ale tu už snad používají jenom servery s uptimem přes 20 let). Když vezmeme v úvahu, že máte dobré heslo a že ho nikdo neuhodne, je tu stále několik pádných důvodů, proč ho nepoužívat:

  • Narozdíl od autentizace veřejným klíčem, se celé heslo přenáší přes síť (tedy přes síť jde to, co útočníkovi stačí k budoucímu přihlášení). SSH má celý provoz mezi klientem a serverem šifrovaný, ovšem jsou tu dvě ALE. První problém je ten, že implementace SSH obvykle umožňují komunikaci i bez šifrování. Může se tedy stát (ať už tím, že někdo úmyslně zmrší nastavení klienta/serveru nebo se prostě nedohodnou), že provoz mezi klientem a serverem půjde skutečně nešifrovaný a tedy celé heslo prolétne po síti tak, jak je (dá se tomu zabránit vykompilováním šifry "None" z sshd, ale i tak je tu druhé ALE). Druhá potíž je ta, že server SSH heslo vidí. Pokud tedy někdo napadne server a pozmění SSH démona, velice lehce heslo zjistí (jelikož server jej už opět vidí dešifrované). Pokud nemáte na každý server jiné heslo, je to docela nepříjemné.
  • Heslo se dá velice jednoduše odposlechnout na klientském počítači. S oblibou to dělají různé trojany a jiná havěť. Může to vypadat jak ze sci-fi, ale už jsem to jednou viděl, že se to stalo :-) Podobný problém trápí i autentizaci veřejným klíčem, ale tam se tomu dá bránit.
  • Heslo se dá snadno "vyžvanit" - prakticky stačí, aby jenom focus dostalo jiné okno. Osobně se mi jednou na postarším notebooku stalo něco podobného - spustil jsem irc klienta a hned po něm SSH na server. IRC klient obvykle nějakou chvíli nabíhal a SSH klient už vyzval k zadání hesla (díky bohu jen ke klíči). No a jak jsem psal, nevšiml jsem si, že IRC klient se v té chvilce objevil a ukrad si focus, po zadání jsem zmáčkl enter a heslo bylo v IRC kanále :-) Byla to přístupová fráze k soukromému klíči, takže to každému bylo k ničemu... ale kdybych používal autentizaci heslem, mohla to být ještě sranda.
  • A v neposlední řadě se připravíte o další skvělé fíčury, které jsou popsány na konci tohoto návodu (a jsou to zrovna ty, o kterých se tak moc nepíše)

Veřejné klíče

Co tedy použít lepšího, než heslo? SSH umožňuje autorizaci klíčem, postavenou na asymetrické kryptografii. Podpora algoritmů se v různých verzích a implementacích liší, nicméně dnes OpenSSH podporuje jak DSS, tak RSA a v zásadě už není důvod RSA nepoužít (patentová ochrana vypršela a ty zkriplené free implementace se už snad také nepoužívají). Princip je v tom, že máte dva klíče - soukromý a veřejný. Veřejný můžete klidně roztrubovat do světa, soukromý si musíte střežit jako oko v hlavě. A šifrování probíhá tak, že při šifrování dat na ně použijete veřejný klíč. A takto zašifrovaná data lze rozšifrovat jedině soukromým klíčem (u RSA to funguje i naopak, ale to už je jiná debata).

A jak to tedy funguje v SSH - na server uložíte svůj veřejný klíč (v OpenSSH se dává obvykle do ~/.ssh/authorized_keys daného uživatele). Při přihlašování nejprve ukážete serveru svůj veřejný klíč. Pokud se mu líbí (= našel ho v authorized_keys, authorized_keys má správná práva a další různé podmínky), provede test, zda k němu vlastníte i soukromý klíč. Vymyslí si nějaká data, zašifruje je veřejným klíčem a pošle vám je. Klient je rozšifruje a pošle zpátky. Pokud se shodují, usoudí, že soukromý klíč máte a pustí vás.

Výhody oproti heslu jsou zřejmé - i pokud SSH si bude povídat nešifrovaně a server bude kompromitovaný, útočník sice nějaké zneužití může vymyslet, ale rozhodně vám neukradne identitu tak, jako s heslem. Jen je potřeba dát si pozor na soukromý klíč. Pokud je uložen v klientském počítači jako soubor, může jej nějaký virus ukrást také (a případně si i odposlechnout heslo k němu). Jako řešení lze SSH klíč uložit například do tokenu nebo smart karty (o tom snad někdy příště).

Konfigurace pro používání veřejných klíčů není nikterak těžká. V první řadě je potřeba si pomocí programu ssh-keygen vygenerovat pár klíčů. Veřejný klíč (má koncovku .pub) pak na serveru uložíte do souboru ~/.ssh/authorized_keys (pokud je zde více klíčů, je každý na jednom řádku). V SSH démonovi je potřeba v konfiguraci autorizaci klíčem povolit direktivou PubkeyAuthentication yes a pak jen klientovi říct, aby klíč používal (parametr -i na příkazovém řádku, direktiva IdentityFile v konfiguračním souboru klienta anebo použít SSH agenta a klíč zavést do něj).

Certifikáty

Kromě klíčů se s trochou snahy dají použít také certifikáty. Běžně se dnes používají X509 certifikáty (například pro HTTPS, podepisování pošty a další) - X509 certifikát obsahuje veřejný klíč a nějaké další informace (typicky alespoň nějakou identifikaci vlastníka certifikátu a podpis nějaké třetí strany, která ověřila jeho totožnost).

Použití certifikátů oproti klíčům má jednu výhodu. Zatímco při autentizaci přímo veřejným klíčem je potřeba na každém serveru k požadovaným uživatelským účtům uložit přímo daný klíč. Pokud máte klíčů více (a chcete je používat všechny), je potřeba také více klíčů uložit. A problém nastává ve chvíli, kdy potřebujete nějaký svůj klíč vyměnit (ztratil jste privátní klíč, byl komprimotován, atd.). V tomto případě je potřeba opět provést distribuci na všechny spravované stroje (tedy odstranit starý klíč, přidat nový). Naproti tomu při autorizaci certifikátem se na dané stroje ukládá pouze předmět tohoto certifikátu (předmět bývá obvykle složen z identifikačních údajů, takže třeba jména, adresy atd.). Zároveň je po certifikátu požadováno, aby byl vydán nějakou konkrétní CA (které věříme natolik, že může vydávat certifikáty, které budeme používat pro autorizaci). Při samotné autorizaci tedy klient ukáže certifikát; pokud se certifikát serveru líbí (odpovídá CA, certifikát je platný a nebyl odvolán, předmět certifikátu souhlasí), klient ještě opětovně dokáže, že vlastní i privátní klíč k veřejnému klíči uvedeném v certifikátu a následně je vpuštěn. Výhoda je ta, že předmět certifikátu pro jednu osobu zůstává stejný. Tedy i po tom, co je váš privátní klíč kompromitován nebo ztracen, necháte zneplatnit současný certifikát a vystavíte si nový. Ovšem nový certifikát má stejný předmět jako předchozí a fakt, že daný veřejný klíč je jiný v tomto případě nevadí.

Tato metoda má ovšem i jeden háček. OpenSSH X509 certifikáty neumí. OpenSSH (narozdíl od původního SSH) má pouze nějaký vlastní systém certifikátů (lze nastudovat v man stránkách). Pro OpenSSH tak akorát existují patche, které podporu X509 certifikátů pro OpenSSH přidávají. Patche fungují a například v distribuci Gentoo Linuxu jsou zahrnuty přímo v balíčkách (USE flag x509). Nicméně nelze opomenout, že tento postup sebou nese i bezpečnostní riziko. Patch způsobuje zásah do kódu OpenSSH - může v něm tak být chyba, o které vývojáři OpenSSH jednak neví a jednak tento kód bude zkoumán podstatně menší skupinou lidí, než kód samotného OpenSSH.

Omezení jednotlivých klientů

A nyní ta nejzajímavější část, o které se skoro nikde nepíše. Pokud jste pro autorizaci zvolili systém veřejných klíčů nebo certifikátů, můžete nyní u každého uživatelského účtu pro každý klíč nebo certifikát specifikovat další sadu omezení a nastavení. Volby se vkládají přímo do souboru authorized_keys (defaultně umístěném obvykle v ~/.ssh) na stejnou řádku s klíčem/certifikátem před specifikaci samotného klíče nebo certifikátu.

Omezení přístupu ze sítě

Klíčovým slovem from lze omezit, odkud se lze tímto klíčem připojit. Hodnotou je seznam jednotlivých hostitelů oddělených čárkami. Lze uvádět jak DNS jména, tak IP adresy či celé rozsahy adres. V hodnotě lze také použít wildcard; například zápis *.spamik.cz povolí připojení ze všech subdomén spamik.cz

Vynucení příkazů

Klíčové slovo command způsobí, že po přihlášení tímto klíčem se vždy spustí uvedený příkaz, nezávisle na tom, o co klient žádal. Tímto způsobem lze také omezit příkazy, které může uživatel spouštět - například vynutit spuštění vlastního skriptu, který provede jen ty příkazy, které se mu líbí (volaný příkaz se nachází v je v proměnné SSH_ORIGINAL_COMMAND).

Je nutné si dát ovšem pozor na některé věci, jinak je ochrana poměrně zbytečná. Například chceme-li zakázat příkaz rm, nestačí kontrolovat SSH_ORIGINAL_COMMAND na to, zda začíná řetězcem "rm " - on takový příkaz "echo ahoj; rm neco" bez problémů projde. Také je potřeba dát pozor na některé aplikace, které umožňují únik do shellu (například less). Pokud povolíme uživateli jen pár příkazů a ne shell a mezi nimi bude třeba zmíněný less, uživatel si přes něj spustí svůj shell a zase si může dělat co chce.

Nastavení proměnných prostředí

Položkou environment lze pro zadané klíče nastavit proměnné prostředí (pokud už taková proměnná existuje, bude nahrazena hodnotou uvedenou zde). Hodnota se zapisuje jako NÁZEV_PROMĚNNÉ=hodnota. Pro úpravu více proměnných je potřeba klíč environment uvést vícekrát.

Možné využití je například u systémů, kde více lidí přistupuje na jeden účet (typické například pro správu serverů, kdy všichni admini přistupují na účet uživatele root). Díky tomuto si tak každý admin ke svému klíči může přiřadit specifická nastavení (například proměnnou EDITOR).

Port forwarding

K jednotlivým klíčům lze také zakázat port forwarding (případně X forwarding). To se provede uvedením klíčového slova no-port-forwarding (případně no-X11-forwarding pro směrování X protokolu). Nutno ovšem podotknout, že pokud uživatel má jinak přístup do shellu, tyto omezení až tolik nezmůžou (ten tunel půjde vždy nějak vyrobit...)

Další volby

Výše jsem uvedl jen ty zajímavější možnosti, kterými lze omezovat připojení z jednotlivých klíčů. Kompletní výčet možností lze najít v manuálové stránce (man authorized_keys)

Příklad souboru

Na závěr tedy ještě nějaký příklad, jak vypadá soubor authorized_keys po aplikování změn. Za normálních okolností jsou v authorized_keys řádky začínající ssh-rsa veřejný klíč (kde veřejný klíč zastupuje samotný klíč, obvykle zakončeným komentářem a pokud není použit algoritmus RSA, bude i za ssh- něco jiného - třeba dss). Pokud tedy našemu klíči umožníme připojení pouze z adresy 82.208.56.58, nastavíme mu proměnnou prostředí EDITOR na emacs a vynutíme spuštění příkazu /usr/local/bin/restrictssh.sh (bude to skript, může vyhovovat nebo zamítat požadavky na daný příkaz), bude řádek v authorized_keys vypadat takto:

  1.     from="82.208.56.58",command="/usr/local/bin/restrictssh.sh",environment="EDITOR=emacs" ssh-rsa klíč...