EN
← wszystkie wpisy

Homelab od zera, cz. 2: AdGuard Home w Dockerze dla całego domu

Docker, Portainer i AdGuard Home na Ubuntu Server, zwolnienie portu 53, podpięcie DNS w routerze i listy CERT Polska. Filtr reklam, śledzenia i phishingu dla każdego urządzenia w domu, bez instalowania czegokolwiek na telefonach.

  • homelab
  • Docker
  • DNS
  • AdGuard
Read in English

W części 1 postawiliśmy Ubuntu Server ze stałym adresem 192.168.0.200 i dostępem przez SSH. Teraz instalujemy na nim AdGuard Home, czyli serwer DNS, który zanim wyśle zapytanie dalej, sprawdza je z listami reklam, trackerów i złośliwych domen. Jeśli domena jest na liście, urządzenie po prostu jej nie dostanie.

Najlepsze jest to, że działa to na wszystkich urządzeniach w sieci naraz: telefonie mamy, tablecie babci, telewizorze. Nikt nic nie instaluje, a strona phishingowa podszywająca się pod bank nawet się nie otworzy.

Jak to zainstalować?

Mamy kilka opcji: AdGuarda można zainstalować bezpośrednio w systemie albo w kontenerze Dockera. Ja lubię pracować z Dockerem, więc pokażę tę drogę. Jest odrobinę bardziej skomplikowana, ale sprzedam wam protip: użyjemy Portainera, czyli graficznej nakładki na Dockera działającej w przeglądarce. Dzięki niemu kolejne usługi stawia się kilkoma kliknięciami.

Krok 1: Docker

Najpierw czyścimy ekran po aktualizacji z części 1 komendą clear (kurczę, jak mi tu brakuje zsh). Większość komend jest na stronie Dockera. Na początek upewniamy się, że mamy curl:

sudo apt install -y curl

U mnie curl już był, więc przechodzimy do meritum. Docker ma oficjalny skrypt instalacyjny:

curl -fsSL https://get.docker.com | sudo sh
Terminal: apt install curl, a potem uruchomienie skryptu get.docker.com
Oficjalny skrypt Dockera dodaje repozytorium i klucz GPG, a potem instaluje paczki.

Uwaga z punktu widzenia bezpieczeństwa: curl ... | sudo sh oznacza „pobierz skrypt z internetu i od razu uruchom go jako root”. Tu jest to oficjalny skrypt Dockera pobierany po HTTPS, więc ryzyko jest małe, ale dobrze wiedzieć, co się robi. Ostrożniejsza wersja to pobrać skrypt, przeczytać go i dopiero wtedy uruchomić:

curl -fsSL https://get.docker.com -o get-docker.sh
less get-docker.sh
sudo sh get-docker.sh

Instalacja się powiodła. Zwróćcie uwagę na ostrzeżenie na końcu: dostęp do Dockera to praktycznie dostęp do roota na całej maszynie.

Koniec instalacji Dockera z ostrzeżeniem, że dostęp do demona Dockera jest równoważny dostępowi roota
Docker sam ostrzega: dostęp do demona = root.

Żeby nie pisać sudo przed każdą komendą Dockera, dodajemy naszego użytkownika do grupy docker, a newgrp od razu odświeża uprawnienia bez wylogowania:

sudo usermod -aG docker $USER
newgrp docker

Pamiętajcie, że przez wspomniane ostrzeżenie grupa docker to w praktyce drugie sudo, więc dodajemy do niej tylko siebie. Sprawdzamy, czy wszystko działa:

docker run --rm hello-world
docker compose version
Terminal: usermod, newgrp, uruchomienie hello-world i komunikat Hello from Docker
Hello from Docker! Działa, jesteśmy w domu.

Krok 2: Portainer

Tworzymy folder dla naszego kolegi i plik docker-compose.yml:

mkdir -p ~/portainer && cd ~/portainer
nano docker-compose.yml

Ja odruchowo wpisałem tu sudo nano, ale w katalogu domowym sudo nie jest potrzebne, a tylko sprawia, że plik należy do roota. Wklejamy:

services:
  portainer:
    image: portainer/portainer-ce:lts
    container_name: portainer
    restart: unless-stopped
    ports:
      - "9443:9443"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - ./data:/data
