Skip to content

Instantly share code, notes, and snippets.

@gbirke
Last active August 12, 2022 19:03
Show Gist options
  • Save gbirke/8608543 to your computer and use it in GitHub Desktop.
Save gbirke/8608543 to your computer and use it in GitHub Desktop.

Apache Proxy mit SSL-Client-Zertifikat für FHEM

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

Voraussetzungen

  • 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).

Dynamic DNS

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!

FHEM konfigurieren

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.

Apache als Proxy einrichten

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.

Root-Zertifikat erstellen

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

Server-Zertifikat erstellen

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

Apache für SSL konfigurieren

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:

  1. 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).
  2. 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.

Client-Zertifikate erstellen und in Apache konfigurieren

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.

Port-Weiterleitung einrichten

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.

Rückruf von Zertifikaten

TODO

Weiterführende Konfiguration

Wem beim Gedanken, einen Rechner öffentlich zugänglich zu machen nicht ganz wohl ist, der kann über folgendes nachdenken:

Danksagungen/Linksammlung

Vielen Dank an folgende Seiten/Tutorials:

@Gizmoh1683
Copy link

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

@Badflex
Copy link

Badflex commented Aug 12, 2022

Da klappt einiges nicht. Das"
cp /etc/ssl/openssl.cnf cp /etc/ssl/ca-openssl.cnf" stimmt wohl nicht.

Wenn ich den "openssl ca -create_serial -out cacert.pem -days 3650 -keyfile private/cakey.pem -selfsign
-extensions v3_ca -config ./ca-openssl.cnf -infiles careq.pem"
eingebe kommt auch ein fehler. Was ich rausbekommen habe: -create_
Muss schon mal weg. Aber trozdem sagt er mor das er den ca key nicht findet. Ich glaub da gibts noch einen fehler mit der ca_openssl.cnf

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment