HTTPS-Absicherung & Authentifizierung via nginx Webserver

Aus FHEMWiki
Wechseln zu: Navigation, Suche

Ä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.

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 WEBhomebridge FHEMWEB 8084
# attr WEBphone stylesheetPrefix smallscreen

# 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;

    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;

      proxy_pass          http://localhost:8083;
      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
      #if ($http_upgrade = "websocket") {
      #  set $my_http_upgrade $http_upgrade;
      #  set $my_connection "upgrade";
      #}

      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)

Quellen & weiterführende Links