Skip to main content

Zabezpečený DNS server s Pi-hole a DoH + záložní DNS2 Raspberry PI5

Příprava prostředí

Nejprve vytvoříme potřebnou adresářovou strukturu na obou serverech:

# Vytvoření hlavního adresáře pro DNS server
sudo mkdir -p /opt/dns-server
sudo mkdir -p /opt/dns-server/pihole
sudo mkdir -p /opt/dns-server/unbound
sudo mkdir -p /opt/dns-server/cloudflared
Docker Compose pro DNS server

Vytvořme soubor /opt/dns-server/docker-compose.yml pro každý server. Začneme s první instancí (dns1.zelina.eu - 192.168.54.2):

version: '3'

services:
  # Unbound pro rekurzivní DNS a keš
  unbound:
    container_name: unbound
    image: klutchell/unbound:latest
    restart: unless-stopped
    hostname: unbound
    volumes:
      - /opt/dns-server/unbound/conf:/opt/unbound/etc/unbound/
    networks:
      dns_net:
        ipv4_address: 172.20.0.2

  # Cloudflared pro DNS-over-HTTPS
  cloudflared:
    container_name: cloudflared
    image: cloudflare/cloudflared:latest
    restart: unless-stopped
    command: proxy-dns --address 0.0.0.0 --port 5053 --upstream https://1.1.1.1/dns-query --upstream https://1.0.0.1/dns-query
    networks:
      dns_net:
        ipv4_address: 172.20.0.3

  # Pi-hole jako DNS filtrovací nástroj
  pihole:
    container_name: pihole
    image: pihole/pihole:latest
    restart: unless-stopped
    hostname: 192.168.54.2    # Změnit na 19*2.168.54.3 - dns2.zelina.eu pro druhý server
    ports:
      - "53:53/tcp"
      - "53:53/udp"
      - "80:80/tcp"
    environment:
      TZ: 'Europe/Prague'
      WEBPASSWORD: 'BezpecneHeslo2024'    # Změnit na silné heslo
      DNS1: '172.20.0.2#53'    # Unbound
      DNS2: '172.20.0.3#5053'  # Cloudflared DoH
      ServerIP: '192.168.54.2' # Změnit na 192.168.54.3 pro druhý server
      HOSTNAME: 'dns1.zelina.eu' # Změnit na dns2.zelina.eu pro druhý server
      DNSMASQ_LISTENING: 'all'
      CACHE_SIZE: 10000
    volumes:
      - '/opt/dns-server/pihole/etc-pihole:/etc/pihole'
      - '/opt/dns-server/pihole/etc-dnsmasq.d:/etc/dnsmasq.d'
    depends_on:
      - unbound
      - cloudflared
    cap_add:
      - NET_ADMIN
    networks:
      dns_net:
        ipv4_address: 172.20.0.4
      
networks:
  dns_net:
    driver: bridge
    ipam:
      config:
        - subnet: 172.20.0.0/24
Konfigurace Unbound pro kešování

Potřebujeme vytvořit konfigurační soubor pro Unbound. Vytvoříme /opt/dns-server/unbound/conf/unbound.conf:

server:
    # Obecná nastavení
    verbosity: 1
    num-threads: 4
    interface: 0.0.0.0
    port: 53
    do-ip4: yes
    do-ip6: no
    do-udp: yes
    do-tcp: yes
    
    # Bezpečnost
    hide-identity: yes
    hide-version: yes
    harden-glue: yes
    harden-dnssec-stripped: yes
    use-caps-for-id: yes
    
    # Nastavení keše
    cache-min-ttl: 300
    cache-max-ttl: 86400
    prefetch: yes
    prefetch-key: yes
    msg-cache-size: 128m
    rrset-cache-size: 256m
    neg-cache-size: 32m
    
    # Výkon
    so-rcvbuf: 4m
    so-sndbuf: 4m
    edns-buffer-size: 1232
    max-udp-size: 1232
    
    # Kořenové servery
    root-hints: "/opt/unbound/etc/unbound/root.hints"
    
    # Přístup z lokální sítě
    access-control: 0.0.0.0/0 allow
    
    # DNSSEC validace
    auto-trust-anchor-file: "/opt/unbound/etc/unbound/root.key"
    
    # Optimalizace pro slabý hardware (RPi)
    outgoing-range: 512
    num-queries-per-thread: 512
    
    # Vylepšení soukromí
    qname-minimisation: yes
    
    # Zápis statistik
    statistics-interval: 600
    extended-statistics: yes
    statistics-cumulative: yes

