Im Docker-Hub gibt es für viele Dienste bereits fertige Images. Die Nutzung der Images ist in der Regel frei. Meine Erfahrung ist gemischt mit der Nutzung. Bei einigen klappt der Einsatz auf Anhieb, bei anderen ist nach mehrstündigen Anpassungen nicht der gewünschte Erfolg in Sicht. Bei Images die nicht zu den CORE-Images zählen ist die Erfolgsquote nicht gerade bei 100%. Auch die Aktualisierung der Images lässt vielfach zu wünschen übrig. Manchmal liegt ein letztes Update Jahre zurück.

Die Lösung für dieses Problem ist aus meiner Sicht einfach und lautet – Containerselbstbau. Bei vielen Diensten handelt es sich um die Dienste, welche auf einem Linux Betriebssystem basieren. Eines der am meisten genutzen und gut unterstützen CORE-Images ist Ubuntu. Grund genug dieses als Basis zu nutzen.

Um aufzuzeigen wie es geht und welche Schritte notwendig sind werde ich hier einmal aufzeichen, wie die VPN Lösung PiVPN in einem Container auf einem Virtual Private Server von Strato mit 1 Core und 1GB Ram erstellt und betrieben werden kann. Ebenfalls installiert habe ich es auf einem Raspberry Pi400-DE, ein PI4 mit 4GB RAM.

Schritt 0: Vorbereitung

Zum Einsatz kommt ein VPS (oder ein Raspberry Pi mit installiertem Linux-OS, Ubuntu). Auf dem Server bin ich per SSH mit dem Benutzer root angemeldet.

Die Docker Installation, falls noch nicht erfolgt, ist im Schritt 0.1 beschrieben.

Portainer-CE ist ein hilfreiches Tool zum Management der Containerlösung – ist aber nicht zwingend notwendig.
Nachfolgende Bilder zeigen einige Details zur Umgebung.

Schritt 0.1: Docker Installation

Auf einem Ubuntu VPS ist die Installation einfach mit folgender Zeile abgeschlossen

Auf einem Raspberry Pi erfolgt die Installation von Docker inklusive der Portainer-CE Installation mit folgenden Befehlen:

Auf Portainer wird dann über https:<IP-Adresse-des-RaspberryPI>:9443 zugegriffen. Die finale Installation erfolgt durch die weitere Menüführung.

Schritt 1: Auswahl und laden des Basisimages

Als Basisimage wähle ich aus dem Docker-HUB UBUNTU in der Version 22.04. Genau diese ist auch auf dem Server als Basis-OS installiert.

Auf der Konsole lade ich das Image in das lokale Repository.

root@ubuntu:~# docker pull ubuntu:22.04
22.04: Pulling from library/ubuntu
Digest: sha256:8eab65df33a6de2844c9aefd19efe8ddb87b7df5e9185a4ab73af936225685bb
Status: Downloaded newer image for ubuntu:22.04
docker.io/library/ubuntu:22.04

Schritt 2: Anpassung des Basisimages

Ziel dieses Arbeitsschrittes ist die generelle Anpassung des Images auf meine Wünsche. Die Anpassungen sind wie folgt:

  • Installation weiterer Pakete für Netzwerkadministration, Textverarbeitung, …
  • Shell Zugang mit SSH

Nun wird es Zeit auf dem Server ein Projektverzeichnis zu erstellen. Ich habe dazu im Homeverzeichnis von root, /root/ ein Verzeichnis docker erstellt und darin gibt es eine weitere Hierachie für die Images+Versionen. In diese Verzeichnisse werden die benötigten Files für die Images kopiert und daraus die Images gebaut.

Die Anpassungen an dem Image werden in einer „Steuerdatei“ dem Dockerfile gemacht. Und genau so heisst die Datei auch. Mein Inhalt ist folgender:

