ntfy.sh – Push-Notifications ohne Firebase

ntfy ist ein Open Source Pub-Sub-Benachrichtigungsdienst. Er ermöglicht es, von jedem Computer aus Benachrichtigungen an Handys oder Computer zu senden. Das Projekt ist Mehrfachlizensiert unter der Apache License 2.0 und der GPLv2 License.

Durch Mehrfachlizenzierung stehen den Anwendern mehrere Softwarelizenzen zur Auswahl. So kann ein Programm z. B. unter einer Open-Source-Lizenz wie der GPL und unter einer proprietären Lizenz benutzt werden. Eine solche Lizenzauswahl wird dann auch duales Lizenzsystem oder -modell genannt. Es ist aber auch möglich, mehrere Open-Source-Lizenzen, z. B. der GPL und einer BSD-Lizenz, zur Auswahl zu stellen.

https://de.wikipedia.org/wiki/Mehrfachlizenzierung

Eine vorgefertigte Installation steht unter ntfy.sh zur Verfügung, die kostenlos einfach so verwendet werden kann.

Senden von Notifications

Das Senden von Push-Notifications ist denkbar einfach. Einfach einen HTTP POST Request an den Server.
Beispiel:

curl -d "Backup successful" ntfy.sh/mytopic

Damit wird die Nachricht mit dem Text „Backup successful“ an den Topic „mytopic“ gesendet. Jeder Client, der Benachrichtigungen des Topics abonniert hat wird dann über eine Benachrichtigung über die neue Nachricht informiert.

Unter Windows sieht das dann z.B. wie folgt aus:

Android:

Wer lieber eine Web-GUI hat, kann auch über Formular der Webseite Push Notifications senden.

Alles, was man hier im Formular eintragen kann, ist auch über die HTTP-Schnittstelle möglich und kann so in beliebige Anwendungen oder IoT Prozesse integriert werden. Eine ausführliche Anleitung zu der HTTP-Schnittstelle kann man unter https://ntfy.sh/docs/subscribe/cli/ finden.

Fertige Apps zum Senden und Empfangen von Push-Notifications gibt es für Android, iOS und Web.

Die Installation unter https://ntfy.sh/ kann kostenlos genutzt werden. Dabei sollte man aber einen möglichst eindeutigen Channel verwenden, denn wenn man z.B. die App installiert und dann den „mytopic“ Channel abonniert, wird man nur mit Push-Notifications überflutet, da jeder diesen Channel für einen ersten Test nutzt.

Noch besser ist es natürlich, eine eigene Instanz zu installieren und damit die volle Kontrolle über das System zu haben.

ntfy Self-Hosting Installation

Die Installation auf einem Server ist denkbar einfach und wird unter https://ntfy.sh/docs/install/#linux-binaries ausführlich beschrieben. Ich wähle hier eine manuelle Installation und führe die folgenden Schritte durch.

