Take back control: Eigener Server zuhause

  Stefan   Lesezeit: 15 Minuten  🗪 9 Kommentare

Mit _docker-compose_ und einem _Reverse-Proxy_ ist ein (Web-)Server schnell aufgesetzt. Und wenn er erst mal läuft, sind weitere Server-Dienste ein Kinderspiel.

take back control: eigener server zuhause

Ich verwende hier als Beispiel-Domain gnulinux.ch. Die Subdomains, die ich verwende, werden zum Zeitpunkt, an dem dieser Artikel entsteht, nicht verwendet. Ich verwende keine unbenutzte Adresse, da es ja möglich ist, dass diese irgendwann doch vergeben wird.

Bei der Konzeption des Servers lege ich besonderen Wert auf Wiederherstellbarkeit. Ich möchte sehr einfach auch nur den Teil eines Dienstes aus einem Backup wieder herstellen können oder einen Dienst auf einem anderen Host aus dem Backup aufsetzen. Deshalb bin ich vorsichtig mit Tools, mit denen man mehrere Docker Stacks aufsetzten kann oder so etwas. Bei meiner Lösung kann man die Verzeichnisse der Container einfach sichern und wieder zurückkopieren. Bei Diensten ohne persistente Daten ist dies natürlich nicht so wichtig.

Bei den Docker-Images verwende ich hier die :latest-Versionen, was ich bei wichtigen Diensten bei mir nie mache. Ich gebe immer eine konkrete Version an, damit ich den gleichen Zustand wieder herstellen kann.

Ich habe schon sehr lange einen eigenen Server, viel länger als es Docker oder NPM gibt. Ich versuche, mit der Zeit zu gehen. Dabei habe ich aber immer die obige Backup-Strategie im Hinterkopf. Die ist mir noch wichtiger, als die neuesten Tools und Features. Ich hoffe, Euch kommt der Artikel trotzdem nicht vor wie von Eurem Opa.

Kommunikationsweg

Im Titelbild habe ich versucht, alle Beteiligten einer https-Anfrage zu veranschaulichen:

  • Nutzer gibt https://ich-mag.gnulinux.ch im Browser ein
  • Browser fragt DNS-Server nach der IP-Adresse
  • Vorher hat der Domain-Hoster den DNS-Eintrag auf den DNS-Server verteilt
  • Jetzt macht der Browser eine Verbindung zur IP-Adresse (des Routers)
  • Der Router hat ein Port-Forwarding konfiguriert und leitet die Anfrage an den Reverse-Proxy weiter
  • Der Reverse-Proxy hat vorher ein SSL-Zertifikat bei let’s encrypt erzeugt
  • Der Reverse-Proxy leitet das SSL-Zertifikat an den Browser
  • Der Browser prüft das Zertifikat
  • Der Reverse-Proxy hat für die URL ich-mag.gnulinux.ch eine Weiterleitung an den Port von Server2 (in der Regel nicht Port 80), der von Docker1 als http-Port exportiert wird
  • In Docker1 wird der Port auf den Standard Http-Port 80 weitergeleitet
  • Jetzt ist die Anfrage beim Service1 und wird bearbeitet

Komponenten

Router

Am Router sollten für IPv4 zwei Ports weitergeleitet werden: Port 80 (http) und 443 (https). Diese sollten am besten auf die gleichen Ports von Server1 zeigen. Auf diesem werden dann wiederum die gleichen Ports des Reverse Proxy Docker-Containers gemappt (kommt unten).

Für IPv6 müssen die genannten Ports geöffnet werden (was auch oft weitergeleitet genannt wird). Dies ist für alle nötig, die keine richtige IPv4 Adresse mehr haben. Weitere Infos dazu in meinem letzten Artikel.

Domain-Hoster

Für den Eintrag der IP beim DNS gibt es zwei Möglichkeiten:

Dynamic DNS

Hier gibt es viele Dienstleister. Falls die IP nicht fest ist, muss sie regelmäßig aktualisiert werden. Das kann entweder im Router passierten (Fritz!Box kann das mit einigen Anbietern) oder mit einem Service am Server. no-ip.com bietet da z.B. fertige Skripte für systemd an. Mit dieser Lösung hat man natürlich keine eigene Domain, es sollte aber fürs erst funktionieren.

Eigene Domain

Wer eine eigene Domain will, muss sich an einen Domain-Anbieter wenden. Bei lima-city.de z.B. kostet das keine 10 € im Jahr. In der Domain-Verwaltung des Hosters muss dann noch ein Eintrag gemacht werden, abhängig von fester IP oder nicht:

Feste IP

Für IPv4 muss ein A-Record angelegt werden:

  • Name: *.gnulinux.ch, Inhalt: IP-Adresse des Routers.