remote-control:
    control-enable: no

Aktualizace root.hints

Pro správnou funkci Unbound potřebujeme stáhnout nejnovější root.hints soubor:

wget -O /opt/dns-server/unbound/conf/root.hints https://www.internic.net/domain/named.root

Cloudflared - DNS over HTTPS

Pro Cloudflared nepotřebujeme speciální konfigurační soubor, jelikož nastavení předáváme přímo jako parametry v docker-compose.yml.

Skript pro kontrolu a restart služeb

Vytvoříme skript, který bude kontrolovat funkčnost DNS serveru a případně jej restartovat:

#!/bin/bash
# Script pro kontrolu DNS serveru
# Umístit do /opt/dns-server/check-dns.sh

LOG_FILE="/opt/dns-server/dns-check.log"
HOSTNAME=$(hostname)
TIMESTAMP=$(date "+%Y-%m-%d %H:%M:%S")

# Funkce pro záznam do logu
log_message() {
    echo "${TIMESTAMP} - ${1}" >> ${LOG_FILE}
    echo "${1}"
}

# Kontrola, zda Pi-hole běží
pihole_check() {
    if docker ps | grep -q pihole; then
        log_message "Pi-hole běží správně."
    else
        log_message "Pi-hole neběží! Pokus o restart..."
        cd /opt/dns-server && docker-compose restart pihole
        sleep 10
        if docker ps | grep -q pihole; then
            log_message "Pi-hole byl úspěšně restartován."
        else
            log_message "Restart Pi-hole selhal! Spouštím kompletní restart DNS stacku..."
            cd /opt/dns-server && docker-compose down && docker-compose up -d
        fi
    fi
}

# Kontrola funkčnosti DNS
dns_check() {
    if dig @localhost google.com +short > /dev/null; then
        log_message "DNS resolving funguje správně."
    else
        log_message "DNS resolving nefunguje! Pokus o restart DNS stacku..."
        cd /opt/dns-server && docker-compose down && docker-compose up -d
    fi
}

# Hlavní kontrolní rutina
log_message "======= Začátek kontroly DNS serveru ($HOSTNAME) ======="
pihole_check
dns_check
log_message "======= Konec kontroly DNS serveru ======="

Nastavíme správná oprávnění a vytvoříme plánovanou úlohu cron:

sudo chmod +x /opt/dns-server/check-dns.sh
# Přidání do cronu pro spouštění každou hodinu
(crontab -l ; echo "0 * * * * /opt/dns-server/check-dns.sh") | crontab -

Záloha konfigurace

Vytvoříme skript pro pravidelné zálohování konfigurace:

#!/bin/bash
# Script pro zálohování DNS serveru
# Umístit do /opt/dns-server/backup-dns.sh

BACKUP_DIR="/opt/dns-server/backups"
TIMESTAMP=$(date "+%Y%m%d-%H%M%S")
HOSTNAME=$(hostname)
BACKUP_FILE="${BACKUP_DIR}/dns-backup-${HOSTNAME}-${TIMESTAMP}.tar.gz"

# Vytvoření adresáře pro zálohy, pokud neexistuje
mkdir -p ${BACKUP_DIR}

# Zápis do logu
echo "Zahajuji zálohu DNS konfigurace - ${TIMESTAMP}" >> ${BACKUP_DIR}/backup.log

# Vytvoření zálohy
tar -czf ${BACKUP_FILE} \
    /opt/dns-server/docker-compose.yml \
    /opt/dns-server/pihole \
    /opt/dns-server/unbound/conf \

# Výsledek
if [ $? -eq 0 ]; then
    echo "Záloha úspěšně vytvořena: ${BACKUP_FILE}" >> ${BACKUP_DIR}/backup.log
    
    # Odstranění starých záloh (starších než 30 dnů)
    find ${BACKUP_DIR} -name "dns-backup-*.tar.gz" -mtime +30 -delete
else
    echo "CHYBA: Vytvoření zálohy selhalo!" >> ${BACKUP_DIR}/backup.log
fi

Nastavíme oprávnění a přidáme do cronu pro týdenní zálohování:

sudo chmod +x /opt/dns-server/backup-dns.sh
# Přidání do cronu pro spouštění každou neděli ve 2:00
(crontab -l ; echo "0 2 * * 0 /opt/dns-server/backup-dns.sh") | crontab -

