Wissenshäppchen: Security

Erlerntes zusammengefasst und aufbereitet
Wenn ich mal wieder experimentiert und mein Wissen erweitert habe, muss ich es dringend aufschreiben.
Aus Notizen entstehen dann irgendwann Niederschriften, die ich euch hier nicht vorenthalten möchte.
Hilfe aus der Ferne
Security Netzwerk
15.02.2023 | Andreas Müller | 0

Als jemand, der in der IT Branche arbeitet, wird man sehr gerne von Familie und Freunden als freier Support-Mitarbeiter für so ziemlich jedes Problem hergenommen.

Viele Sachen lassen sich per Telefon oder E-Mail lösen, doch ab und an führt der Weg nicht daran vorbei zu sagen "Ja, ich komm mal vorbei".
Doch wenn - wie öfters mal in meinem Fall - viele Kilometer dazwischen liegen, so wird das etwas komplizierter, da es eben nicht "mal schnell" geht.

Hier kommen dann Lösungen, wie TeamViewer oder AnyDesk zum Einsatz.
Leider ist es jedoch so, dass solche Produkte gerne mal empfindlich an der Preisschraube drehen... zudem sind es proprietäre Lösungen und am Ende weiß niemand so genau wo die Daten eigentlich langlaufen und was der Datenschutz davon hält.

Vor kurzem bin ich über die Anwendung RustDesk gestolpert.
RustDesk ist eine OpenSource Software - somit kann sich Jeder den Quellcode ansehen (und sich auf die Suche nach Sicherheitslücken und Fehlern begeben).
Doch nicht nur der Client ist OpenSource, sondern beim ersten Besuch der Webseite prangert auch schon ein "self-host" direkt im Blick. Es gibt also die Möglichkeit, selber einen Server zu betreiben - PERFEKT!

Ich finde, ein Blick lohnt sich und wer möchte, darf gerne meine Server-Instanz benutzen.
Host: rd.am-wd.de
Key: RuqrpL4mJ1XsR6V3yZmFdxeijLu5sm3OBeEJQLDQOEk=

Um den Server einzustellen, muss man links in der Spalte auf die drei Punkte neben der ID gehen und dann auf "ID/Verbindungsserver" - siehe Screenshots!
Unter Windows klappt es auch durch die Benennung der Datei, sofern RustDesk nicht bereits einmal lief und ein eigener Server eingestellt wurde:
rustdesk-host=rd.am-wd.de,key=RuqrpL4mJ1XsR6V3yZmFdxeijLu5sm3OBeEJQLDQOEk=.exe.


SSLH - Ausbruch aus den Firewall-Barrieren
Security VPN Linux
19.10.2019 | Andreas Müller | 0

Die klassischen Leiden eines Systemadministrators...

Ich bin gerade mit dem Auto unterwegs und bekomme einen Anruf "Das geht nicht, bestimmt ist auf dem Server was kaputt". Also geht es an der nächsten Rastanlage hinaus und mit dem Laptop ab ins kostenfreie WLAN, das glücklicherweise verfügbar ist.
Anmeldung per SSH auf den Server und nach dem Rechten sehen... doch die Verbindung zu Port 22 wird blockiert - war wohl nix!

Also geht der nächste Griff zum Smartphone und dort schnell einen Hotspot eingerichtet... doch auch hier muss ich passen.
Das Netz ist mal wieder nur mäßig und reicht entweder zum telefonieren oder Hotspot etablieren... da klingelt es schon wieder... Hotspot fällt also auch flach.

Wieder zurück zum kostenfreien WLAN: Wie komme ich nun durch die Firewall hindurch zu meinem gewünschten Ziel?


So oder so ähnlich lässt es sich ganz gut darstellen, was einem Admin gern mal den letzten Nerv raubt. Also begann eine Recherche, wie sich das Ganze optimieren lässt.

Analyse

Beim genaueren Untersuchen von verschiedenen freien Netzwerken ließ sich immer wieder beobachten, dass HTTP Anfragen auf Port 80 gerne gefiltert werden. Ob zur Datenerhebung oder einfach nur zum Schutz vor unerwünschtem Inhalt sei jetzt mal dahingestellt.
Doch Fakt ist: Die Daten werden angesehen und unerwünschter Traffic somit auch effektiv unterbunden. Also keine Lösung, um hier was anderes als HTTP zu senden .

Der zweite Port auf meiner Liste ist 443, denn HTTPS muss schließlich auch möglich sein. Hier gibt es den gewünschten Erfolg, denn da die Daten gesichert per SSL/TLS übertragen werden, sehen für ein Analysetool die Daten alle "gleich" aus, nämlich nach irgendwelchen Daten-Paketen .
Wichtig ist der Aspekt, dass TCP als Transport Layer verwendet werden muss, da eine Filterung in der Firewall nach TCP/UDP weiterhin möglich ist.

Blöd war nun lediglich noch der Aspekt, dass auf meinem Server die Webseiten mit HTTPS angeboten werden, sprich dieser Port bereits belegt ist. Also wird ein Tool benötigt, dass diesen Port teilen kann.
Ursprünglich hatte ich gehofft, dass mein NginX dies könne... doch dies war leider nicht möglich1.

OpenVPN als erste Lösung

Wie sich herausstellte, bietet OpenVPN eine Konfigurationsoption an, um Datenpakete, die nicht für OpenVPN bestimmt sind (der Server einfach nicht versteht) weiterzuleiten.

So wurde mein OpenVPN Server kurzer Hand umkonfiguriert:

proto tcp port 443 port-share 127.0.0.1:4433

Alle Datenpakete würden nun also erst durch den OpenVPN Server gehen und anschließend zu meinem Webserver weitergeleitet werden, den ich auf Port 4433 umgestellt hatte.

Soweit, sogut... bis ich eine zweite Domain auf meinem Server hosten wollte, denn dies war nicht mehr möglich, da OpenVPN den angeforderten Domainnamen nicht weiterleitete und alles auf meiner "Default" Webseite landete .

SSLH - Ziel der Suche

Zum Glück gab es einen findigen Entwickler, der ein kleines Tool schrieb, das genau diese Probleme beheben kann!

SSLH hört nun also auf Port 443, kann durch eine Analyse des Datenstroms erkennen, um welches Protokoll es sich handelt und dann korrekt weiterleiten. Ebenso werden HTTPS Pakete nicht verändert, sodass auch eine Namensauflösung möglich ist .

Konfiguriert wird es sehr einfach über eine Konfigurationsdatei, die auf Debian-Systemen unter /etc/default/sslh zu finden ist.
DAEMON_OPTS="--user sslh -- listen 0.0.0.0:443 --ssh 127.0.0.1:22 --ssl 127.0.0.1:4433 --openvpn 127.0.0.1:1194 --timeout 5 --pidfile /var/run/sslh/sslh.pid"

Hier einmal noch die Parameter aufgeschlüsselt:

  • --user: Der System-Benutzer, unter dem sslh ausgeführt wird
  • --listen: IP und Port auf dem gelauscht werden soll
  • --ssh: Das Ziel zu dem SSH Pakete weitergeleitet werden sollen
  • --ssl: Das Ziel auf dem der HTTPS Server läuft (Hatte ich für OpenVPN verstellt, ist somit einfach geblieben)
  • --openvpn: Das Ziel zu dem OpenVPN Pakete weitergeleitet werden (Wieder der originale Port, aber weiterhin TCP)
  • --timeout: Timeout in Sekunden, bevor die Weiterleitung zu den internen Servern abgebrochen wird
  • --pidfile: Pfad, wo die Prozess-ID abgelegt werden soll

1 Seit Version 1.9.0 kann NginX auch als Proxy für Streams agieren und seit Version 1.11.5 kann auch eine Stream-Server Unterscheidung per SNI durchgeführt werden (SSL PreRead).

NginX Basic-Auth mit Datenbank-Backend
Security Webserver
21.08.2019 | Andreas Müller | 0

Vor einigen Jahren bin ich von Apache zu NginX umgestiegen, da ich meine Webseiten in Docker-Container verpackt hatte und der WebServer zum Proxy für den jeweiligen Container wurde.
Hier hat NginX einfach die Nase vorne und glänzt durch eine deutlich einfachere Konfiguration. Zudem lassen sich auch andere Streams durchleiten und nebenbei mittels SSL Zertifikat absichern (z.B. ein TCP-Stream).

Basic-Auth für NginX

Unter Apache ist die Absicherung von Verzeichnissen mittels Basic-Auth sehr einfach zu realisieren, wenn eine .htaccess-Datei verwendet wird. Eine andere Art habe ich damals nie benutzt.
Diese Authentifizierung ist auch unter NginX relativ einfach möglich, benötigt jedoch die Installation der apache2-utils.

Installation htpasswd und erzeugen einer htpasswd Datei:

# apt-get install apache2-utils
# htpasswd /etc/nginx/auth/htpasswd $user

Mit dieser Datei kann nun die Einrichtung stattfinden: z.B. /etc/nginx/sites-enabled/default

