Saltar a contenido

Pi-hole

DNS local con filtrado de publicidad mediante Pi-hole Gravity.

Resumen

Pi-hole corre en k3s y es el DNS primario de la LAN: el DHCP de UniFi reparte 192.168.20.90 en las VLANs Default, Clientes, IoT, IPTV e Invitados, con 8.8.8.8/8.8.4.4 como respaldo. Filtra publicidad y trackers con Gravity y la adlist StevenBlack; el filtrado es de mejor esfuerzo porque los clientes pueden usar los resolvers públicos. Sustituyó al Pi-hole legacy de lebron (192.168.1.2), ya retirado.

Estado

Campo Valor
Estado Operativo
Argo CD Application pihole
Namespace pihole

Despliegue

Campo Valor
Método Manifests nativos
Chart / imagen pihole/pihole:2026.10.0
Origen clusters/luka/config/pihole/
Config en el repo clusters/luka/apps/pihole/ y clusters/luka/config/pihole/

Red

Campo Valor
Service pihole-dns (LoadBalancer) + pihole-web (ClusterIP)
IP 192.168.20.90 (MetalLB, DNS)
Puertos DNS 53/TCP y 53/UDP; web 80/TCP interno
URL interna https://pihole.local.gorkarevilla.com/admin/
URL externa — (sin ruta web externa)
TLS cert-manager wildcard *.local.gorkarevilla.com

El firewall de UniFi es el límite de seguridad entre redes. El LoadBalancer publica únicamente DNS por TCP/UDP 53; la web permanece en un ClusterIP y solo tiene ruta interna HTTPS, sin exposición web externa. La interfaz de Pi-hole v6 se sirve bajo /admin/; no requiere configurar FTLCONF_webserver_domain para esta ruta.

Acceso y usuarios

  • Usuarios/roles: administrador único para la interfaz web.
  • Credenciales: la contraseña web se gestiona en Vaultwarden (colección Homelab) → VKS → Secret pihole-web-password en el namespace pihole. Nunca se incluye el secreto en claro en esta documentación.

Configuración

  • Configuración mediante variables FTLCONF_* de Pi-hole v6:
  • FTLCONF_dns_upstreams=8.8.8.8;8.8.4.4.
  • FTLCONF_dns_listeningMode=all, necesario para atender consultas desde otras subredes; el firewall de UniFi determina qué redes pueden llegar.
  • FTLCONF_dhcp_active=false; DHCP sigue gestionado por UniFi.
  • Adlist predeterminada de StevenBlack.
  • FTLCONF_dns_revServers (conditional forwarding): las consultas PTR de las VLANs (192.168.1.0/24, .10, .20, .50, .70, .100) se reenvían al gateway UniFi de cada red (x.x.x.1) para resolver el nombre de cada cliente y mostrarlo en Query Log/estadísticas en vez de la IP.
  • FTLCONF_dns_queryLogging=true: fija explícitamente el logging de consultas (el default es true) para que el sidecar reciba las líneas query[...].
  • FTLCONF_misc_extraLogging=true: añade a cada línea de /var/log/pihole/pihole.log un serial y la IP/puerto del cliente. Es necesario para que la regex de Alloy extraiga client en todas las líneas, incluidos los bloqueos gravity blocked.
  • Un sidecar tail-pihole-log comparte un emptyDir de 256 Mi en /var/log/pihole, filtra solo query[...] y gravity blocked y los escribe a su stdout para que Alloy los recoja y los envíe a Loki. Corre como uid/gid 1000, non-root, con root FS de solo lectura, drop: ["ALL"] y seccomp RuntimeDefault.
  • El DHCP mantendrá .90 como DNS primario y 8.8.8.8/8.8.4.4 como resolvers públicos de respaldo. Algunos clientes pueden consultar los DNS públicos independientemente o en paralelo y eludir Gravity; el bloqueo es de mejor esfuerzo, no está impuesto a todos los clientes. No se añaden restricciones de firewall DNS ni se eliminan los backups públicos.
  • Una réplica (replicas: 1) con estrategia Recreate; la base SQLite de FTL no se replica.
  • securityContext.fsGroup: 1000 para el acceso al volumen.
  • El Service DNS usa externalTrafficPolicy: Local para preservar la IP real del cliente (con Cluster, kube-proxy hace SNAT y Pi-hole registraría a todos los clientes con la IP del nodo).

Almacenamiento

  • PVC pihole-config (Longhorn, 1 Gi) montado en /etc/pihole, para la configuración (pihole.toml) y la base SQLite de FTL (pihole-FTL.db). No hay HA ni replicación de Pi-hole/SQLite.

Dependencias

  • Depende de: MetalLB (IP DNS .90), Traefik (web interna), Longhorn (PVC), VKS (contraseña web), Alloy/Loki (envío del query log) y Grafana (dashboard).
  • Usado por: clientes de la LAN (DNS primario por DHCP de UniFi); el filtrado es de mejor esfuerzo porque permanecen los resolvers públicos.

Backup y recuperación

La configuración declarada se puede reconstruir desde Git. El estado y la base SQLite residen en el PVC; la instancia es única y no dispone de réplica.

Monitorización

La imagen oficial no proporciona un endpoint nativo de métricas Prometheus /metrics. No se instala ningún exporter y no hay scraping configurado.

Las consultas DNS se envían a Loki vía Alloy (ver Alloy) y se visualizan en el dashboard Pi-hole DNS (uid: pihole-dns, carpeta Homelab): detalle por consulta, filtros por dispositivo y dominio, top dominios/dispositivos y bloqueos. Requiere que el cliente use Pi-hole como DNS; los clientes con DoH o DNS público no aparecen.

El dashboard excluye el tráfico DNS de los pods del cluster (10.42.0.0/16) y de localhost; las estadísticas y el desplegable de dispositivos cubren solo clientes de la LAN.

Para verificar el flujo:

kubectl -n pihole logs deploy/pihole -c tail-pihole-log --tail=20 | grep 'query\['

y en Grafana/Loki: {namespace="pihole", client=~".+"} |= "query[" (o abrir el dashboard y comprobar que el desplegable device se puebla).

Referencias

Notas / TODO

  • Pendiente (opcional): exporter de métricas Prometheus y probes/resources en el Deployment.
  • Sin HA: una sola instancia; la redundancia requeriría dos Pi-holes y Orbital Sync como proyecto aparte.