Docker mit NGINX-Proxymanager, UFW, Fail2Ban, MariaDB und Nextcloud — Teil 5

Mittlerweile sind wir schon beim fünften Teil meines Dockertutorials angekommen. Sofern ihr quasi von null startet und ein ähnliches Projekt (Nextcloud, Mariadb, ufw, Fail2ban, NPM etc.) umsetzen wollt, empfehle ich hier zu beginnen.

Ich gehe in diesem Teil davon aus, dass bei euch Docker, UFW – mit Adaptierung für Docker,  Portainer, Nextcloud, Mariadb und der Nginx-Proxymanager laufen. Thema dieses Parts wird die Inbetriebnahme von Fail2Ban sein. Fail2Ban überwacht Logdateien des Systems auf gewisse „Muster“ = Fehler und sperrt zb. IP-Adressen die zu oft fehlerhafte Authentifizierungen durchführen. Um die Filterung durchzuführen, werden Filter (=via reguläre Ausdrücke, regexp) genutzt. Ich bin, was die Erstellung eigener regulärer Ausdrücke angeht nicht gut und meistens auf „das Internet“ angewiesen (nur so nebenbei angemerkt).

Fail2Ban wird in einem Docker Container laufen und sowohl den Host, als auch die Container schützen. Wichtig ist, dass sämtliche Logverzeichnisse (Docker und Host) im Fail2Ban-Dockercontainer „read-only“ (nur lesen) eingebunden werden müssen, da Fail2Ban sonst ja keinen Zugriff auf die Logs hätte und seine Arbeit nicht verrichten könnte.

Logverzeichnisse

Ich greife hier etwas vor, obwohl wir den Container  für Fail2Ban noch nicht ausgerollt haben. Es ist wichtig, dass man folgenden Zusammenhang versteht.

  • Die Logverzeichnisse, der zu überwachenden Dienste,  werden als sog. „Bindmounts“ – nur lesend in den Fail2Ban Container eingebunden.
  • Die Einbindung im Fail2Ban-Container erfolgt im Verzeichnis /remotelogs/<name des Containers oder Dienstes>
  • Es sollen der Nginx-Proxy-Manager, Nextlcoud und der SSH-Zugang des Hosts überwacht werden.
  • Bei mir werden alle Dockervolumes nach /var/lib/docker/volumes/<Name des Volumes> gemountet.

Das sieht dann bzgl. Volumes bei mir zb so aus (in Portainer – Container Fail2Ban):

Aufschlüsselung der (Logging)Verzeichnisse

  • SSH loggt am Host in das Verzeichnis /var/log
  • Nextcloud loggt (in meinem Fall) nach /var/lib/docker/volumes/Nextcloud_data/_data/data
  • NPM nach /var/lib/docker/volumes/nginxpm_data/_data/logs
  • das /config Verzeichnis hat mit dem Logging selbst nichts zu tun, sondern enthält die Konfiguration von Fail2ban

Ausrollung von linuxserver/fail2ban

Wie sicher schon gewohnt, wird der Container von Fail2Ban via Portainer ausgerollt. Der Name des Image ist linuxserver/fail2ban.

Volumes

Es muss nur ein Volume für die Konfiguration von Fail2Ban erzeugt werden. Ich nenne es fail2banconfig.

Abgesehen von der Zuordnung des Volumes für die Fail2Ban-Konfiguration, ges es bei der Zuordnung der weiteren Volumes, um das jeweilige Loggingverzeichnis der diversen Dienste (am Host). Wenn ihr euch 1:1 an die Anleitung hier  gehalten habt, sollte das so ausehen. (Kann aber variieren, wenn ihr andere Bezeichnungen für die Volumes gewählt habt!) Achtet auch darauf, dass die Logs via „BIND“ eingebunden werden!

Netzwerk, Ports

Wir benötigen für Fail2Ban weder eine IP-Adresse noch irgendwelche Ports.

Command & Logging

  • Interactive & TTY

Environment

  • PUID 1000 (die ID eures Standardusers, der ganz am Anfang angelegt worden ist. Seht ihr wenn ihr in /etc/passwd des Hosts reinschaut)
  • PGID 1000 (Gruppe des Standardusers. Seht ihr auch, wenn ihr in /etc/passwd des Hosts reinschaut)
  • TZ Europe/Berlin
  • SSMTP_HOST (Hostname eures SMTP-Servers) -> OPTIONAL: Nur wenn ihr Mailbenachrichtigungen erhalten wollt!
  • SSMTP_PORT (normalerweise 587)
  • SSMTP_HOSTNAME – Der Hostname eures Dockerhosts (ich hab hier den FQDN meines Dockerhosts eingetragen)
  • SSMTP_USER (Der Benutzer zur Benutzerauthentifizierung am Mailserver)
  • SSMTP_PASSWORD (Das Passwort, des Benutzers am Mailserver)
  • SSMTP_TLS (Normalerweise auf YES zu setzen)
  • SSMTP_STARTTLS (Normalerwese auf YES zu setzen)

Restart Policy

  • Always

Capabilities

  • Hier muss zusätzlich „NET_ADMIN“ und „NET_RAW“ aktiviert werden.

Ausrollen

  • Nun kann der Container ausgerollt werden „Deploy the Container“.

Anpassung der Konfiguration von fail2ban

Fail2Ban läuft jetzt zwar, jedoch ohne Konfiguration der sog. „Jails“. In den Jails, werden Dienste definiert, die überwacht werden sollen. Ebenso muss eine „Action“ (also Aktion) definiert werden, um Festzulegen, wie denn mit „bösen“ Anfragen umgegangen werden soll.

