GitHub Webhooks: Konfiguration des Webservers
Dies ist Folgeartikel 1 aus meiner Serie zu GitHub-Webhooks.
Ich möchte GitHub-Webhooks empfangen und potentiell mit Perl weiterbearbeiten, daher habe ich in MetaCPAN gesucht und bingo: Plack::App::GitHub::WebHook
Aber was ist Plack?
Perl Superglue for Web frameworks and Web Servers (PSGI toolkit)
Das hinterlässt mich so schlau wie zuvor.
Plack is a set of tools for using the PSGI stack. It contains middleware components, a reference server and utilities for Web application frameworks.
Das ist schon verständlicher.
Und PSGI::FAQ erklärt mir, was PSGI ist:
PSGI is an interface between web servers and perl-based web applications akin to what CGI does for web servers and CGI scripts.
Das klingt genau nach dem, was ich will! Mit diesem Zusatzwissen sind auch die Beispiele in Plack::App::GitHub::WebHook verständlich geworden.
Plack, plackup und PSGI
Ich habe mir dann zum Ausprobieren ein PSGI-Hello-World gebaut und
mittels plackup testweise auf localhost
laufen lassen. plackup ist
quasi ein Starter für PSGI-Anwendungen.
Hier das PSGI-Skript:
- my $app = sub {
- my $http_status = 200;
- my $headers = [];
- my $body = [ '<html>',
- ' <body>',
- ' <p>Hello World</p>',
- ' </body>',
- '</html>' ];
- return [ $http_status, $headers, $body ];
- };
Der Start geht dann so: plackup helloworld.psgi
Ich bin kein Docker-Nutzer
Was macht man heutzutage nun mit so einem Service?
Richtig: man steckt ihn in einen eigenen Container.
Da das mein Server performancetechnisch nicht hergibt und ich mich da
nicht auch noch einarbeiten will, habe ich das gelassen. Stattdessen
habe ich das so gelöst, wie man das damals™ zu meiner Zeit gemacht
hat: Ich habe im System einen neuen User webhook
angelegt, in dessen
Homeverzeichnis der Service dann leben wird. Das ist besser als
nichts, denn dieser User kann auf dem restlichen System erstmal
herzlich wenig Unfug anstellen.
Rückwärtsproxy
plackup kann meine PSGI-Anwendung laufen lassen, ist aber nicht wirklich dafür gedacht, direkt mit Kontakt zum Internet zu laufen.
Andererseits habe bereits einen Apache laufen, auf dem sind unter anderem SSL/HTTPS sowie DSGVO-konformes Logging bereits fertig eingerichtet.
Wenn nun der Apache einfach bestimmte Anfragen nehmen und an plackup durchreichen könnte… Das kann er natürlich, das Stichwort ist „Reverse Proxy“.
Einen Proxy habe ich schon öfter benutzt (und sogar schon einige geschrieben[1], [2]), damit lässt man ausgehende Verbindungen von vielen Rechnern über einen einzigen Rechner laufen (so ähnlich wie NAT).
Ein Reverse Proxy macht genau das Gegenteil: Er kümmert sich auf einem zentralen Rechner um eingehende Verbindungen und verteilt sie nach innen auf viele einzelne Rechner weiter (so ähnlich wie Load-Balancing, aber mit unterschiedlichen Zielen abhängig von der aufgerufenen URL).
Nach einem kurzen Blick in die Doku ist das trivial einfach zu konfigurieren, es reicht eine Zeile in der Konfiguration des Apache aus:
- ProxyPass "/webhooks/" "http://localhost:8000/"
(Falls mod_proxy
noch nicht geladen ist, zusätzlich einmal a2enmod
proxy
aufrufen.)
Alles, was jetzt auf meinem öffentlich erreichbaren Apache unter
/webhooks/
ankommt, wird an plackup, der nur auf localhost
läuft, weitergeleitet; gleiches gilt für die Rückantworten.
Jetzt muss ich plackup keine SSL-Zertifikate unterschieben und
laufend austauschen (das ginge bestimmt halbwegs einfach, aber man
muss sich ja nicht jeden Schuh anziehen) und der Wechsel vom User
www-data
zum User webhook
passiert im Rahmen der internen
HTTP-Verbindung.
systemd: Container des Kleinen Mannes
Jetzt muss plackup nur noch dauerhaft laufen. Initskripte kann ich schreiben, aber das ist 80er-Jahre-Linux. Dieses Projekt ist ein prima Anlass, mich mal genauer mit systemd zu beschäftigen. Dienste zu starten ist ja quasi dessen Hauptaufgabe. Mit minimaler Webrecherche habe ich mir folgendes Startskript gebaut:
- [Unit]
- Description=Github Webhook Receiver Service
- [Service]
- Type=simple
- ExecStart=/home/webhook/perl5/bin/plackup -I/home/webhook/perl5/lib/perl5 --listen localhost:8000 /home/webhook/webhook-receiver.psgi
- User=webhook
- Group=webhook
- StandardOutput=journal
- StandardError=journal
- ProtectSystem=full
- PrivateTmp=true
- [Install]
- WantedBy=apache2.service
Type=simple
startet und überwacht einen Prozess, der im Vordergrund
läuft, also genau passend für einen einfachen plackup-Aufruf ohne
--daemonize
. Dem eigentlichen Aufruf in ExecStart
gebe ich noch
per -I
den Pfad zu weiteren Perl-Modulen mit, weil nicht alle
benötigten Module als Distributionspaket verfügbar waren.
plackup läuft mit User
und Group
webhook
und schreibt
StandardOutput
und StandardError
nach journald.
ProtectSystem
und PrivateTmp
sind ein paar
systemd-Sicherheitsfeatures, die in Richtung Containerisierung
gehen: Ersteres macht für den gestarteten Prozess die Verzeichnisse
/usr
, /boot
und /etc
readonly (da sollte der User webhook
sowieso nicht schreiben dürfen) und letzteres erzeugt ein privates
/tmp
nur für den Prozess, damit er sich das Verzeichnis nicht mit
dem Rest des Systems teilen muss. Diese Features wollte ich immer
schon mal nutzen. Es gibt noch mehr solche Einstellungen, siehe
systemd.exec(5)
.
WantedBy
gibt an, dass systemd diesen Service beim Systemstart
nach dem Start von Apache starten soll. Andersrum macht das ja auch
keinen Sinn, weil ohne den Reverse Proxy im Apache keine
Verbindung zum plackup zustandekommen kann.
Abgelegt wird die Servicebeschreibung unter
/etc/systemd/system/local-webhook-receiver.service
und danach
aktiviert über
- systemctl daemon-reload
- systemctl enable local-webhook-receiver.service
- systemctl start local-webhook-receiver.service
Das Logging im journald kann man so anzeigen:
- journalctl -u local-webhook-receiver.service
Hängt man da noch ein -f
dran, kann man live mitlesen wie bei
tail -f /var/log/irgendwas
.
Tipp: Ich habe meinen normalen User in /etc/groups
der Gruppe
systemd-journal
hinzugefügt und jetzt muss ich journalctl
nicht
mehr als root
aufrufen. (Das ist zumindest bei Debian so.)
Fazit
Viel gelernt, neue Begriffe, alles funktioniert und wenn man genauer
hinguckt, ist eigentlich alles ganz einfach und logisch.
Und hey, endlich mal die Features von systemd benutzt!
Heute würde ich dafür Imagemagick nehmen ;-)
Richard Lippmann am um :
Guter Artikel, Fußnoten im Artikel-Blog. Danke!
Richard Lippmann
Netz - Rettung - Recht am : Wellenreiten 01/2019