server { # ... sonstige Einrichtung des Server-Bereichs location /data { # ... Einrichtung des Ordners # Hier wird Basic-Auth aktiviert (und das Realm gesetzt) auth_basic "Restricted Area"; # Hier wird angegeben, wo die Benutzer und Passwörter gespeichert sind auth_basic_user_file /etc/nginx/auth/htpasswd; location /public { # Auf diesen Ordner dürfen wieder alle zugreifen auth_basic off; } } }

Mehr Flexibilität mit Lua

Zur Erweiterung der Serverlogik mit komplexeren Abfragen steht in NginX ein Lua-Modul zur Verfügung. Dies muss vermutlich noch installiert werden:

# apt-get install libnginx-mod-http-lua lua-nginx-string

Im Anschluss kann die Konfiguration so geändert werden, dass ein Lua-Script die Basic-Auth Prüfung übernimmt.

server { # ... location /restricted { # Hier wird das Realm gesetzt set $lua_realm 'Restricted Area'; # Wir definieren noch eine bestimmte Gruppe für die spätere Abfrage set $lua_group 'restricted group'; # Hier wird die Authentifizierung an das Lua-Script übergeben access_by_lua_file /etc/nginx/auth/auth.lua; # ... restliche Einrichtung } }

Wer nun der Sprache Lua mächtig ist, kann hergehen und mittels Lua eine vollständige Authentifizierung auf Basis von Basic-Auth vornehmen.
Ich für meinen Teil wollte gerne einen Benutzer-Abgleich über MySQL realisieren. Doch da ich Lua jetzt nicht so wirklich beherrsche, habe ich recherchiert, wie man andere Scripte (z.B. Bash oder eben PHP) ausführen kann.

So wird nun in Lua nun die Basic-Auth Implementierung vorgenommen und der Abgleich mit der Datenbank erledigt ein PHP-Script im Hintergrund.
/etc/nginx/auth/auth.lua:

-- Basic-Auth with lua function authenticate() -- does the auth header exist? local header = ngx.req.get_headers()['Authorization'] if header == nil or header:find(' ') == nil then return false end -- check whether its a Basic-Auth local divider = header:find(' ') if header:sub(0, divider-1) ~= 'Basic' then return false end -- try to decode the Basic-Auth local auth = ngx.decode_base64(header:sub(divider+1)) if auth == nil or auth:find(':') == nil then return false end -- parse the auth information divider = auth:find(':') local username = auth:sub(0, divider-1) local password = auth:sub(divider+1) -- now my lua knowledge is done... -- so request something to validate the information local handle = io.popen(string.format('php /etc/nginx/auth/auth.php %s %s %s', ngx.var.lua_group, username, password)) local response = handle:read("*a") handle:close() -- check the script response if response == nil or response ~= 'OK' then return false end return true end local isValid = authenticate() if not isValid then ngx.header.content_type = 'text/plain' ngx.header.www_authenticate = string.format('Basic realm="%s"', ngx.var.lua_realm) ngx.status = ngx.HTTP_UNAUTHORIZED ngx.say(string.format('ERROR 401: Unauthorized\nArea: %s', ngx.var.lua_realm)) end
/etc/nginx/auth/auth.php
<?php // The MySQL database information $db_host = 'localhost'; $db_port = 3306; $db_name = 'nginx-auth'; $db_user = 'auth-user'; $db_pass = 'some-password'; $group = ''; $user = ''; $password = ''; if (!empty($argv[1])) { $group = trim($argv[1]); } if (!empty($argv[2])) { $user = trim($argv[2]); } if (!empty($argv[3])) { $password = trim($argv[3]); } try { $db = new PDO('mysql:host='.$db_host.';port='.$db_port.';dbname='.$db_name.';', $db_user, $db_pass); $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTIION); $query = 'SELECT 1 AS OK FROM `nginx-auth-table` WHERE `is_active` = 1 AND `group` = :group AND `username` = :user AND `password` = SHA2(:pass, 256);'; $stmt = $db->prepare($query); $stmt->bindValue(':group', $group); $stmt->bindValue(':user', $user); $stmt->bindValue(':pass', $password); $stmt->execute(); $res = $stmt->fetchAll(PDO::FETCH_OBJ); if (count($res) == 1) { echo 'OK'; exit(0); } } catch (Exception $ex) { echo 'ERROR: '.$ex->getMessage(); exit(1); } echo 'ERROR: Invalid credentials'; exit(1); ?>

Ein kurzes Fazit

Ich persönlich bin immer wieder sehr erstaunt darüber, wie groß die Palette an Optionen ist, mit der man NginX erweitern kann. Dabei muss man sich zwar manchmal ein paar Tricks überlegen, doch letzten Endes gibt es immer eine Lösung.
Die Idee über Lua ein beliebiges anderes Script auszuführen eröffnet viele neue Möglichkeiten, wie man die Authentifizierung lösen könnte (PHP war lediglich meine Wahl).