Spuštění DNS serveru

Spuštění Docker Compose stack:

cd /opt/dns-server
docker-compose up -d

Konfigurace pro druhý server (dns2.zelina.eu - 192.168.54.3)

Pro druhý server postupujeme stejně, jen upravíme následující hodnoty v docker-compose.yml:

  • hostname: dns2.zelina.eu (pro kontejner pihole)
  • HOSTNAME: 'dns2.zelina.eu' (v environment pro pihole)
  • ServerIP: '192.168.54.3' (v environment pro pihole)

 

Pro testování DNS je potřeba nainstalovat balíček dnsutils.

apt-get update && apt-get install -y dnsutils

 

Testování funkcí DNS serveru

Pro testování funkčnosti vytvořme jednoduchý testovací skript:

#!/bin/bash
# Skript pro testování DNS serveru
# Umístit do /opt/dns-server/test-dns.sh

# Barvy pro výstup
GREEN='\033[0;32m'
RED='\033[0;31m'
NC='\033[0m' # No Color

# Testovací domény
TEST_DOMAINS=("google.com" "facebook.com" "youtube.com" "github.com" "zelina.eu")
# Test blokované domény (známé reklamní domény)
BLOCKED_DOMAINS=("doubleclick.net" "googleadservices.com" "advertising.com")

echo "==== Testování DNS serveru ===="
echo "Testovací server: $(hostname)"
echo ""

# Test základních DNS dotazů
echo "1. Test základních DNS dotazů"
for domain in "${TEST_DOMAINS[@]}"; do
    echo -n "Dotaz na $domain: "
    if dig @localhost $domain +short > /dev/null; then
        echo -e "${GREEN}OK${NC}"
    else
        echo -e "${RED}SELHALO${NC}"
    fi
done
echo ""

# Test blokování reklam
echo "2. Test blokování reklam"
for domain in "${BLOCKED_DOMAINS[@]}"; do
    echo -n "Kontrola blokování $domain: "
    RESULT=$(dig @localhost $domain +short)
    if [[ "$RESULT" == *"0.0.0.0"* ]] || [[ -z "$RESULT" ]]; then
        echo -e "${GREEN}BLOKOVÁNO${NC}"
    else
        echo -e "${RED}NENÍ BLOKOVÁNO${NC}"
    fi
done
echo ""

# Test DNS over HTTPS
echo "3. Test DNS over HTTPS"
echo -n "Kontrola funkčnosti Cloudflared: "
if docker logs cloudflared 2>&1 | grep -q "Starting DNS over HTTPS proxy"; then
    echo -e "${GREEN}OK${NC}"
else
    echo -e "${RED}SELHALO${NC}"
fi
echo ""

# Test výkonu DNS
echo "4. Test výkonu DNS"
echo "Měření rychlosti DNS dotazů:"
time dig @localhost google.com > /dev/null
echo ""

# Test kešování
echo "5. Test DNS kešování"
echo "První dotaz (nekešovaný):"
time dig @localhost wikipedia.org > /dev/null
echo "Druhý dotaz (měl by být kešovaný):"
time dig @localhost wikipedia.org > /dev/null
echo ""

# Test zátěže
echo "6. Test zátěže (10 paralelních dotazů)"
for i in {1..10}; do
    dig @localhost example.com > /dev/null &
done
wait
echo -e "${GREEN}Test zátěže dokončen${NC}"
echo ""

echo "==== Test DNS serveru dokončen ===="

Nastavíme správná oprávnění:

sudo chmod +x /opt/dns-server/test-dns.sh

Dodatečné zabezpečení a vylepšení

Povolení DNSSEC v Pi-hole

Pro zapnutí DNSSEC v Pi-hole:

# Připojení do kontejneru Pi-hole
docker exec -it pihole bash

# Editace konfiguračního souboru
echo "dnssec-validate=yes" >> /etc/dnsmasq.d/01-pihole.conf

# Restart dnsmasq
pihole restartdns

Přidání blokačních listů

Můžeme přidat další blokační listy do Pi-hole pro lepší filtrování:

# Připojení do kontejneru Pi-hole
docker exec -it pihole bash

# Přidání blokačních listů (můžeš přidat přímo v rozhraní Pi-hole)
pihole -a adlist add https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts
pihole -a adlist add https://s3.amazonaws.com/lists.disconnect.me/simple_tracking.txt
pihole -a adlist add https://s3.amazonaws.com/lists.disconnect.me/simple_ad.txt