Jetzt brauchen wir noch eine sshd_config Datei. Ich nehme gern einfach die vom Hostsystem und passe die entsprechenden Parameter einfach an. Es kann auch eine neue mit den gewünschten Einstellungen erzeugt werden.

Hier ein Beispiel für eine minimale Datei

So wird sie vom Hostsystem kopiert.

Um das neue image zu erstellen wird folgender Befehl verwendet. Zu beachten ist der . am Ende.

Wenn bis hier keine Fehlermeldungen gekommen sind kann mit folgendem Befehl geprüft werden, ob es jetzt ein neues Image gibt.

Schritt 3: Starten des Image und Anmelden am System

Das angepasste Ubuntu-Image soll jetzt gestartet werden. Die Parameter bedeuten folgendes:

  • run = starten des Containers
  • -d = detached
  • -p = Netzwerkport Mapping, mehfache Parameter möglich
  • vhs-vpn:1.0 = das zu startende Image

root@ubuntu:~/docker/VHS/VPN1.0# docker run -d -p 222:22 -p 1195:1194 vhs-vpn:1.0
a8d09a138e8dd037021ac7cedf250da74c5c7f9eaa1f618da83bd02da3f82a97
root@ubuntu:~/docker/VHS/VPN1.0#

Mit folgendem Befehl lässt sich prüfen, ob der Container läuft.

Taucht der Container in der Tabelle auf – läuft er. Nun läuft der Container und der SSH-Server ist auf dem Hostsystem unter Port 222 zu erreichen. Der noch nicht installierte openVPN-Server ist später unter Port 1195 auf dem Hostsystem erreichbar. Der Port ist bereits jetzt allokiert, noch nicht genutzt.

Jetzt ist es an der Zeit mit Putty eine SSH Verbindung zum Container aufzubauen. Anmeldung mit den Benutzer root und dem Kennwort startpasswd und auf dem Port 222 des Hostsystems. Nach der Anmeldung das Kennwort ändern nicht vergessen

Der Container verhält sich nun wie ein eigener Server mit dem Betriebssytem Ubuntu. Er stellt jetzt eine gute Grundlage für die Installation von weiteren Services, in unserem Fall PiVPN, dar.

An dieser Stelle habe ich meinen für mich angepassten Basis-Container erstellt. Dieses Image kann nun als „Microservice“ mehfach auf dem System gestartet werden oder kann als Basis für die Installation von weiteren Diensten dienen. In den folgenden Schritten wird dieses Basis-Image dazu verwendet die VPN-Lösung PiVPN zu installieren.

Noch einmal als Hinweis, das Image verhält sich jetzt generell wie ein ganz normaler Server und auch die Installation von PiVPN ist identisch wie auf einem Server.

Schritt 4: Basisinstallation von PiVPN

Die Netzwerkintegration von Docker erfordert das die Services auf Basis TCP kommunizieren. Das ist wichtig, denn openVPN verwendet per default UDP. Weiterhin beachtet werden muss, der Port ist 1194, jedoch kann dieser Port am Host nur ein einziges Mal vergeben werden. Laufen mehrere Container mit dieser Anforderung muss ein anderer Hostport verwendet werden. In unserem Beispiel 1195.

Die Daten (VPN-Konfiguration und Konfigdateien der Benutzer) liegen innerhalb des Containers und befinden sich im Conatinerimage. Wird der Container gelöscht sind die Daten verloren. Eine Lösung dazu kommt in den weiteren Schritten.

PiVPN wird mit einem Link auf eine Webseite installiert. Das Installationsprogramm wird heruntergeladen und lokal auf dem System ausgeführt. Da die Installation weitesgehend Menügeführt ist beschreibe ich diese nicht weiter. Die wichtigen Parameter sind:

  • Protokoll = TCP
  • Port = 1195 (weil ich bereits einen Dienst auf 1194 laufen habe)
  • IP-Adresse (DNS Name) = IP-Adresse oder DNS Name des Hostsystems
  • DNS Server = Google
  • unattended upgrades = NO
  • Reboot = No