Die Konfiguration erledigen wir via SSH am HOST (im Konfigurationsverzeichnis von Fail2Ban).

/var/lib/docker/volumes/fail2banconfig/_data/fail2ban

WICHTIG: Alle *.conf Dateien werden bei einem Restart des Containers überschrieben. Deshalb ist für jede Adaptierung einer .conf-Datei eine .local Datei zu erstellen.

Am Besten ist es, neue Dateien mit der „Endung“ .local zu erstellen, oder bestehende Dateien zu kopieren, wenn man den Inhalt benötigt.

fail2ban.conf -> fail2ban.local

An der.conf-Datei wird KEINE ÄNDERUNG durchgeführt.

Wir kopieren die Datei (vorbeugend) und erstellen so eine .local Datei:

cp fail2ban.conf fail2ban.local

jail.conf -> jail.local

An der.conf-Datei wird KEINE ÄNDERUNG durchgeführt.

Wir kopieren die Datei (vorbeugend) und erstellen so eine .local Datei:

cp jail.conf jail.local

Der Inhalt der Datei jail.local sieht in meinem Fall so aus:

[DEFAULT]
bantime = 21600
findtime = 120
maxretry = 3

[DEFAULT]
mta = sendmail
action = %(action_)s
destemail = admin@it-networker.at
sender = fail2ban@it-networker.at

Filter für NPM und Nextcloud erstellen

Wir wechseln am Host in das Verzeichnis /var/lib/docker/volumes/fail2banconfig/_data/fail2ban/filter.d

Dort erstellen wir eine Datei mit Namen: npm.local

Inhalt des NPM Filters (npm.local)

[INCLUDES]
before = common.conf

[Definition]
failregex = ^\s*(?:\[\]\s+)?- 401 \d+ - [A-Z]+ \w+ \S+ "[^"]+" \[Client <ADDR>\]
^<HOST> -.*"(GET|POST|HEAD).*HTTP.*" 403
^<HOST> -.*"(GET|POST|HEAD).*HTTP.*" 404
^<HOST> -.*"(GET|POST|HEAD).*HTTP.*" 444

Weiters erstellen wir eine Datei für Nextcloud mit Namen nextcloud.local

Inhalt des Nextcloud Filters (nextcloud.local)

