Homelab Anwendung extern erreichbar machen mit Uberspace
Innerhalb meines HomeLabs betreibe ich seit einiger Zeit interne Services, die Familie und Freunde im (W)LAN auch immer wieder nutzen.
Zuletzt gab es nun den Request für eine Online-Wunschliste, welche genutzt werden kann, um Wünsche zu verwalten und auch die Verteilung der Wünsche zu vereinfachen. Nach einer relativ kurzen Recherche kamen wir dann bei https://github.com/cmintey/wishlist an.
Als einige Personen an Weihnachten vor Ort waren, haben wir der Software im Heimnetz einen Testlauf unterzogen und anschließend entschieden, dass wir die Anwendung als hilfreich erachten und damit weitergehen wollen.
Daraufhin stand ich vor einem Problem. Damit die Wunschliste für alle sinnvoll einsetzbar wird, muss sie aus dem Internet erreichbar sein. Bisher waren alle Anwendungen nur lokal über die vom OpenWrt-Router bereitgestellte .lan TLD erreichbar.
Mein erster Gedanke war, die Anwendung einfach direkt auf meinem Uberspace laufen zu lassen, aber durch die Abhängigkeit von NodeJS 24 war dies unter Uberspace U8 leider nicht möglich.
Nach einigem Hin- und Herüberlegen, was man stattdessen nutzen könnte, bin ich aber auf die Idee gekommen, die Wunschliste so wie für den Test weiter in meinem Homelab zu hosten, und die Uberspace-Funktion der Web-Backends zu nutzen, um die Aufrufe aus dem Internet möglich zu machen. Die Web-Backends von Uberspace sind letztendlich Reverse Proxy’s die innerhalb des Astroiden (der Webspace User des Uberspace-Accounts) jeden beliebigen Port ansprechen können.
Da mein ISP mir von außen erreichbares IPv6 zur Verfügung stellt, könnte ich natürlich dafür sorgen, dass der Service so erreichbar wird, ich möchte aber keine direkte Verbindung von außen in mein Homelab zulassen. Außerdem hätte diese Variante für IPv4 nicht funktioniert.
Daher habe ich mir eine Lösung einfallen lassen, bei der die Verbindung zu Uberspace direkt von meinem Homelab aus einem Docker-Container heraus aufgebaut wird.
Hierfür baut, zusätzlich zum eigentlichen Service, ein extra Container eine SSH-Verbindung auf, die ausschließlich ein Remote-Portforwarding für den Port des Applications-Container’s herstellt.
Alles andere ist dann nur noch etwas Fleißarbeit. In der Docker-Compose-Datei muss eine Verknüpfung zwischen beiden Containern eingetragen werden, damit der SSH-Container auf den Application-Container zugreifen kann. Der SSH-Container muss einmalig einen SSH-Key generieren und den öffentlichen Schlüssel ausgeben.
Anschließend muss der Schlüssel bei Uberspace in die ~/.ssh/authorized_keys aufgenommen werden und der Service neu gestartet werden. Hierfür ggf. die Option restart kurzzeitig deaktivieren, um wiederholte fehlgeschlagene Logins durch die Neustarts nicht zu provozieren.
Hier die gesamte docker-compose.yml für den Wunschlisten-Service.
services:
wishlist:
container_name: wishlist
restart: unless-stopped
image: ghcr.io/cmintey/wishlist:latest
ports:
- 3280:3280
volumes:
- ./uploads:/usr/src/app/uploads # This is where user image uploads will be stored
- ./data:/usr/src/app/data # This is where the sqlite database will be stored
environment:
ORIGIN: https://example.com # The URL your users will be connecting to
TOKEN_TIME: 72 # hours until signup and password reset tokens expire
DEFAULT_CURRENCY: EUR
sshport:
image: alpine
restart: unless-stopped
links:
- wishlist
volumes:
- ./sshkey:/data
command: sh -c "apk add openssh-client && if [ ! -f /data/sshkey ]; then ssh-keygen -t rsa -b 4096 -f /data/sshkey -P '' && cat /data/sshkey.pub; fi && ssh -i /data/sshkey -o StrictHostKeyChecking=no -o GatewayPorts=true -N -R 3280:wishlist:3280 user@example.uber.space"
Anschließend muss, wie in der Uberpace manual web backends beschrieben, das Webbackend eingerichtet werden:
uberspace web backend set example.com/ --port 3280 --http
Anschließend sollte die Verbindung sofort klappen. Viel Erfolg beim nachmachen!