# Aktualizace blokačních listů
pihole -g

Nastavení replikace mezi servery

Pro zajištění synchronizace mezi oběma DNS servery vytvoříme skript pro replikaci nastavení z primárního na sekundární server:

#!/bin/bash
# Skript pro synchronizaci mezi primárním a sekundárním DNS serverem
# Umístit na primární server do /opt/dns-server/sync-dns.sh

PRIMARY_SERVER="dns1.zelina.eu"
SECONDARY_SERVER="dns2.zelina.eu"
SECONDARY_IP="192.168.54.3"
SSH_KEY="/root/.ssh/id_rsa"
SYNC_LOG="/opt/dns-server/sync.log"

# Kontrola, zda běží na primárním serveru
if [ "$(hostname)" != "$PRIMARY_SERVER" ]; then
    echo "Tento skript musí běžet na primárním serveru ($PRIMARY_SERVER)!"
    exit 1
fi

timestamp() {
    date "+%Y-%m-%d %H:%M:%S"
}

log() {
    echo "$(timestamp) - $1" >> "$SYNC_LOG"
    echo "$1"
}

log "Začátek synchronizace DNS serverů"

# Export Pi-hole nastavení a blokačních listů
log "Export nastavení Pi-hole..."
docker exec pihole pihole -a teleporter > /opt/dns-server/pihole-teleporter.tar.gz

# Synchronizace souborů na sekundární server
log "Synchronizace souborů na sekundární server..."
rsync -avz -e "ssh -i $SSH_KEY" \
    --exclude="backups" \
    --exclude="*.log" \
    /opt/dns-server/pihole-teleporter.tar.gz \
    /opt/dns-server/unbound/conf/ \
    root@$SECONDARY_IP:/opt/dns-server/

# Import nastavení na sekundárním serveru
log "Import nastavení na sekundárním serveru..."
ssh -i $SSH_KEY root@$SECONDARY_IP "docker exec -i pihole pihole -a -t /opt/dns-server/pihole-teleporter.tar.gz"

# Restart služeb na sekundárním serveru
log "Restart služeb na sekundárním serveru..."
ssh -i $SSH_KEY root@$SECONDARY_IP "cd /opt/dns-server && docker-compose restart"

log "Synchronizace DNS serverů dokončena"

Nastavíme oprávnění a přidáme do cronu pro denní synchronizaci:

sudo chmod +x /opt/dns-server/sync-dns.sh
# Přidání do cronu pro spouštění každý den ve 3:00
(crontab -l ; echo "0 3 * * * /opt/dns-server/sync-dns.sh") | crontab -

Pro automatickou synchronizaci musíme také nastavit SSH klíče mezi servery:

# Generování SSH klíčů na primárním serveru
ssh-keygen -t rsa -b 4096

# Kopírování klíče na sekundární server
ssh-copy-id root@192.168.54.3

Monitorování DNS serveru

Pro monitorování vytvoříme jednoduchý skript:

#!/bin/bash
# Skript pro monitorování DNS serveru
# Umístit do /opt/dns-server/monitor-dns.sh

MONITOR_LOG="/opt/dns-server/monitor.log"
HOSTNAME=$(hostname)
TIMESTAMP=$(date "+%Y-%m-%d %H:%M:%S")

# Záznam do logu
echo "===== DNS Monitor Report - $TIMESTAMP =====" >> $MONITOR_LOG

# Kontrola stavu kontejnerů
echo "Docker kontejnery:" >> $MONITOR_LOG
docker ps -a | grep -E 'pihole|unbound|cloudflared' >> $MONITOR_LOG
echo "" >> $MONITOR_LOG

# Pi-hole statistiky
echo "Pi-hole statistiky:" >> $MONITOR_LOG
docker exec pihole pihole -c >> $MONITOR_LOG
echo "" >> $MONITOR_LOG

# Využití systémových prostředků
echo "Vytížení CPU:" >> $MONITOR_LOG
top -bn1 | head -n 5 >> $MONITOR_LOG
echo "" >> $MONITOR_LOG

echo "Využití paměti:" >> $MONITOR_LOG
free -h >> $MONITOR_LOG
echo "" >> $MONITOR_LOG

echo "Využití disku:" >> $MONITOR_LOG
df -h /opt >> $MONITOR_LOG
echo "" >> $MONITOR_LOG