[Definition]
_groupsre = (?:(?:,?\s*"\w+":(?:"[^"]+"|\w+))*)
failregex = ^\{%(_groupsre)s,?\s*"remoteAddr":"<HOST>"%(_groupsre)s,?\s*"messag>
^\{%(_groupsre)s,?\s*"remoteAddr":"<HOST>"%(_groupsre)s,?\s*"messag>
datepattern = ,?\s*"time"\s*:\s*"%%Y-%%m-%%d[T ]%%H:%%M:%%S(%%z)?"

Jail für SSH (des Hosts) NPM und Nextcloud erstellen

Ganz wichtig ist bezüglich „JAILS“ der Parameter chain = 

  • Ist es ein Dockercontainer, den wir schützen wollen, müssen wir bei chain = DOCKER-USER verwenden!
  • Ist es der Host, den wir schützen wollen, müssen wir bei chain = INPUT verwenden!

Für die Anpassungen müssen wir (wieder am Host) in  das Verzeichnis:

/var/lib/docker/volumes/fail2banconfig/_data/fail2ban/jail.d

wechseln.

Jail für Nextcloud (nextcloud.local)

Via nano nextcloud.local erstellen wir eine Datei,  mit Folgendem Inhalt.

[nextcloud]
backend = auto
enabled =
port = 80,443
chain = docker-user
protocol = tcp
filter = nextcloud
logpath = /remotelogs/nextcloud/nextcloud.log
action = %(known/action)s

Jail für Nextcloud-auth (nextcloud-auth.local)

Via nano nextcloud-auth.local erstellen wir eine Datei,  mit Folgendem Inhalt.

[nextcloud-auth]
enabled = true
chain = docker-user
port = http,https
logpath = %(remote_logs_path)s/nextcloud/nextcloud.log
destemail = admin@it-networker.at
sender = fail2ban@it-networker.at
sendername = Fail2Ban

Jail für den NPM (npm.local)

Via nano npm.local erstellen wir eine Datei,  mit Folgendem Inhalt.

[npm]
enabled = true
chain = docker-user
logpath = /remotelogs/npm/fallback_error.log
/remotelogs/npm/default-host_access.log
/remotelogs/npm/proxy-host-*_error.log
/remotelogs/npm/default-host_error.log
/remotelogs/npm/fallback_access.log

destemail = admin@it-networker.at
sender = fail2ban@it-networker.at
sendername = Fail2Ban

Jail für SSH des Hosts (sshd.local)

Via nano sshd.local erstellen wir eine Datei,  mit Folgendem Inhalt.

[sshd]
enabled=true
chain = INPUT
logpath = /remotelogs/authloghost/auth.log
backend = %(sshd_backend)s

destemail = admin@it-networker.at
sender = fail2ban@it-networker.at
sendername = Fail2Ban

Restart von Fail2Ban bzw. des Containers

Fail2ban kann mit dem Befehlt fail2ban-client restart neu gestartet werden. Hier hat es bei mir allerdings immer wieder ein wenig „gehakelt“.

Am Besten ist es, einfach den Fail2Ban-Container neu zu starten. Nach einem Neustart des Containers, sollte nun auch Fail2Ban ordnungsgemäß funktionieren und sowohl Host, als auch Container schützen.

Beobachten kann man das Verhalten im Fail2Ban – Log. Das Logging findet allerdings im Container statt. Entweder verwendet ihr die Shell, die via Portainer erreichbar ist, oder greift einfach über die Konsole des Hosts darauf zu.

Der Zugriff auf die Container-Konsole gelingt mit:

docker exec -it fail2ban bash

fail2ban = hierbei der Name des Containers.

Das Prompt sieht so aus (bis auf das was vor dem : steht:

root@vps2348723:/# docker exec -it fail2ban bash
root@8ddbafffbff4:/#

Das Logverzeichnis befindet sich hier: /config/log/fail2ban

Die Datei heißt: fail2ban.log

Am Besten ist es ihr lasst mittels tail -f /config/log/fail2ban/fail2ban.log das Log live mitlaufen und gebt bei den übewachten Diensten zb. mehrfach ein falsches Passwort ein. Das Log sollte darauf reagieren. (Bitte drauf achten, dass ihr euch nicht selbst aussperrt!)

Diskussionsforum zum Thema

Docker mit NGINX-Proxymanager, UFW, Fail2Ban, MariaDB und Nextcloud — Teil 4

MariaDB installieren

Um Nextcloud mit einer  Datenbank versorgen zu können, greife ich auf MariaDB (innerhalb eines Dockercontainers) zurück. MariaDB wird -wie alle anderen Container- die eine fixe IP benötigen, eine manuelle IP, aus dem von uns erstellten „Docker-Netzwerk“ erhalten. Wie immer wird das Image via Portainer ausgerollt.

Volume erstellen

MariaDB benötigte ein Volume, um persistente Daten zu speichern (ich nenne es mariadb_lib).

 

 

Ports und Imagename

  • Image: mariadb:latest
  • Port 3306:3306

Netzwerk

Beim Netzwerk wählen wir unser benutzerdefiniertes Netzwerk und vergeben dem Container eine statische IP, (Gateway, DNS sind optional).

Volume zuordnen

Das erstellte volume wird dem Verzeichnis

/var/lib/mysql

zugeordnet.

Environment Variablen

  • MYSQL_ROOT_PASSWORD -> bei Value ein Rootpasswort für MariaDB setzen

Restart Policy

  • Always

Command and Logging

  • Console -> Interactive & TTY

Container ausrollen

  • via Klick auf „Deploy the container“

Bestenfalls sieht das dann bei euch so aus (bis auf die Netzwerkadresse, die ihr lt. euren Netzwerk vergeben könnt)

Standardabsicherung von Mariadb

Via Portainer und nach Auswahl des Containers „MariaDB“ wechseln wir in die Konsole, des MariaDB-Containers.

Wir tippen: mariadb_secure_installation

um ein paar grundlegende Basiseinstellungen bzgl. Sicherheit zu tätigen. Das Rootpasswort = das Passwort, das wir vorhin bei den Umgebungsvariablen für den MariaDB-Root-User gesetzt haben!

  • switch to unix_socket authentication: Y
  • Passwort für Root setzen: haben wir schon, also N
  • Remove anonymous users: Y
  • Disallow root remotly: Y
  • Remove Testdatabase: Y
  • Reload privileges tables now: y

Datenbank für Nextcloud erstellen

Von den Containern her, wird es wie folgend laufen.

  • MariaDB hat eine fixe IP (10.0.0.100)
  • Nextcloud wird die IP 10.0.0.200 bekommen
  • Wir müssen nun auf unserer Mariadb
    • Eine Datenbank erstellen
    • Einen User erstellen, der via 10.0.0.200 Vollzugriff auf die erstellte Datenkbank hat

Folgende Aktionen sind in der Konsole des MariaDB-Dockercontainers durchzuführen.

Verbinden mit MariaDB

mariadb

User „nextcloud“ erstellen, der von 10.0.0.200 aus auf die DB zugreifen darf

CREATE USER 'nextcloud'@'10.0.0.200' IDENTIFIED BY 'geheimespasswort';

Datenbank erstellen

CREATE DATABASE IF NOT EXISTS nextcloud CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;

Dem oben erstellten User den Zugriff gewähren

GRANT ALL PRIVILEGES on nextcloud.* to 'nextcloud'@'10.0.0.200';

FLUSH PRIVILEGES;

quit

Nextcloud installieren

Ich gehe davon aus, dass ihr bereits einen DNS-Namen (FQDN) für eure Cloud -bei eurem Provider- eingerichtet habt. Ich nehme für dieses Beispiel hier meine Test(sub)domain nc.it-networker.at

Volume einrichten

Wie mittlerweile schon gewohnt, ist ein Volume zu erstellen. (Name: nextcloud_data). Ich gehe hier jetzt nicht nochmals ins Detail, wie man via Portainer ein Volume erstellt.

Container erstellen

Netzwerkkonfiguration (Ports)

Hier müsst ihr darauf achten, dass ihr hostseitig kein Port verwendet, das bereits vergeben ist. Ich wähle hier in meinem Fall, Port 88 am Host -> Port 80 im Container.

Command and Logging

  • Interactive & TTY

Volumes

  • /var/www/html -> volume Nextcloud_data

Network

Da wir unserem MariaDB Benutzer „nextcloud“@“10.0.0.200“ den Zugriff auf die Nextcloud Datenbank gestattet haben, bekommt der Nextcloudcontainer IP 10.0.0.200 in unserem Dockernetzwerk.

Umgebungsvariablen

Hier ist an sich nichts hinzuzufügen, da die Konfiguration ja erst beim Aufruf der FQDN eurer Nextcloudinstallation startet (zb. nc.euredomain.com). Später kann jedenfalls der Parameter PHP_UPLOAD_LIMIT und PHP_MEMORY_LIMIT angepasst werden.

Restart Policy

  • Always

Container ausrollen

  • Mit Klick auf „Deploy the container“. Bei Erfolg:

NGINX-Proxy-Manager anpassen

Damit wir unsere Nextcloudinstallation ansprechen können, muss nun im NPM ein Host hinzugefügt werden. Wie oben erwähnt, soll meine Nextcloud Installation unter der Subdomain nc.it-networker.at erreichbar sein. Das Ganze soll via SSL erfolgen.

SSL muss sein

Deshalb erstelle ich mir via NPM ein SSL Zertifikat.

  • Login in den NPM
  • Oben SSL-Certificates -> Add SSL Certificate -> Letsencrypt
  • Domain: nc.it-networker.at (add unterhalb anklicken)
  • Eure Mailaddresse angeben
  • I Agree to…. anklicken
  • Save

Host erstellen

  • Login in den NPM
  • Oben Hosts -> Proxy Host
  • Rechts -> Add Proxy Host
  • Domain Namen eingeben
  • Port eingeben (80)
  • Block Common Exploits -> aktivieren  (ist im Screenshot fälschlicherweise inaktiv)

  • Reiter SSL -> das zuvor erstellte SSL – Zertifikat wählen
  • Force SSL
  • ggf. HTTP/2 Support
  • HSTS Enabled
  • Save

Weitere Schritte via GUI von Nextcloud

Nun sollte die Adresse eurer Nextcloud aufrufbar sein.

Folgend die Einstellungen lt. meinem Tutorial

Nach  Klick auf installieren dauert es nun ggf. ein wenig bis ihr in Nextcloud landet.

Nun könnt ihr User anlegen und die Cloud nutzen.

Optimierungen

Vor Allem die Warnung „Du greifst über eine sichere Verbindung auf deine Instanz zu, deine Instanz generiert jedoch unsichere URLs….“ muss ernst genommen werden. Um die Warnung los zu werden ist die config.php von nextcloud anzupassen. Wir erinnern uns, dass wir während dem Setup von Nextcloud ein Volume erstellt haben. Sofern ihr die Konfiguration von Docker nicht adaptiert habt, landen alle erstellten Volumes (im Dateisystem des Hosts) auf /var/lib/docker/volumes/<Containername>/_data.

Die Nextcloud-Konfiguration liegt bei mir auf : /var/lib/docker/volumes/Nextcloud_data/_data/config

Die Datei config.php muss bearbeitet werden.

nano config.php

Nach der Zeile

'version' => '25.0.2.3',

muss Folgendes ergänz werden. (Natürlich mit eurer Domain bzw. eurer Docker-IP des NPM)

'overwritehost' => 'nc.it-networker.at',
'overwrite.cli.url' => 'http://nc.it-networker.at',
'overwriteprotocol' => 'https',
'trusted_proxies' =>
array (
0 => '10.0.0.3',
),

Nach dem Restart des Nextcloud Containers, ist die Warnung dann verschwunden.

Caldav/ Carddav

Eventuell (naja, eigentlich ziemlich sicher) seht ihr auch noch folgende Meldungen, wenn ihr als Administrator in NC (=Nextcloud) einsteigt und „Verwaltung / Übersicht“ aufruft.

Dein Webserver ist nicht richtig konfiguriert, um „/.well-known/caldav“ aufzulösen. Weitere Informationen hierzu findest du in unserer Dokumentation ?.
Dein Webserver ist nicht richtig konfiguriert, um „/.well-known/carddav“ aufzulösen. Weitere Informationen hierzu findest du in unserer Dokumentation ?.

Um dieses Problem zu lösen, muss der Host von Nextcloud im NPM angepasst werden.

Konkret gesprochen, müsst ihr diese Werte im Fenster (Reiter Advanced des NC-Proxy-Hosts) einfügen.

location /.well-known/carddav {
return 301 $scheme://$host/remote.php/dav;
}
location /.well-known/caldav
{
return 301 $scheme://$host/remote.php/dav;
}
location /.well-known/webdav
{
return 301 $scheme://$host/remote.php/dav;
}

und speichern.

Dann verschwindet die Meldung.

Hier gehts zu Teil 5

Diskussionsforum zum Thema

Docker mit NGINX-Proxymanager, UFW, Fail2Ban, MariaDB und Nextcloud — Teil 3

Willkommen zum dritten Teil meiner Docker-Serie. Bitte lest unbedingt auch die vorhergehenden Teile!

Der NPM ist nun via Full-Qualified-Domain-Name und einem offiziellem SSL Zertifikat von Let’s Encrypt erreichbar. Der nächste Schritt wäre nun, auch Portainer mittels offiziellem SSL Zertifikat und über HTTPS (ohne Port 9443) erreichbar zu machen.

Portainer erhält ein zusätzliches Netzwerk

Da unser NPM sich in einem 10.0.0.x Netzwerk befindet, Portainer aber noch immer mit der Docker-Standard-IP 172.17.0.x läuft, muss unser Netzwerk (10.0.0.x — bei mir mit Namen „it-yourself“) mit Portainer verbunden werden.

Dies ist mit wenigen Klicks erledigt.

  • Einloggen in Portainer
  • Auswahl von Containers (links)
  • Danach Portainer wählen (den Container)
  • Ganz nach unten scrollen
  • Bei Connected Networks das Dropdownmenü öffnen und das selbst erstellte Netzwerk auswählen (it-networker bei mir)
  • Abschließend auf Join network klicken

Von nun an, ist der Portainer mit 2 Netzwerken verbunden. Der Standardbridge (172.17.0.x) und dem benutzerdefinierten Netzwerk (10.0.0.x).

NPM – SSL Zertifikat erstellen und Host eintragen

Wir müssen jetzt via NPM einerseits ein SSL-Zertifikat erstellen und andererseits einen Hosteintrag für portainer.it-networker.at (ihr nehmt natürlich euren Sub(domainnamen) den ihr gewählt habt).

Auch das ist in wenigen Schritten erledigt.

  • Einloggen in den NPM
  • Menü SSL Certificates
  • Add SSL Certificate -> Letsencrypt (rechts)
  • Domain im „Domain Names“ Feld angeben. (nicht Return drücken, sondern unterhalb der eingabe auf „Add <……>“ klicken
  • Email-Address for Letsencrypt eintragen
  • Den „Terms of Service“ zustimmen
  • Auf Save klicken
  • Nach rund 30 Sekunden erscheint nun auch dieses SSL Zertifikat in der Übersicht

Einen Hosteintrag erstellen

Wieder befinden wir uns im NPM-Backend.

  • Auswahl von Hosts -> Proxyhost
  • Rechts auf „Add Proxy Host“ gehen
  • Domain Names: Eure Sub(Domain) bei mir portainer.it-networker.at
  • Scheme: HTTPS
  • Forwart Hostname/IP: IP oder Containername von Portainer. Bestenfalls immer den Namen nehmen. In meinem Fall: portainer
  • Forward Port: 9443
  • Block Common Exploits
  • Reiter SSL: Auswahl des zuvor erstellten SSL Zertifikates für Portainer.
  • Force SSL
  • Save

Testlauf

Nach Eingabe von https://portainer.it-networker.at (ohne Port) gelangen wir jetzt direkt zum Portainer-Login. Wie immer müsst ihr hier natürlich eure Sub(domain) nehmen.

Absicherung und eine kurze Zusammenfassung

Von außen ist nur unser NPM auf Port 80 und 443 erreichbar. Wir prüfen das mittels den aktuellen UFW-Firewallregeln. Wichtig ist, dass alle Regeln die ALLOW FWD als Action hinterlegt haben sich ausschließlich auf den Zugriff auf die Docker-Container beziehen. Standardregeln – Action: ALLOW IN, beziehen sich auf den Host und nicht auf die Container.

Nachdem wir uns via ssh auf unseren Server eingeloggt haben, tippen wir:

ufw status verbose

Abgesehen von einer ALLOW IN Regel, die uns den Zugriff auf SSH erlaubt, sollte es hier nur 2 weitere Regeln geben.

  • 10.0.0.3 ist in dem Fall mein NPM-Container (bei euch könnte der Container eine andere IP haben)
  • ALLOW FWD = Action
  • Anywhere = From
  • Also: Erlaube den Zugriff von überall her auf Port 80 und 443 des Containers mit der IP 10.0.0.3

Mehr sollte in unseren Firewallregeln nicht enthalten sein!

Zugriffssteuerung per NPM

Der NPM erlaubt es uns, den Zugriff auf die angebotenen Services einzuschränken. Dies gelingt mit sogenannten Access-Lists, die direkt im NPM angelegt werden können.

Einschränkung

Es ist NICHT möglich, Seiten die bereits eine Benutzerauthentifizierung druchführen zusätzlich via NPM-Accesslists mit User/Passwort zu schützen.  Eine Einschränkung auf IP-Adressen ist möglich. (Funktioniert nur dann, wenn man eine statische öffentliche IP hat!)

Der Grund hierfür ist:

Having an Access Control List (ACL) with username and password requires the browser to always send this username and password in the Authorization header on each request. If your proxied app also requires authentication (like Nginx Proxy Manager itself), most likely the app will also use the Authorization header to transmit this information, as this is the standardized header meant for this kind of information. However having multiples of the same headers is not allowed in the internet standard and almost all apps do not support multiple values in the Authorization header. Hence one of the two logins will be broken. This can only be fixed by either removing one of the logins or by changing the app to use other non-standard headers for authorization.

Quelle: https://nginxproxymanager.com/faq/#do-i-have-to-use-docker

Das heißt soviel wie, dass man – sofern eine Website hat – die bereits eine Autorisierung mittels Benutzername und Passwort durchführt, nicht noch eine zweite Authorisierung mit Benutzername und Passwort (in dem Fall von NPM aus) erfolgen kann. Man hätte dann nämlich mehrfache sogenannte Authorization Headers, die bei jeder Anfrage mitgeschickt werden müssten. Dies widerspricht den Internetstandards und funktioniert somit nicht!

Sofern ihr also nicht die Möglichkeit habt, die Anfragen zu kritischen Bereichen euerer Infrastruktur zumindest auf eure statische öffentliche IP einzuschränken, wäre dies, wenn man auf Nummer sicher gehen will, eventuell per VPN zu lösen.

Zugriff auf die IP Adresse einschränken

Durch einen Klick auf das Menü Access Lists und folgend die Auswahl von „Add Access List“ kann der Zugriff auf angelegte Hosts eingeschränkt werden. Dies geschieht entweder mittels Username / Passwort (wenn möglich! siehe oben!), oder direkt via eurer öffentlichen IP. Natürlich können beide Einschränkungen auch kombiniert werden. Hier hat man dann eine Differenzierung vorzunehmen. Gemeint ist mit Differenzierung der Parameter Satify Any, im Zuge der Erstellung einer Access-List.

Wie oben ersichtlich, will ich eine IP Zugriffsliste erstellen. Der Parameter Satisfy Any ist deaktiviert. Was bedeutet das?

Angenommen, ihr würdet hier nur einen Benutzer&Passwort anlegen und die IP-Settings auf den Standardeinstellungen belassen. IP Seitig wären dann alle IPs auf „Deny“ = verweigern. Selbst wenn ihr Usernamen und Passwort korrekt angebt, könnt ihr euch deshalb nicht einloggen. (Es müssen beide Kriterien zutreffen)

Setzt man aber den Parameter Satisfy Any auf ON (Eingeschaltet), so reicht es, wenn entweder Username und Passwort angegeben werden, oder die IP Regel stimmt.

Das heißt also: Sobald die User/Passwortkombination ok ist, funktioniert der Einstieg.

Wir wollten aber eine Einschränkung auf IP-Addresse vornehmen und belassen den Schalter „satisfy any“ auf aus / off.

  • Im Reiter „Access“ fügen wir im Feld „allow“ unsere öffentliche statische IP hinzu.
  • Und klicken auf Save

Die angelegte Access-List kann dann einem Host hinzugefügt werden.

  • Im Reiter Hosts -> Proxyhost des NPM, bearbeiten wir einen bereits angelegten Host (=Drei Punkte rechts des Hosteintrages anklicken -> EDIT).
  • Gleich im ersten erscheinenden Fenster (Edit Proxy Host) ist nun unten unsere Access-Liste zu wählen. (Dropdown-Menü)

 

  • Abschließend mit „Save“ speichern

Schon ist der Zugriff auf unseren NPM-Container auf unsere IP eingeschränkt. Somit kann niemand (mit anderer IP) auf die Seiten zugreifen.

Hier  geht’s zu Teil 4…

Diskussionsforum zum Thema

Docker mit NGINX-Proxymanager, UFW, Fail2Ban, MariaDB und Nextcloud — Teil 2

Nicht benötigte Container, Volumes & Images löschen

Willkommen zum zweiten Teil meiner Docker-Serie.

Bitte lest unbedingt auch die vorhergehenden Teile!Nachdem wir unsere Dockerinstallation mittels des  „hello-world-Container“ nur getestet haben, ist nun ein für uns nun nutzloser Container vorhanden. Deshalb löschen wir selbigen. Es ist generell ratsam, nicht verwendete Container, Images, Volumes etc. zu entfernen. Nach dem Löschen des Containers, ist auch das Image von „hello-world“ zu löschen.

Installation NGINX-Proxy Manager

Der NGINX-Proxymanager (kurz NPM) wird anfragen vom externen Netzwerk (Internet) entgegen nehmen und diese dann zum jeweiligen Container weiterleiten. Für jede Weiterleitung muss ein Full-Qualified-Domain-Namen (eine Domäne, Subdomäne…) vorhanden sein.

D.h. beispielsweise, dass die Subdomain portainer.it-networker.at an Container „portainer“ bzw. dessen Container-IP weitergeleitet wird.

Ebenso wäre es möglich eine Subdomain proxy.it-networker.at zu erstellen und diese dann vie NPM an unseren NPM-Container weiterleiten zu lassen.

Widmen wir uns aber zuerst der Einrichtung.

Volumes erstellen

Wir benötigen 2 sogenannte Volumes. Ein Volume verweist auf das Dateisystem des Servers (Hosts). Selbst nach einem Neustart, oder der Neuerstellung des betroffenen Containers, bleiben die Daten auf diesem quasi „gemappten“ Dateisystem erhalten.

Es sind 2 Volumes zu erstellen:

  • Ein Datenverzeichnis für den NPM
  • Ein Datenverzeichnis für Letsencrypt (SSL Zertifikate)

Anmerkung: Der NPM macht es möglich, durch Einbindung von Let’s encrypt, kostenlose offizielle SSL Zertifikate zu erstellen.

In Portainer wählen wir links Volumes, dann oben rechts „Add Volume“ und vergeben (ganz oben) als Name „nginx_data“. Es ist völlig egal, welchen Namen ihr hier vergebt. Wichtig ist nur, dass ihr das Volume später eindeutig zuordnen könnt. (Ihr müsst wissen, welches Volume ihr hier für welchen Container vorbereitet habt 😉 ).

Alle anderen Einstellungen belassen wir auf den Vorgaben und klicken auf „Create the volume“.

Das Volume für Letsencrypt erstellen wir auf die gleiche Art und Weise. Ich wähle hierfür den Namen „letsencrypt_data“.

Container erstellen

In Portainer klicken wir links im Menü auf „Containers“ und danach oben rechts auf „Add Container“.

  • Der Name kann beliebig vergeben werden. Ich wähle „NginxPM“.
  • Als Image bitte: jc21/nginx-proxy-manager:latest eingeben.
  • Unter „Network ports configuration“ -> Manual network port pubishing auf „publish a new port“ klicken und folgende Ports erstellen. (Für jedes zusätzliche Port immer wieder auf „publish a network port“ klicken.

Sieht dann so aus:

NGINX-PM wird anfragen auf 80 und 443 annehmen. Port 81 ist das Managementport.

Advanced Container Settings

Command and Logging

Interactive & TTY wählen (=man hat dann eine Konsole für den Container, in die man sich einloggen kann).

Volumes

Im Reiter Volumes müssen wir nun unsere erstellten Volumes für den NPM zuordnen. Wir klicken also 2x auf „map additional volume„, da wir ja auch 2 Volumes zuordnen wollen.

Die Zuordnung der Volumes sollte nun so aussehen:

Network

Network wird aktuell noch auf den Standardeinstellungen belassen.

Env & Labels

Hier kann man Umgebungsvariablen (ENVironment variables) für den Container ein-/erstellen. In unserem Fall müssen wir hier aber nichts ändern oder eintragen. Dies gilt ebenso für den Reiter Labels.

Restart policy

Hier wird es wieder interessant, denn mit dieser Policy kann gesteuert werden, wie sich der Container z.B. nach einem Hostneustart verhält. Wir setzen die Policy auf „Always“.

Runtime & Resources / Capabilities

Können so belassen werden. Die Standardeinstellungen sind in dem Fall ok.

Container „deployen“

Nun ist es an der Zeit den Container auszurollen und zu starten. Dies gelingt mit einem simplen Klick auf „Deploy the container„. Läuft alles glatt, wird nun das Image heruntergeladen und der Container mit unseren Einstellungen gestartet. Landet ihr nach dem Deployment auf folgender Seite, dann hat alles wie gewünscht funktioniert.

Erreichbarkeit prüfen

An sich müsste der NGINX-PM nun auf Port 81 ansprechbar sein. Der Versuch eines Aufrufs von http://portainer.it-networker.at:81/ führt jedoch zu einem Fehler. Die Website ist nicht erreichbar.

Nach kurzer Überlegung wird klar, weshalb das so ist. Wir hatten ja im ersten Teil via ufw-Firewall den Zugriff auf Port 22 und 9443 eingeschränkt. Ein anderer Port ist ja eingehend nicht offen. Hat man eine öffentliche statische IP, kann man den Zugriff auf Port 81 des Containers (NGINX-PM) freigeben:

ufw route allow from <eure öffentliche IP> to 172.17.0.3 port 81

Jetzt klappts…

Danach könnt ihr euch bei eurem NGINX-PM einloggen. Der Standarduser ist admin@example.com, das Passwort changeme

Nach dem Login, müsst ihr eure „Admindetails“ angeben und ein neues Passwort setzen.

UFW Firewall anpassen & IP-Adresse des NGINX-PM adaptieren

Alle eingehenden Anfragen auf Port 80 oder 443 sollen ausschließlich auf den NGNIX-PM weitergeleitet werden. Es ist klar, dass unsere UFW –  Firewall angepasst werden muss. Hier kommt es jetzt aber zu einem kleinen Problem, denn Docker vergibt innerhalb des Standardnetzwerkes (172.17.0.x) die IP Adressen automatisch (DHCP). Es könnte theoretisch passieren, dass die Container bei jedem Neustart eine andere IP-Adresse bekommen.

Unsere Firwallregeln wären somit dann im schlimmsten Fall nutzlos bzw. würden zumindest ins Leere laufen.

Ein eigenes Netzwerk erstellen

Um dem entgegen zu wirken, erstellen wir uns ein eigenes Netzwerk.

  • In Portainer wählen wir auf der linken Seite „Networks“ und folgend oben rechts „Add Network“.
  • Der Name kann frei vergeben werden.
  • Driver verbleibt auf „bridge“.
  • Unter „IPV4 Network configuration“ müssen wir nun aber eine IP Adresse vergeben. Ich wähle hier „10.0.0.0/24“. Hiermit stehen uns also 254 Adressen zur Verfügung.
  • Ebenso müssen wir einen Gateway eintragen. (10.0.0.1)

Abschießend einfach noch ganz unten auf „Create the network“ klicken.

Wir haben nun also ein „eigenes Netzwerk“ mit dem Adressraum 10.0.0.1-10.0.0.254 erstellt. Aufgrund dessen können nun Container erstellt werden, die eine fixe IP bekommen. Diese IP-Adresse bleibt auch erhalten, wenn der Container neu gestartet wird.

NGINX-PM IP Anpassungen

Wir wollen, dass der NGINX-PM immer die IP Adresse 10.0.0.3 erhält. Der Container muss also angepasst werden. In Portainer wählen wir links „Containers„, danach suchen wir uns in der Liste rechts den NPM heraus, klicken ihn an und klicken oben auf „Duplicate / Edit„.

Bei den nun angezeigten Einstellungen, nach unten scrollen, bis zu „Advanced container settings“ und wählen den Reiter „Network“ aus.

Im Dropdown „Network“ wählen wir das zuvor erstellte Netzwerk bei mir „it-networker„. Weiter unten bei „IPV4 Address“ ist nun die gewünschte IP – bei mir 10.0.0.3 – einzugeben.

Letztlich „deployen“ wir den Container nochmals per Klick auf „Deploy the containter„. Die Nachfrage „Are you sure?“ quittieren wir mit „Replace„.

Unser NPM hat nun die IP 10.0.0.3

Achtung: Ufw-Firwall

Nachdem sich die IP unseres NGINX-PM geändert hat, muss nun ufw wieder angepasst werden!

Mittels ufw status numbered lassen wir uns die geltenden Regeln anzeigen.

Die „alte Regel“ -unten Nr 3- für den NPM (NGINX Proxymanager) kann gelöscht werden.

ufw delete 3

…und entsprechend bestätigen.

Um  weiterhin Zugriff auf den NPM zu haben, muss eine neue Regel erstellt werden:

ufw route allow from <eure öffentliche IP> to 10.0.0.3 port 81

Jetzt ist der NPM wieder für euch erreichbar! (Port 81)

Dies sollte nur eine kurzfristige Lösung sein! Vor  allem dann, wenn ihr keine statische öffentliche IP habt. Spätestens, sobald der NPM via Subdomain und HTTPS erreichbar ist, ist die oben angeführte Firewallregel zu löschen, denn die Verbindung ist nicht verschlüsselt!

Eigene Subdomain für NPM

Das, was wir oben bezüglich des Netzwerkes durchexerziert haben, war einerseits nötig, diente aber auch Verständnis. Es ist jetzt an der Zeit, den NPM über eine Subdomain anzusprechen. Natürlich via SSL!

Ich habe deshalb eine neue Subdomain im DNS meines Providers angelegt. Diese lautet proxy.it-networker.at

Zusätzliche Regeln mit ufw erstellen

Jeder soll von extern auf Port 80 und 443 unseres NGINX-PM Container (der intern auf IP 10.0.0.3 läuft) zugreifen können. Die Aktion ist auch nötig, damit wir uns via Letsencrypt ein valides SSL Zertifikat für die Subdomain proxy.it-yourself.at holen können!

 ufw route allow from any proto tcp to 10.0.0.3 port 80

ufw route allow from any proto tcp to 10.0.0.3 port 443

Die  weitere Einrichtung erfolgt nun im Adminbackend des NPM (Port 81/http mit dem FQDN)

  1. Einloggen in den NPM
  2. Auswahl des Reiters SSL Certificates
  3. Klick (oben rechts) auf Add SSL Certificates -> Letsencrypt
  4. Bei „Domain Names“ tragt ihr bitte eure Subdomain ein, die ihr für den NPM vorgesehen habt. Achtung wenn ihr den FQDN im Feld eingegeben habt, nicht Return drücken, sondern gleich unterhalb auf „Add <euer FQDN>“ klicken.
  5. Unten „I Agree……….“ wählen
  6. Auf Save klicken

Der Test „Test Server Reachability“ ist nicht immer erfolgreich. Ich führe ihn deshalb nicht jedesmal durch. Egal, ob der Test erfolgreich ist, oder nicht. Nach dem Klick auf Save, dauert es meist rund 30 – 40 Sekunden und ihr habt euer Zertifikat.

Regel im NPM für proxy.it-networker.at erstellen

  • Oben klick auf Hosts -> Proxyhosts
  • Mittig (grüner Button) -> Add Proxy Host
  • Domain Names: proxy.it-networker.at (nach Eingabe wieder auf das Add… unten klicken)
  • Scheme: http
  • Forward Hostname / IP: Name des Containers: NginxPM / oder IP
  • Forward Port: 81
  • Block common exploits
  • Reiter SSL: Das zuvor erstellte SSL Zertifikat auswählen
  • Force SSL
  • HTTP/2 Support
  • Save

Gebt ihr nun https://<eure Subdomain> ein -> in meinem Fall https://proxy.it-networker.at gelangt ihr HTTPS-Verschlüsselt direkt zum Login euers NPM.

Zugriffssteuerung / Absicherung

Es ist jetzt an der Zeit, die Firewallregel, die einen direkten Zugriff auf Port 81 (des NPM) erlaubt, zu löschen. Mittlerweile wisst ihr schon, wie das geht.

ufw status numbered

Danach die betreffende Regel sichten und per Nummer davor in der Eckigen klammer löschen.

ufw delete 2

Natürlich kann es sein, dass die betroffene Regel bei euch eine andere Nummer hat!

Weiter zu Teil 3

Diskussionsforum zum Thema

Debian Stretch Mariadb – Rootlogin phpmyadmin funktioniert nicht mehr

Es ist zwar kein neuer Hut mehr, dass es mit Mariadb in Verbindung mit dem rootlogin in phpmyadmin Probleme gibt, ich möchte es an dieser Stelle aber dennoch erwähnen und eine – meiner Meinung nach gute – Lösung anbieten.

Hintergrund

Bei Mariadb (aktuell in Version 10.1.26-0+deb9u1) ist für den root-user ein unix_socket Plugin aktiv. Dieses Plugin prüft, ob man:

1.) Als Rootuser in der Konsole eingeloggt ist und
2.) den vorhandenen Mariadb-root-user.

Wenn man sich nun bei phpmyadmin als Benutzer root einloggen will, schlägt dies fehl. Klar! In dem Fall meldet man sich ja nicht per Konsole (als User root) bei der Mariadb an, sondern über das Web.

Lösungsweg

Es gäbe nun die Möglichkeit, das Plugin für den Mariadb-Benutzer root zu deaktivieren. (Davon sehe ich hier aber ab). Besser ist es, einen neuen Datenbankbenutzer anzulegen, der -so wie root- auch Vollzugriff auf alle Datenbanken hat, um letztendlich alle Datenbanken per phpmyadmin verwalten zu können.



Im Terminal des Servers  bzw. des Linuxsystems sind folgende Schritte (eingeloggt als root) durchzuführen:

mysql

grant all on *.* to neuerrootuser@localhost (RETURN)
identified by 'deinpasswort' with grant option;

Es ist nun ein weiterer Benutzer mit DB-Rootrechten angelegt worden.

Absicherung der Installation

Es ist immer eine gute Idee, den Befehl

mysql_secure_installation

auszuführen, um einige DB-Einstellungen sicherer zu gestalten. Hierbei kann (und sollte!) auch für den bereits vorhandenen DB-Benutzer „root“ ein Passwort gesetzt werden. (In der Standardkonfiguration hat dieser DB-Benutzer kein Passwort, da ja u.a. auf das unix_socket-Plugin zurückgegriffen wird).

Bestehende MariaDB-Benutzer abfragen / Plugincheck

Will man prüfen, welche User angelegt sind / für wen das oben erwähnte Plugin aktiv ist, begibt man sich abermals in ein Rootterminal und startet folgende Abfrage:

mysql

select user, plugin from mysql.user;