Campus-Internet is an application to manage a whitelisting DHCP server. Since this application will be used by non-technical users, the following german documentation is written as non-technical as possible.
I use arch btw.
Du möchtest schnell loslegen ohne viel zu lesen? Los!
- Installiere Docker
- Lade den Ordner herunter
- Navigiere in den Unterordner infrastructure
- Erstelle eine Konfigurationsdatei
.env
, dazu kann diese Vorlage verwendet werden - Erstelle ein Docker Volume für die Datenbank mit
docker volume create dhcp-db
, falls nicht vorhanden - Starte die Anwendung mit
docker-compose up -d
- Prüfe mit
docker ps
ob alles funktioniert hat
Etwas funktioniert nicht oder diese Anleitung ist zu ungenau? Dann hier die ausführliche Dokumentation:
Die Anwendung soll eine einfache und wartbare Lösung darstellen, um in einem Netzwerk nur bestimmten Geräten Zugriff auf das Internet zu gewähren. Dazu soll mithilfe der Anwendung eine Liste an erlaubten Geräten (die sogenannte whitelist) erstellt werden.
Im Programm sollen die Nutzer und die sogenannte MAC-Addresse ihrer Geräte hinterlegt werden. Erst wenn die MAC-Adresse eines Gerätes auf der Whitelist steht, bekommt das Gerät eine IPv4-Adresse zugewiesen und auf das Internet zugreifen. Ohne IPv4-Adresse ist ein Internetzugriff nicht möglich.
Das Programm besteht aus mehreren einzelnen Services. Diese werden im Folgendem aufgelistet und ihre Funktion erläutert.
Der DHCP-Server nutzt das Dynamic Host Configuration Protocol v4 (DHCPv4) um Geräten auf Anfrage eine IPv4-Adresse zuzuteilen. Wir nutzen in dieser Anwendung den Kea DHCP Server. Dieser wird in der kea-dhcp4.conf konfiguriert. Die Dokumentation zur Konfiguration der hier verwendet Kea Version 2.4.0 findet sich hier: https://kea.readthedocs.io/en/kea-2.4.0
Das Frontend ist die Website, auf der Nutzer angelegt, bearbeitet oder gelöscht werden können. Es kann ganz normal über einen gängigen Webbrowser aufgerufen werden. Alle angezeigten Daten werden vom Backend abgefragt, alle Formulare werden an das Backend gesendet.
Alle im Frontend eingegebenen Informationen werden an das Backend gesendet. Hier werden zum Beispiel freie IPv4-Adressen ermittelt und Nutzerdaten mithilfe der Datenbank abgespeichert. Das Backend erstellt und verwaltet die Whitelist. Wenn sich diese ändert, etwa durch einen neuen oder gelöschten Nutzer, sendet das Backend ein Signal an den DHCPv4-Server, wodurch dieser seine Konfiguration neu lädt. Auch der Login im Frontend wird im Backend behandelt.
Wir nutzen PostgreSQL 15 als Datenbank. Sie dient zur Persistierung der Nutzerdaten auf der Festplatte des Servers. Wie bei allen relationalen Datenbanken werden auch hier die Daten in Form von Tabellen gespeichert. Es werden regelmäßige Backups empfohlen, dazu später mehr.
Um die Anwendung unabhängig vom Betriebssystem zu halten, die Installation zu vereinfachen und den Nutzern das management der einzelnen Services zu erleichtern, nutzen wir die Virtualisierungssoftware Docker. Docker startet die einzelnen Services in sogenannten Containern. Ein Docker Container ist vergleichbar mit einer Virtuellen Maschine (VM), nur sehr viel leichtgewichtiger bezüglich zusätlicher Arbeisspeicher- und Prozessorbelastung.
Docker-Compose dient dazu, eine Anwendung mit mehreren Services und daher auch mehreren Containern zu managen. Wir benötigen also sowohl Docker als auch Docker-Compose
Ich empfehle einen Rechner mit mindestens 4GB RAM und einem Prozessor, der mindestens dem an Leistung entspricht, was 2020 einmal Mittelklasse war. Wenn du, lieber Netzer, diese Dokumentation liest und darüber nachdenkst einen neuen Rechner anzuschaffen, sind wahrscheinlich ausnahmslos alle Prozessoren auf dem Markt ausreichend.
Es sollte ein modernes und verbreitetes Betriebssystem installiert werden, idealerweise eine aktuelle Linux-Distribution mit großer Community und einfacher Bedienung. Es sollte eine Long-Term-Support (LTS) Variante verwendet werden. Wir nutzen das aktuellste Ubuntu LTS 22.04
Es gibt sowohl Docker mit grafischer Oberfläche als auch Docker ausschließlich für die Kommandozeile. Da unser Server keine grafische Oberfläche besitzt, sollte unbedingt Docker ohne grafische Oberfläche installiert werden. Bei Docker heißt das Docker Engine. Die Dokumentation für die Installation der Docker Engine findet man hier: https://docs.docker.com/engine/install.
Die Anwendung selbst besteht aus einem Ordner mit mehreren Unterordnern und befindet sich in einem GitHub Repository unter https://github.com/jrasko/campus-intenet. Es existieren 2 Möglichkeiten, die Anwendung zu installieren:
-
(empfohlen) Es wird git verwendet werden. Unter Ubuntu lässt sich git ganz einfach mit dem Paketmanager installieren:
sudo apt-get insall git
. Mithilfe von git kann man sich den Ordner mit folgendem Befehl auf den eigenen Rechner kopieren:git clone https://github.com/jrasko/campus-intenet.git
. -
Alternativ kann man sich die Dateien als ZIP-Ordner auf der GitHub Seite herunterladen und entpacken. Diese Methode wirkt einfacher, hat allerdings nicht die Vorteile, die eine Versionskontrolle wie git bietet.
Im Ordner befinden sich neben dieser README.md Datei 3 Unterordner. Konfigurieren, Starten und Stoppen der Anwendung findet im Unterordner infrastructure statt.
Die anderen beiden Ordner sind nur dann relevant, wenn an der Programmierung der Services etwas geändert werden soll. backend enthält den in go oder golang geschriebenen backend-Service, dessen Aufgabe bereits oben schon erläutert wurde. In frontend befindet sich die in Vue.js geschriebene Webanwendung, auch diese wurde bereits in einem oberen Abschnitt beschrieben.
Hier eine Vorlage für eine Standardkonfiguration:
POSTGRES_PASSWORD= # >= 20 zufällige Bytes
LOGIN_USER= # beliebiger nutzername
LOGIN_PASSWORD_HASH= # generiert mit argon2
HMAC_SECRET= # 64 zufällige Bytes
CIDR= # Subnetzmaske mit erster vergebenen IP
Die Konfiguration der Anwendung findet hauptsächlich mithilfe sogenannter Umgebungsvariablen statt. Diese können im Service ausgelesen werden. Eine Umgebungsvariable hat immer einen Namen und einen Wert. Es sollte unbedingt eine .env-Datei im infrastructure Ordner zum Setzen der Umgebungsvariablen verwendet werden.
Dabei sieht die Datei .env wie folgt aus:
NAME_1=ein-Wert
ANDERE_UMGEBUNGSVARIABLE=anderer-Wert
...
Es können die folgenden Konfigurationen in .env hinterlegt werden.
Name | Pflicht | Services | Details |
---|---|---|---|
POSTGRES_HOST | Backend,DB | DB/Postgres | |
POSTGRES_DB | Backend,DB | DB/Postgres | |
POSTGRES_USER | Backend,DB | DB/Postgres | |
POSTGRES_PASSWORD | X | Backend,DB | DB/Postgres |
LOGIN_USER | X | Backend | Authentifikation |
LOGIN_PASSWORD_HASH | X | Backend | Authentifikation |
HMAC_SECRET | X | Backend | Authentifikation |
CIDR | X | Backend | CIDR |
SKIP_DHCP_RELOAD | Backend | Sonstiges | |
URL | Backend | Sonstiges | |
OUTPUT_FILE | Backend | Sonstiges |
Mit folgendem Befehl lässt sich unter Linux eine zufällige Zeichenfolge erzeugen:
head -c <Bytes> /dev/random | base64 -w 0
Bytes gibt dabei an, wie viele zufällige Bytes generiert werden sollen. Die zufälligen Bytes werden danach mit Base64 kodiert, um eine lesbare Ausgabe zu erhalten. Die Ausgabe ist etwa 1/3 länger als Bytes.
Diese Konfigurationen betreffen die Datenbank. Außer POSTGRES_HOST werden diese Umgebungsvariablen sowohl von der Datenbank als auch vom Backend eingelesen, damit das Backend eine Verbindung zur Datenbank herstellen kann.
- POSTGRES_HOST wird nur vom Backend eingelesen und gibt an, unter welcher IP oder welchem Hostnamen die Datenbank zu
finden ist. Da innerhalb des Docker-Netzwerkes die Containernamen als Hostnamen verwendet werden, ist POSTGRES_HOST
standardmäßig
dhcp_db
, also der Name des Datenbankcontainers. - POSTGRES_DB ist der Name der Standard-Datenbank. Dieser ist defaultmäßig
postgres
. - POSTGRES_USER ist der Standard-Benutzername der Datenbank. Defaultwert ist
postgres
. - POSTGRES_PASSWORD ist das Password für den Standard-Benutzer. Dies ist das einzige verpflichtende Feld. Ich empfehle etwa 20 zufällige Bytes.
Wenn sich ein Nutzer im Frontend anmeldet, prüft das Backend Benutzername und Passwort und erstellt dann ein sogenanntes Token (JWT), das 2 Stunden gültig ist und bei jeder Anfrage an das Backend mitgesendet wird. Das Token wird mithilfe einer kryptografischen Funktion signiert, dazu wird ein Secret benötigt. Jeder, der Kenntnis über das Secret hat, kann valide Token ausstellen. Daher sollte das Secret niemals auf irgendeine Weise mit irgendjemanden geteilt und ausschließlich in der .env Datei vorhanden sein.
Damit ergeben sich folgende Konfigurationen:
- LOGIN_USER ist der verwendete Nutzername, der auch im frontend angegeben werden muss
- LOGIN_PASSWORD_HASH ist der Hash des Passwortes, näheres dazu im nächsten Abschnitt
- HMAC_SECRET ist das bereits erläuterte Secret, mit dem die Token signiert werden. Als Wert sollten 64 zufällige Bytes mit einem kryptografischen Zufallsgenerator erzeugt werden.
Um zu verhindern, dass ein Angreifer im Falle einer schweren Sicherheitslücke das Passwort möglicherweise mehrfach verwendete Passwort im Klartext auslesen kann, wird in der .env Datei ein Hash-Wert des Passwortes gespeichert. Ein kryptografischer Hash ist eine Einwegfunktion, sodass sich nicht aus dem Hash auf das Passwort schließen lässt.
Als Hashfunktion nutzen wir Argon2ID, welche Schutzfunktionen gegen Angriffe mit spezialisierter Hardware oder mit mehreren Threads anbietet. Argon2ID kann über verschiedene Parameter eingestellt werden:
- Memory ist die Belastung des Arbeitsspeichers zur Berechnung des Hashes.
- Parallelism: Die Anzahl an aufzuwendenden Threads. Muss eine Zahl zwischen 1 und den zur Verfügung stehenden CPU-Kernen sein.
- Salt sollte mindestens aus 16 zufälligen Bytes bestehen
- KeyLength 32 Bytes empfohlen, sollte nur aus gutem Grund geändert werden
- Iterations gibt an, wie lange die Berechnung dauert. Am besten werden erst alle anderen Faktoren eingestellt und Iterations auf 1 gesetzt. Erst danach sollte Iterations so eingestellt werden, dass Generierung eines Hashes etwa 1s dauert.
Memory und Parallelism sollten so eingestellt werden, ohne dass durch das Hashing so viele Ressourcen beansprucht werden, dass andere Prozesse gestört werden. So hoch wie möglich, so niedrig wie nötig.
Unter Ubuntu kann das Programm argon2 benutzt werden. Es kann mit sudo apt-get install argon2
installiert
werden.
Es wird wie folgt benutzt:
echo -n <password> | argon2 <salt> -id -m <memory> -p <parallelism> -t <iterations>
KeyLength 32 ist bereits der Standardwert. Mit argon2 -h
wird der Befehl erklärt und Einheiten angezeigt.
Das Programm gibt sowohl den Hash, der in die Konfiguration übernommen werden kann, als auch die benötigte Zeit an.
Mit dem Feld CIDR lässt sich Einstellen, in welchem Subnetz IP-Addressen vergeben werden. Dazu soll die erste IP-Addresse angegeben werden, die verteilt werden sollte, sowie die Subnetzmaske.
Beispiel:
192.168.1.15/24
würde zwischen 192.168.1.15
und 192.168.1.254
IP-Adressen vergeben.
192.168.0.20/16
würde zwischen 192.168.0.20
und 192.168.255.254
Adressen vergeben.
Die letzte IP eines Subnetzes ist die Broadcast-Adresse und bleibt daher unbelegt.
- SKIP_DHCP_RELOAD verhindert das Senden eines config-reload Signals des Backends an den DHCP-Server. Dadurch kann das Backend funktionieren, auch wenn kein DHCP Server läuft.
- URL sollte nicht geändert werden, außer es gibt einen triftigen Grund. Ändert nur die URL INNERHALB des docker containers. Zum Ändern der von außen erreichbaren URL, docker umkonfigurieren (siehe unten).
- OUTPUT_FILE sollte nicht geändert werden, außer es gibt einen triftigen Grund. Ändert nur den Dateipfad INNERHALB des docker containers. Zum Ändern des Pfades auf dem Rechner, docker umkonfigurieren (siehe unten).
Zum Starten der Anwendung muss im Ordner infrastructure der Befehl docker-compose up -d
ausgeführt werden.
Mit docker-compose down
wird die Anwendung gestoppt.
Mit docker ps
lassen sich alle in Docker laufenden Services anzeigen. Es empfiehlt sich, nach dem Starten wenige
Sekunden zu warten und mit dem Befehl zu prüfen, ob alle container den Status UP haben. Falls nicht, sollte mithilfe
von Logs versucht werden, das Problem zu identifizieren
Einzelne Services (container) lassen sich mit docker stop <containername>
stoppen und mit
docker start <containername>
wieder starten.
Sollte ein Programm abstürzen, wird es von Docker automatisch neu gestartet.
Alle wichtigen Daten werden mithilfe von sogenannten Docker volumes gespeichert, sodass sich alle container beliebig starten, stoppen und sogar löschen lassen, ohne das ein Datenverlust eintritt.
docker logs <containername>
zeigt die Logs von einem container. Mit -f
werden die Logs auch fortlaufend angezeigt.
Insbesondere die Logs vom Backend und vom DHCPv4-Server sind zur Fehlersuche relevant.
Sollte sich die Konfiguration des DHCPv4-Servers ändern, muss das container Image neu gebaut werden. Dazu muss
docker-compose up -d --build dhcp4
ausgeführt werden. Dies kann etwas länger dauern.
Ich beziehe mich auf einen Post in Stack Overflow für Linux Systeme: [https://stackoverflow.com/a/29913462]
Folgendes Kommando erstellt ein Backup der Datenbank, die als Docker Container mit dem Namen <containerName>
läuft
und speichert es in der Datei <Ordner>/backup_<Datum>
docker exec -t <containerName> pg_dumpall -c -U postgres > <Ordner>/backup_`date +%d-%m-%Y"_"%H_%M_%S`.dump
Mit dem folgenden Befehl lässt sich ein backup in der Datenbank einspielen:
cat <dateiname> | docker exec -i <containerName> psql -U postgres
Man nutze das Programm crontab -e
(ggf. als sudo ausführen), um automatisch regelmäßig ein Backup zu erstellen.
Es kann hilfreich sein, einen cron expression generator zu verwenden (einfach googlen).