Wissenshäppchen

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.


Die Verwirrung der Tmux Configs
Linux
12.09.2020 | Andreas Müller |

Ich habe vor Kurzem meinen Server von Ubuntu 18.04 auf Ubuntu 20.04 aktualisiert. Dabei wurden — wie sollte es auch anders sein — viele Programme, die ich über die Paketquellen installiert habe, aktualisiert... so auch Tmux.

Tmux (Webseite) ist ein Terminal Multiplexer. Wenn einem z.B. ein Terminal (eine Shell) per SSH nicht ausreichend vorkommt, so kann man sich gleich mehrere neue Fenster erstellen lassen — ohne eine zweite (oder noch mehr) SSH Verbindungen. Für die Leute, denen die Beschreibung bekannt vorkommt: Ja, Tmux ist eine Alternative zu screen, welches ich als sehr in die Jahre gekommen ansehe.

Ich habe mir dafür vor vielen Jahren auch eine Konfiguration erstellt, die meine Bedürfnisse sehr gut abdeckt... doch mit dem Update schmiss es auf einmal sehr viele Fehler, was in der Konfiguration alles nicht mehr passte.

Ich fing also an zu fluchen und wollte von vorne beginnen, doch zum Glück hatte sich garnicht so viel getan.
Dies war die relevante Information aus dem Changelog:

* The individual -fg, -bg and -attr options have been removed; they
  were superseded by -style options in tmux 1.9.

... gut ein Tippfehler im Changelog sagt, dass es angeblich Version 1.9 ist, dabei reden wir von Version 2.9, aber das soll uns jetzt egal sein. Wichtig ist, dass die einzelnen "durchgeschriebenen" Attribute weggefallen sind und stattdessen Optionen hinzugekommen sind.

Aus alt... ... wird also neu
set -g pane-border-bg black
set -g pane-border-fg white
set -g pane-border-style fg=white,bg=black

Das war natürlich leicht zu bewerkstelligen. Somit habe ich nun also eine neue Konfiguration für alle Tmux Versionen von 2.9 an aufwärts.


Kurze Einführung in Tmux

Befehle außerhalb von Tmux:

Befehl Beschreibung
tmux
Startet eine neue Session
tmux new -s <name>
Startet eine neue Session mit dem Namen <name>
tmux ls
Listet alle vorhandenen Sessions auf
tmux at
Bindet in eine existierende Session (attach)
tmux at -t <name>
Bindet in eine explizte Session mit dem Namen <name>
tmux kill-session -t <name>
Terminiert die Session mit dem Namen <name>

Tatenbelegung für Tmux mit dieser Konfiguration:

Tastenkombination Beschreibung
Ctrl + a Generelles Prefix für alle weiteren Interaktionen
c Erzeugt ein neues Fenster in der aktuellen Session
d Lösen aus der aktuellen Session (detach = Die Session läuft im Hintergrund weiter)
s Wechseln zwischen verschiedenen Sessions (Liste)
0 bis 9 Wählt ein Fenster innerhalb einer Session direkt an
w Wechseln zwischen Fenstern einer Session (Liste)
p, n Vorheriges/Nächstes Fenster in der Session (previous/next)
R Lädt die Konfiguration für Tmux erneut aus der Datei (z.B. nach Änderungen)
/ Teilt das aktuelle Panel mit einer vertikalen Linie (rechts/links)
- Teilt das aktuelle Panel mit einer horizontalen Linie (oben/unten)
, , , Bewegen zwischen den Panels innerhalb eines Fensters
k Terminiert (kill) das aktuelle Panel (und alle darin enthaltenen Prozesse)
t Zeigt die aktuelle Uhrzeit im Panel an
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).

.NET Core und das Schreiben von Log-Files
.NET
09.08.2019 | Andreas Müller |

Mit .NET Core hat in das Logging von Anwendungen das Interface ILogger Einzug erhalten.

Es gibt verschiedene Implementierungen, die durch Microsoft angeboten werden.
So kann man direkt auf die Console schreiben oder auch in das Windows EventLog. Doch das Schreiben der Informationen ganz klassisch in eine Datei wurde nicht bedacht.

Also habe ich mir das Interface nun einmal angesehen und eine eigene kleine Implementierung für das gute alte Schreiben der Informationen in Dateien erstellt.

Soweit, so gut. Doch beim Beenden der Anwendung war das Log-File nicht vollständig geschrieben, da die reine Implementierung des ILogger Interface keine saubere Terminierung vorsieht. So musste noch das IDisposable Interface implementiert werden, das auf das Fertigschreiben der Datei wartet und dann erst beendet.

Anbei findet ihr die Implementierung für den eigenen Bedarf.