wget https://github.com/binwiederhier/ntfy/releases/download/v1.28.0/ntfy_1.28.0_linux_x86_64.tar.gz
tar zxvf ntfy_1.28.0_linux_x86_64.tar.gz
sudo cp -a ntfy_1.28.0_linux_x86_64/ntfy /usr/bin/ntfy
sudo mkdir /etc/ntfy && sudo cp ntfy_1.28.0_linux_x86_64/{client,server}/*.yml /etc/ntfy
sudo ntfy serve

Etwas komplizierter wird es, wenn man den ntfy.sh Server z.B. mit HTTP-Authentifizierung absichern möchte und die Anfragen über ein Let’s Encrypt Zertifikat Verschlüssen möchte.

Genau für diesen Zweck gehe ich folgende Schritte durch.

Den ntfy.sh Server so konfigurieren, dass er nur auf der IP 127.0.0.1 (localhost) lauscht.
Dadurch kann der Server von außen nicht mehr angesprochen werden.
Dieses Verhalten kann man mit der folgenden Zeile in der „server.yml“ Datei erreichen.

# PORT muss durch die Port Nummer ersetzt werden, auf der ntfy lauschen soll.
listen-http: "127.0.0.1:PORT"

Bei Start des Servers wird dann folgende Log-Ausgabe erzeugt und schon ist der ntfy Server nur noch über localhost erreichbar.

2022/11/13 09:42:35 INFO Listening on 127.0.0.1:PORT[http], ntfy 1.28.0, log level is INFO

In Computernetzwerken ist localhost ein Hostname, der sich auf das aktuelle Gerät bezieht, mit dem darauf zugegriffen wird. Er wird verwendet, um auf die Netzwerkdienste zuzugreifen, die auf dem Host über die Loopback-Netzwerkschnittstelle ausgeführt werden. Durch die Verwendung der Loopback-Schnittstelle wird die Hardware der lokalen Netzwerkschnittstelle umgangen.

Nun ist auch der richtige Zeitpunkt, ntfy als sestemd.service zu konfigurieren. Dazu ist auch schon eine ntfy.service Datei vorbereitet, die in das Verzeichnis /etc/systemd/system/ koiert werden muss und dann gestartet werden kann.

# Create ntfy user
useradd ntfy
# Copy service config
sudo cp ntfy.service /etc/systemd/system/
# Enable service
systemctl enable ntfy
#Start service
systemctl start ntfy

Ob der Service ordnungsgemäß gestartet wurde, kann man mit dem folgenden Befehl prüfen:

systemctl status ntfy

Die Ausgabe sollte dann wie folgt aussehen.

● ntfy.service - ntfy server
   Loaded: loaded (/etc/systemd/system/ntfy.service; enabled; vendor preset: enabled)
   Active: active (running) since Sun 2022-11-13 10:00:53 CET; 5s ago
 Main PID: 18248 (ntfy)
    Tasks: 13 (limit: 4915)
   CGroup: /system.slice/ntfy.service
           └─18248 /usr/bin/ntfy serve --no-log-dates

Nov 13 10:00:53 Ubuntu-1804-bionic-64-minimal systemd[1]: Started ntfy server.
Nov 13 10:00:53 Ubuntu-1804-bionic-64-minimal ntfy[18248]: INFO Listening on 127.0.0.1:PORT[http], ntfy 1.28.0, log level is INFO

Nun läuft der Server und man kann auf der Konsole folgenden Befehl aufrufen, um eine Push-Notification zu senden.

curl -d "Backup successful" http://127.0.0.1:PORT/mytopic

Das Kommando wird mit folgendem JSON Objekt beantwortet.

{"id":"VnldHpVABG6s","time":1668330407,"event":"message","topic":"mytopic","message":"Backup successful"}

Da es noch keine Subscriber gibt, läuft die Nachricht leider noch ins Leere, was man mit dem Befehl systemctl status ntfy überprüfen kann.

Um das zu ändern, muss nun der ntfy Server nun „veröffentlicht“ werden. Ich mache das eigentlich immer über einen Proxy im Apache Webservice, mit dem man verschiede, Backend-Server über einen Port (80) ansprechen kann wie unter https://httpd.apache.org/docs/2.4/howto/reverse_proxy.html beschrieben ist.

Dafür lege ich in der Apache Konfiguration einen neuen Virtuellen-Host mit folgender Konfiguration an.

<Directory /var/www/ntfy.jentsch.io/http>
    Require all granted
    AllowOverride all
</Directory>

<Directory /var/www/ntfy.jentsch.io/https>
    Require all granted
    AllowOverride all
</Directory>

<VirtualHost *:80>
    ServerName ntfy.jentsch.io
    ServerAlias www.ntfy.jentsch.io

    ServerAdmin info@jentsch.io
    DocumentRoot /var/www/ntfy.jentsch.io/http

    ErrorLog ${APACHE_LOG_DIR}/ntfy.jentsch.io.error.log
    CustomLog ${APACHE_LOG_DIR}/ntfy.jentsch.io.access.log combined
</VirtualHost>

<VirtualHost *:443>
    ServerName ntfy.jentsch.io
    ServerAlias www.ntfy.jentsch.io

    ServerAdmin info@jentsch.io
    DocumentRoot /var/www/ntfy.jentsch.io/https

    ErrorLog ${APACHE_LOG_DIR}/ntfy.jentsch.io.error.log
    CustomLog ${APACHE_LOG_DIR}/ntfy.jentsch.io.access.log combined

    SSLEngine on

    ProxyRequests Off
    ProxyPreserveHost On

    ProxyPass / http://localhost:PORT/
    ProxyPassReverse / http://localhost:PORT/
    RewriteEngine on
    RewriteCond %{HTTP:Upgrade} websocket [NC]
    RewriteCond %{HTTP:Connection} upgrade [NC]
    RewriteRule ^/?(.*) "ws://127.0.0.1:PORT/$1" [P,L]

    <Location />
        AuthType Basic
        AuthName "Wrapper auth"
        AuthBasicProvider file
        AuthUserFile "/var/www/ntfy.jentsch.io/.htpasswd"
        Require valid-user
    </Location>

    SSLCertificateFile /etc/letsencrypt/live/ntfy.jentsch.io/cert.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/ntfy.jentsch.io/privkey.pem
    SSLCertificateChainFile /etc/letsencrypt/live/ntfy.jentsch.io/chain.pem
</VirtualHost>

Der virtuelle Host auf Port 80 dient nur dazu, http Anfragen direkt an den HTTPS Port 443 umzurouten. In dem Verzeichnis „/var/www/ntfy.jentsch.io/http“ liegen dazu 2 Dateien.

index.php

<?php

$redirect = 'https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
header('HTTP/1.1 301 Moved Permanently');
header('Location: ' . $redirect);
exit();

?>

.htaccess

<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^.*$ /index.php [L]
</IfModule>

Die beiden Dateien sind schon ausreichend, um alle HTTP-Requests an den virtuellen HTTPS-Server umzuleiten. In dem Verzeichnis „/var/www/ntfy.jentsch.io/https“ liegen keine Dateien und ich bin nicht mal sicher, ob das Verzeichnis überhaupt existieren muss, da alle Anfragen an den HTTPS-Port weitergeleitet werden. Dafür sorgen die Zeilen ProxyXXX in der Konfiguration. Da ntfy mit Websockets arbeitet, sind auch die RewriteCond und RewriteRule Zeilen von großer Bedeutung und dürfen nicht vergessen werden.

Damit der Server nur von authentifizierten Benutzern verwendet werden kann, muss in der <Location /> „Basic Authentication“ aktiviert werden. In der angegebenen .htpasswd Datei stehen dann die Benutzer und die Hashes der Passwörter der berechtigten Nutzer. Details dazu können unter https://httpd.apache.org/docs/2.4/howto/auth.html nachgelesen werden.

Glücklicherweise unterstützt sowohl die Web- als auch die Android App HTTP Basic-Authentication, so dass ein Passwortschutz kein Problem ist.

Für die HTTPS Komnuikation ist ein Zertifikat nötig, dass man einfach mit dem Certbot von Let’s Encrypt erstellen kann.

certbot --apache certonly -d ntfy.jentsch.io -d www.ntfy.jentsch.io

Da die Zertifikate von Let’s Encrypt eine sehr kurze Lebensdauer hat, sollte man auf jeden Fall einen Cronjob einrichten, der regelmäßig das Zertifikat aktualisiert. Dafür hat der Certbot das folgende Kommando vorgesehen, den ich einfach jede Nacht laufen lasse.

certbot renew

Wichtig ist noch zu erwähnen, dass im Apache diverse Module aktiviert sein müssen, damit das alles funktioniert.

Mit dem Befehl „apache2ctl -M“ kann man sich die geladenen Module anzeigen lassen und mit a2enmod kann man ein Modul aktivieren. Folgende Module sind unbedingt notwendig.

auth_basic_module
proxy_module
proxy_http_module
proxy_wstunnel_module
rewrite_module
ssl_module

Test

Zum Test kann man sich einfach die App aus dem Google Play Store installieren. Diese Version nutzt aber Firebase (https://ntfy.sh/docs/faq/) daher empfehle ich eher die Version von F-Droid, die frei von Google und anderen externen Diensten ist.

Die letzten verbliebenen Apple Jünger sollen hier auch nicht zu kurz kommen und können ja mal hier ihr Glück versuchen. 🙂

Fazit

Mit dem Setup kann man sich nun selbst oder anderen sehr schnell und einfach Benachrichtigungen schicken, die man in beliebige Programme integrieren kann. Eine Funktion für die ich in der Vergangenheit gerne Telegram (https://telegram.org/) verwendet habe. Nun kann ich also auch hier auf einen externen Service verzichten.

Bonus

Auch ein Teilen von Texten, Links, Bildern etc. ist mit dem ntfy Server sehr einfach möglich, da sich die Android App in die „Teilen“ Funktion des Android Betriebssystems einhängt. So kann man aus jeder beliebigen App, die das Teilen ermöglicht einfach Inhalte an einen Topic senden, der dann von allen Subscribern empfangen wird – sehr praktisch, wenn man sich z.B. selbst einen Link vom Handy auf den Desktop schicken möchte.