Für IPv6 muss ein AAAA-Record angelegt werden:

  • Name: *.gnulinux.ch, Inhalt: IP-Adresse des Servers.

Dynamische IP

Es muss ein CNAME-Record angelegt werden:

  • Name *.gnulinux.ch, Inhalt: Dynamische Adresse.

Dieser zeigt zum Beispiel auf einen Eintrag bei no-ip.com, wo wiederum Einträge für IPv4 und/oder IPv6 konfiguriert sind.

Reverse-Proxy

Der Reverse Proxy ist die nächste wichtige Komponente. Ich verwende Nginx-Proxy-Manager (NPM). Alternativen sind Traefik oder Caddy. Da kann ich aber noch gar nichts dazu sagen.

Ich kann aber sagen, dass auch bei dieser Komponente das Backup wichtig ist. Bei manchen Diensten ist die Nginx Konfiguration ziemlich hakelig, man bastelt einige Zeit hin, bis alles funktioniert. Wenn man dann aus einem Backup das System schnell wieder aufsetzen will, ist es ärgerlich, wenn man dann wieder alles herausfinden muss. Bei NPM gibt es zwar (bisher) keine Backup-Funktion, aber im Ordner ./data sind die Konfigurationen für alle Hosts gespeichert.

Aufgaben

Der Reverse-Proxy hat (bei mir) folgende Aufgaben:

  • Komplettes SSL Handling. Er holt die Zertifikate von Let’s Encrypt und liefert diese an den Browser aus.
  • Weiterleitung einer angefragten URL an den richtigen Server und Port.

Installation

Es kann ein beliebiger Rechner verwendet werden, der vom Router erreichbar ist und der auf die Server zugreifen kann.

Ich installiere den NPM wie fast alles andere über docker-compose. Dazu muss docker-compose natürlich installiert sein. Um docker/docker-compose ohne Root-Rechte benutzen zu können, muss der verwendete User in der Regel in der Gruppe docker sein.

Man erstellt dann ein Verzeichnis z.B. proxy und legt darin die Datei docker-compose.yaml mit dem Inhalt aus der Dokumentation an.

version: '3.8'
services:
  app:
    image: 'jc21/nginx-proxy-manager:latest'
    restart: unless-stopped
    ports:
      - '80:80' # Public HTTP Port
      - '443:443' # Public HTTPS Port
      - '81:81' # Admin Web Port
    volumes:
      - ./data:/data
      - ./letsencrypt:/etc/letsencrypt

Die Ports würde ich so lassen, damit der NPM auch lokal verwendet werden kann. Die Verzeichnisse ./data und ./letsencrypt müssen noch angelegt werden. Dort werden die Nginx-Konfigurationen bzw. die SSL-Zertifikate gespeichert. Diese kann man dann auch in andere Docker-Container einbinden, z.B. für einen IMAP-Server.

Dann wird der Container gestartet:

docker-compose up -d

Das -d kann man am Anfang auch weglassen, dann sieht man sofort das Log und kann den Container einfach mit ^C beenden. Man kann sich das Log aber auch anschauen, wenn der Dienst im Hintergrund läuft:

docker-compose logs -f --tail 10

Zum Anhalten des Containers sollte

docker-compose down

verwendet werden.

Falls man sich in den Container einloggen will, geht das so

docker-compose exec app sh

Alle Docker-Container, die mit docker-compose erstellt wurden, sollten bei einem Neustart automatisch neu gestartet werden. Es kann aber sein, dass die Reihenfolge nicht passt. Das lässt sich nicht ganz einfach vermeiden, wenn die Stacks unabhängig bleiben sollen. Also nach einem Neustart immer mal schauen, ob alles läuft.

Einrichtung

Die Web-GUI des NPM wird mit http://Server1:81 aufgerufen. Dort muss man sich anmelden und dann mit Hosts->Add Proxy Host einen neuen Eintrag erzeugen.

Details:

  • Domain Names: ich-mag.gnulinux.ch
  • Schema: http
  • Forward Hostname / IP: Server2
  • Forward Port: 81 (das muss dann im docker-compose.yaml so konfiguriert werden)
  • Der Rest ist optional

SSL:

Bei vielen Diensten ist es von Vorteil oder sogar notwendig, SSL/https zu verwenden. Dazu muss bei SSL Certificate einfach Request a new SSL Certificate gewählt werden. Dann muss man natürlich noch seine E-Mail angeben und den Terms of service zustimmen. Der Rest ist wieder optional.

Zugriff Einschränken:

Es gibt dann noch die Access Lists. Damit kann man den Zugriff einschränken. Dies ist vor allem bei Diensten nützlich, die SSL zwingend voraussetzen. Wenn man auf diese über einen Intranet-Namen zugreift, ist das Zertifikat ungültig oder man muss sich mit selbst signierten Zertifikaten herumärgern. Beim Zugriff über den externen Namen, für den NPM ein Zertifikat geholt hat, ist das kein Problem. Und der Zugriff ist dann trotzdem nicht von außen möglich.

Ich habe mir eine Access List private only angelegt:

Details

Name: private only

Access

  • allow: meine feste IP Adresse
  • allow: 192.168.0.0/16 (mein Intranet)
  • allow: 10.0.0.0/8 (mein VPN)
  • deny: all

Bei dem Proxy Host kann dann unter Details->Access Lists die neue Liste private only ausgewählt werden.

Um die Adressen, die nicht geblockt werden sollen, zu bestimmen, kann man auch erst mal alles sperren und im Log schauen, was geblockt wurde.

Server-Dienst

So, jetzt kommen wir zum eigentlichen Ziel: der Web-Dienst. Auf Server2 wird ein Verzeichnis angelegt. Da ich einige Dienste laufen habe, habe ich ein Verzeichnis docker und darunter lege ich dann Service1 an. Hier wird für einen simplen Web-Server die Datei docker-compose.yaml mit folgendem Inhalt angelegt:

version: "3"

services:
  app:
    image: nginx
    ports:
     - 81:80 # 81 ist der Port, der in NPM angegeben wurde
    volumes:
     - ./html:/usr/share/nginx/html

Jetzt muss das Verzeichnis ./html und dort die Datei index.html angelegt werden mit beliebigem Inhalt, z.B.:

Gnulinux.ch ist toll.

Und schon kann der Container gestartet werden:

docker-compose up -d

Das war’s schon. Jetzt sollte unter http[s]://ich-mag.gnulinux.ch die neue Seite angezeigt werden.

Hardware

Hier noch ein paar Worte zu meinen Erfahrungen mit der Hardware:

Womit ich angefangen habe, kann ich gar nicht mehr sagen. Irgendwann bin ich dann beim Raspberry Pi 2 Modell B oder so gelandet. Ich hatte insgesamt 4 Stück, die alle sehr oft abgestürzt sind. Ich bin dann auf einen Intel NUC umgestiegen: Intel Celeron J3160 (Quad-Core 4x 1.6-2.24GHz, 4 Threads, 2 MB Cache, 6W TDP). Mit der Nextcloud hat der dann aber auch irgendwann geschwächelt. Habe dann noch einen zweiten NUC angeschafft: Intel Core i5-9300H (4x 2.40-4.10 GHz Quad-Core, 8 Threads, 8 MB Cache, 45 W TDP). Der hat ein paar Jahre ganz gute Dienste geleistet. Die system-load ist meistens unter 1 geblieben. Irgendwann ist sie aber immer öfter darüber gestiegen. Und wenn sie über 4 stieg (quad-core) dann ist nicht mehr viel gegangen. Die Last ist dann oft dauerhaft über 10 geblieben.

Sehr interessant ist aber, dass die Last jetzt wieder meistens unter 1 liegt. Und zwar ziemlich genau seit dem Zeitpunkt, als ich gelesen habe, dass wieder mal ein großes Botnetz hochgenommen wurde. Was da auf jeden Fall helfen sollte, ist fail2ban (siehe unten).

Inzwischen denke ich, dass die Taktfrequenz der CPU nicht so entscheidend ist, wie man erst mal denkt, sondern die TDP (6W bzw. 45W). Wenn die Last steigt, sinkt sofort auch die Frequenz der CPU, wenn diese die Wärme nicht abführen kann, was wieder zu einer höheren Last führt. Ich habe den NUC dann hochkant aufgestellt, damit durch Konvektion die Belüftung besser wird. Das hat schon etwas gebracht. Ich hatte schon überlegt, wieder mal etwas Schnelleres zu kaufen, bis auf einmal alles gut war. Momentan bin ich wieder sehr zufrieden.

Man sieht also, die Performance des Servers hängt nur zum Teil an der Hardware, ein nicht zu unterschätzender Teil hängt an der Verteidigung gegen Angriffe.

Ausblick

Wenn man den ersten Dienst mal am Laufen hat, sind weitere Dienste oft ein Kinderspiel. Dienste, die ohne viel Konfiguration auskommen, sind in einer Minute am Laufen.

Eine Übersicht aller Container bekommt man z.B. mit Portainer.

Quelle: https://nginxproxymanager.com

Tags

Self Hosted, server, Selfhosting

Marco
Geschrieben von Marco am 29. Januar 2024 um 12:40

Netter Artikel, Danke

Tom
Geschrieben von Tom am 29. Januar 2024 um 13:32

