📱 Hey Siri Kurzbefehle, Apple Shortcuts -> SSH -> MQTT -> FHEM, Access Control

Begonnen von Torxgewinde, 12 Mai 2023, 22:03:57

Vorheriges Thema - Nächstes Thema

Torxgewinde

"Hey Siri, schalte die Gartenbeleuchtung ein" - ohne Homekit

Siri Kurzbefehle sind in iOS Geräten eine komfortable Option, um mittels Widgets oder auch mit der "Hey Siri" Sprachsteuerung Befehle auszuführen. Einer der Kurzbefehle erlaubt es SSH Befehle auszuführen, was einem viele weitere Schnittstellen ermöglicht.

Da die iOS Geräte zwar schon recht sicher sind, aber trotzdem einen Angriffsvektor darstellen, ist es nicht ratsam, direkt die Befehle an FHEM oder die Konsole weiterzugeben. Zu einfach wäre es, einen schädlichen Befehl abzusetzen.

Als Schnittstelle zwischen Apple-Gerät (iPhone, Mac, iPad) kommt SSH aus einem Kurzbefehl zum Einsatz. Empfangen wird der Befehl von einem SSH Daemon. Als SSH Daemon reicht bereits etwas minimales wie Dropbear auf einem System wie z.B. OpenWRT. Damit man mit SSH nicht auf dem Server beliebige Befehle ausführen kann, wird ein Nutzer erstellt, der wenig Rechte im System hat, und es wird mit "forced_command" nur ein einziges Programm erlaubt. Dieses erlaubte Programm ist hier ein Shell-Skript, welches die Daten dann an FHEM weitergibt. In diesem Fall habe ich die Daten noch über MQTT verschickt und dann erst in FHEM mit einem Device bearbeitet. Damit in FHEM nicht jeder Befehl ausgeführt wird, gibt es eine Whitelist von regulären Ausdrücken - so kann dann das Apple-Gerät nur genau diese Befehle ausführen.

Erstmal benötigt man die App "Kurzbefehle" bzw. "Shortcuts" von Apple.

Dann erstellt man sich einen Shortcut mit dem SSH Befehl. Extra ein Shortcut anzulegen bietet den Vorteil, nur an einer Stelle den SSH Zugriff einstellen zu müssen.
Du darfst diesen Dateianhang nicht ansehen. Du darfst diesen Dateianhang nicht ansehen.

Nun erstellt man sich seine Shortcuts für Aktionen, hier schalten wir ein Radio mit einer Funkschaltsteckdose ein:
Du darfst diesen Dateianhang nicht ansehen.Du darfst diesen Dateianhang nicht ansehen.
Der Name des Shortcut ist auch gleichzeitig der Sprachbefehl.

Zu dem SSH Daemon, Dropbear oder analog auch OpenSSH:
Es wird ein Nutzer angelegt, der wenig Rechte im System hat. Das geht zum Beispiel bei einem OpenWRT System in der Art (Doku dazu):
opkg update
opkg install shadow-useradd
useradd appleshortcut
passwd appleshortcut
--> Passwort festlegen

mkdir -p /home/appleshortcut
chown appleshortcut /home/appleshortcut
vi /etc/passwd
---> Die gewünschte Shell ergänzen (ganz hinten)
---> appleshortcut:x:1000:1000::/home/appleshortcut:/bin/ash

/etc/init.d/dropbear restart

Nun hat man also einen User "appleshortcut", der seine Dateien im Ordner "/home/appleshortcut" findet und der sich mit SSH auf Port 22 einloggen kann. Nun macht es Sinn, diesen SSH Zugriff nicht so universell zu gestalten - der User soll ja nur ganz bestimmte FHEM Befehle absetzen können, mehr nicht. Das gelingt, indem man dem SSH Daemon den Zugriff mit Passwörtern verwehrt, was mit der LuCI-Weboberfläche recht einfach ist:
Du darfst diesen Dateianhang nicht ansehen.

Von nun an gelingt der SSH Zugriff nur noch mit SSH-Schlüsseln. Dem jeweiligen fremden System teilt man den öffentlichen Schlüssel mit, anhand dem man authentifiziert wird. Das ist eh sicherer als ein Passwort, da die Schlüssel länger bzw. schwerer zu erraten sind.

Den öffentlichen Schlüssel vom Kurzbefehl vom iOS Gerät braucht man gleich. Den kann man aus den Details kopieren, wenn man sich den SSH Kurzbefehl anschaut:
Du darfst diesen Dateianhang nicht ansehen.