Edytor nano z plikiem docker-compose.yml dla Portainera
Plik dla Portainera. Ctrl+S zapisuje, Ctrl+X wychodzi.

Co tu się dzieje:

  • portainer-ce:lts to darmowa wersja Community Edition z gałęzi długoterminowej (LTS);
  • restart: unless-stopped oznacza, że kontener wstanie sam po restarcie serwera;
  • 9443 to port panelu (HTTPS);
  • docker.sock daje Portainerowi kontrolę nad Dockerem, a więc nad całą maszyną. Dlatego hasło do Portainera musi być naprawdę mocne;
  • ./data to miejsce, w którym Portainer trzyma swoje ustawienia, żeby przetrwały aktualizację kontenera.

Zapisujemy (Ctrl+S), wychodzimy (Ctrl+X) i wrzucamy Portainera do gry:

docker compose up -d
docker ps
Terminal: docker compose up -d i docker ps z działającym kontenerem portainer
Kontener portainer działa i nasłuchuje na 9443.

Pierwsze logowanie

W przeglądarce wchodzę na 192.168.0.200:9443 i widzę:

Przeglądarka z komunikatem Client sent an HTTP request to an HTTPS server
Przeglądarka spróbowała po HTTP, a Portainer mówi tylko po HTTPS.

Spokojnie, trzeba tylko dopisać protokół: https://192.168.0.200:9443. Teraz przeglądarka ostrzeże, że połączenie nie jest prywatne. To dlatego, że Portainer używa certyfikatu samopodpisanego, którego nie wystawił żaden zaufany urząd. W naszej własnej sieci i na naszym własnym serwerze możemy kliknąć Zaawansowane → Przejdź do witryny. Na obcych stronach takiego ostrzeżenia nie ignorujemy.

Ostrzeżenie przeglądarki Twoje połączenie nie jest prywatne, NET::ERR_CERT_AUTHORITY_INVALID
Certyfikat samopodpisany. Na własnym serwerze w LAN można przejść dalej.

Pokazuje się logo Portainera i ustawiamy hasło dla admina, minimum 12 znaków. Portainer prosi też o setup token, który znajdziemy w logach kontenera:

docker logs portainer

Token jest w ramce z =====, kopiujemy go i wklejamy. Przy okazji, na zrzucie poniżej widać, że najpierw wpisałem docker ps portainer, co oczywiście nie działa. Logi to docker logs.

Formularz New Portainer installation: użytkownik admin, hasło i pole setup token
Konto admina i setup token z logów (zamaskowany).
Logi Portainera z ramką setup_token, komunikatem o timeoucie i komendą docker restart portainer
Token jest w ramce. Niżej widać timeout po 5 minutach, moje „docker ps portainer” i restart.

I wpadka: za długo zwlekałem i dostałem timeout. Ze względów bezpieczeństwa świeżo postawiony Portainer czeka na utworzenie konta admina tylko 5 minut, żeby nikt w sieci nie zdążył go przejąć przed nami. Restartujemy kontener i powtarzamy kroki z nowym tokenem:

docker restart portainer
Komunikat Portainera: instancja wyłączona ze względów bezpieczeństwa po przekroczeniu czasu
Za wolno. Po restarcie dostajemy nowe 5 minut i nowy token.

Po zalogowaniu kreator zapyta o środowisko. Klikamy Get Started, czyli lokalnego Dockera, na którym działa sam Portainer. Opcje Edge pomijamy: służą do zarządzania urządzeniami za NAT-em, a my mamy wszystko w jednej sieci LAN. Zawsze można to dokonfigurować później.

Kreator środowiska w Portainerze z opcjami Get Started i Add Environments
Get Started = lokalny Docker.

Krok 3: zwolnienie portu 53

Serwer DNS działa na porcie 53. W Ubuntu ten port zajmuje już systemd-resolved, czyli systemowy pośrednik DNS nasłuchujący na 127.0.0.53. Jeśli go nie wyłączymy, AdGuard nie będzie mógł zająć portu. Wpisujemy ten stos komend:

sudo mkdir -p /etc/systemd/resolved.conf.d
printf "[Resolve]\nDNS=127.0.0.1\nDNSStubListener=no\n" | sudo tee /etc/systemd/resolved.conf.d/adguard.conf
sudo mv /etc/resolv.conf /etc/resolv.conf.bak
sudo ln -s /run/systemd/resolve/resolv.conf /etc/resolv.conf
sudo systemctl restart systemd-resolved
sudo mkdir -p /opt/adguardhome/work /opt/adguardhome/conf