Hi,

ich bin gerade dabei mein NginxProxyManager durch Nginx und acme.sh in einem Proxmox-Kontainer abzulösen. Es ist oft so, dass NPM nach einem Update nicht mehr wie erwartet funktioniert. Alleine deswegen würde ich nicht das latest-Image sondern immer eine bestimmte Version (z.B. 2.10.4) nehmen. Backup vor dem Update ist hier essentiell !

Maik
Geschrieben von Maik am 29. Januar 2024 um 17:19

Traefik macht in einer Docker-Umgebung viel mehr Sinn. Die vielen Vorteile will ich hier gar nicht aufzählen. Ich würde nicht mehr zu Nginx zurück wechseln.

Tim Moritz Admin
Geschrieben von Tim Moritz am 30. Januar 2024 um 08:16

Sehe ich auch so, hab ja auch schon nen Traefik Artikel geschrieben. Du darfst aber gerne noch einen schreiben, wo du alles aufzählst, was du hier in den Kommentaren nicht tun willst :)

Stefan
Geschrieben von Stefan am 30. Januar 2024 um 07:49

Was ist der große Vorteil von Docker und nginx? So im Vergleich zu einem LAMP-Stack? Ständig stolpert man über Sachen, die jemand im Docker laufen lässt.

Tim Moritz Admin
Geschrieben von Tim Moritz am 30. Januar 2024 um 08:13

Innerhalb von Docker ist es egal was "in" der Webanwendung steckt. Du kannst also Problemlos Webanwendungen nebeneinander fahren, die unterschiedliche Stacks haben, also die eine Anwendung in Java, die andere in Go und noch 3 andere mit unterschiedlichen PHP-Versionen und -Konfigurationen. Um das alles brauchst du dich nicht kümmern und nichts von den Abhängigkeiten auf deinem Host installieren oder konfigurieren.

kamome
Geschrieben von kamome am 30. Januar 2024 um 08:51

Ja, nur müssen dann (natürlich) auch alle drei PHP-(Container-)Versionen aktualisiert werden (und user namespace sollte für Docker ohnehin Pflicht sein (https://docs.docker.com/engine/security/userns-remap/).

kamome
Geschrieben von kamome am 30. Januar 2024 um 08:54

Danke!

> Ich verwende keine unbenutzte Adresse, da es ja möglich ist, dass diese irgendwann doch vergeben wird.

Genau dafür gibt's doch example.com (oder .test (oder inoffiziell auch .lan)).

Mutant77
Geschrieben von Mutant77 am 30. Januar 2024 um 13:58

Ich habe aktuell im letzten Monat mit genau der beschriebenen Konfiguration gekämpft.

Zunächst ist meist das Problem, dass man einen DynDNS braucht und in dem Fall nur eine Domain zu Verfügung hat. Was bei einem Dienst kein Problem ist, aber wenn man viele kleinere Sachen - nextcloud, piwigo, twiki usw. - nutzen möchte, wird es schnell komplex und wie schon einige Kommentatoren hier anmerken, traefik ist dann vermutlich besser.

Ich habe NPM zuerst installiert und muss sagen, es ist in so einem Umfeld kompliziert. Bzw. führt nicht immer zum Erfolg. Ich habe piwigo nicht vernünftig zum laufen bekommen bisher. Daher werde ich mir demnächst traefik anschauen, das scheint eher auf die Problematik der verschiedenen Dienste einzugehen.

Der eigene Server zu Hause kann also schon komplexer sein als manche Beschreibungen über Docker, NPM oder Traefik das meist darstellen.

und mit der Nextcloud docker installation hatte ich schwer zu kämpfen. Sie lief zwar und war auch erreichbar, aber ich konnte nicht mit mehreren Acounts Verzeichnisse teilen. Weil, aus welchen Gründen auch immer, NC die dazugehörigen Javascripte nicht auslieferte und daher die Funktion im Browser nicht aufgerufen werden konnte. Aber in der App auf den Mobilgeräten. Damit war dann das Problem gelöst. Hat mich aber auch ein paar Tage gekostet, wo ich am Desktop saß und versucht habe rauszufinden was das Problem ist.

und aktuell hatte ich im beruflichen Umfeld auch mit einem Dockerproblem zu kämpfen. Das hat mich dann drei Tage gekostet, da Fehlersuche im Container auch erst gelernt sein muss. So einfach wie man das gerne darstellt sind Container dann nicht mehr. Zumal immer mehr Schichten oben drauf, auf den einschlägigen Kanälen beworben werden. Sei es so was Proxmox, Portainer, Kubernetes oder was weiß ich, was es noch alles gibt. Ich bin da schnell als Hobbyserveradmin überfordert und ständig am suchen.