Hlavní navigace

Certifikáty Let‘s Encrypt validované přes proxy DNS

Dan Ohnesorg

Let‘s Encrypt už nějakou dobu vystavuje wildcard certifikáty. Jejich vystavení je ale podmíněno validací přes DNS, která je poněkud pracnější než nejpoužívanější metoda ověření přes vystavení souboru do známé cesty na serveru.

Doba čtení: 4 minuty

Na začátek mi ale dovolte odbočku, víte, že wildcard certifikáty ve skutečnosti nechcete? Nevěříte? Tak se nejdříve zamyslete nad následujícím.

Proč wildcard nechcete

Má to bezpečnostní důvody (ostatně nejdůvěryhodnější EV certifikáty se jako wildcard ani nesmí vystavit). Pokud totiž máte certifikát pro doménu www.example.com, tak dokážete hlídat, kam směřuje DNS dané domény, dokážete hlídat, jaký tam vidíte certifikát, jinak řečeno když vám někdo začne dělat neplechu, tak to hned víte.

Pokud vám ale útočník dokáže do DNS vložit záznam pro web.example.com a ukradne wildcard certifikát, tak ani nevíte, že máte takové jméno hlídat, ani snadno nezjistíte, že používá váš certifikát. Přitom i poměrně pečlivý návštěvník uvidí všechno v pořádku. Uvidí platné jméno (a to není zase takový problém do DNS dopravit) a uvidí platný certifikát, dokonce stejný jako na jiném vašem webu.

Druhý důvod je organizační, při typické platnosti certifikátu 2 roky budete těžko hledat, kde všude je certifikát nasazen a pro co se používá. Ať se to eviduje jakýmkoliv nástrojem, pokaždé při výměně wildcard najdete místo, kde zůstal původní. Tohle asi omezená platnost Let‘s Encrypt certifikátu řeší, ale i tak to na přehlednosti nepřidá.

Validace pomocí DNS

Ale zpět k původnímu tématu. Let‘s Encrypt tedy wildcard certifikáty už vystavovat umí. Princip je stále stejný, vygenerují náhodný řetězec, ten dáte do DNS a CA ověří, že je stejný a pokud ano, vystaví certifikát. A tohle by mělo proběhnout cca jednou za dva měsíce, v budoucnu možná častěji.

Je jasné, že dělat tohle ručně je strašná práce, ve výsledku daleko dražší než koupit klasický certifikát. Takže musí nastoupit automatizace. Ale to má dva problémy, ne každý DNS poskytovatel má API a zase ta zatrolená bezpečnost.

Proč bezpečnost? Nu vemte si, že na serveru leží API klíče, které umožňují automaticky změnit libovolný záznam v DNS. Takže případný útočník (nebo napruzený zaměstnanec) může nejen vystavovat libovolné certifikáty, on dokonce může celý web poslat úplně jinam. Brrrr…

Samozřejmě můžete API klíče omezit jen na změnu jednoho konkrétního záznamu… teoreticky, možná už někdo slyšel o někom, kdo má tak jemně nastavitelná práva pro API. Pro čtenáře z budoucnosti: vím o poskytovatelích, kteří to plánují na rok 2019.

Přesto ale existuje řešení, jak takové ověření provést i když stávající DNS provozovatel API nemá. Asi je načase naznačit, jak ten ověřovací záznam vypadá:

_acme-challenge.example.com. IN TXT hroznenahodnyretezec

minimálně takto jej bude nabízet většina návodů. Ale může vypadat i jinak, buď jej můžete delegovat přes NS na jiný nameserver, ale to asi nemá moc smysl a nebo místo něj můžete zavést CNAME záznam. A tady to začíná být zajímavé, protože takový záznam vypadá stále stejně a může směrovat i do jiné domény:

_acme-challenge.example.com. IN CNAME _acme-challenge.domena-s-api.cz.

V tu chvíli stačí mít jen jednu doménu u poskytovatele, který má API. Ten, kdo k ní má přístup nemůže měnit obsah základní domény. Let‘s Encrypt akceptuje ověřovací hash i když je jen jeden z mnoha v doméně, takže ani při současném vystavení více certifikátů nevadí, že přidáte pro každý z nich jeden TXT záznam do stejné domény.

Výběr poskytovatele

Poskytovatelů DNS, kteří mohou takto fungovat je hodně. Asi nejsnáze si vyberete, když projdete ve svém oblíbeném ACME klientu seznam podporovaných API pro ověření přes DNS. Tím, že je doména jen jedna, je možné klidně používat i ty placené, protože pro jednu doménu se jedná většinou o dolarové částky.

API má ale i oblíbený nameserver BIND, byť je trochu méně košaté, pro naše potřeby je zcela dostačující. Můžeme totiž použít script, který zavolá příkaz nsupdate a ten už obstará zbytek. Postup je hezky  popsaný např. na wiki projektu Dehydrated.

Tento návod volí variantu, kdy CNAME nesměřuje do jedné úrovně zóny, ale zakládá v zóně pro každou delegaci jméno certifikátu včetně jména domény takto:

_acme-challenge.example.com.domena-s-api.cz

Tím garantuje, že kdyby došlo k tomu, že dvě domény budou mít stejný hash, tak nedojde ke kolizi, pravděpodobnost takové situace je ale poměrně malá a spíše by k tomu mohlo dojít, kdyby byla chyba v Let‘s Encrypt systému.

Pro variantu s delegací přes CNAME platí text na konci návodu, tady takto vypadající deploy_challenge a clean_challenge:

ZONE="acme.mydomain.com"
...
    "deploy_challenge")
        printf "server %s\nupdate add _acme-challenge.%s.%s. %d in TXT \"%s\"\nsend\n" "${DNSSERVER}" "${2}" "${ZONE}" "${TTL}" "${4}" | $NSUPDATE
        ;;
    "clean_challenge")
        printf "server %s\nupdate delete _acme-challenge.%s.%s. %d in TXT \"%s\"\nsend\n" "${DNSSERVER}" "${2}" "${ZONE}" "${TTL}" "${4}" | $NSUPDATE

Princip je zřejmý, $2 obsahuje CN certifikát, $4 obsahuje ověřovací hash. Deploy_challenge do DNS umístí ověřovací hash, clean_challenge jej zase odstraní. Odstranění není nutné, nikdo nekontroluje, jestli bylo provedeno, ale určitě přispívá k přehlednosti zónového souboru a zjednodušuje ladění případných chyb.

MIF18 tip v článku ROSA

Nevygeneroval jsem tímto způsobem nijak mnoho certifikátů, ale ty, co jsem potřeboval, byly vystaveny bez jakéhokoliv problému.

Hledám kolegy, linuxové administrátory, kteří by se mnou chtěli podobné věci řešit. Pokud máte zájem, ozvěte se mi na prace@adminit.cz.

Našli jste v článku chybu?