Zum Inhalt springen
EuroVPSEuroVPS

Eine Eingangstür für jedes interne Tool: ein Muster fürs Self-Hosting

Wie Sie Task-Boards, Dashboards, einen Passwort-Tresor und mehr hinter einem Reverse Proxy betreiben: Wildcard-DNS, Container unter systemd, mehrstufiger Zugriff, sichere Umbenennungen.

Von EuroVPS Engineering

5 Min. Lesezeit
Eine Eingangstür für jedes interne Tool: ein Muster fürs Self-Hosting

Jedes Betriebsteam sammelt kleine Tools an. Ein Task-Board, einen Dashboard-Server, eine Container-Konsole, einen Passwort-Tresor, ein paar interne Webanwendungen, die jemand geschrieben hat. Jedes beginnt sein Leben auf seinem eigenen Port, mit seinem eigenen Zertifikat und seiner eigenen Vorstellung von Authentifizierung, und innerhalb eines Jahres ist sich niemand sicher, welche davon exponiert sind, welche gepatcht sind oder welche noch ein gültiges Zertifikat haben. Dies ist das Muster, mit dem wir einen ganzen Satz interner Tools als einen gepflegten Dienst betreiben: eine Eingangstür, eine Art, Dinge zu starten, eine Zugriffsrichtlinie.

Die Form des Stacks

Das Muster hat vier Ebenen, und jede hat eine Aufgabe:

  1. DNS: ein Wildcard-Name wie *.tools.example.com, der auf einen Host zeigt.
  2. Reverse Proxy: Traefik auf diesem Host, der TLS terminiert und jeden Hostnamen zum richtigen Container routet.
  3. Container: jedes Tool in seinem eigenen Podman-Container in einem privaten Container-Netzwerk, ohne auf dem Host veröffentlichte Ports außer dem des Proxys.
  4. Überwachung: systemd startet, startet neu und ordnet alles, damit der Stack einen Neustart ohne menschliches Zutun übersteht.

Steht das, ist das Hinzufügen eines Tools eine kurze Checkliste statt eines Projekts: einen Container im Netzwerk starten, eine Route hinzufügen, fertig. Es gibt kein neues Loch in der Firewall, keinen neuen DNS-Eintrag und keine neue Zertifikatsanfrage.

DNS: ein Wildcard, und dann vergessen

Ein Wildcard-Eintrag bedeutet, dass tasks.tools.example.com, grafana.tools.example.com und vault.tools.example.com alle auf den Proxy-Host auflösen, ohne dass jemand das DNS noch einmal anfasst. Kombinieren Sie ihn mit einem Wildcard-Zertifikat für denselben Namen, und jedes neue Tool wird mit gültigem TLS geboren.

Halten Sie das Wildcard auf einer eigenen Subdomain. Sie möchten nicht, dass anything.example.com auf Ihrem Tooling-Host landet.

Der Proxy: Routen als Dateien, die man lesen kann

Traefik kann Routen aus Container-Labels oder aus einfachen Konfigurationsdateien entdecken. Für einen kleinen, langlebigen Stack bevorzugen wir Dateien: Sie sind leicht zu prüfen, leicht unter Versionskontrolle zu stellen, und sie ändern sich nicht, weil jemand einen Container mit anderen Labels neu gestartet hat. Eine Route für einen Task-Manager sieht so aus:

http:
  routers:
    tasks:
      rule: "Host(`tasks.tools.example.com`)"
      entryPoints: [websecure]
      service: tasks
      middlewares: [mgmt-only]
      tls: {}
  services:
    tasks:
      loadBalancer:
        servers: [{ url: "http://tasks:3456" }]
  middlewares:
    mgmt-only:
      ipAllowList:
        sourceRange: ["192.0.2.0/24"]

Die Middleware ist der wichtige Teil. Der Proxy ist der eine Ort, an dem Sie entscheiden, wer ein Tool erreichen darf, also legen Sie die Entscheidung dorthin.

Zugriff: zwei Schlösser, nicht eines

Interne Tools haben ihre eigenen Logins, und deren Qualität schwankt. Verlassen Sie sich nicht allein darauf. Schalten Sie zwei Kontrollen davor:

  • Eine Quell-Allowlist am Proxy, beschränkt auf die Netzwerke, aus denen Ihr Team tatsächlich arbeitet. Expandieren Sie jedes CIDR, bevor Sie es schreiben, und lesen Sie es danach zurück. Eine vertippte Präfixlänge kann aus dem Bereich eines einzelnen Büros einen großen Teil des Internets machen:
python3 -c "import ipaddress,sys; n=ipaddress.ip_network(sys.argv[1],strict=False); print(n, n[0], '->', n[-1], n.num_addresses)" 192.0.2.0/24
  • Eine Authentifizierungs-Middleware (mindestens Basic Auth über TLS, oder Forward Authentication zu Ihrem Identity Provider) für Tools, die selbst von innen nicht ohne zweites Credential erreichbar sein sollten.

