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:
portainer:# Unbound pro rekurzivní DNS a keš
unbound:
container_name: unbound
image: portainer/portainer-ce:klutchell/unbound:latest
container_name: portainer
restart: unless-stopped
hostname: unbound
volumes:
- /var/run/docker.sock:opt/dns-server/unbound/conf:/var/run/docker.sockopt/unbound/etc/unbound/
-networks:
portainer_data:/datadns_net:
ports:ipv4_address: -172.20.0.2
"9000:9000"# -Cloudflared "9443:9443"pro DNS-over-HTTPS
cloudflared:
container_name: cloudflared
image: cloudflare/cloudflared:latest
container_name: cloudflared
restart: unless-stopped
command: 'cloudflaredproxy-dns --no-autoupdateaddress tunnel run0.0.0.0 --tokenport YOUR_TUNNEL_TOKEN'5053 unbound:--upstream image:https://1.1.1.1/dns-query klutchell/unbound:latest--upstream https://1.0.0.1/dns-query
networks:
dns_net:
ipv4_address: 172.20.0.3
# ZměnaPi-hole najako ARMDNS kompatibilnífiltrovací imagenástroj
pihole:
container_name: unbound
restart: unless-stopped
volumes:
- unbound_data:/opt/unbound/etc/unbound
ports:
- "5335:53/tcp"
- "5335:53/udp"
pihole:pihole
image: pihole/pihole:latest
container_name: pihole
restart: unless-stopped
depends_on:hostname: 192.168.54.2 # Změnit na 19*2.168.54.3 - unbounddns2.zelina.eu -pro cloudflareddruhý server
ports:
- "53:53/tcp"
- "53:53/udp"
- "80:80/tcp"
environment:
TZ: 'Europe/Prague'
WEBPASSWORD: 'vaše_heslo'BezpecneHeslo2024' # Změňtenit na vlastnísilné heslo
DNS1: '127.172.20.0.0.1#5335'2#53' # Použití unbound jako upstream DNSUnbound
DNS2: 'no'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:
- pihole_data:'/opt/dns-server/pihole/etc-pihole:/etc/piholepihole'
- pihole_dnsmasq:'/opt/dns-server/pihole/etc-dnsmasq.d:/etc/dnsmasq.dd'
volumes:depends_on:
portainer_data:- unbound_data:unbound
pihole_data:- pihole_dnsmasq: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í:
- Primární server: http://192.168.54.2/admin
- Sekundární server: http://192.168.54.3/admin
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ů:
- Základní DNS dotaz:
dig @192.168.54.2 google.com
dig @192.168.54.3 google.com
- Test blokování reklam:
dig @192.168.54.2 doubleclick.net
- 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
- Komplexní test pomocí vytvořeného testovacího skriptu:
/opt/dns-server/test-dns.sh
Doporučení pro dlouhodobý provoz
- 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
- 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ů.
- Rotace logů: Zajisti automatickou rotaci logů pro všechny vytvořené logy.
- Hardening serveru: Zabezpeč samotný Raspberry Pi správným nastavením firewallu a SSH přístupu.
- 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
neborpi-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.