Skip to main content

        Cloudflare: Creación de un gestor DNS autoalojado bajo Zero-Trust - Featured image

Cloudflare: Creación de un gestor DNS autoalojado bajo Zero-Trust

Administrar los registros DNS de tu dominio personal o homelab puede volverse un poco tedioso si tienes que entrar constantemente al panel oficial de Cloudflare. Además, el panel oficial te muestra todos tus registros críticos (como los TXT, MX, DKIM, DMARC), lo que aumenta el riesgo de cometer algún error y borrar algo importante por accidente.

Adicionalmente, existía el reto de mantener actualizados los registros tipo A que apuntan a los servicios de mi homelab, ya que mi ISP cambia mi dirección IP pública cada cierto tiempo. Durante años, dependí del cliente de DDNS integrado en mi firewall Sophos para realizar esta tarea. Sin embargo, hace poco dejó de funcionar porque Sophos requiere usar la Global API Key de Cloudflare (junto con el correo de la cuenta) para autenticarse. Cloudflare ya considera este método obsoleto y recomienda migrar a los API Tokens. Tener que darle a un equipo de terceros tu llave global (que básicamente tiene acceso a toda tu cuenta de Cloudflare) ya no es una buena práctica de seguridad.

Para solucionar tanto la exposición de registros críticos como el fallo de la actualización de IP, decidí construir mi propio Cloudflare DNS GUI: una pequeña aplicación web autoalojada en Docker (NodeJS + Vanilla JS) que usa la API de Cloudflare para gestionar únicamente los registros que de verdad uso en mi día a día (A y CNAME).

Más allá de la funcionalidad, quise enfocarme en la seguridad. Quería construir esta herramienta aplicando el concepto de Zero Trust.

A continuación, detallo algunas de las configuraciones de seguridad que le apliqué al proyecto.


1. Zero Trust: Autenticación MFA por Acción (Step-up Authentication)

Lo normal en muchas plataformas es pedirte el código de 6 dígitos (Google Authenticator o Authy) al iniciar sesión y después confiar ciegamente en tu sesión. Incluso Cloudflare lo hace de esa manera.

Para este panel, implementé MFA basado en acciones (Step-up Authentication). Si alguien lograra robar mi cookie de sesión, o si simplemente dejo la computadora desbloqueada, la persona podría ver el panel, pero no hacer ningún cambio.

Cada vez que intento editar, borrar o cambiar el Proxy de un registro, el backend rechaza la petición a menos que adjunte el código de 6 dígitos actual generado por mi teléfono. Así me aseguro de que nadie pueda modificar los registros sin tener mi teléfono a la mano.


2. Principio de Mínimo Privilegio (PoLP) en la Interfaz

Un clic por error en un registro TXT o MX te puede arruinar la configuración del correo del dominio entero. Aplicando el principio de mínimo privilegio, hice que el backend simplemente filtre y oculte todo lo que no sea un registro tipo A o CNAME.

La aplicación solo funciona con estos dos tipos de registros. Si por alguna razón el panel se viera comprometido, el impacto sería menor porque los registros más importantes ni siquiera son accesibles desde ahí.


3. Cierre de Sesión Estricto (Hard TTL)

Muchos portales mantienen tu sesión viva mientras sigas moviendo el mouse. Para evitar dejar ventanas abiertas, le puse un tiempo de expiración (TTL) estricto de 5 minutos:

  • En el Frontend: Un contador visual muestra el tiempo restante y cierra la sesión forzosamente al llegar a cero, sin importar lo que estés haciendo.
  • En el Backend: La cookie auth_token caduca en el servidor exactamente a los 5 minutos.
  • No hay opción de extender el tiempo; la idea es entrar, hacer el cambio de DNS y salir inmediatamente.


4. Rate Limiting y Protección MFA

Aunque el servicio corre en mi red privada y no está expuesto a internet, protegí el login contra ataques de fuerza bruta usando express-rate-limit. El servidor bloquea automáticamente la IP si hay 10 intentos fallidos de login.

Además, agregué una función para que si alguien (ya autenticado) falla el código OTP 3 veces seguidas al querer hacer un cambio, la sesión se cierre por completo por precaución.


5. Configuración del Contenedor Docker (No-Root)