Po kolei:

  1. tworzymy katalog na dodatkową konfigurację systemd-resolved;
  2. w pliku adguard.conf wyłączamy nasłuch na porcie 53 (DNSStubListener=no) i mówimy systemowi, żeby sam też pytał AdGuarda (DNS=127.0.0.1);
  3. robimy kopię starego /etc/resolv.conf;
  4. podmieniamy go na plik z prawdziwymi serwerami DNS zamiast nieistniejącego już pośrednika;
  5. restartujemy usługę, żeby zmiany weszły;
  6. tworzymy katalogi na dane i konfigurację AdGuarda.
Terminal z komendami zwalniającymi port 53 i zawartością pliku adguard.conf
Zwalniamy port 53 dla AdGuarda.

Dla pewności sprawdzamy, czy na porcie 53 nic już nie nasłuchuje. Komenda nie powinna nic zwrócić:

sudo ss -tulpn | grep :53

Krok 4: AdGuard Home jako stack w Portainerze

W Portainerze wybieramy środowisko local, wchodzimy w Stacks i po prawej klikamy Add stack. Nazywamy go adguard, a w Web editor wklejamy:

services:
  adguardhome:
    image: adguard/adguardhome:latest
    container_name: adguardhome
    restart: unless-stopped
    network_mode: host
    volumes:
      - /opt/adguardhome/work:/opt/adguardhome/work
      - /opt/adguardhome/conf:/opt/adguardhome/conf
Portainer, tworzenie stacka adguard w Web editorze z plikiem compose
Stack to po prostu docker-compose wklejony w przeglądarce.

network_mode: host oznacza, że kontener korzysta bezpośrednio z sieci serwera, bez przekierowywania portów. Dla serwera DNS to najprostsze rozwiązanie: AdGuard sam zajmuje port 53 i widzi prawdziwe adresy urządzeń, które o coś pytają, więc w statystykach wiemy, kto co odpytuje. Katalogi w volumes sprawiają, że konfiguracja i statystyki przetrwają aktualizację kontenera.

Mała uwaga: :latest oznacza zawsze najnowszą wersję. W domu jest to wygodne, ale na poważnym serwerze lepiej przypiąć konkretną wersję, żeby aktualizacja nie przyszła z zaskoczenia.

Klikamy Deploy the stack na samym dole. Stack ma status running, więc jest klasa światowa, ekstraliga.

Szczegóły stacka adguard w Portainerze z kontenerem adguardhome w stanie running
Kontener adguardhome działa.

Krok 5: konfiguracja AdGuarda

Żeby dostać się do AdGuarda, otwieramy w przeglądarce adres serwera z portem 3000, czyli u mnie http://192.168.0.200:3000. Wita nas kreator konfiguracji. Przeklikujemy go, ale w drugim kroku zatrzymajmy się na chwilę:

  • Serwer DNS: wszystkie interfejsy, port 53. Tego nie zmieniamy;
  • Interfejs administratora: tu wybieramy, na jakim porcie będzie panel po instalacji. Domyślnie kreator proponuje 80. Jeśli planujecie postawić na tym serwerze więcej usług z interfejsem webowym, lepiej zostawić port 80 wolny i dać panelowi inny port, na przykład 3000.
Kreator AdGuard Home: interfejs administratora i serwer DNS na wszystkich interfejsach, port DNS 53
Port panelu i port DNS. DNS zostaje na 53.

Potem ustawiamy login i hasło do panelu. Nawet jeśli AdGuard jest dostępny tylko w sieci lokalnej, nie chcemy, żeby ktokolwiek z domowników albo gości mógł w nim grzebać. Na koniec otwieramy panel dowodzenia. Na razie wszystkie statystyki są zerowe, bo nikt jeszcze z niego nie korzysta.

Krok 6: cały dom przez AdGuarda

Teraz najważniejsze: musimy sprawić, żeby wszystkie urządzenia pytały o DNS nasz serwer. Robimy to w routerze, bo to router przez DHCP rozdaje urządzeniom adresy i serwery DNS.

Ja mam TP-Linka Archer AX55, więc loguję się na 192.168.0.1 i wchodzę w Advanced → Network → DHCP Server. Tam ustawiam:

  • Primary DNS: 192.168.0.200
  • Secondary DNS: 192.168.0.200