Damit der SSH Daemon einen Nutzer anhand des Schlüssels authentifiziert, legt man eine Datei an unter "/home/appleshortcut/.ssh/authorized_keys" mit folgendem Inhalt:
no-port-forwarding,no-X11-forwarding,no-pty,command="sh script.sh" ssh-ed25519 hier_den_öffentlichen_Schlüssel_vom_iOS_Gerät Kommentar_zum_GerätGemäß der Doku haben wir also recht viele Beschränkungen hinzugefügt und den öffentlichen Schlüssel in diese Datei kopiert. Die wichtigste Beschränkung ist, dass nur noch ein Skript ausgeführt wird und keine universelle Shell. Das Skript heißt kreativerweise "script.sh" und liegt hier unter "/home/appleshortcut/script.sh". Es hat folgenden Inhalt:
#!/bin/sh

mosquitto_pub -t "AppleShortcut" -m "$SSH_ORIGINAL_COMMAND" -h brokeradresse -u nutzername -P geheimes_passwort

So, nun landen Befehle schon mal in unserem MQTT Broker. Es ist übrigens sinnvoll, den Broker auch TLS zu verschlüsseln, falls dieser auf einem anderen Gerät läuft, aber das habe ich jetzt mal weggelassen. Jetzt kann FHEM auf dieses MQTT Topic reagieren. Dafür kann man ein Device definieren:


defmod AppleShortcut.device MQTT2_DEVICE
attr AppleShortcut.device IODev Mosquitto
attr AppleShortcut.device alias Apple Shortcut
attr AppleShortcut.device devicetopic AppleShortcut
attr AppleShortcut.device icon logo_apple
attr AppleShortcut.device readingList $DEVICETOPIC:.* { my $ret=json2nameValue($EVENT);; $ret->{state}=lc($ret->{action}) if (defined($ret->{action}));; return $ret }
attr AppleShortcut.device stateFormat {\
    my @ts = ReadingsTimestamp($name,"state","") =~ /^(\d+)-(\d+)-(\d+)\s(\d+:\d+):(\d+)$/;;\
    my $value = ReadingsVal($name, "action", "???");;\
    \
    return "<b>$value</b><br \><small>($ts[2].$ts[1].$ts[0] - $ts[3]:$ts[4])</small>";;\
}
attr AppleShortcut.device userReadings action_result:action.* {\
    my $value = ReadingsVal($name, "action", "???");;\
    my $response = "nok";;\
\
    ## Liste von erlaubten Befehlen als regulärer Ausdruck\
    ##   Zeilenende $ und Anfang ^ mit beachten,\
    ##   sonst sind unbeabsichtigte Befehlsketten/Escapes möglich\
    my @commands = (\
        '^set SteckdoseKuechenradio\.device o(?:ff|n)$',\
        '^set G\.Ding1 on-for-timer [0-9]+$',\
        '^set G\.Ding2 o(?:ff|n)$'\
    );;\
    \
    ##wandel von String in RegEx um\
    @commands = map { qr{$_} } @commands;;\
\
    ##behalte was matcht, speichere die Arraylänge\
    my $match = scalar grep { $value =~ $_ } @commands;;\
\
    if ($match) {\
        fhem("$value");;\
        $response = "ok";;\
    }\
\
    return "$response";;\
}

In diesem Define ist als UserReading die Reaktion auf MQTT Telegramme hinterlegt. Was erlaubt ist, wird mit dem Array @command spezifiziert. Es ist eine Liste von Strings mit regulären Ausdrücken.

Die Verzögerung vom Starten eines Befehls, bis zur Ausführung beträgt ca. 500ms bis 2s.

Mit den Tipps kann man mit Siri -> Kurzbefehle -> SSH -> MQTT -> FHEM steuern. Alternativ, falls man MQTT nicht in dieser Kette nutzen möchte, kann man in dem "script.sh" auch direkt fhem.pl aufrufen, sofern es auf dem gleichem System läuft. Dann sollte man in dem Skript die Whitelist einfügen und die Kommandos entsprechend mit Shell-Befehlen prüfen.


M.Piet

Hallo Torxgewinde (viel besser als PZ oder PH ;) )

Ich suche genau das. Ich möchte nur erreichen, dass ich von Siri aus Befehle an FHEM schicken kann. Und das nicht nur, wenn ich im heimischen LAN bin.
Homebride möchte ich ungern, da ich da ein Apple-Gerät im Haus brauch.

Da bin ich über das hier gestolpert. Ist das soweit noch aktuell (ich frage, weil der Beitrag schon älter ist)?

Bei SSH mit Schlüssel sollte es auch kein Sicherheitsrisiko sein, wenn ich den Port öffentlich erreichbar mache, oder siehst du das anders?

passibe

Wenn es nur lokal ist musst du das nicht per SSH machen.
Da reicht es, wenn du das einfach per http(s) machst (also so wie du ohnehin schon aus dem lokalen Netzwerk auf FHEMWEB zugreifst).