Un fallo de seguridad común es correr contenedores de Docker como root. Para evitar esto, configuré el Dockerfile para que use un usuario sin privilegios llamado node (UID 1000).

# Crear el directorio y transferir permisos
RUN mkdir -p /app/data && chown -R node:node /app
# Transición al usuario sin privilegios
USER node
# Los archivos copiados también deben pertenecer al usuario 'node'
COPY --chown=node:node server.js ./
COPY --chown=node:node public ./public

En el host, los permisos de la carpeta donde se guarda la base de datos de sesiones (/app/data) están asignados estrictamente al UID 1000. De esta manera, si llegara a existir una vulnerabilidad en NodeJS y alguien logra acceder al contenedor, se quedará atrapado con un usuario que no tiene permisos en el sistema.


6. Motor DDNS Integrado

Para no volver a depender de Sophos, integré un script de actualización dinámica (DDNS) en el mismo panel de NodeJS. El funcionamiento es muy sencillo:

  1. Si quiero que un subdominio apunte siempre a la IP pública de mi casa, solo marco la casilla de Monitoring (lo cual me pide confirmar con mi código MFA).

  2. El servidor revisa periódicamente mi dirección IP pública. Si nota que cambió, actualiza automáticamente el registro en Cloudflare usando un API Token que solo tiene permisos para editar esa zona DNS específica.

  3. Cualquier registro que se esté monitoreando se resalta en verde en la interfaz, para poder identificar rápido qué subdominios están vinculados a la red de mi casa.

  4. En la parte inferior agregué un área de Logs del Sistema, donde puedo ver un historial de cuándo cambió mi IP y si la actualización de DNS fue exitosa.



7. Instalación Rápida (Quick Start)

He publicado la imagen lista para producción en Docker Hub. Puedes encontrarla aquí: mxlit/cloudflare-dns-gui en Docker Hub.

Opción 1: Docker Compose (Recomendado para Producción)

El método más seguro para evitar que tus contraseñas queden guardadas en el historial de tu terminal es usar Docker Compose con un archivo .env.

  1. Crea un archivo llamado .env con tus secretos:

    API_TOKEN=tu_api_token_restringido_de_cloudflare
    PASSWORD=tu_contraseña_maestra_segura
    
  2. Crea un archivo docker-compose.yml:

    services:
      cloudflare-dns-gui:
        image: mxlit/cloudflare-dns-gui:latest
        container_name: cloudflare-dns-gui
        ports:
          - "3000:3000"
        env_file:
          - .env
        volumes:
          - ./data:/app/data
        restart: unless-stopped
    
  3. Inícialo:

    docker compose up -d
    

Opción 2: Docker Run

Si prefieres un solo comando (Advertencia: tus secretos quedarán guardados en el historial de tu terminal), puedes pasarlos mediante flags -e:

docker run -d \
  --name cloudflare-dns-gui \
  -p 3000:3000 \
  -e API_TOKEN="tu_api_token_restringido_de_cloudflare" \
  -e PASSWORD="tu_contraseña_maestra_segura" \
  -v /ruta/a/tus/datos:/app/data \
  --restart unless-stopped \
  mxlit/cloudflare-dns-gui:latest

Variables de Entorno

  • API_TOKEN: Créalo en Cloudflare. Solo necesita permisos de Zone.DNS (Edición) para tus zonas específicas.
  • PASSWORD: La contraseña maestra para iniciar sesión en la interfaz web.

Volúmenes

  • /app/data: Almacena tu monitored.json (objetivos DDNS) y mfa.json (tu secreto OTP). Asegúrate de que el directorio host pertenezca al UID 1000 (chown -R 1000:1000 /ruta/a/tus/datos).

Configuración de MFA

  1. Inicia sesión en el panel con tu PASSWORD.
  2. En tu primer inicio de sesión, se mostrará un código QR.
  3. Escanéalo con tu aplicación de Autenticador (Google Authenticator o Authy).
  4. Ingresa el código para verificar. A partir de entonces, todas las acciones destructivas requerirán un token.

Conclusión

Desarrollar herramientas para nuestro Homelab no significa que debamos descuidar la seguridad. Aplicando MFA por acción, tokens de API con permisos reducidos y asegurando bien los contenedores, podemos tener paneles muy seguros para nuestra red.

¿Qué medidas de seguridad o buenas prácticas aplicas tú en tus servicios autoalojados?