Chrootowany Unbound – lokalny resolver DNS trochę bezpieczniej
Na początku tego artykułu omówię czym jest i do czego służy Unbound. Jest to lekki, szybki i bezpieczny resolver DNS (Domain Name System) o otwartym kodzie źródłowym, stworzony głównie do lokalnego rozwiązywania nazw domen. Jest szeroko wykorzystywany jako lokalny serwer DNS, zarówno w środowiskach domowych, jak i w organizacjach, zapewniając prywatność, wydajność oraz kontrolę nad […]
Na początku tego artykułu omówię czym jest i do czego służy Unbound. Jest to lekki, szybki i bezpieczny resolver DNS (Domain Name System) o otwartym kodzie źródłowym, stworzony głównie do lokalnego rozwiązywania nazw domen. Jest szeroko wykorzystywany jako lokalny serwer DNS, zarówno w środowiskach domowych, jak i w organizacjach, zapewniając prywatność, wydajność oraz kontrolę nad procesem rozwiązywania nazw domen. Przetwarza on zapytania DNS, łącząc się bezpośrednio z serwerami DNS w hierarchii od root (serwery stref głównych) aż po serwery domen docelowych. Dzięki temu eliminuje konieczność korzystania z zewnętrznych serwerów cache DNS. Obsługuje szyfrowanie zapytań DNS za pomocą DNS-over-TLS (DoT) i DNS-over-HTTPS (DoH), co chroni przed podsłuchiwaniem i modyfikowaniem zapytań DNS przez osoby trzecie.
Unbound utrzymuje lokalną pamięć podręczną (cache) zapytań DNS, co znacznie przyspiesza odpowiedzi na często powtarzające się zapytania. Odpytujemy bowiem serwer lokalny, nie zdalny. Unbound jest dodatkowo zoptymalizowany do obsługi dużej liczby równoczesnych zapytań i skalowalny w różnych środowiskach. W kontekście bezpieczeństwa co jest tu również kluczowe Unbound obsługuje DNSSEC (DNS Security Extensions), co zapewnia weryfikację autentyczności i integralności odpowiedzi DNS. Jest odporny na różne ataki, takie jak ataki cache poisoning, które mogą zmienić kierunek ruchu internetowego na złośliwe serwery.
Wiemy już czym jest i do czego służy, w skrócie przypomnę tylko czym jest chroot i jakie ma znaczenie w kontekście lokalnego resolvera. Chrootowanie (ang. change root) to mechanizm w systemach operacyjnych Linux, który pozwala uruchomić aplikację w tzw. „chroot jail”, czyli w izolowanym środowisku plikowym. W praktyce oznacza to, że aplikacja widzi jako swój „root” (root directory) jedynie wybrany katalog, a nie pełną strukturę systemu plików. Dzięki temu ograniczamy dostęp aplikacji do zasobów systemowych, takich jak pliki konfiguracyjne, dane użytkowników czy systemowe narzędzia, które mogłyby zostać wykorzystane w razie kompromitacji aplikacji. Mechanizm ten jest często stosowany w celu zwiększenia bezpieczeństwa, szczególnie w przypadku usług sieciowych, które mogą być potencjalnym wektorem ataku.
W kontekście lokalnego resolvera DNS, chrootowanie pozwala zminimalizować ryzyko związane z potencjalnymi podatnościami w jego kodzie. Resolver DNS, taki jak BIND lub Unbound, działa na styku wewnętrznej sieci i Internetu, co czyni go atrakcyjnym celem dla atakujących. Gdyby jednak został uruchomiony w środowisku chroot, atakujący, który uzyska dostęp do usługi, nie będzie w stanie wyjść poza ograniczoną przestrzeń systemu plików. W takiej przestrzeni znajdują się jedynie pliki niezbędne do działania resolvera, co uniemożliwia dostęp do krytycznych plików systemowych, takich jak hasła czy klucze prywatne. Dzięki temu chroot staje się skutecznym narzędziem redukcji potencjalnych szkód w przypadku kompromitacji aplikacji.
Tyle w teorii, chroot nie jest bowiem rozwiązaniem doskonałym. Nie zabezpiecza przed wszystkimi możliwymi atakami i powinien działać z innymi mechanizmami bezpieczeństwa, ale jest to na pewno dobry punkt wyjścia.
W tym artykule pokażę jak przygotować i uruchomić takie środowisko na przykładzie systemu CentOS Stream 9 oraz na prostym przykładzie pokażę, że jest szybciej niż odpytywanie zdalnego DNS’a.
Aktualizujemy system i instalujemy serwis:
dnf -y update
dnf install -y unbound
Tworzymy strukturę katalogów i plików:
mkdir -p /var/chroot/unbound
mkdir -p /var/chroot/unbound/var/lib
mkdir -p /var/chroot/unbound/etc
mkdir -p /var/chroot/unbound/var/log
mkdir -p /var/chroot/unbound/var/run
touch /var/chroot/unbound/var/log/unbound.log
chown -R unbound:unbound /var/chroot/unbound/var/log/unbound.log
Edytujemy konfigurację:
nano /etc/unbound/unbound.conf
Szukamy i zmieniamy poszczególne parametry:
server:
chroot: „/var/chroot/unbound „
directory: „/var/lib/unbound”
logfile: „/var/log/unbound.log”
username: „unbound”
cp -R /etc/unbound /var/chroot/etc/
Ustawiamy uprawnienia:
chown -R unbound:unbound /var/chroot/unbound/var/lib/unbound/
Jeśli wszystko ustawiliśmy poprawnie to sprawdzenie konfiguracji powinno zwrócić „no errors”.
unbound-checkconf
Uaktywniamy serwis w systemie i restartujemy:
systemctl enable –now unbound
systemctl restart unbound
Testujemy konfigurację:
dnf -y install dnsutils
dig MariuszQ.0.0.1 wp.pl
Porównanie czasów między lokalnym resolverem a zewnętrznym DNS:
dig @8.8.8.8 wp.pl
dig @9.9.9.9 wp.pl
Tymczasem na lokalnym serwerze:
dig MariuszQ.0.0.1 wp.pl
Przy jednym zapytaniu być może nie ma to dużego znaczenia, ale jeśli utrzymujemy tu środowisko, które odpowiada za hostowanie aplikacji do której uderza ruch wielkości kilkudziesięciu tysięcy zapytań to mogą zrobić się naprawdę duże różnice, idąc w sekundach. Lokalny resolver DNS, np. nasz przykładowy unbound, znacząco poprawia szybkość ładowania stron internetowych dzięki redukcji opóźnień związanych z zapytaniami DNS do zewnętrznych serwerów. Unbound przechowując wyniki zapytań w lokalnej pamięci podręcznej (w RAMie), kolejne żądania dotyczące tych samych domen są obsługiwane natychmiast, co eliminuje czas potrzebny na komunikację sieciową. W porównaniu z zdalnymi resolverami, lokalny Unbound działa bardziej efektywnie i mniej obciąża sieć i zasoby.
Uruchomienie dodatkowo Unbound’a w środowisku chroot dodaje istotną dodatkową warstwę bezpieczeństwa, izolując resolver od reszty systemu operacyjnego. W przypadku potencjalnej podatności lub kompromitacji usługi, atakujący jest ograniczony do ściśle określonej przestrzeni plików, co zapobiega eskalacji ataków i dostępowi do kluczowych zasobów systemowych.