Knot DNS od verze 3.5 umožňuje provést externí validaci nové verze zóny před její aktualizací. Článek popisuje využití pro validaci (nejen) .CZ zóny.
Motivace
Před pěti lety jsme začali Knot DNS používat ke správě DNSSEC pro .CZ zónu.
Z procesu podepisování jsme odstranili veškeré in-house vyvíjené skripty a plně se spolehli na implementaci v Knot DNS. Praxe z reálného provozu potvrdila, že šlo o správné rozhodnutí. Snížila se komplexita systému podepisování, celý proces je velmi spolehlivý a zároveň zajišťuje kontrolu, že DNS data, která se posílají na veřejný anycast, jsou správně podepsaná, validní a konzistentní.
Proces validace jsme vyčlenili na samostatné validační servery zařazené mezi hidden master (HM) a veřejný anycast. Na nich běží také Knot DNS se zapnutou volbou
dnssec-validation: on
Tato funkce podchytí v podstatě většinu problémů, které se mohou vyskytnout při podepisování zóny (kompletní seznam validací je uvedený v dokumentaci), a díky inkrementálnímu zpracování je dopad na dobu propagace minimální. V procesu validace jsme ale chtěli podchytit i další rizika.
Jedním z nich je kontrola DNSSEC podpisů nejen v rámci zóny samotné, ale i vůči DS záznamu v nadřazené zóně. Druhým rizikem je potenciální chyba v implementaci samotného Knot DNS. Každý software může obsahovat chybu a používáme-li ten stejný jak pro podepisování tak pro validaci, je vhodné zařadit do kontrol druhou nezávislou implementaci jiným nástrojem.
Obojí lze podchytit právě pomocí externích validací.
Jak funguje externí validace
Na úvod je třeba zmínit, že externí validace není nutně náhrada za DNSSEC validaci nebo ZONEMD verifikaci, ale může fungovat vedle nich.
Na rozdíl od výše zmíněných možností se v praxi nejedná o aktivní ověřování ze strany Knot DNS. Místo toho Knot DNS zpřístupní novou verzi zóny (nebo její část) ve formě zónového souboru, zatímco sám si podrží verzi stávající. Následně pozastaví veškeré změnové operace (aktualizace zónového souboru, příchozí IXFR/AXFR, DNSSEC přepodepisování) a vyčká na příkaz zone-commit nebo zone-abort. Pokud ani jeden z nich neobdrží během nastaveného časového okna, aktualizace zóny neproběhne.
Konfigurace
Konfigurace externí validace sestává ze dvou částí. Nejprve je třeba nadefinovat parametry pro validaci ve vlastní sekci:
external:
- id: STR
timeout: TIME
dump-new-zone: STR
dump-removals: STR
dump-additions: STR
Z uvedených parametrů má výchozí hodnotu pouze timeout – uvádí se ve vteřinách a bez nastavení má hodnotu 300. Všechny tři dump parametry specifikují cestu souboru, do kterého se má uložit nová verze zóny (v případě dump-new-zone) nebo její část (pro zbylé dva). Výsledná konfigurace pak může vypadat například takto:
external:
- id: validate-zone
timeout: 300
dump-new-zone: "/var/lib/knot/validations/%s.zone"
Uvedená validace s ID validate-zone ukládá zónový soubor s názvem domena.zone do složky /var/lib/knot/validations a čeká na potvrzení po dobu pěti minut.
Následně se uvedená konfigurace přiřadí těm zónám, které mají procházet externí validací. Jedná se o parametr pro zónu, který lze aplikovat i na template:
zone:
- domain: DNAME
external-validation: external_id
...
Opět ještě konkrétní příklad:
template:
- id: validated-zones
storage: "/var/lib/knot/zones"
journal-max-usage: 8G
journal-max-depth: 10
master: hm-remote
dnssec-validation: on
external-validation: validate-zone
Všechny zóny využívající template validated-zones budou zkontrolovány interní DNSSEC validací a vyčkají na externí validaci.
Uvedená konfigurace je sice funkční, ale má jeden velký nedostatek. V současném stavu operátor není žádným způsobem informován o probíhajícím čekání na výsledek externí validace. Dle dokumentace existují dvě řešení. Buď kontrolovat stav zón pomocí knotc nebo využít D-Bus. Aby bylo možné použít D-Bus, je potřeba přidat odpovídající nastavení v sekci server:
server:
dbus-event: external-verify
Pokud je vše nakonfigurované správně, příchozí transfer zóny bude vypadat následovně:
[cz.] refresh, ... remote serial 1781259435, zone is outdated
[cz.] IXFR, incoming, ... started
[cz.] IXFR, incoming, ... finished, remote serial 1781259435, 0.00 seconds, 2 messages, 30183 bytes
[cz.] DNSSEC, incremental validation successful, checked RRSIGs 191
[cz.] waiting for external validation
[cz.] control, received command 'zone-commit'
[cz.] refresh, ... zone updated, 8.84 seconds, serial 1781257635 -> 1781259435, expires in 10783 seconds
První tři řádky odpovídají běžné aktualizaci zóny přes IXFR. Čtvrtý řádek informuje o úspěšném výsledku DNSSEC validace. Následně Knot DNS vyexportuje zónu a na D-Bus vygeneruje signál external_verify, čemuž odpovídá pátý řádek. Na dalším řádku je vidět, že validace proběhla úspěšně a změna zóny je potvrzena signálem zone-commit. Teprve poté dojde k aktualizaci zóny na straně Knot DNS.
Ovšem aby skutečně došlo ke zvalidování zóny, je nejprve nutné nějakým způsobem zpracovávat D-Bus zprávy.
D-Bus klient
Součástí oficiální dokumentace pro Knot DNS je i vzorová implementace D-Bus klienta. Základní struktura (v našem případě) Python skriptu určeného k nasazení jako systemd služba může vypadat například takto1:
import socket
import dasbus.connection
import dasbus.loop
import signal
import sys
import time
import subprocess
import os
import logging
if __name__ == '__main__':
bus = dasbus.connection.SystemMessageBus()
loop = dasbus.loop.EventLoop()
def handler_shutdown(signum, frame):
log.info(f"stopping validator")
loop.quit()
sys.exit(0)
signal.signal(signal.SIGTERM, handler_shutdown)
signal.signal(signal.SIGINT, handler_shutdown)
signal.signal(signal.SIGHUP, handler_shutdown)
def connect_to_signal(_signal, _callback):
bus.connection.signal_subscribe(
"cz.nic.knotd",
"cz.nic.knotd.events",
_signal,
"/cz/nic/knotd",
None,
0,
lambda _, sender, path, interface, signal, args: _callback(
sender, path, interface, signal, args.unpack()
),
)
connect_to_signal("external_verify", handler_external)
log.info("starting validator")
loop.run()
sys.exit(0)
Nejdůležitější je funkce connect_to_signal, která naváže specifikovanou funkci na konkrétní D-Bus signál a předá ji příslušné parametry. Samotná validace pak probíhá ve funkci handler_external. V našem případě nevalidujeme pouze .CZ zónu, ale také námi spravované zóny domén druhého řádu (SLD) a katalogové zóny. Funkce handler_external tedy slouží pouze jako rozcestník, který na základě typu zóny volá příslušnou validační funkci.
Kromě samotné validace je stěžejní její svázání s propsáním výsledku do Knot DNS:
def handler_external(sender, path, interface, signal, args):
(zone, serial) = args
if zone_is_valid(zone):
subprocess.run(["/usr/sbin/knotc", "zone-commit", zone])
else:
subprocess.run(["/usr/sbin/knotc", "zone-abort", zone])
A na závěr zbývá ještě příklad definice samotné systemd služby:
[Unit]
Description=Knot validator service
Requires=knot.service
After=knot.service
PartOf=knot.service
[Service]
Type=simple
ExecStart=/usr/bin/python3 /usr/sbin/knot-validator.py
Restart=on-failure
User=knot
Environment=PYTHONUNBUFFERED=1
TimeoutStopSec=5
SyslogIdentifier=knot-validator
[Install]
WantedBy=multi-user.target
Jeho hlavní a jedinou funkcí je spolu s Knot DNS spouštět i výše uvedený D-Bus klient.
Validace
Prováděné validace mohou být vcelku jakékoliv, ale musí proběhnout dřív, než vyprší timeout. Zvlášť je třeba mít na paměti, že značnou dobu trvá načtení samotné zóny, zejména při větším počtu záznamů. Z toho důvodu se většina validací pro .CZ zónu provádí ihned po vygenerování.
Na validátorech probíhá už pouze validace řetězce důvěry pro DNSSEC. K tomu stačí načíst apex zóny, kde se nachází relevantní DNSKEY záznamy. Následně se z NS záznamů nadřazené zóny získají adresy jejího autoritativního serveru, odkud se stáhnou DS záznamy pro validovanou zónu. Pak stačí porovnat stažené DS záznamy s těmi, které se vygenerují z DNSKEY záznamů a v případě (alespoň jedné) shody je řetězec důvěry potvrzen. Stejný způsob validace probíhá i pro vybrané SLD zóny v naší správě.
Validace katalogové zóny spočívá v ověření počtu a výskytu konkrétních member zón. Cílem je zamezit nechtěné propagaci prázdné katalogové zóny, což by mělo za důsledek rychlé odstranění příslušných zón z internetu.
Veškeré zpracování DNS probíhá přímo v Python skriptu za použití knihovny dnspython.
Monitoring
Žádoucí součástí procesu validace je monitoring. V našem případě používáme platformu Centreon, která vychází z platformy Nagios. Monitorování probíhá buď aktivním voláním lokálních skriptů na straně monitorovací platformy nebo tzv. pasivně, kdy se stav monitorované služby upravuje nastavením stavu skrz REST API. Právě druhý způsob lze poměrně snadno využít v kombinaci s vyhodnocováním D-Bus signálů.
Nejprve je vhodné upravit konfiguraci Knot DNS tak, aby kromě externí validace generoval signály informující o stavu aktualizace zóny:
server:
dbus-event: [ external-verify, zone-updated ]
Díky tomu je možné přidat novou dvojici funkcí, nezávislou na externí validaci:
if __name__ == '__main__':
# ...
connect_to_signal("zone_updated", handler_updated)
connect_to_signal("zone_not_updated", handler_not_updated)
Jejich obsahem je pak volání API monitorovací platformy a aktualizace stavu příslušné služby. Alternativně je možné využít už existující handler_external, a spolu s voláním knotc přidat i volání API monitorovací platformy.
Závěr
Knot DNS nabízí robustní a přímočarý způsob implementace externí validace. Díky použití souboru k uložení nové verze zóny (nebo jejích změn) je možné vhodným nastavením práv zcela oddělit validaci od DNS daemona a interakci omezit na předání signálu pro potvrzení nebo zamítnutí změny zóny.
Pokud někdo preferuje provádět validace vůči aktivnímu DNS serveru namísto zónového souboru, má tuto možnost také – může totiž své dotazy směřovat na primární server, ze kterého přišla nová verze zóny.
Kromě .CZ zóny používáme externí validace s výhodou také u domény gov.cz, kde je mj. nasazen zmiňovaný doménový katalog a dále je tato forma validace dostupná i pro TLD domény, pro které poskytujeme hosting na našem DNS anycastu.
Dodatečná validace zabere nějaký čas na zpracování oproti stavu bez ní, takže se může projevit na rychlosti propagace změn. Z našeho pohledu má ovšem prioritu validita DNS dat, stabilita a robustnost.