HTTPS-Absicherung & Authentifizierung via nginx Webserver
Ähnlich wie im Artikel Apache Authentication Proxy kann FHEM auch mit dem schlankeren nginx Webserver abgesichert werden. Nach diesem Artikel ist die Kommunikation zum FHEM-Server via HTTPS verschlüsselt und eine Authentifizierung nur via Benutzername und Passwort möglich. Externer Zugriff aus dem eigenen Netzwerk/Internet soll damit unterbunden werden; die Kommunikation und Authentifizierung findet verschlüsselt statt.
Voraussetzungen
Vorausgesetzt wird eine funktionierende FHEM Installation. Die dadurch erbrachten Linux Kenntnisse reichen für die Umsetzung dieses Artikels. Weiterführende Links zu Zertifikaten und dem nginx-Webserver befinden sich im Anhang des Artikels.
Ausgegangen wird von einer FHEM-Installation, die auf Port 8083 via HTTP hört. Dieser Port wird abgesichert, indem er nur noch lokal und nicht mehr über das Netzwerk erreichbar ist. Anfragen auf Port 80 (HTTP, nginx) werden weitergeleitet zum lokalen Port 443 (HTTPS, nginx), der wiederum einen Reverse Proxy auf den Port 8083 (HTTPS, fhem) einrichtet. Mit leichten Modifizierungen könnte nginx auch auf einem separaten Server betrieben werden. Die benötigten Zertifikate für die HTTPS Verschlüsselung können eigenhändig angelegt werden. Der/die autorisierten Benutzer werden in einer verschlüsselten Datei hinterlegt, die nginx für die Authentifizierung benutzt.
Getestet wurde das Setup auf einem Raspberry Pi (Raspbian - basierend auf Debian 8 und 9) und fhem 5.7 bzw. fhem 5.8. Die beschriebenen Befehle sollten sich ohne weiteres auf Ubuntu oder andere Debian-Derivate übertragen lassen. Das beschriebene Konzept lässt sich aber auf jedes Unix-Derivat anwenden.
Unterscheidung von Browser auf PC und mobilen Endgeräten
Zusätzlich kann man mit nginx sehr bequem zwischen PC und mobilen Endgeräten unterscheiden und damit verschiedene FHEMWEB - Instanzen aufrufen.
Anpassungen: fhem-Config
In der FHEM-Konfiguration muss sichergestellt werden, dass kein Client außerhalb des Servers zugreifen kann. Dazu muss das normalerweise gesetzte Flag global von jeglicher Konfiguration entfernt werden.
Beispielkonfiguration:
... # define telnetPort telnet 7072 global define WEB FHEMWEB 8083 attr WEB stylesheetPrefix dark # attr WEB HTTPS define WEBphone FHEMWEB 8084 attr WEBphone stylesheetPrefix darksmallscreen # define WEBtablet FHEMWEB 8085 global # attr WEBtablet stylesheetPrefix touchpad
fhem neustarten:
$ sudo service fhem stop && sudo service fhem start
Installation: nginx als reverse Proxy
nginx installieren:
$ sudo apt-get install nginx
Danach im Pfad /etc/nginx/sites-available eine neue Proxy Konfiguration anlegen:
$ sudo touch /etc/nginx/sites-available/reverse-proxy
Inhalt der Datei reverse-proxy:
server {
listen 80;
return 301 https://$host$request_uri;
}
server {
listen 443;
server_name fhempi;
# check user agent
if ($http_user_agent ~* '(iPhone|iPod|Opera Mini|Android.*Mobile|NetFront|PSP|BlackBerry|Windows Phone)') {
set $ua_type "@mobile";
}
ssl_certificate /etc/nginx/cert.crt;
ssl_certificate_key /etc/nginx/cert.key;
ssl on;
ssl_session_cache builtin:1000 shared:SSL:10m;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers HIGH:!aNULL:!eNULL:!EXPORT:!CAMELLIA:!DES:!MD5:!PSK:!RC4;
ssl_prefer_server_ciphers on;
access_log /var/log/nginx/jenkins.access.log;
location / {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_http_version 1.1;
# Gehe zu FHEMWEB wenn kein mobiler Browser
if ($ua_type != "@mobile"){
proxy_pass http://localhost:8083;
}
# Gehe zu FHEMWEB smallscreen wenn mobiler Browser
if ($ua_type = "@mobile"){
proxy_pass http://localhost:8084;
}
proxy_read_timeout 90;
#proxy_read_timeout 20736000;
#proxy_buffering off;
# User Sickboy's Erweiterung für verschlüsselte Websocket-Kommunikation (siehe Diskussionsseite)
# Für normale Benutzer derzeit kommentiert, vom Autor Andremotz noch bisher ungetestet
# ... daher derzeit auskommentiert
# Wird für 'longpoll' benötigt (z.B. bei FTUI)
#set $my_http_upgrade "";
#set $my_connection "Connection";
#if ($http_upgrade = "websocket") {
# set $my_http_upgrade $http_upgrade;
# set $my_connection "upgrade";
#}
#proxy_set_header Upgrade $my_http_upgrade;
#proxy_set_header Connection $my_connection;
auth_basic "Restricted Content";
auth_basic_user_file /etc/nginx/.htpasswd;
# proxy_redirect http://localhost:8083 https://localhost;
}
}
Mit dieser Konfiguration werden zwei Server-Instanzen auf Port 80 und Port 443 angelegt. Surft ein Browser/eine App Port 80 an, wird er umgehend zum verschlüsselten Port 443 weitergeleitet. Die Zertifikate befinden sich im lokalen Verzeichnis unter /etc/nginx und verschlüsselt wird via TLS.
Nginx' Default-Konfiguration muss noch deaktiviert und die Reverse-Proxy-Einstellungen verlinkt werden
$ sudo unlink /etc/nginx/sites-enabled/default
$ sudo ln -s /etc/nginx/sites-available/reverse-proxy /etc/nginx/sites-enabled/reverse-proxy
Hinweis: Ein User hatte für mich nicht nachvollziehbare Probleme und empfiehlt folgende Settings:
proxy_read_timeout 2073600;
proxy_buffering off;
User anlegen für Authentifizierung
Der Username und das verschlüsselte Passwort werden mit den folgenden Kommandos in die Datei ’’ ’/etc/nginx/.htpasswd’’’ geschrieben, das nginx in der Konfiguration für die User-Authentifizierung benutzt.
Username maxmustermann anlegen (Doppelpunkt nach Username nicht übersehen!):
$ sudo sh -c "echo -n 'maxmustermann:' >> /etc/nginx/.htpasswd"
Passwort-Prompt aufrufen:
$ sudo sh -c "openssl passwd -apr1 >> /etc/nginx/.htpasswd"
Zertifikatserstellung
Erst durch die Zertifikate ist eine verschlüsselte Kommunikation zwischen Browser und nginx möglich Beide beschriebenen Optionen sind dabei gleichermaßen sicher. Damit allerdings nicht jedes ausgestelle Zertifikat vertraut wird, gibt es das Konzept der Certificate Authorities ('CA', näheres siehe Links). Jeder gängige Browser warnt den Benutzer vor einem Zertifikat, das von keiner Thrusted Authority ausgestellt wurde, bietet jedoch dem Benutzer die Möglichkeit, trotzdem fortzufahren. Durch die Tatsache, dass das Zertifikat selbst erstellt und signiert wird, kann diese Warnung im Browser später getrost umgangen werden.
Selbst signiertes Zertifikat erstellen:
$ sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /etc/nginx/cert.key -out /etc/nginx/cert.crt
Dabei werden für die Erstellung ein paar Angaben abgefragt. Wichtig ist dabei der Common Name, der identisch mit der später verwendeten Domain oder öffentliche IP-Adresse sein muss.
Country Name (2 letter code) [AU]:AT State or Province Name (full name) [Some-State]:Vienna Locality Name (eg, city) []:Vienna Organization Name (eg, company) []:privat Organizational Unit Name (eg, section) []:privat Common Name (e.g. server FQDN or YOUR name) []:meine_domain_oder_ip Email Address []:admin@meinedomain
nginx neustarten, um die Änderungen und das Zertifikat zu übernehmen:
$ sudo service nginx reload
Der folgende Aufruf sollten bereits eingerichteten FHEM-Server via Passwort-Abfrage und HTTPS absichern:
http://<fhemserver>
Bekannte Fehlerquellen
Beim Aufruf http://<fhempi> wird nur eine "Wilkommen bei nginx"-Seite angezeigt, statt der erwartete Login-Screen
Nach einer Neuinstallation dem Autor selber passiert. Fehler war, dass die nginx-Default-Konfiuguration noch aktiv war. Der Link zur Default-Konfiguration muss entfernt werden, bzw. gleich komplett gelöscht werden
Wenn ich mich einlogge, bekomme ich nur einen 500 internal Server error. Ich habe garantiert alles richtig konfiguriert!
Ebenfalls dem Autor nach einer Neuinstallation passiert: Fehler war, dass das Passwort durch einen Copy & Paste-Fehler falsch eingetragen wurde. Der Fehlerfall kann umgangen werden, indem zuerst die .htpasswd-Datei gelöscht wird und der User und ein simples Passwort neu angelegt werden. Danach mit einer neuen Browser-Session Fhem ansurfen (am besten Firefox im Privat-Modus, damit sämtliche Cookies & sonstiges Buffering umgangen wird)