Notificaciones push privadas para tus dispositivos con ntfy

Foto de Brett Jordan en Unsplash

Uno de mis servicios favoritos para alojar por la gran utilidad que aporta es ntfy, el cual nos permite disponer de un sistema de notificaciones personal. En este artículo explicaré cómo configurarlo con Docker y ejemplos para poderle sacar partido.

 

Tabla de contenidos

 

Introducción

Enviar un mensaje cuando ocurre un evento en un servidor o software es un caso muy común que nos puede salvar de más de un aprieto. Existe como opción gestionarlo mediante envíos de correo, pero cuando se trata de muchos mensajes en poco tiempo podemos llegar rápidamente a los límites de envío de nuestro proveedor de correo.

Otra opción bastante popular (y que anteriormente utilizaba) es enviar el mensaje mediante un bot de Telegram. Sin embargo, aunque el proceso de crear un bot no tiene coste económico, es un tanto engorroso tener que crear uno por cada categoría para evitar tener todas las notificaciones mezcladas bajo la misma conversación. Además, se queda algo limitado para mi gusto a la hora de poder gestionar varios usuarios como destinatarios.

Finalmente, me decanté por ntfy, una solución de código abierto que permite ser alojada en nuestro servidor y que tiene clientes en Android, iOS y en navegador web. Y, como suele ser de costumbre, dispone de una imagen oficial de Docker para desplegar el servicio con mucha facilidad.

 

Desplegando el servicio con Docker

En primer lugar, creamos una carpeta donde almacenaremos tanto el archivo de compose.yaml de Docker como los archivos correspondientes al servicio. Dejo aquí uno de ejemplo:

services:
  ntfy:
    image: binwiederhier/ntfy
    container_name: ntfy
    command:
      - serve
    environment:
      TZ: Europe/Madrid
      NTFY_BASE_URL: https://subdominio.ejemplo.es
      NTFY_CACHE_FILE: /var/lib/ntfy/cache.db
      NTFY_CACHE_DURATION: 72h
      NTFY_AUTH_FILE: /var/lib/ntfy/auth.db
      NTFY_AUTH_DEFAULT_ACCESS: deny-all
      NTFY_AUTH_USERS: 'usuario-ejemplo-admin:hash-ejemplo-admin:admin,usuario-ejemplo-lectura:hash-ejemplo-lectura:user'
      NTFY_AUTH_ACCESS: 'usuario-ejemplo-lectura:tema-solo-lectura:ro'
      NTFY_BEHIND_PROXY: true
      NTFY_ATTACHMENT_CACHE_DIR: /var/lib/ntfy/attachments
      NTFY_ENABLE_LOGIN: true
      NTFY_REQUIRE_LOGIN: true
      NTFY_AUTH_TOKENS: 'usuario-ejemplo-admin:token:descripcion'

    volumes:
      - ./:/var/lib/ntfy
    ports:
      - 2586:80
    healthcheck:
        test: ["CMD-SHELL", "wget -q --tries=1 http://localhost:80/v1/health -O - | grep -Eo '\"healthy\"\\s*:\\s*true' || exit 1"]
        interval: 60s
        timeout: 10s
        retries: 3
        start_period: 40s
    restart: unless-stopped
    init: true

Como podemos ver, exponemos el puerto 80 del contenedor al puerto 2586 de nuestro host y los archivos de la aplicación al mismo directorio donde se encuentra el archivo de compose.

Explico en detalle en las diferentes configuraciones disponibles: 

Zona horaria

Con la variable TZ indicamos la zona horaria de la aplicación, para que se muestre la hora correcta de las notificaciones.

TZ: Europe/Madrid

Configuración 

Especificamos la URL donde se servirá la aplicación. Posteriormente, veremos un ejemplo de cómo hacerlo con Caddy

NTFY_BASE_URL: https://subdominio.ejemplo.es

Es importante también especificar que la aplicación va a ser servida detrás de un proxy inverso.

NTFY_BEHIND_PROXY: true

Caché

La aplicación dispone de una base de datos SQLite que sirve como caché para almacenar los mensajes que no han podido ser enviados a todos los destinatarios (por ejemplo, si un dispositivo se encontraba apagado o sin conexión a internet) para poder transmitirlos a posteriori.

Podemos especificar la ruta con el siguiente parámetro, teniendo en cuenta que es la ruta del sistema de archivos del contenedor.

NTFY_CACHE_FILE: /var/lib/ntfy/cache.db

También podemos indicar un tiempo máximo de duración de esta caché. En mi caso, la he limitado a 72 horas.

NTFY_CACHE_DURATION: 72h

Por último, podemos establecer la ruta donde se almacenará la caché de los archivos adjuntos de los mensajes.

NTFY_ATTACHMENT_CACHE_DIR: /var/lib/ntfy/attachments

Haciendo nuestro servicio de acceso privado

Dado que no queremos que cualquier persona pueda leer y enviar notificaciones con nuestro servicio, es necesario definir ciertas configuraciones.

Por defecto, queremos que el permiso de los usuarios sea denegado a todo a no ser que se indique lo contrario por usuario.

NTFY_AUTH_DEFAULT_ACCESS: deny-all

También habilitaremos y requeriremos que el acceso a la aplicación se haga mediante autenticación.

