Un panel propio para el homelab (adiós, Homarr)

Por qué cambié Homarr por un panel hecho a medida en Python: qué muestra, qué tareas puede lanzar, cómo está protegido y qué problemas salieron por el camino.

Desde junio la página de inicio del homelab era Homarr. Cumplía, pero se me quedaba corto: era sobre todo una colección de accesos directos con un par de indicadores, y para cualquier cosa más (ver cuándo fue la última copia de un contenedor, qué está bloqueando el DNS, quién está conectado a la VPN) tenía que abrir el panel de cada servicio. Así que he hecho uno propio, en Python, con el mismo aspecto que la web.

Qué quería

  • Ver datos de verdad, no solo un punto verde: CPU, memoria y disco de cada contenedor, las copias de seguridad, el DNS, la VPN, la nube y la vigilancia externa.
  • Lanzar tareas sin entrar en Proxmox: arrancar, apagar o reiniciar un contenedor, hacer un snapshot o una copia.
  • Consultar registros desde el mismo sitio.
  • Que se pareciera a la web y se pudiera usar también desde el móvil.

Qué muestra

La portada es una rejilla de tarjetas que se refrescan solas:

  • Proxmox: el nodo y cada contenedor con su CPU y memoria, con una pequeña gráfica de las últimas horas.
  • Copias de seguridad: la última copia de cada contenedor y si salió bien, y lo que queda libre en el disco de copias.
  • Uptime Kuma: el estado de cada monitor.
  • AdGuard Home: consultas del día, porcentaje bloqueado, los dominios más bloqueados y los clientes que más preguntan.
  • WireGuard: qué dispositivos están conectados y cuándo hablaron por última vez.
  • Nextcloud: versión, modo mantenimiento, usuarios activos y espacio libre.
  • Healthchecks.io: el estado de los latidos que el servidor manda hacia fuera.

Cada contenedor tiene además su propia página con el detalle, sus snapshots y sus últimas tareas. Hay una página de registros con el log del host, las tareas de Proxmox, las últimas consultas DNS y el registro del propio panel. Y abajo del todo siguen los accesos a los paneles de administración de cada servicio, que era lo único que de verdad usaba de Homarr.

Cómo encaja

Cómo llega una petición al panel y de dónde saca los datosEl navegador, desde casa o por la VPN, entra por HTTPS al proxy inverso, que reenvía al panel. El panel pide los datos por API, con credenciales propias, a Proxmox, Uptime Kuma, AdGuard Home, WireGuard, Nextcloud y Healthchecks.io.Mi navegadoren casa o por la VPNProxy inversoNPMplus · HTTPSPanelcontraseña + 2FAnombresolo élProxmoxCT · copias · logsUptime KumamonitoresAdGuardDNS y bloqueosWireGuardpares de la VPNNextcloudestado y espacioHealthcheckslatidos externosCada fuente, por API y con su propia credencial. Solo lectura, salvo las tareas de Proxmox y la pausa de AdGuard.
El panel solo acepta conexiones del proxy inverso, y cada servicio le da una credencial con lo justo.

Tareas, con red de seguridad

Desde la tarjeta de cada contenedor se puede arrancar, apagar, reiniciar, hacer un snapshot o lanzar una copia de seguridad, y desde la de AdGuard pausar la protección unos minutos. Como un clic equivocado aquí puede dejar la casa sin Internet, hay tres frenos:

  • El panel no puede apagarse a sí mismo. Su propio contenedor no tiene botones de energía.
  • Los contenedores críticos (DNS, proxy y VPN) piden escribir su número para confirmar, como hace GitHub al borrar un repositorio.
  • Todo queda registrado: quién, qué, cuándo y desde dónde, en un registro que también se ve en la página de registros.

Al lanzar una tarea, el panel abre su registro en un diálogo y lo va siguiendo hasta que acaba.

