Řešil jsem teď nějaké problémy s ověřování klientským TLS/SSL certifikátem vůči web serverům v NLB farmě bez afinity (tzn. HTTPS/TLS/SSL client certificate authentication for web sites load balanced with Microsoft NLB with no client afinity).
NLB bez afinty znamená, že každé TCP spojení, klidně z jednoho počítače může končit na libovolném členovi NLB klastru. Internet Explorer jich obvykle ustavuje více současně, takže každé pak vede na jiný web server - aniž by o tom prohlížeč sám věděl - NLB má jen jednu IP adresu. Z pohledu prohlížeče se tedy jedná o jeden web server na jednom host header a jedné IP adrese.
Proč ověřování klientským certifikátem?
O důvodech, proč se přihlašovat pomocí klientského certifikátu si můžete přečíst v mém starším článku. V případě vnitřního přístupu do SharePoint byste to asi moc nepotřebovali - tam se můžete krásně ověřovat pomocí Kerberosu a jeho hladkého SSO (single-sign-on). I když se přihlašujete pomocí čipových karet, stejně vám pojede Kerberos.
Ale pro přístup z internetu to má podstatně větší smysl.
Jak na to?
Tohle se dá nastavit dvěma způsoby. Buď vynucujete ověření uživatelovým certifikátem přímo na HTTP.SYS listeneru, nebo použijete nastavení až ve vlastnostech web site v IIS konzoli. Metoda konfigurace HTTP.SYS listeneru je vhodnější pro SharePoint, protože nemusíte vůbec zasahovat do nastavení IIS a tím pádem se vám to nezkazí při aktualizacích ani kdoví jakých úpravách nastavení SharePoint.
Navíc, jak se ukázalo, pokud použijete tu druhou metodu s IIS vlastní konfigurací v kombinaci s NLB (network load balancing) bez afinity (no afinity), tak to nebude fungovat - budete velice často a chaoticky dostávat error 403 forbidden. Takže preferovaná metoda je přímá konfigurace HTTP.SYS.
I tak bych ale doporučoval pro ověření certifikátem dát na NLB single afinity, protože to tak může využít různé optimalizace v klíčování mezi jednotlivými TCP sešnami, které jsou k dispozici na TLS 1.1 a TLS 1.2.
Vynutit TLS client certificate authentication rovnou na HTTP.SYS znamená následující. To je metoda, kdy jde v úplně prvním Server Hello rovnou hodnota HandShakeType = Certificate (0x0B) a následuje odmítnutí klienta, který to prostě zkusí znovu, tentokrát už s certifikátem.
Narozdíl od toho, pokud vynutíte SSL/TLS přes IIS konzoli, bude se dělat secure renegotitation a to v případě NLB with no afinity někdy selže (viz. dále).
Jak se nastaví HTTP.SYS listener pro TLS client certificate authentication
Nastavení se provede pomocí NETSH HTTP kontextu například takto:
netsh http add sslcert ipport=0.0.0.0:443 certhash=<hashvasehocertifikate< appid={4dc3e181-e14b-4a21-b022-59fc669b0914} DsMapperUsage=enable ClientCertNegotiation=enable
Hodnotu appid a její vysvětlení najdete tady. Jediné, co si musíte změnit je certhash, kterou si nastavte na hodnotu thumbprint vašeho certifikátu. Na všech členech NLB klastru mohou být různé certifikáty, pokud obsahují stejné jméno. Takže jejich klíče nejsou podstatné. Thumbprint certifikátu zjistíte například v PowerShell pomocí:
dir cert:\LocalMachine\My
Podstatné jsou v tomto případě dvě hodnoty - dsmapperusage a clientcertnegotiation. Hodnota dsmapperusage zapíná Active Directory certificate mapper, tedy mapování TLS client certifikátů na účty v Active Directory pomocí jejich userPrincipalName atributu v SAN. Bez toho byste museli zadávat současně i login a heslo uživatel do normálního přihlašovacího dialogu.
Druhá hodnota je právě clientcertnegotiation, která přinutí klienta, rovnou během úvodní výměny (zprávy ServerHello a CertificateRequest), aby se klíče domlouvaly vzájemně ověřené pomocí klientova certifikátu. Takže každé TLS/TCP spojení bude hned od začátku autentizované uživatelovým certifikátem.
Vzniká to jako první spojení, takže se nejedná o re-negotiation (renego) a klientův certifikát se použije načisto. Je tedy jedno, na kterou nodu NLB clusteru tohle spojení jde.
A je to. Potom už jen klienti musí mít uživatelské certifikáty, které splňují následující požadavky:
Klientské certifikáty pro TLS
Jen bleskové shrnutí (v podstatě je to stejné jako v případě EAP-TLS klientského certifikátu):
- EKU = Client Authentication (1.3.6.1.5.5.7.3.2)
- SAN = user principal name (UPN) uživatele, tedy něco jako ondrej@gopas.cz
- Key usage = Digital signature
- Request handling = Signature
- Publish certificate in Active Directory = off/unchecked
- Subject name = Build from this Active Directory information - User principal name (UPN)
- Issuer = certifikační autorita, která certifikát vydala musí být umístěna v NTAuth store v configuration partition v Active Directory
Jak se liší metoda vynucení klientského certifikátu pomocí IIS konzole?
Oproti předchozímu případu můžete TLS klientský certifikát vynutit také přes IIS konzoli - v ikonce SSL Settings můžete zapnout Require SSL a Require client certificate. Jenže tohle je nastavení, které jde do applicationHost.config (by default) nebo web.config souborů jako <access> element s hodnotou sslFlags = SslRequireCert.
Pro SharePoint je nevýhodné měnit přímo nastavení IIS, které si SharePoint potom čas od času "poškodí". To je první nevýhoda.
Druhý problém je zmiňovaný HTTP status 403 forbidden. Problém je v tom, že se klientský certifikát nevynucuje už při ServerHello, ale až později, pomocí secure re-negotiation (to je bezpečné, jenom je to až později). Později znamená, že až potom, co prohlížeč zašle do web serveru první HTTP GET ve zprávě SSL ApplicationData.
Na to ale prohlížeč reaguje vytvořením nového TCP spojení, které je ovšem klíčově (kryptograficky) vázáno na předchozí spojení. Proč? Chce provádět secure renego (možná byste se něco dozvěděli v mé prezentaci z konference TechEd). To se pozná tak, že uvnitř nového ServerHello je obsaženo stejné SessionId, jako z předchozího TCP spojení.
No a tady je kámen úrazu. Pokud máte NLB bez afinity, tak to další TCP spojení může jít úplně kamkoliv. Na libovolnou nodu toho NLB clusteru. Která tím pádem zaručeně nezná symetrické klíče, které se vygenerovala noda předchozí.
Pokud byste tedy chtěli mít to ověřování nastaveno přes IIS konzoli, musíte přepnout NLB na single afinity. Pak to pojede.