Věcem, které popisuji v tomto článku, jsem se věnoval nově a detailněji a připravil rozsáhlý seriál Kerberos protokol se zaměřením na SSO v AD DS. S tímto článkem hlavně souvisí posledních několik dílů, nejvíce díl Kerberos část 11 - konfigurace Apache a využití v PHP.
Rozchození Kerberosu v Apachi a použití v PHP se věnoval můj kolega Ondra, který vymyslel i řešení pro přechod na standardní autentizaci, když SSO nefunguje u klienta. Bohužel je tu jedna zásadní podmínka, Apache musí běžet na Linuxu. Nepodařilo se nám nalézt jedinou Kerberos knihovnu (ani jinou pro využití SSO) pro Apache na Windows. Na Windows ovšem můžeme použít jiný aplikační server, třeba IIS nebo Tomcat, kde SSO knihovny máme.
Související články jsou Kerberos protokol a Single sign-on a Kerberos SSO - nastavení Internet Exploreru a Firefoxu.
Vytvoření účtu pro Apache službu v AD
Z principu Kerberosu víme, že každá služba, ke které se chceme přihlašovat, musí mít záznam (účet) v Active Directory. A že aplikační server potřebuje secret key, aby mohl dešifrovat komunikaci, která mu bude přicházet. Protože náš aplikační server je Linuxový, tak jej nemůžeme normálně zařadit do domény, ale musíme ručně vytvořit účet a vyexportovat šifrovací klíč.
Dále popisovaný postup je možno použít na Windows Sever 2000, 2003, 2008 i 2008 R2. Liší se pouze nějaké detaily, jako jsou podporované šifrovací algoritmy. Uvedené příklady jsou testované na Windows Server 2008 R2. Používaný příkaz ktpass
je dostupný na Windows 2000 Resource Kit, Windows 2003 Support Tools a u Server 2008 je součástí nástrojů, které se instalují na doménový kontrolér. Vždy je třeba použít verzi odpovídající úrovni domény.
Vytvoření záznamu v DNS
Pokud ještě nemáme, tak musíme nejprve vytvořit v DNS záznam pro server/službu (adresa web serveru, třeba mujweb.domena.local).
Vytvoření uživatelského účtu v AD
Potřebujeme jakoby zařadit náš Linuxový server do domény, takže by se zdálo, že budeme vytvářet účet počítač. Ve skutečnosti nám jde pouze o účet služby, ten nemůžeme vytvořit samostatně, ale můžeme jej navázat na účet uživatele. Takže vytváříme uživatelský účet pro náš server/službu.
- například pomocí Active Directory Users and Computers vytvoříme v AD uživatelský účet
- tento účet musí být umístěn v defaultním kontejneru Users
- jméno můžeme zvolit stejné (není podmínkou), jako jméno serveru (třeba mujweb)
- samozřejmě žádný takový účet již nesmí existovat a nesmíme nastavit vyžadování změny hesla
Nastavení SPN a exportování keytab souboru
Service Principal Name (SPN) je název služby, jak ji bude volat klient (webový prohlížeč), když chceme provést Kerberos autentizaci k webu. SPN je svázáno s účtem (uživatele, počítače, skupiny). Můžeme jej vytvořit pomocí příkazu setspn
, ale my použijeme rovnou příkaz ktpass
, který nám zajistí i vytvoření keytab souboru. Pro webovou aplikaci má SPN tvar HTTP/<hostname>
(tedy HTTP/mujweb.domena.local), kde hostname musí odpovídat DNS adrese serveru. To platí i v případě, že používáme protokol HTTPS (stále je to HTTP/hostname). Toto SPN svážeme s naším vytvořeným uživatelem (mujweb@domena.local).
Pozn.: Hostname v SPN musí odpovídat A záznamu v DNS a nikoliv Aliasu (CNAME). Například když máme server web.domena.local se stejným jménem v DNS a k tomu máme alias www.domena.local, tak do SPN musíme dát web.domena.local, i když uživatelé pro přístup používají alias.
Pomocí příkazu ktpass
provedeme namapování SPN a zároveň exportování Kerberos keytab souboru. Syntaxe a příklad pro naši situaci:
ktpass -out <jméno souboru> -princ HTTP/<hostname>@<DNS JMÉNO AD DOMÉNY VELKÝMI PÍSMENY> -mapUser <název uživatele>@<název domény> -mapOp set -pass * -ptype KRB5_NT_PRINCIPAL -kvno 0 ktpass -out mujweb.keytab -princ HTTP/mujweb.domena.local@DOMENA.LOCAL -mapUser mujweb@domena.local -mapOp set -pass * -ptype KRB5_NT_PRINCIPAL -kvno 0
Výstupní keytab soubor (mujweb.keytab) obsahuje SPN a secret key služby. Principal name (parametr princ) se skládá z SPN a určení domény vůči které budeme uživatele autentizovat (musí být zadáno velkými písmeny). MapOp určí, že se SPN nastaví na účet (ne přidá další). Pokud chceme uložit keytab soubor, tak musíme zadat heslo. Toto heslo se nastaví na uživatelský účet (změní se původní heslo). Když zadáme hvězdičku, tak jsme následně dotázáni na heslo.
Ve chvíli, kdy vytváříme keytab soubor, se uživatelský účet musí nacházet v kontejneru Users, jinak dostaneme následující chybovou hlášku (možná je to bug ve Windows Server 2008 R2). Po vytvoření souboru již můžeme účet přesunout do libovolné organizační jednotky.
Password set failed! 0x00000020 Aborted.
Důležité je zajistit bezpečnost keytab souboru, protože obsahuje údaje pro přihlášení k AD jménem dané služby. Nějaké informace můžete nalézt v MS článcích o Service Principal Names a o nástroji Ktpass.
Doplněno 8.4.2013 - Vyšel Firefox 20.0 a v něm přestalo fungovat SSO nastavené dle tohoto popisu. Místo aby došlo k přihlášení, tak se zobrazil dialog na HTTP autentizaci. Po celém dni bádání jsem přišel na to, že novému Firefoxu vadí, že server se jmenuje mujweb.domena.local
a toto jméno používáme v SPN, ale pro přístup na web používáme www.domena.local
. Když jsem vytvořil nový Keytab soubor a použil jsem www.domena.local
, tak ve Firefoxu začalo SSO fungovat, ale zas přestalo chodit z Internet Exploreru. Takže jsem ještě musel změnit DNS záznamy, aby A záznam byl www.domena.local
a CNAME mujweb.domena.local
. Po několika minutách pak vše začalo fungovat.
Kerberos modul pro Apache - mod_auth_kerb
Testování jsme prováděli na Oracle Enterprise Linux 5.5 (OEL). Zvolili jsme nejrozšířenější knihovnu mod_auth_kerb (Kerberos Module for Apache), jejíž balíček je součástí OEL.
- nainstalujeme balíček
mod_auth_kerb
a povolíme jej v Apachi (pokud se nepovolí automaticky) vhttpd.conf
LoadModule auth_kerb_module modules/mod_auth_kerb.so
- keytab soubor (mujweb.keytab) nahrajeme na server a umístíme jej do bezpečného adresáře tak, aby k němu měl přístup Apache
- nastavíme parametry pro adresář v Apachi, kde chceme použít autentizaci
Konfigurace Apache Directory
Konfiguraci můžeme provést pomocí souboru .htaccess
nebo pomocí directivy Directory
či Location
, buď v kontextu celého web serveru nebo v rámci určitého Virtual Hosta, standardně v souboru httpd.conf
. Parametry nastavujeme na jeden určitý adresář, kam umístíme přihlašovací skript (pokud použijeme standardní metodu přihlášení a údaje dále máme v session, jinak můžeme nastavit i na celý web a každá stránka se bude autentizovat). Ukázka konfigurace:
<Directory /var/www/mujweb/sso/> AuthName "Kerberos Login" AuthType Kerberos KrbAuthRealms DC.DOMENA.LOCAL DOMENA.LOCAL KrbServiceName HTTP/mujweb.domena.local@DOMENA.LOCAL Krb5KeyTab /etc/apache2/mujweb.keytab require valid-user </Directory>
- AuthType Kerberos - musíme zadat, aby se použila autentizace Kerberos
- KrbServiceName - musí přesně odpovídat principal name, které jsme použili při generování keytab souboru
- Krb5KeyTab - cesta ke keytab souboru uloženém na filesystému
Pro ještě větší zabezpečení můžeme na adresář vyžadovat použití HTTPS přidáním directivy:
SSLRequireSSL
V konfiguraci Apache je ještě třeba mít nastaveno plné jméno (FQDN, adresu) serveru, buď pro celý web nebo pro daný Virtual Host (což asi máme):
ServerName mujweb.domena.local
Použití SSO v PHP
Princip komunikace (Kerberos autentizace) jsme si popsali v minulém článku, nyní jen zopakujeme výměnu informací mezi klientem (prohlížečem) a web serverem.
- Klient (webový prohlížeč) požaduje nějakou stránku (GET požadavek).
- Ta je v adresáři, kde vyžadujeme Kerberos autentizaci, takže server odpoví v HTTP hlavičce, že požaduje autentizaci (HTTP/1.1 401 Authorization Required, WWW-Authenticate: Negotiate, WWW-Authenticate: Basic realm="Kerberos Login").
- Pokud klient podporuje Kerberos, tak odešle v hlavičce autentizační údaje (což je service ticket, Authorization: Negotiate následuje šifrovaný obsah). Pokud ne, tak nijak neodpovídá, ale zobrazí okno pro HTTP autentizaci (to je dáno dalšími údaji, které dostal ze serveru). Na Apachi je možno nastavit více autentizačních metod, které se použijí když některá selže.
- Pokud je vše v pořádku, tak server opět v hlavičce odešle potvrzení (HTTP/1.1 302 Found, WWW-Authenticate: Negotiate šifrovaná data serveru).
Pokud dojde k úspěšnému přihlášení, tak Apache uloží jméno uživatele (uživatelské-ID@doména, třeba user@DOMENA.LOCAL) do serverové proměnné $_SERVER['REMOTE_USER']
, z které jej můžeme v PHP jednoduše získat.
Jednoduché použití tedy je, že přihlašovací skript umístíme do speciálního adresáře a na něj aplikujeme výše popsané nastavení Apache. Když se takovou stránku pokusí uživatel otevřít, tak proběhne SPNEGO (Kerberos) autentizace. Pokud tato selže, tak se vůbec nevyvolá skript, ale server odešle chybu. Proto můžeme ve skriptu počítat s tím, že SSO proběhlo úspěšně a již pouze kontrolujeme uživatelské jméno. Velice jednoduchý skript /sso/login.php
může vypadat třeba takto.
<?php session_start(); $_SESSION['username'] = $_SERVER['REMOTE_USER']; header('Location: /'); ?>
Složitější situace ovšem je, pokud chceme zajistit, aby při selhání SSO (například uživatel ji nemá na stanici povolenu) došlo k přepadu na klasickou form-based autentizaci. Běžně při selhání SSO vyskočí okno prohlížeče s HTTP autentizací (která v našem nastavení nefunguje) a když klikneme na Cancel nebo něco zadáme, tak se zobrazí chybová stránka. To, že standardně vyskočí okno pro HTTP autentizaci, když selže SSO, je způsobeno tím, že Apache odesílá v hlavičce
WWW-Authenticate: Basic realm='Kerberos Login'
Hlavičky, které odesílá Apache při Kerberos autentizaci, můžeme vytvořit ručně a otestovat, jestli přichází odpověď (tedy je podporováno SSO). V tom případě provedeme SSO autentizaci, v opačném případě zobrazíme klasickou autentizaci přes formulář. Schématicky tuto metodu ukazuje následující skript /login.php
. Funguje to na tom principu, že pokud prohlížeč podporuje SSO pro danou stránku, tak přijme v hlavičce požadavek na vyjednání autentizace a již nezobrazuje stránku, kterou přijal, ale odešle autentizační údaje na stejnou adresu. V opačném případě zobrazí přijatou stránku, takže do ní můžeme vložit běžný přihlašovací formulář.
<?php session_start(); $headers = apache_request_headers(); if(empty($headers['Authorization'])) { header("HTTP/1.1 401 Authorization Required"); header("WWW-Authenticate: Negotiate"); // SSO není podporováno, zobrazím standardní login či informace echo "SSO autentizace neprobehla."; exit; } else { // SSO OK, přesměruju na SSO login header('Location: /SSO/login.php'); exit; } ?>
Ahoj,
pěkný článek na téma SSO. Nechybí zde ale část o vlastní konfiguraci kerberos klienta na straně linuxového serveru?
V mém případě vypadá takto:
[logging]
default = FILE:///var/log/krb5libs.log
kdc = FILE:///var/log/krb5kdc.log
admin_server = FILE:///var/log/kadmind.log
[libdefaults]
default_realm = DEVELOPMENT.TIMEIMPORT.CZ
dns_lookup_realm = true
dns_lookup_kdc = true
ticket_lifetime = 3600h
forwardable = yes
default_keytab_name = FILE:///etc/krb5.keytab
[realms]
DEVELOPMENT.TIMEIMPORT.CZ = {
kdc = andromeda.development.timeimport.cz:88
admin_server = andromeda.development.timeimport.cz:749
default_domain = development.timeimport.cz
}
[domain_realm]
prometheus.development.timeimport.cz = DEVELOPMENT.TIMEIMPORT.CZ
.development.timeimport.cz = DEVELOPMENT.TIMEIMPORT.CZ
[appdefaults]
pam = {
debug = false
ticket_lifetime = 36000
renew_lifetime = 36000
forwardable = true
krb4_convert = false
}
odpověď na [1]Ladislav Jech: Moc jsem to nepochopil, co je to za konfiguraci a čeho se týká? Pro web server nic takového nepotřebuji.
odpověď na [2]Samuraj: Tohle se hodí v případě, že "něco" nefunguje tak jak má, je to prakticky zrcadlení konfiguračních informací z modulu kerberos pro apache do konfiguráku linuxového kerberos klienta, který je na CentOS v lokaci /etc/krb5.conf (verze 5), a pomocí kterého lze pak různé indispozice požadované funkcionality ladit. Klient podporuje následující příkazy: kinit, klist, kvno, kdestroy, ksu, kpasswd.
Samozřejmě mód jako takový funguje i bez tohoto, a veškeré informace z modulu jdou do apache logu access_log, případně ssl_access.
To je jen jako extra, návod je perfektní a funkční.
Super článek. Na netu se na toto téma najde dost informací ale žádný zdroj nebyl takhle ucelený.
Rozchodil jsem to bez problémů, ale Internet explorer mě zlobí (ve Firefoxu no problem). Když mu pošlu
header("HTTP/1.1 401 Authorization Required");
header("WWW-Authenticate: Negotiate");
a on pro danou stránku SSO nepodporuje, tak stejně zobrazí ten ošklivý dialog, který chce jméno a heslo. až po kliknutí na "cancel" zobrazí můj formulář. Neexistuje nějaký trik, aby se IE choval taky takhle? Diky
Ve firmě to používám tak, jak je zde popsáno, a funguje to OK v IE i ve FF. Netuším, kde může být problém.
Máte ten skript v adresáři, na kterém není nastaveno SSO v Apachi?
odpověď na [5]Samuraj: Děkuji za odpověď, mám ten skript v adresáři, kde není nastaveno SSO. Teď jsem provedl i test na serveru, kde SSO není vůbec. Tohle je komunikace IE a serveru zachycená Wiresharkem:
GET /xsso.php HTTP/1.1
Accept: */*
Accept-Language: cs-CZ
User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; MALC)
Accept-Encoding: gzip, deflate
Host: www.diktatorek.cz
Connection: Keep-Alive
HTTP/1.1 401 Authorization Required
Content-Type: text/html
Server: Microsoft-IIS/7.0
X-Powered-By: PHP/5.2.11
WWW-Authenticate: Negotiate
X-Powered-By: ASP.NET
Date: Fri, 11 Feb 2011 19:19:09 GMT
Content-Length: 66
HTML formular = (odstranil jsem)
Na základě těchto dat IE zobrazí standardní autentikační dialog, Firefox rovnou zobrazí to mojo HTML.
Nevíte prosím o nějaké veřejně dostupné stránce, která by tento přístup používala? Podíval bych se co přesně server posílá a zařídil bych se podle toho. (potřebuju pouze aby se mi zobrazil login formulář.) Děkuju moc
odpověď na [6]Josef Andrýsek: Tak jsem to testoval a u mě v IE vyskočí to dialogové okno pouze, pokud se v hlavičce posílá:
WWW-Authenticate: Basic realm="
To tam posílá Apache, když přistupuji do adresáře, kde je nastaveno SSO.
Na své testovací stránce to posílám bez tohoto parametru a IE 8.0 zareaguje zobrazením html, které je součástí dat.
odpověď na [7]Samuraj: Děkuju, možná bude záležet na verzi I.E., nebo na nějakém dalším nastavení. Není ta Vaše testovací stránka na nějaké veřejné adrese? Jen abych měl 100% jistotu, že nedělám něco špatně. Diky
odpověď na [8]Josef Andrysek: Tak ten skript, který je na konci článku jsem uložil na adresu http://www.samuraj-cz.com/download/test-sso.php, pouze místo redirectu zobrazuji info. U mě to chodí správně, nevyskakuje žádné okno.
odpověď na [9]Samuraj: Děkuju moc, bohužel ten skript nevyhodí v clientu žádný dialog proto, že posílá nevalidní hlavičku:
WWW-Authenticate: Negotiate realm="20041"
IE 8 zobrazi rovnou chybovou hlasku. IE 6 zobrazi formular tak jak jsem chtel, ale nepokusi se o SSO, ikdyz muze.
Nechci Vas uz s tim otravovat. Kazdeopadne Diky
odpověď na [10]Josef Andrýsek: Tak nevím, proč to tam doplňuje, ve skriptu to není a na produkčním prostředí, kde to používáme úplně stejně, to neposílá.
Každopádně, mě se to v IE8 zachová správně a zobrazí ten text, že není podporováno SSO.
odpověď na [11]samuraj: Tak tohle dela zapnutej safe mod v PHP. Kdyz ho vypnu, zustane tam jen spravne WWW-Authenticate: Negotiate