Fallback přihlašování z mod_auth_kerb na login stránku aplikace

V určitých případech je vhodné na webové aplikaci přebírat přihlášení uživatelů odjinud. Jedním z takových případů může být přebírání Kerberos autentizace – ve větších organizacích celkem běžná věc, protože na Kerberu je postavena například Active Directory doména. Ve výsledku je pak možné nabídnout uživateli komfortní přístup do aplikace, protože se nemusí znovu přihlašovat. Stačí, že je přihlášen pod svým účtem na pracovní stanici.

Při implementaci CzechIdM podobný požadavek často řešíme, jmenovitě konfigurací Apache HTTPD modulu mod_auth_kerb, který autentizaci doménou implementuje. Modul běží na reverzní proxy představené před aplikačním serverem identity manageru a informace o přihlášení uživatele mu posílá přidané v proxovaných požadavcích.

Tento způsob funguje velmi hezky, ale má pár vad na kráse:

  • Uživatelův prohlížeč nemusí rozumět HTTP hlavičce WWW-Authenticate: Negotiate. Výsledkem je odepření přístupu na aplikaci s chybou HTTP 401 Unauthorized.
  • Uživatel nepřichází ze stanice zapojené do domény. Toto není v zásadě problém, protože mod_auth_kerb je schopen zobrazit malý login dialog a uživatele do domény přihlásit sám. Dialog ovšem vypadá stejně jako při HTTP Basic autentizaci, takže to není nic hezkého na pohled a běžného uživatele bude spíše mást.
  • Uživatelů je vícero druhů, přičemž někteří mají a někteří nemají účet v doméně. Uživatelé bez doménového účtu se do aplikace nemají šanci dostat. Můžeme to sice obejít vytvořením separátních virtual hostů, což ale nemusí být úplně žádoucí.

Jak to tedy udělat hezky – aby všichni uživatelé chodili pouze na jednoho virtual hosta, aby se přihlašovali tím, čím se opravdu přihlásit mohou, aby je neotravovaly chybové hlášky?

Technické řešení

Řešení je identifikace uživatelů a/nebo schopností jejich prohlížečů. Pokud uživatele správně identifikujeme, můžeme mu přihlašování pomocí mod_auth_kerb selektivně vypnout. Samotný autentizační modul totiž žádnou možnost “soft fail” (uživatel nepřihlášen, ale stejně ho přes proxy do aplikace pustíme, ať si ho přihlásí sama) nenabízí.

Vše je naštěstí možné realizovat pouze s pomocí Apache HTTPD a trochy konfigurace. Je to řešení trošku krkolomné, ale plně funkční. V následujícím příkladu jsem místo ověřování Kerberem použil HTTP Basic, ale postup je totožný:

<VirtualHost *:443>
ServerName www.server.tld
...
RewriteEngine on
RewriteCond %{HTTP_COOKIE} nokerb=true [NC]
RewriteRule ^ - [E=nokerb:1]
<Location ~ "/protected-url/">
 Order allow,deny
 Allow from all
 Deny from env=!nokerb

 AuthType Basic
 AuthName "Tohle by spravne mel byt kerberos" 
 AuthUserFile /var/www/html/.htpasswd
 Require valid-user
 Satisfy Any

ErrorDocument 401 "<html><head><meta http-equiv=\"refresh\" content=\"3;url=https://www.server.tld/spooky-redirect\"></head></html>" 
</Location>

RewriteRule ^/spooky-redirect$ https://www.server.tld/protected-url/ [L,R=302,CO=nokerb:true:www.server.tld]
...
</VirtualHost>

Nyní si vysvětleme, jak celá věc funguje. Základem je, že uživatel přejde na https://www.server.tld/protected-url, kde zabere autentizační modul. Pokud uživatel svoje přihlašovací údaje zadá, je modulem autentizován a vpuštěn do aplikace. Pokud ovšem autentizace selže (prohlížeč nezná Negotiate hlavičku, uživatel nemá doménový účet, …) je prohlížečem zobrazena speciální webová stránka (nastavená direktivou ErrorDocument), která zajistí přesměrování na https://www.server.tld/spooky-redirect.

