Diese Anleitung beschreibt, wie ein Apache-Webserver als Reverse Proxy für eine FHEM-Installation eingerichtet wird. Bei der Einrichtung hatte ich folgende Ziele:
- Erreichbarkeit von FHEM aus dem Internet (mit Host-Name per Dynamic DNS)
- Absichern der Kommunikation per SSL (mit selbst signiertem Zertifikat)
- Zugriff ohne Passworteingabe, Absicherung per SSL-Client-Zertifikat
Disclaimer: Ich bin weder ausgewiesener Sicherheitsexperte noch Administrator, alle Tips hier habe ich mir aus diversen Tutorials zusammengesucht. Wer dringende Änderungswünsche oder Verbesserungsvorschläge hat, mailt an gb@birke-software.de
- Ein Raspberry Pi mit Raspbian oder die notwendige Intelligenz, alles was auf eurem System anders ist als unter Raspbian auch anders zu machen.
- FHEM auf dem System installiert und im internen Netz über die Standard-Weboberfläche erreichbar
- IP-Adresse des Raspberry Pi, am besten statisch. Als Beispiel-Adresse im internen Netz benutze ich die
192.168.1.42
. - Ein Router, der Dynamic DNS und Port-Weiterleitungen unterstützt. Fritzboxen und Router mit DD-WRT oder OpenWRT haben da kein Problem.
- Wissen, wie man auf der Linux-Konsole Dateien editiert (nano, pico, vim, emacs, etc).
Das Einrichten eines Dynamic DNS übersteigt wegen der Vielfalt der Router und Diensteanbieter den Umfang dieses Tutorials, ist aber über die Weboberflächen von Router und Anbieter einfach. Als Beispiel-Hostname verwende ich example.com
.
Ich benutze zur Zeit eine Fritzbox und den kostenlosen Service des Anbieters no-ip.com. Wichtig ist nur, dass ihr den Hostnamen example.com
in den Beispielen durch euren Hostnamen ersetzt.
Wichtig: Einige Tutorial-Schritte hinterlassen das System im unsicheren Zustand (ohne Passwort- bzw. Zertifikats-Abfrage). Deshalb an dieser Stelle noch keine Portweiterleitung einrichten, das kommt erst später!
In meiner fhem.cfg
stehen folgende Zeilen:
define WEB FHEMWEB 8083
define WEBphone FHEMWEB 8084
attr WEBphone stylesheetPrefix smallscreen
define WEBtablet FHEMWEB 8085
attr WEBtablet stylesheetPrefix touchpad
Achtet darauf, dass hinter den Port-Angaben das "global" weg kommt. Falls ihr noch ein attr WEB authBasic
drin habt, dann kommentiert dieses aus.
Ich richte für die drei Web-Oberflächen (Web, Tab, Phone) drei Port-basierte Virtuelle Hosts ein, die zum jeweiligen FHEM-Port einen Proxy bilden. Ich benutze Ports statt Subdomains weil ich dann nur ein SSL-Zertifikat brauche. Ich benutze Ports statt Unterverzeichnisse, weil ich dann a) das von FHEM gelieferte HTML nicht umschreiben muss und b) einen winzig kleinen Sicherheitsgewinn durch nicht-standardisierte Ports habe.
Weil Heimüberwachung so schön an "Big Brother" erinnert, werde ich die Ports 1983, 1984 und 1985 benutzen und auf die FHEM-Ports 8083, 8084 und 8085 weiterleiten. Die folgende Anleitung zeigt nur exemplarisch die Konfiguration von Port 1984. Die Konfigurationsdateien in /etc/apache2/sites-available
sind bis auf die Port-Angabe für alle Hosts identisch. Ich habe bisher noch keine Lösung gefunden, die mit einer Konfigurationsdatei auskommt.
Als erstes mal Apache installieren mit
sudo apt-get install apache
Damit die Web-Anfragen auch auf den Ports ankommen, müssen folgende Zeilen in /etc/apache2/ports.conf
eingefügt werden:
# FHEM Ports
Listen 1983
Listen 1984
Listen 1985
Es folgt die Datei /etc/apache2/sites-available/fhemweb
für den virtuellen Host
<VirtualHost *:1983>
ServerAdmin webmaster@example.com
ServerName example.com
ProxyRequests Off
# ProxyPass/ProxyPassReverse leitet HTTP requests auf eine andere URL um
ProxyPass / http://127.0.0.1:8083/
ProxyPassReverse / http://127.0.0.1:8083/
# ProxyHTMLURLMap passt Links im HTML/JavaScript Source an
ProxyHTMLURLMap / /fhem/
ProxyHTMLURLMap /fhem/ /fhem/
# Logging in extra Dateien
ErrorLog ${APACHE_LOG_DIR}/fhem-error.log
LogLevel warn
CustomLog ${APACHE_LOG_DIR}/fhem-access.log combined
</VirtualHost>
Dupliziert die Datei als /etc/apache2/sites-available/fhemtablet
und ändert die Ports auf 1984 und 8084. Dupliziert die Datei als /etc/apache2/sites-available/fhemphone
und ändert die Ports auf 1985 und 8085.
Jetzt einmal mit dem Kommando
sudo a2ensite fhemweb
sudo a2ensite fhemtablet
sudo a2ensite fhemphone
den Host aktivieren und mit dem Kommando
sudo service apache2 restart
den Webserver neu starten. Tipp: Bitte wirklich restart
statt reload
verwenden, sonst gibt es Fehlermeldungen!
Jetzt sollte FHEM (ohne SSL oder Passwortabfrage) unter http://192.168.1.42:1983/ erreichbar sein.
Für die Client-Zertifikate und die selbst-signierten Server-Zertifikate brauchen wir eine Zertifizierungsinstanz (Certificate Authority, CA). Dazu könnte ich viel Geld an Verisign oder andere CAs bezahlen, weil ich mir aber selbst vertraue, bin ich einfach meine eigene CA.
Sicherheitshinweis: Der Root-Schlüssel und das CA-Zertifikat sind so mächtig wie Generalschlüssel! Sicherheitsbewusste Zeitgenossen führen die folgenden Befehle auf einem vom Internet getrennten System aus, das frisch von einer Sicherheitsorientierten Linux Live-CD gebootet wurde. Die Zertifikats-Dateien können dann später per USB-Stick auf den Raspberry Pi und die Clients übertragen werden.
Unter Debian sind die openssl-Daten im Verzeichnis /etc/ssl
. Als erstes kopieren wir mit
cp /etc/ssl/openssl.cnf cp /etc/ssl/ca-openssl.cnf
die Einstellungdatei und die passen default-Einstellungen in /etc/ssl/ca-openssl.cnf
ein wenig an. Eine ausführliche Anleitung und Erklärung dazu gibt es unter http://www.phildev.net/ssl/opensslconf.html, die einzige Änderung, die wir gegenüber den Vorgaben aus Debian machen ist das Verzeichnis dir = /etc/ssl
zu ändern. Wenn ihr euch später Tipp-Arbeit sparen wollt, ändert noch die Angaben im Bereich req_DN
.
Alle folgendes Kommandos führe ich als root aus.
cd /etc/ssl
mkdir -p certs crl newcerts private
chmod 700 private
touch index.txt
openssl req -new -newkey rsa:2048 -keyout private/cakey.pem -out careq.pem -config ./ca-openssl.cnf
chmod 400 private/cakey.pem
Damit habe ich den Root-Schlüssel für die CA und das Certificate Signing Request (CSR).
Auf dem Raspberry Pi funktioniert das automatische Erzeugen der Zertifikats-Seriennummer mit -create_serial
nicht (zumindest mit meiner Version "1.0.1e 11 Feb 2013"). Deshalb erzeuge ich sie selbst mit
echo "10001" > serial
Dann erzeuge ich das CA Root-Zertifikat mit
openssl ca -create_serial -out cacert.pem -days 3650 -keyfile private/cakey.pem -selfsign \
-extensions v3_ca -config ./ca-openssl.cnf -infiles careq.pem
mkdir -p /etc/ssl/servers
cd /etc/ssl/servers
openssl genrsa -out example.com.encrypted.key.pem 1024
Das erzeugt unseren privaten Schlüssel für den Web-Server. Bei der Schlüssel-Erstellung muss man eine Passphrase eingeben. Damit wir beim Apache-Neustart nicht sie nicht immer eingeben müssen, entfernen wir sie:
openssl rsa -in example.com.encrypted.key.pem -out example.com.key.pem
Um die Sicherheit zu erhöhen, legen wir fest, dass nur der Benutzer root
auf die Schlüssel zugreifen darf:
chmod 400 example.com.key.pem example.com.encrypted.key.pem
Jetzt folgt der Certificate Signing Request.
openssl req -config /etc/ssl/ca-openssl.cnf -new -key example.com.key.pem -out example.com.csr
Die CA bestätigt nun den CSR mit dem Befehl
openssl ca -config /etc/ssl/ca-openssl.cnf -out example.com.crt -infiles example.com.csr
Folgende Zeilen müssen in den Dateien in /etc/apache2/sites-available/
eingefügt werden:
SSLEngine on
SSLCertificateFile /etc/ssl/servers/example.com.crt
SSLCertificateKeyFile /etc/ssl/servers/example.com.key.pem
SSLCertificateChainFile /etc/ssl/cacert.crt
Nach einem
service apache2 restart
sollte FHEM unter https://192.168.1.42:1983/ erreichbar sein. Der Browser zeigt wegen des selbst-signierten Zertifikats eine oder mehrere Warnungen an. Das ist ok, denn es gibt zwei Dinge zu meckern:
- Das Root-Zertifikat der CA ist nicht in der Liste der vertrauenswürdigen CAs des Browsers (und falls ihr den privaten Schlüssel der CA nicht ausschließlich auf einem USB-Stick habt, solltet ihr das Zertifikat auch nicht hinzufügen).
- Der Hostname stimmt nicht (weil wir nur die IP-Adresse verwenden). Dieser Fehler lässt sich später durch den Dynamic DNS beheben.
Wer anstelle von Client-Zertifikaten lieber Benutzername und Passwort benutzen möchte, sei an folgende Anleitung verwiesen http://www.laub-home.de/wiki/Apache_Basic_Authentifizierung und kann den nächsten Abschnitt überspringen.
Ich zeige im Folgenden das Erstellen eines einzelnen Zertifikats mit dem Namen client
. Das Zertifikat kann auf alle Clients kopiert werden. Ich würde aber für jedes Mobilgerät, das sich außerhalb meiner vier Wände bewegt, ein eigenes Zertifikat erzeugen, das beim Verlust des Gerätes zurückgerufen werden kann.
mkdir -p /etc/ssl/clients
cd /etc/ssl/clients
openssl req -new -newkey rsa:2048 -keyout client.key.pem -out client.req.pem -config ../ca-openssl.cnf
Dieser Befehl erzeugt den privaten Schlüssel für den Client-Key und das CSR. Jetzt das CSR bestätigen:
openssl ca -config ../ca-openssl.cnf -out client.crt -infiles client.req.pem
Das Zertifikat als PK12 Zertifikat exportieren:
openssl pkcs12 -export -in client.crt -inkey client.key.pem -certfile ../cacert.pem -out client.p12
Damit der Browser die FHEM-Seiten ausschließlich an Clients mit Zertifikat ausliefert, folgende Zeilen in den Dateien in /etc/apache2/sites-available
einfügen:
SSLCACertificateFile /etc/ssl/cacert.pem
SSLVerifyClient require
SSLVerifyDepth 1
Nach einem
service apache2 restart
sollte der Zugriff auf https://192.168.1.42:1983/ nicht mehr möglich sein. Wie die Datei client.p12
im Browser importiert wird, ist Betriebssystem- und Browser-abhängig. Nach dem Import des Zertifikats im Browser sollte der Zugriff wieder möglich sein.
Nachdem jetzt alles abgesichert ist, können wir auf dem Router eine Port-Weiterleitung einrichten. Das bedeutet, dass aller Traffic, der beim Router auf Port 1983, 1984 und 1985 ankommt, auf die gleich lautenden Ports des Raspberry Pi weiter geleitet wird. Wie das genau geht, ist vom Router abhängig.
TODO
Wem beim Gedanken, einen Rechner öffentlich zugänglich zu machen nicht ganz wohl ist, der kann über folgendes nachdenken:
- Apache absichern
- HSTS und Umleitung auf HTTPS, wenn per HTTP zugegriffen wird
- Verbesserte TLS-Konfiguration in Apache mit Perfect Forward Secrecy
- Verbessern der Sicherheit mit fail2ban oder/und mod_security
Vielen Dank an folgende Seiten/Tutorials:
- Einfaches Beispiel für Virtual Host mit Proxy
- Englisches Tutorial zu CAs und wie man sie komfortabel und sicher konfiguriert. Die meisten der Beispiele in diesem Tutorial wurden mit Hilfe dieser Anleitung erstellt
- Deutsches Tutorial zu CA und Zertifikatserstellung
- Deutsches Tutorial zur authentifizierung mit Client-Zertifikaten
- Umfangreiches englisches Tutorial zu CAs, Zertifikaten und Rückruf (revocation) von Zertifikaten
- Überblick durch den Dschungel von .pem, .key, etc
- Umgang mit selbstsignierten Client- und Server-Zertifikaten unter iOS
- Pro und Kontra von Passworten versus Client-Zertifikaten
Funktioniert leider ohne weiteres nicht mehr so mit der aktuellen Apache Version. Die sites-available müssen z.B. jetzt ".conf" als extrension haben um aktiviert werden zu können