# Kontrola DNS dotazů
echo "Počet DNS dotazů za poslední hodinu:" >> $MONITOR_LOG
docker exec pihole sqlite3 /etc/pihole/pihole-FTL.db "SELECT COUNT(*) FROM queries WHERE timestamp >= strftime('%s', 'now', '-1 hour');" >> $MONITOR_LOG
echo "" >> $MONITOR_LOG

# Top 10 blokovaných domén
echo "Top 10 blokovaných domén:" >> $MONITOR_LOG
docker exec pihole sqlite3 /etc/pihole/pihole-FTL.db "SELECT domain, COUNT(*) FROM queries WHERE status IN (1,4,5,6,7,8,9,10,11) GROUP BY domain ORDER BY COUNT(*) DESC LIMIT 10;" >> $MONITOR_LOG
echo "" >> $MONITOR_LOG

# Top 10 povolených domén
echo "Top 10 povolených domén:" >> $MONITOR_LOG
docker exec pihole sqlite3 /etc/pihole/pihole-FTL.db "SELECT domain, COUNT(*) FROM queries WHERE status = 2 GROUP BY domain ORDER BY COUNT(*) DESC LIMIT 10;" >> $MONITOR_LOG
echo "" >> $MONITOR_LOG

echo "===== Konec DNS Monitor Report =====" >> $MONITOR_LOG
echo "" >> $MONITOR_LOG

# Omezení velikosti logu na posledních 100 kB
if [ -f "$MONITOR_LOG" ] && [ $(stat -c%s "$MONITOR_LOG") -gt 102400 ]; then
    tail -c 102400 "$MONITOR_LOG" > "$MONITOR_LOG.tmp" && mv "$MONITOR_LOG.tmp" "$MONITOR_LOG"
fi

Nastavíme oprávnění a přidáme do cronu pro hodinové monitorování:

sudo chmod +x /opt/dns-server/monitor-dns.sh
# Přidání do cronu pro spouštění každou hodinu
(crontab -l ; echo "30 * * * * /opt/dns-server/monitor-dns.sh") | crontab -

Přístup k webovému rozhraní Pi-hole

Pro přístup k Pi-hole rozhraní:

Výchozí heslo: BezpecneHeslo2024 (doporučuji změnit na silnější heslo v konfiguračním souboru docker-compose.yml a následně restartovat kontejner)

Nastavení klientů

Pro používání tvého DNS serveru je třeba nastavit DNS servery na klientech nebo přímo v routeru:

  • Primární DNS: 192.168.54.2
  • Sekundární DNS: 192.168.54.3

Testování funkčnosti

Po nastavení a spuštění obou serverů můžeš otestovat funkčnost pomocí následujících příkazů:

  1. Základní DNS dotaz:
dig @192.168.54.2 google.com
dig @192.168.54.3 google.com
  1. Test blokování reklam:
dig @192.168.54.2 doubleclick.net
  1. Test failover (vypnutí primárního serveru):
# Vypnout primární server nebo Docker na něm
# Testovat z klienta, který má nastaveny oba DNS servery
ping google.com
  1. Komplexní test pomocí vytvořeného testovacího skriptu:
/opt/dns-server/test-dns.sh

Doporučení pro dlouhodobý provoz

  1. Pravidelné aktualizace: Nastav automatické aktualizace kontejnerů pomocí Watchtower:
docker run -d \
  --name watchtower \
  --restart unless-stopped \
  -v /var/run/docker.sock:/var/run/docker.sock \
  containrrr/watchtower \
  --cleanup \
  --interval 86400 \
  pihole unbound cloudflared
  1. Monitorování výkonu: Přidej do monitorovacího skriptu odeslání upozornění při překročení limitů systémových prostředků.
  2. Rotace logů: Zajisti automatickou rotaci logů pro všechny vytvořené logy.
  3. Hardening serveru: Zabezpeč samotný Raspberry Pi správným nastavením firewallu a SSH přístupu.
  4. Zálohování:
    • Pravidelně zálohuj Pi-hole nastavení pomocí teleporter funkce
    • Zálohuj SD kartu nebo systémový disk pomocí nástroje jako je dd nebo rpi-clone

Tímto máš kompletní řešení pro dva zabezpečené DNS servery s Pi-hole, Unbound a Cloudflared pro DNS-over-HTTPS. Celé řešení běží v Dockeru, je redundantní, automaticky se zálohuje a monitoruje.