Cómo está hecho

  • Python con FastAPI en el servidor y plantillas Jinja2. La interactividad es HTMX: las tarjetas se piden al servidor ya pintadas y se reemplazan solas cada pocos segundos. No hay Node ni paso de compilación.
  • Un recolector en segundo plano consulta cada fuente con su propio intervalo (Proxmox cada diez segundos, Nextcloud cada cinco minutos) y guarda el último dato. La página nunca espera a una API lenta, y si una fuente falla su tarjeta lo dice sin tirar el resto.
  • Las gráficas son SVG generado en el servidor. Nada de librerías de gráficos.
  • El aspecto es el de la web: mismas fuentes, colores y tema claro/oscuro. En el móvil las tarjetas pasan a una columna.
  • Corre en un contenedor propio en Proxmox como servicio de systemd, con un usuario sin privilegios y el sistema de ficheros en solo lectura salvo su carpeta de registros.

Seguridad

Un panel que puede apagar contenedores es un objetivo apetecible, así que lo traté como tal:

  • Solo desde casa o por la VPN. Tiene un nombre interno que AdGuard resuelve hacia NPMplus, con certificado de Let’s Encrypt por DNS challenge, igual que el resto de servicios internos. No está en el túnel de Cloudflare.
  • Solo habla con el proxy. Cualquier conexión que no venga de NPMplus se rechaza, aunque venga de la propia red.
  • Contraseña y segundo factor. La contraseña se guarda con scrypt y el segundo factor es TOTP, el de cualquier app de autenticación. La cookie de sesión es HttpOnly, Secure y SameSite=Strict, y cada acción comprueba un token CSRF y el origen de la petición.
  • CSP estricta, la misma idea que en la web: default-src 'none', sin scripts ni estilos en línea, y HTMX servido desde el propio panel en lugar de un CDN.
  • Permisos mínimos en Proxmox. El panel tiene su propio usuario con un rol hecho a medida: ver el estado y los registros, encender y apagar, hacer snapshots y copias. No puede borrar ni restaurar contenedores, ni tocar la red o los usuarios. El resto de servicios le dan claves de solo lectura siempre que lo permiten.
  • Los secretos no están en el código. El repositorio es privado de todos modos, pero los tokens viven en un fichero de configuración en el servidor que solo puede leer el servicio.

Lo que salió por el camino

  • El certificado de Proxmox no incluía la IP por la que lo llamaba el panel, y la verificación TLS fallaba. En vez de desactivarla, el panel llama al nodo por el nombre que sí aparece en el certificado.
  • La interfaz de AdGuard no estaba donde creía. El puerto que usa el asistente de instalación no es el de la interfaz una vez instalado.
  • No hay reescritura DNS comodín en AdGuard, sino una por cada nombre interno. Hasta que no añadí la del panel, el navegador no lo encontraba.
  • La confirmación escrita no dejaba confirmar. El campo para escribir el número estaba oculto con el atributo hidden, pero una regla de CSS del propio panel lo volvía a mostrar siempre y el botón se quedaba bloqueado. Una línea de CSS ([hidden] { display: none !important; }) lo arregló.

Adiós a Homarr

Con el panel ya funcionando y probado, Homarr se fue: copia final protegida en el disco de copias, y después fuera el contenedor, su usuario de Proxmox, su entrada en el proxy, su nombre en AdGuard y su monitor en Uptime Kuma. Si alguna vez lo echo de menos, la copia sigue ahí.

Qué viene después

La siguiente fase es leer los registros de dentro de cada contenedor (por ejemplo, el de NPMplus o el de fail2ban) sin tener que entrar por consola. Y, ya que el panel lo sabe todo, que sea él quien avise de lo que Uptime Kuma no ve, como un disco que se llena poco a poco.

Comentarios

Los comentarios usan Giscus sobre GitHub Discussions. Al cargarlos, tu navegador se conectará a giscus.app y GitHub.Más información.