Der Passwort-Tresor verdient besondere Überlegung. Ein Bitwarden-kompatibler Server wie Vaultwarden speichert die Tresorinhalte von den Clients verschlüsselt, sodass der Server sie nie in lesbarer Form hält. Das ist eine starke Eigenschaft, aber sie beseitigt nicht die Notwendigkeit, einzuschränken, wer den Server erreichen kann.

Container unter systemd

Container von Hand zu starten funktioniert bis zum ersten Neustart. Podman integriert sich über Quadlet in systemd: Sie beschreiben den Container in einer kleinen Unit-Datei, und systemd behandelt ihn wie jeden anderen Dienst.

# /etc/containers/systemd/tasks.container
[Unit]
Description=Task manager
After=network-online.target

[Container]
Image=docker.io/vikunja/vikunja:<pinned-tag>
Network=tools.network
Volume=/srv/tools/tasks:/app/vikunja/files:Z

[Service]
Restart=always

[Install]
WantedBy=multi-user.target

Nach systemctl daemon-reload führt systemctl start tasks ihn aus, und von da an startet er beim Boot. Pinnen Sie Image-Tags fest, statt latest zu folgen, damit ein Upgrade eine Entscheidung ist, die Sie treffen, mit Dump und Rollback bereit, statt etwas, das Ihnen beim nächsten Neustart passiert.

Lassen Sie SELinux auf enforcing. Labeln Sie jedes Volume für den Container, dem es gehört (:Z für ein privates Volume, :z für eines, das mehr als ein Container teilen muss).

Umbenennen, ohne etwas kaputtzumachen

Früher oder später zieht ein Tool um: ein neuer Host, eine neue Domain, ein aufgeräumterer Name. Halten Sie den alten Namen funktionsfähig, indem der Proxy ihn mit einer permanenten Weiterleitung auf den neuen beantwortet. Verwenden Sie 308 Permanent Redirect statt 301, denn ein 308 sagt den Clients, dieselbe Methode und denselben Body an der neuen Adresse zu wiederholen.

Es gibt einen Haken, der Skripte trifft, nicht Browser. Browser folgen Weiterleitungen. Viele Kommandozeilen-Clients und Bibliotheken tun es nicht, wenn man es ihnen nicht sagt. Ein Skript, das einen POST an den alten Namen sendet, bekommt ein 308 zurück, folgt ihm nicht und macht weiter. Nichts wurde angelegt, nichts ist laut fehlgeschlagen, und wenn das Skript nur prüft, dass irgendetwas geantwortet hat, meldet es Erfolg. Mit curl verwenden Sie -L zum Folgen, und prüfen Sie immer Statuscode und Body:

curl -s -o out.json -w '%{http_code}\n' -X POST https://tasks.tools.example.com/api/v1/...

Ein Antwort-Body mit den Worten «Permanent Redirect», wo Sie JSON erwartet haben, ist das Anzeichen. Wenn Sie umbenennen, durchsuchen Sie Ihre Skripte und Integrationen nach dem alten Namen und aktualisieren Sie sie, und behalten Sie die Weiterleitung nur als Sicherheitsnetz.

Backups, Monitoring und Patchen

Überwachen Sie jede Route von außerhalb des Hosts auf die erwartete Seite, nicht nur auf eine Antwort, und patchen Sie ein Tool nach dem anderen, mit dem vorherigen Image aufbewahrt, bis das neue eine Weile gelaufen ist.

Eine Checkliste für Ihr eigenes Tooling

  • Können Sie jedes interne Tool, seinen Hostnamen und die Personen aufzählen, die es erreichen dürfen?
  • Veröffentlicht irgendein Tool seinen eigenen Port auf dem Host oder im Internet und umgeht den Proxy?
  • Sind alle Ihre Allowlist-Bereiche so eng, wie Sie denken? Expandieren Sie sie und sehen Sie hin.
  • Kommen Ihre Container nach einem Neustart von selbst zurück?
  • Prüfen Ihre Skripte Statuscodes, oder nur, dass irgendetwas geantwortet hat?
  • Wann haben Sie Ihren Passwort-Tresor zuletzt wiederhergestellt?

Eine Eingangstür, eine Richtlinie und eine Art, Dinge zu starten, machen aus einer Schublade loser Tools einen Dienst, über den Sie klar nachdenken können.

CTA

Wenn Sie möchten, dass die Tools Ihres Teams so betrieben werden, auf Ihrer eigenen Instanz, können unsere Ingenieure helfen. Mit einem Ingenieur sprechen

Related

Verwandt: Managed Self-Hosted Operations-Tooling · Die Hardening-Baseline auf jedem Host

Veröffentlicht am 24. August 2026.

Alle Notizen aus der Praxis