Link um die Installation zu starten. Diese Zeile einfach im Putty-Fenster als Benutzer root ausführen.

Nach der Installation muss der Container neu gestartet werden. Jetzt zeigt sich der Unterschied zu einem echten Server. Der Befehl reboot hilf hier nicht, denn das bei einem Server vorhandene INIT-System gibt es hier nicht.

benötigt wird die Container-ID. Diese wird mit folgendem Befehl angezeigt.

Mit folgendem Befehl wird der Container neu gestartet.

Nach dem erneuten anmelden am System kann durch eingabe von „pivpn“ überprüft werden, ob die Installation erfolgreich war.

Schritt 5: Starten des Containers mit privileged Flag

Um den Container zu starten muss das laufende Image gestoppt werden und neu aufgerufen werden.

Eine Änderung im laufenden Betrieb geht nicht ohne weiteres. Da wir hier auch noch das starten weiterer Dienste innerhalb des Containers behandeln müssen erstellen wir vom laufenden Container ein neues Image mit folgendem Befehl.

Jetzt gibt es ein neues Imge in dem unsere bisherige Installation enthalten ist. Dieses Image kann jetzt gestartet werden. Vorher das Image mit Version 1.0 stoppen.

Ohne das „privileged-Flag“ würde es openVPN nicht möglich sein das TUN-Interface zu erstellen. Das prüfen wir nun durch starten des openVPN-Servers. Der Prozess wird in den Hintergrund gelegt damit wir weiter auf der Shell arbeiten können.

Mit ifconfig läst sich prüfen, ob das TUN0-Interface vorhanden ist. Prinzipiell ist der VPN-Server damit am laufen, jedoch startet der VPN-Dienst noch nicht automatisch. Das wird im folgenden Schritt angepasst.

Schritt 6: Anpassen des Startsystems, Starten meherer Dienste

Nun wechseln wir auf dem Hostsystem in das Projektverzeichnis /root/docker/VHS/VPN1.2 und erstellen dort die Datei Dockerfile mit folgendem Inhalt

Danach wird eine Datei erstellt mit dem Namen startup.sh und folgendem Inhalt.

Auf Basis des Dockerfiles wird das Image jetzt angepasst.Mit folgendem Befehl wird daraus ein neues Image erstellt.

In der Tabelle wird jetzt ein neues Image mit dem namen vhs-vpn und dem Tag 1.2 angezeigt. Das kann jetzt gestartet werden. Vorher den laufenden Container beenden.

Nach dem erneuten anmelden am System wird mit „ifconfig“ und „netstat -an“ geprüft ob das Interface und die Dienste aktiv sind. Jetzt kann ganz normal mit PiVPN Benutzerprofile erzeugt werden und die Verbindung zum openVPN Server aufgebaut werden.

Es gibt einige Optionen welche in das Image eingebaut werden könnten. Eine Anforderung könnte sein, die Konfigurationsdaten des openVPN Server zu speichern. Auch hierzu gibt es mehere Wege. Der Container verhält sich in vielen Fällen wie ein Server, bdeutet auch das Thema Backup & Restore müsste analog betrachtet werden. In diesem Zusammenhang könnte dann auch die Datensicherung der Konfiguration und Profile erfolgen.

Ein anderer Weg ist, Dateien und/oder Verzeichnisse aus dem Container in das Hostsystem zu mappen. Damit sind die Daten im Hostsystem verfügbar und sind auch nach dem löschen eines Containers nicht verloren. Diese Einstellungen würden dann über den Befehl docker run und entsprechende Parameter (-v) umgesetzt werden.

Die Beschreibung ist auf Basis eines x86 Servers erstellt worden. Die Plattform sollte keine Rolle spielen und kann auch genau so auf einem Raspberry Pi umgesetzt werden.