U was panel routera może wyglądać inaczej, ale szukajcie ustawień DHCP i pól DNS.

Dlaczego dwa razy ten sam adres? Gdybym jako zapasowy wpisał publiczny DNS, np. 1.1.1.1, urządzenia czasem pytałyby właśnie jego i reklamy zaczęłyby przeciekać. Ceną jest to, że jeśli serwer padnie, cały dom straci internet, bo nic nie przetłumaczy nazw domen. U mnie host z Fedorą działa 24/7, więc to akceptuję, ale miejcie to z tyłu głowy, zanim wyłączycie komputer, na którym stoi AdGuard.

Potem trzeba zrestartować router (nie resetować, bo przycisk reset przywraca ustawienia fabryczne!) albo po prostu odłączyć i podłączyć urządzenia do sieci, żeby pobrały nowe ustawienia.

Szybki test na jednym komputerze

Jeśli nie chcecie jeszcze ruszać routera, możecie najpierw sprawdzić AdGuarda na jednym komputerze z Windowsem: Ustawienia → Sieć i Internet → Ethernet (albo Wi-Fi) → Przypisanie serwera DNS → Edytuj i wpisujecie adres serwera.

Ustawienia Windows Ethernet: ręczne serwery DNS 192.168.0.200
DNS ustawiony ręcznie na jednym komputerze.

Krok 7: listy filtrów

Domyślna lista AdGuarda jest dobra na start, ale dorzućmy polskie. W panelu wchodzimy w Filtry → Listy zablokowanych DNS → Dodaj listę zablokowanych → Wybierz z listy i w sekcji regionalnej zaznaczamy:

  • POL: CERT Polska List of malicious domains: lista domen phishingowych i złośliwych prowadzona przez CERT Polska. To ona chroni bliskich przed fałszywymi stronami banków, kurierów i „dopłatami do paczki”;
  • POL: Polish filters for Pi-hole: polskie domeny reklamowe i śledzące.
Okno Wybierz listy zablokowanych z zaznaczonymi listami CERT Polska i Polish filters for Pi-hole
Polskie listy: CERT Polska i Polish filters for Pi-hole.

Resztę dobierzcie według uznania. Każda lista to więcej blokowania, ale też większa szansa, że coś potrzebnego przestanie działać. Wtedy w Dzienniku zapytań łatwo znaleźć winną domenę i dodać ją do wyjątków.

Efekt

Wchodzę na mocno zareklamowaną stronę, np. Onet, i nie ma reklam. Po kilku minutach statystyki wyglądały tak:

Panel AdGuard Home: 237 zapytań DNS, 17 zablokowanych, najczęściej blokowane domeny reklamowe i śledzące Onetu
Jeden komputer, kilka minut: 237 zapytań, 17 zablokowanych.
  • 237 zapytań DNS, z czego 17 (7%) zablokowanych, choć korzystał z niego praktycznie jeden komputer;
  • wśród zablokowanych domen są m.in. onet.hit.gemius.pl (statystyki i śledzenie), events.ocdn.eu i csr.onet.pl;
  • średni czas odpowiedzi wyniósł 15 ms, więc nikt nie odczuje spowolnienia.

A to jedno urządzenie i kilka minut. Przy całym domu i całym dniu robi się z tego naprawdę dużo.

Co dalej

Pobawcie się. List jest mnóstwo, a dziennik zapytań świetnie pokazuje, co wasze urządzenia robią w tle. Czasem można się zdziwić, gdzie dzwoni telewizor.

Ja mam do tego jeszcze VPN na Tailscale: niezależnie od tego, gdzie jestem, telefon łączy się z moją siecią domową i zapytania DNS idą do mojego AdGuarda, więc reklamy są wycinane nawet poza domem. Jak to ustawić, żeby być chronionym 24/7, pokażę w następnym wpisie. Na liście czekają jeszcze klucze SSH zamiast haseł i liczenie podsieci.

Pamiętajcie: jeśli możecie, chrońcie swoich bliskich. Nie każdy ma czas i ochotę siedzieć w komputerach, każdy ma inne zainteresowania. Ale każdy zasługuje na to, żeby nie wpaść na fałszywą stronę banku.

← wszystkie wpisy