Na tuto speciální URL zabíra přepisovací pravidlo. To nastaví uživateli speciální cookie, které značí že uživatel nepoužívá přihlášení Kerberem, a přesměruje uživatele zpět na https://www.server.tld/protected-url.

V tomto okamžiku se provede stejný request jako poprvé, ale uživatelův prohlížeč k němu přibalí i cookie, kterou jsme si ho označili. Na začátku ukázky jsou tyto dva řádky:

RewriteCond %{HTTP_COOKIE} nokerb=true [NC]
RewriteRule ^ - [E=nokerb:1]

ty zajistí, že pokud má uživatel cookie, nastaví se proměnná prostředí s názvem nokerb na hodnotou 1. Hodnota nás moc nezajímá, ale pokud ji nespecifikujeme, proměnná prostředí nemusí vůbec vzniknout (pozorováno na httpd 2.2.x na CentOS 6).

Na základě této nastavené/nenastavené proměnné pak zabírá kombinace direktiv Allow, Deny, Require a Satisfy. Direktivy Allow a Deny nejdříve povolí přístup všem uživatelům kteří nemají nastavenu nokerb cookie (a tedy nebude nastavena ani proměnná prostředí nokerb). Direktiva Require povolí přístup pouze autentizovaným uživatelům.

Vše je řízeno direktivou Satisfy, jejíž hodnota Any znamená logický OR pro Allow(Deny) a Require. Tedy uživateli bude umožněn vstup pokud “má nastavenu nokerb cookie (=Allow from all) nebo se úspěšně přihlásil autentizačním modulem (=Require valid-user)”. V případě že cookie nastavenu nemá tak Allow from all neprojde (resp. zabere ještě i Deny from env!=nokerb), což vede k vyvolání autentizace.

Pokud projde Allow a nezabere Deny, tak se autentizační modul vůbec nevolá a uživatel je vpuštěn do aplikace. Jelikož CzechIdM spoléhá na informaci z autentizačního modulu, kterou nedostane, zobrazí uživateli svůj vlastní login dialog a vyzve uživatele k přihlášení.

V celém řešení se používají cookies. Samozřejmě by šlo využít např. i části URI nebo query stringy. Jejich problém je ale v tom, že z pohledu použití žijí pouze na Apache proxy a aplikace za proxy o nich vůbec neví. Pokud taková aplikace generuje hyperlinky, musela by do nich URI i query stringy nějak zahrnout – a tedy by musela být upravena přinejmenším po konfigurační stránce. Použijeme-li cookies, vše si elegantně vyřeší prohlížeč s proxy a samotná aplikace o ničem nemá ani tušení.

Závěr

V článku popsaný způsob můžeme hezky využít v případech, kdy chceme mít možnost za určitých okolností (a samozřejmě z dobrého důvodu) obejít autentizační mechanismy nastavené v Apache HTTPD pro určité URI.

V případě mod_auth_kerb se pak hodí vypnout jeho fallback na zobrazení jednoduchého login dialogu a zároveň nastavit prohlížeče klientů (třeba doménovou politikou), aby rovnou odesílaly autentizační údaje pro Kerberos a nečekaly na HTTP 401 od serveru.

Tyto údaje se posílají v HTTP hlavičce Authorization. Pokud víme, že prohlížeče uživatelů posílají autentizační údaje “bez vyzvání”, můžeme celý příklad výše zjednodušit tak, že změníme rewrite podmínku z cookie na %{HTTP:Authorization} a rovnou podle přítomnosti hlavičky rozhodneme, zda autentizační modul použít či ne. V takovém případě nejsou zapotřebí už žádná další přesměrovávání.

Napojením CzechIdM na různé autentizační mechanismy se zabýváme velmi často a drtivou většinu jich řešíme pomocí Apache HTTPD, který je standardní součástí nasazení našeho identity manageru. Pokud máte dotazy ohledně článku nebo problematiky autentizace s použitím Apache HTTPD, neváhejte mne kontaktovat na petr.fiser@bcvsolutions.eu .

Leave a Reply