Hier mal ein Beispielshortcut, den ich verwende:
https://www.icloud.com/shortcuts/7dd93835493047d8bafed7ad5689795e
Du musst da nur die URL anpassen (und ggfs. den Port hinzufügen, wenn du keinen reverse Proxy nutzt).

Das ist quasi ein Master-Shortcut, den du einfach aus anderen Shortcuts heraus aufrufen kannst und ihn mit dem jeweiligen Befehl "fütterst".
z.B. so:
https://www.icloud.com/shortcuts/1ecfcc2158fa4c35983e052fb815b71d
Hat den Vorteil, dass man nicht jedes Mal die ganze CSRF-Token-Sache neu einbauen muss, usw.

Wenn du es ganz einfach haben willst, kannst du dafür dein bestehendes FHEMWEB/allowed-Device nutzen.
Ich habe das mit einem extra FHEMWEB/allowed-Device gemacht und sämtliche Shortcuts laufen über einen anderen Port bzw. eine anderen Subdomain, einfach damit ich etwas genauer einstellen kann, welche FHEM-Befehle Shortcuts ausführen dürfen und von welchen IP-Adressen der Shortcut steuern kann. (Vielleicht war es aber auch, dass mein Passwort aus dem regulären Allowed-Device Sonderzeichen beinhaltet hat und das bei der URL-Codierung Probleme bereitet hat? Erinnere mich nicht mehr genau.)

Hoffe das hilft!

Torxgewinde

Moin,
Genau, das Ganze geht auch so direkt wie von @passibe beschrieben mit dem Signalpfad: "Apple Shortcuts --[HTTP(S)]--> FHEMWEB+Allowed".

Bei mir war der Weg, wie im ersten Post damals, sinnvoller; aber für dein Setup ist der Weg vermutlich direkter und einfacher. Mein Signalpfad wurde genutzt um von überall (auch unterwegs) auf mein FHEM zuzugreifen und weil SSH mit einem Key sicherheitstechnisch sehr gut erprobt und verbreitet ist. FHEMWEB würde ich, trotz aller Begeisterung für das FHEM-Projekt, nur mit einem Reverseproxy zum Internet hin öffnen, wenn überhaupt.

Alternativ geht auch der Signalpfad: "Apple Shortcuts --[HTTP(S)]--> HTTPSRV". Solch ein Webhook wäre in https://forum.fhem.de/index.php?topic=140160.0 beschrieben. Vorteil hier ist, dass man immer noch die absetzbaren Befehle mit einem Regex beschränkt. Bei einem Reverseproxy könnte man das natürlich auch, es gibt eben viele Wege.

Ich denke, wenn es einfach nachzubauen sein soll, ist der Shortcut von @passibe den ersten Versuch wert.

Falls du es zum Internet öffnen willst, dann lohnt es sich einen gut konfigurierten Reverseproxy davor zu setzen und auf jeden Fall verschlüsselte Protokolle und betriebsbewährte Dienste/Daemons/Techniken mit nennenswerter Verbreitung zum Internet hin zu verwenden.

Torxgewinde

Direkt auf deine Fragen:

Zitat von: M.Piet am 15 Januar 2025, 15:25:28Da bin ich über das hier gestolpert. Ist das soweit noch aktuell (ich frage, weil der Beitrag schon älter ist)?

Bei SSH mit Schlüssel sollte es auch kein Sicherheitsrisiko sein, wenn ich den Port öffentlich erreichbar mache, oder siehst du das anders?

Bei dem Script oben ist das Stateformat ggf. nicht ganz abgesichert, da man so den $value direkt in den HTML Code der FHEMWEB-Übersicht einbaut. Bei großen Bedenken würde ich das Stateformat weglassen, oder wenigstens "action" nicht einbinden.

Das funktioniert immer noch und ein SSH Server mit Keys ist weiterhin ziemlich sicher (absolut sicher gibt es IMHO nicht). Es gibt selten erfolgreiche Angriffe auf OpenSSH oder Dropbear und wenn du regelmäßig updates (zB unattended-upgrade bei Debian) machst, bist du schon ziemlich sicher unterwegs. Guck' dir aber auf jeden Fall die Konfiguration genau an um da keine Konfigurationslücken zu belassen. Steigern kann man das noch mit Portknocking und (Geo-)Beschränkung der zulässigen Source-Adressen, verschieben auf andere Ports.

Ob man den Signalpfad über MQTT noch nutzen möchte, ist ein wenig fraglich. Aus dem "script.sh" kannst du im Grunde auch alles erst Prüfen und dann ausführen, was von da aus erreichbar ist.