To jsem se zas jednou nachomýtnul k hurá akci. Že já vůl taky všem na všechno kývnu. To si tak zákazník vymyslel, že když teda jako skončila podpora pro Windows 2003 (před více než rokem!), tak si upgraduje. Windows Server 2012 by pro něj byly asi moc velký technologický skok nebo co, takže si přelezl na 2008. Společně s tím si přetáhl aplikaci běžící na Tomcatu a až pan Košťál mávnul, kapela začala hrát a pštros byl vypuštěn do produkce, zjistilo se, že to funguje nějak divně. On totiž na nových serverech nikdo nenainstaloval IIS, které mělo plnit funkci reverzní proxy a starat se o redirecty, NTLM autentizaci a podobné maličkosti.
Kocourkov
Samotná javovská aplikace běží na Tomcatu 6, ale níže popsané akce a metody budou fungovat prakticky s jakýmkoliv aplikačním serverem nebo JSP/servlet kontejnerem komunikujícím skrze AJP protokol. Princip je stejný jako u nastavení jakýchkoliv jiných reverzních proxy. Do webového serveru se zavede modul, který umí překládat požadavky do příslušného protokolu aplikačního serveru a pak se v konfiguraci tohoto modulu definují pravidla pro URL, aby bylo jasné, jaké kontexty se mají předávat jaké aplikaci. Nejprve je samozřejmě potřeba do provozuschopného stavu uvést samotný Tomcat. Tady v celku žádná zrada není. Nastavení v server.xml může zatím zůstat ve výchozím stavu, a pokud jsou nainstalovány i Tomcatí examples aplikace, návštěva URL http://localhost:8080/examples/ vás dovede k jednoduchému rozcestníku. Kontext /examples/ tedy bude představovat moji aplikaci, na kterou se budu chtít dostat skrze IIS. To se sice lehko řekne, ale v případě IIS je tu pár klacků hozených pod nohy, které bylo potřeba překonat. Dokumentace Tomcatu, respektive jeho redirectoru pro IIS je v tomto směru trochu zmatená. Například zmiňované zásahy do registrů vůbec nejsou potřeba, což se tam sice píše, ale už se tam nepíše co všechno je potřeba nastavit na straně IIS. Nicméně ze střípků se dá poskládat, jak že by to nastavení vlastně mělo vypadat.
Instalace IIS
Mám anglické Windows Server 2008 R2, takže názvy budu popisovat anglicky. Pokud jste takoví dobrodruzi, že instalujete servery v regionálních jazykových mutacích, nic není ztraceno, protože překlady jsou poměrně jasné, akorát se s nimi budete muset poprat sami.
Celkem nepřekvapivě je na Windows Serveru potřeba přiřadit roli Web Server (IIS). U této role je nutno zapnout několik featur, které zajistí, že se IIS s Jakarta redirectorem bude kamarádit a že bude podporovat i ostatní požadovaná nastavení. Výchozí výběr tedy ponechte a přidejte
Web Server > Application Development > ISAPI Extensions
Web Server > Application Development > ISAPI Filters
Web Server > Security > Windows Authentication
95 % webových serverů, se kterými pracuji, jsou Apache. Proto mě celkem překvapilo, že IIS neumí out-of-the-box takovou pitomost jako přepisování (rewrite) URL nebo přesměrování (redirect) na základě sady pravidel. Sice umí HTTP Redirect, ale ten prostě šmahem vezme všechno, co mu přijde pod ruku, a pošle to někam pryč. Pro přesměrování kořenového kontextu na aplikační kontext (tedy http://server/ -> http://server/examples/) je třeba doinstalovat ještě IIS URL Rewrite Module. Ten se dá získat přímo u zdroje, případně nainstalovat skrze Web Platform Installer. Šup tam s ním.
Indonéská spojka
Java, Jakarta... jeden by si myslel, že tu plánuju dovolenou, ale to se nám jen tak hezky sešly názvy. Ono konec konců najít u Apache Foundation nějaké šílenosti není nic neobvyklého. Ant, Camel, Jackrabbit, Pig, Tomcat... není divu, že mají i ZooKeepera. Mám pocit, že porady, na kterých se rozhoduje o názvech produktů, mají formu stand-up comedy. Ale zpátky k tématu. Použitý redirector je v tomto případě isapi_redirect.dll stažený rovnou jako zkompilovaná binárka. Už tady je nastražená první past, protože IIS 6 na Win2003 vyžaduje 32bitovou variantu, ale IIS 7, resp. 7.5 na Win2008 už jsou 64bitové. Stáhněte tedy příslušnou variantu, vytvořte adresář C:\inetpub\jakarta a rozbalte do něj z archivu výše zmíněný isapi_redirect.dll. Dále v tomto adresáři ještě vytvořte podadresář logs, u kterého ve vlastnostech zabezpečení povolte měnit a zapisovat skupině Users, jinak se vám nebudou vytvářet logy.
Poté stále v adresáři jakarta vytvořte soubor isapi_redirect.properties a naplňte zhruba následujícím obsahem.
extension_uri=/jakarta/isapi_redirect.dll
log_file=C:\inetpub\jakarta\logs\isapi_redirect.%Y-%m-%d.log
log_level=info
log_rotationtime=86400
worker_file=C:\inetpub\jakarta\workers.properties
worker_mount_file=C:\inetpub\jakarta\uriworkermap.properties
První řádek je celkem jasný. Říká, která knihovna se má použít pro zpracování požadavků. Logovací direktivy na dalších třech řádcích nastavují název souboru a zajistí, že redirector bude vytvářet log pro každý den odděleně. Poslední dva řádky už jsou trochu zajímavější, protože oba odkazované soubory je nutno vytvořit. Soubor workers.properties nastavuje parametry vláken zajišťujících spojení a předávání požadavků mezi IIS a Tomcatem, takže v něm bude řečeno, kde sedí a poslouchá AJP port Tomcatu. Také je zde definován worker řešící logování a hlídání stavu.
worker.list=ajp13,jkstatus
worker.ajp13.type=ajp13
worker.ajp13.host=localhost
worker.ajp13.port=8009
worker.jkstatus.type=status
Soubor uriworkermap.properties pak nastavuje kontexty, které jsou obsluhovány jednotlivými workery a které mají být předávány aplikacím běžícím v Tomcatu (podobně jako JkMount u mod_jk pro Apache). Nastavuji aplikaci s kontextem /examples/ a také můžu nastavit diagnostickou URL /jkmanager, abych mohl koukat na stav workerů. Tu ale na produkci určitě nedávejte nebo si ji nějak pořádně zabezpečte. Soubor bude tedy mít následující obsah.
/examples/*=ajp13
/jkmanager=jkstatus
Tím konfigurace redirectoru končí. To byla, alespoň podle mého, ta jednodušší část.
Pain in the IIS
Zahřejte myš, bude se klikat. Nejprve je třeba v Internet Information Services (IIS) Manager milý isapi_redirect.dll vůbec povolit spouštět. To provedete ve Features View v kontextu celého serveru volbou ISAPI and CGI Restrictions a následným kliknutím na Add... v pravém panelu. Vyplňte následující hodnoty a potvrďte kliknutím na OK.
ISAPI or CGI path: C:\inetpub\jakarta\isapi_redirect.dll
Description: Apache Tomcat Jakarta connector
Allow extension path to execute: zaškrtnout (zapnout) checkbox
Dále se dá pokračovat několika způsoby. Buď se ISAPI filtr a pár dalších nastavení provede na úrovni celého serveru nebo pouze pro příslušný web. Pak je tu ještě varianta, při které se nastavení ISAPI filtrů zapisuje i do souboru web.config v kořenovém adresáři webu, takže je možno odnést web i s celým nastavením. Ale v takovém případě je potřeba tuto skutečnost IIS napřed skrze Configuration Editor sdělit, a požádat jej o odemknutí sekcí system.webServer/isapiFilters
a system.webServer/handlers
. Já na svém serveru měl pouze jedinou aplikaci, takže jsem nic takového dělat nepotřeboval a nastavení ISAPI filtru jsem provedl rovnou v kontextu celého serveru. V ISAPI Filters opět klikněte na Add... a přidejte následující.
Filter name: Apache Tomcat Jakarta connector
Executable: C:\inetpub\jakarta\isapi_redirect.dll
Pak ještě, stále ve Features View v kontextu serveru, dvojklikněte na Handler Mappings, v pravém panelu vyberte Edit Feature Permissions... a zatrhněte (zapněte) i zbývající Execute.
Globální nastavení IIS je hotovo a můžeme se vrhnout na samotnou aplikaci. Výchozí Default Web Site jsem smazal (což ale není povinný krok) a místo toho si pravým klikem na Sites a vybráním Add Web Site... vytvořil novou. Název sítě a hostname si přizpůsobte svým potřebám. Fyzická cesta by v tomto případě měla ukazovat do adresáře se statickými soubory, které mají být vybavovány přímo IIS, ale já žádné nemám, takže jsem cestu práskl tam, kde sídlí isapi_redirect.dll a jeho parta.
Site name: Examples
Physical path: C:\inetpub\jakarta
Host name: localhost
Poté na nově vytvořený web ve stromečku vlevo opět klikněte pravým myšítkem a vyberte Add Virtual Directory.... Opět vyplňte následující údaje, přičemž jméno virtuálního adresáře, resp. relativní cesta k isapi_redirect.dll se v tomto případě musí shodovat s tím, co se píše na prvním řádku v isapi_redirect.properties.
Alias: jakarta
Physical path: C:\inetpub\jakarta
Přemýšlivý čtenář možná namítne, že pokud mám pouze jedinou prezentaci a nemám žádné statické soubory, mohl jsem si udělat adresář pro web, který by měl jakarta přímo jako fyzický podadresář a nemusel se obtěžovat s virtuálním adresářem. Ano, fungovalo by to, ale best practice je virtuální adresář. V případě, že statické soubory přibydou, nebudu muset přestavět půlku konfigurace. Stejně tak, pokud přibude další prezentace, bylo by nežádoucí knihovnu a všechny properties soubory duplikovat.
Pak už jen otočte IIS, buď přímo z IIS manažera, nebo ze správce služeb a první část je za námi. Aplikace by teď měla být dostupná i na http://localhost/examples/.
Our princess is in another castle
Teď bych rád, aby má aplikace byla dostupná přímo po zadání pouhého http://localhost/ nebo alespoň dle původního zadání bylo http://localhost/ přesměrováno na http://localhost/examples/. Jak jsem zmínil výše, featura HTTP Redirect, kterou IIS obvykle poskytuje, by v tomto případě nedostačovala, protože chceme přesměrovávat pouze jedinou konkrétní (prázdnou) URL a ne úplně všechno. Záležitost, která je na Apache HTTP Serveru otázkou dvou řádků (tří, pokud počítám i nahrání mod_rewrite) a kterou jsem dělal už tisíckrát, mi na IIS pořádně napálila kudrlinku. Samotné pravidlo je v principu stejné. Potřebuju redirectovat vše, co odpovídá regulárnímu výrazu ^$
na http://localhost/examples/. To se dá vyklikat i přes IIS Manager. Otevřete kontext webu Examples, dvojklikněte na URL Rewrite featuru, vpravo zvolte Add Rule(s)... a pak Blank rule a OK. Do polí vyplňte následující hodnoty a pak klikněte na Apply.
Name: Root context redirect
Pattern: ^$
Action type: Redirect
Redirect URL: http://localhost/examples/
Nastavení se projeví okamžitě. Bohužel se ale projeví tak, že celý web začne zobrazovat chybu
HTTP Error 500.52 - URL Rewrite Module Error.
The page cannot be displayed because an internal server error has occurred.
Module: RewriteModule
Notification: SendResponse
Handler: ISAPI-dll
Error Code: 0x800700b7
Config Error: Cannot add duplicate collection entry of type 'rule' with unique key attribute 'name' set to 'Root context redirect'
Config File: \\?\C:\inetpub\jakarta\web.config
Nejužitečnější část bude patrně popis v Config Error. Milé IIS se z nějakého důvodu snaží s každým vnořeným adresářem v URL ono pravidlo opakovaně přidávat do sady již existujících pravidel. Také si můžete povšimnout, že je zmíněn soubor C:\inetpub\jakarta\web.config. To je lokální konfigurace v kořenovém adresáři webu, která obsahuje onen nově vytvořený redirect. Řešení problému je naštěstí jednoduché jak facka, kterou by si mimochodem programátor tohoto modulu zasloužil. Onu problematickou sadu je totiž možno před každým zpracováním vyprázdnit, ale to už se nedá udělat přes GUI. Otevřete tedy C:\inetpub\jakarta\web.config v editoru a změňte jeho obsah na
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<rewrite>
<rules>
<clear />
<rule name="Root context redirect" stopProcessing="true">
<match url="^$" />
<action type="Redirect" url="http://localhost/examples/" />
</rule>
</rules>
</rewrite>
</system.webServer>
</configuration>
Vidíte tam to malé <clear />? Tak to mě stálo hodinu a půl času. Doufám, že vám to nějaký ušetří.
NTLM Autentizace
Jelikož jsem samozřejmě jenom lopata s bílým límečkem, přihlašovací údaje k aplikaci mi nedali, takže veškeré autentizační a autorizační mechanismy jsem si nechal na konec. Aplikace, ke které nemáte přístup, se konfiguruje a testuje celkem blbě. Na rozdíl od všech doposud provedených úprav se NTLM (nebo jakákoliv jiná externí) autentizace týká i přímo Tomcatu. V server.xml je nutno u AJP konektoru nutno explicitně povolit příjem těchto informací parametrem tomcatAuthentication="false"
. Ve výchozím stavu totiž Tomcat okázale ignoruje jakékoliv autentizační hlavičky a předpokládá, že si autentizaci bude řešit sám. Otevřete tedy tomcat.xml a řádek s definicí AJP konektoru změňte na něco podobného.
<Connector port="8009" redirectPort="8443" protocol="AJP/1.3" tomcatAuthentication="false" />
Nezapomeňte pak Tomcat restartovat s novým nastavením.
Na straně IIS je pak nastavení NTLM až trapně jednoduché. Aby taky ne, když je společně s IIS z jednoho hnízda. V IIS Manageru vyberte web Examples a ve Feature View poklepejte na Authentication. Zde vyberte Windows Authentication a v pravém panelu jej pomocí Enable aktivujte. V závislosti na tom, jak je aplikace napsaná a jestli čeká explicitně NTLM hlavičky nebo si umí poradit i jinak, můžete stejným způsobem chtít vypnout ostatní metody autentizace.
Na konec ještě jednou pro jistotu otočte server a máte hotovo.
Apache HTTP Server
Jen tak pro zajímavost.
LoadModule jk_module modules/mod_jk.so
JkWorkersFile /etc/httpd/conf/workers.properties
JkMount /examples/* ajp13
LoadModule rewrite_module modules/mod_rewrite.so
RewriteEngine On
RewriteRule ^/$ /examples/ [R]
LoadModule authn_ntlm_module modules/mod_authn_ntlm.so
AuthType SSPI
NTLMAuth On
NTLMAuthoritative On
Require sspi-group "DOMENA\APPUSERS"
Nevím, možná jsem divnej, ale tak nějak mi tohle připadá daleko jasnější, jednodušší a přenositelnější.