NTFY_ENABLE_LOGIN: true
NTFY_REQUIRE_LOGIN: true

Configuración de usuarios

Primero, indicamos dónde almacenar la base de datos SQLite de los usuarios en el sistema de archivos del contenedor.

NTFY_AUTH_FILE: /var/lib/ntfy/auth.db

A continuación, tenemos que especificar la configuración de cada usuario, separado con comas, con la estructura nombre_usuario:hash_contraseña:tipo_usuario.

Para generar el hash de la contraseña, disponemos de un comando de la aplicación:

sudo docker exec -it ntfy ntfy user hash 

Importante a tener en cuenta: en el archivo compose.yaml hay que indicar el símbolo $ dos veces respecto al hash generado por el comando.

Como tipo de usuario, podemos elegir entre admin o user. El de tipo admin tendrá acceso a todo con permisos de lectura y escritura, mientras que user sólo a lo que le especifiquemos manualmente.

Aquí podemos ver un ejemplo con un usuario de cada tipo:

NTFY_AUTH_USERS: 'usuario-ejemplo-admin:hash-ejemplo-admin:admin,usuario-ejemplo-lectura:hash-ejemplo-lectura:user'

Configuración de permisos

Los usuarios no administradores pueden tener cuatro tipos de permisos por tema. Los temas (topics) son la manera de categorizar las notificaciones en distintos canales.

  • Lectura y Escritura (read-write)
  • Sólo lectura (read-only)
  • Sólo escritura (write-only)
  • Denegar (deny)

En el siguiente ejemplo le daríamos al usuario usuario-ejemplo-lectura permisos de sólo lectura (read-only) al tema tema-solo-lectura. Podemos añadir más permisos separados por comas.

NTFY_AUTH_ACCESS: 'usuario-ejemplo-lectura:tema-solo-lectura:read-only'

Tokens de autenticación

Es posible autenticarnos mediante un token en vez de con una contraseña. Esto es una opción bastante recomendable por seguridad si vamos a integrarnos con otras aplicaciones, además de ser obligatorio con algunas de ellas ciertas.

Para generar un token aleatorio, disponemos del siguiente comando:

sudo docker exec ntfy ntfy token generate

En este ejemplo para el usuario usuario-ejemplo-admin indicaríamos un token (sustituyéndolo por el generado en el paso anterior) con una descripción para que sepamos identificarlo. Como en los casos anteriores, si necesitamos indicar más configuraciones, las podemos separar con comas.

NTFY_AUTH_TOKENS: 'usuario-ejemplo-admin:token:descripcion'

Una vez configurado todo, levantamos el contenedor con docker compose.

sudo docker compose up -d

Configurando Caddy como reverse proxy

En este ejemplo utilizaré Caddy, pero se podría utilizar cualquier otra solución de proxy inverso. Para más información sobre Caddy, recomiendo echar un vistazo al artículo. donde explico cómo configurarlo.

Modificamos el archivo Caddyfile para añadir la configuración del sitio como proxy inverso, referenciando la red del host al puerto que configuramos en el archivo de compose de ntfy.

subdominio.ejemplo.es {
        reverse_proxy host.docker.internal:2586 
}

 

Cliente Android

Existe la opción de recibir mediante notificaciones web los avisos en nuestros dispositivos, accediendo a la URL donde hemos configurado la aplicación, pero es más práctico utilizar la aplicación nativa para Android.

Existe también un cliente para iOS, aunque hay que tener en cuenta que, debido a una limitación en la ejecución de aplicaciones en segundo plano, es necesario realizar una configuración adicional para recibir las notificaciones de forma inmediata.

Su configuración no puede ser más sencilla: primero, iniciamos sesión con uno de los usuarios previamente configurados. Después, pulsamos en el icono de más para suscribirnos a un tema. Por último, indicamos el nombre del tema, seleccionamos la opción de Usar otro servidor e indicamos la URL donde hemos publicado la aplicación.

Pantalla de suscripción a un nuevo tema de ntfy

 

Ejemplos de uso

Para probar el funcionamiento de las notificaciones, nos podemos valer de curl para hacer una petición. El mensaje deberá aparecer en todos los clientes suscritos al tema tema-ejemplo.

curl \
    -u 'usuario-ejemplo-admin:contraseña' \
    -d "¡Hola, mundo!" \
    https://subdominio.ejemplo.es/tema-ejemplo

 A continuación, indico unos ejemplos de uso:

  • Alerta de acceso de SSH a nuestro servidor (ejemplo de configuración).
  • Alerta de falta de espacio en nuestro servidor (ejemplo de configuración).
  • Integración con Uptime Kuma para avisarnos cuando un sitio web se encuentra caído.
  • Integración con Diun para recibir una notificación cuando exista una actualización de imagen de una de nuestras aplicaciones Docker.

Además de las integraciones oficiales listadas en la documentación de ntfy, podemos integrarlo fácilmente en nuestras propias aplicaciones o procesos, dado que simplemente nos es necesario hacer una petición POST de HTTP.

Desde que la descubrí, ntfy se ha convertido en una herramienta esencial para mantener un seguimiento de mis aplicaciones y servidores. Espero que con este artículo te hayan entrado ganas de probarla y que te aporte tanta utilidad como a mí.