NEWS
[Frage] PEM-Zertifikate in ioBroker importieren ohne Admin-GUI
-
Die Idee ist gut und würde mir vollkommen ausreichen.
Das Object system.certificates gibt es bei mir nicht.
Wenn ich Systemobjekte in admin mit anzeigen lasse, sehe ich das object nicht.
Im Screenshot von bluefox ist es zu sehen, aber bei mir ist das nicht vorhanden.
Wie komme ich an das object ran?
-
Die Idee ist gut und würde mir vollkommen ausreichen.
Das Object system.certificates gibt es bei mir nicht.
Wenn ich Systemobjekte in admin mit anzeigen lasse, sehe ich das object nicht.
Im Screenshot von bluefox ist es zu sehen, aber bei mir ist das nicht vorhanden.
Wie komme ich an das object ran? `
sicher ? Ich musste auch 3 mal schaun aber hier steht das object:
-
Das mit dem Wald und den Bäumen . Naja, man muss schon den Filter richtig setzen. Der Stand auf "state".
Mit "config" oder "all" kommt man schon weiter.
Danke für die Anregungen. Ich werde es mal probieren mit der Simple API es zu aktualisieren und den String in system.certificates zurück zuschreiben.
-
Das mit dem Wald und den Bäumen . Naja, man muss schon den Filter richtig setzen. Der Stand auf "state".
Mit "config" oder "all" kommt man schon weiter.
Danke für die Anregungen. Ich werde es mal probieren mit der Simple API es zu aktualisieren und den String in system.certificates zurück zuschreiben. `
:lol: bist nicht der erste und ach nicht der letzte, schoen das es geholfen hat. Bin gespannt ob du es hinbekommst klinkt namelich gut deine loesungsrichtung !
-
Ich werdes mal umsetzen und berichten.
Mit getObject("system.certificates").native.certificates.defaultPrivate); kann ich jedenfalls sogar jedes einzelne Zertifikat ansprechen. Das sollte es sogar realtiv einfach machen.
-
So, ich habe nun gebastelt. Es waren schon einige Hindernisse zu überwinden, aber ich habe eine funktionierende Lösung.
Allerdings sind da ein paar Sachen, die mir noch nicht so gefallen.
Im Detail:
Ich nutze ioBroker auf einem Windows-Server, daher habe ich ein VBScript gebaut, welches das Zertifikate-Update in ioBroker durchführt (siehe Attachement). Das kann aber sicher auch für den Pi als Ansatz genutzt werden.
Voraussetzungen:
-
in ioBroker zwei Datenpunkte (Zeichenketten), die die Strings der neuen PEM-Zertifikate aufnehmen. Bei mir „admin.0.certUpdate“ und „admin.0.keyUpdate“
-
laufende simpleAPI
-
JavaScript für das eigentliche Update der Zertifikate (siehe weiter unten)
Ich habe zwei Varianten umgesetzt:
1. Update der Datenpunkte über die simpleAPI
Das Update der Datenpunkte habe ich über einen POST-Request mit "setValueFromBody“ umgesetzt, da ein Senden der Zeichenkette via GET-Parameter in der URL an Sonderzeichen wie "=" scheitert. Ich habe versucht die URL entsprechend zu kodieren und das "=" durch "%3D" zu ersetzen, aber die simpleAPI ignoriert das. Es kommt dann auch tatsächlich "%3D" im Datenpunkt an. Warum das URL-Encoding da nicht so funktioniert, weiß ich nicht. POST ist aber eh eleganter, wie ich finde.
Also der Aufruf lautet dann:
http://<server>:<port>/setValueFromBody/admin.0.certUpdate</port></server>
Im Body wird dann der blanke String des Zertifikats geschrieben (ohne irgendwas dazu).
Wichtig: der String muss die vorhandenen Zeilenumbrüche beibehalten, was aber in einem POST-Request problemlos klappt. Hier hätte man bei der GET-Methode das nächste Problem.
2. Update der Datenpunkte über die ioBroker-Konsole
Mit ioBroker state set admin.0.certUpdate "String" kann ich den Datenpunkt auch aktualisieren. Auch das funktioniert.
Hier gibt's aber zwei Sachen, die ich umschiffen musste. Zunächst kann man als Kommandozeilenparameter keine Zeichenketten mit Zeilenumbruch übergeben. D.h. ich muss in meinen Zertifikats-String erstmal die Zeilenumbrüche (‚\n‘ und/oder ‚\r‘) in druckbare Zeichen umwandeln. In dem Fall habe ich die direkt in „\n“ und „\r“ umgewandelt, was kein Problem darstellt, da im PEM-Encoding kein Backslash vorkommt.
Das zweite Problem ist, dass der String, den ich schreiben muss mit einem Minus ("-") beginnt. Das mag die Kommandozeile von ioBroker nicht (invalid value). Ich vermute, das hat mit dem Parsen der Argumente zu tun, da Parameter auch mit einem oder doppeltem Minus beginnen. Gelöst habe ich das durch ein Voranstellen eines Space e.g.
C:\ioBroker\ioBroker state set admin.0.certUpdate " -----BEGIN RSA PRIVATE KEY-----\\nM…"
Hier das Skript in ioBroker, welches dann die aktualisierten Datenpunkte in den „Zertifikate-Speicher“ schreibt:
const CERTCONFIG = "system.certificates"; // Zertifikate-Konfiguration const DELAYTRIGGER = 1000; var locked = false; function updatePEMcert (certName, newCertValue) { // neuen PEM-Stringm erzeugen: Leerzeichen trimmen, druckbare Zeilenumbrüche in echte Umwandeln var newPEMString = newCertValue.trim().replace(/\\r/g, '\r').replace(/\\n/g, '\n'); // neuer PEM-Zertifikat-String // Zertifikate-Config aus DB laden var certsConfig = getObject(CERTCONFIG); // Zertifikate aus Config var certs = certsConfig.native.certificates; // Update Key-Cert certs[certName] = newPEMString; // DB zurückschreiben setObject(CERTCONFIG, certsConfig, function (err) { if (err) log('Cannot write object: ' + err); }); } function wait(ms){ var start = new Date().getTime(); while(new Date().getTime() < start + ms); } // Trigger bei Update Key on({id: "admin.0.keyUpdate"/*keyUpdate*/, change: "ne"}, function (obj) { var timeout = 0; if (!locked) locked = true; else timeout = DELAYTRIGGER; // Verzögert ausführen, wegen zurückschreiben in DB und konkurrierenden Zugriff setTimeout(function () { updatePEMcert("test key", obj.state.val); if (timeout > 0 ) locked = false; }, timeout); }); // Trigger bei Update Cert on({id: "admin.0.certUpdate"/*admin.0.certUpdate*/, change: "ne"}, function (obj) { var timeout = 0; if (!locked) locked = true; else timeout = DELAYTRIGGER; // Verzögert ausführen, wegen zurückschreiben in DB und konkurrierenden Zugriff setTimeout(function () { updatePEMcert("test cert", obj.state.val); if (timeout > 0 ) locked = false; }, timeout); });
Das Skript macht im Wesentlichen Folgendes:
Es stellt zwei Trigger für die beiden Datenpunkte bereit, auf denen ich das neue Zertifikat über die simpleAPI oder per Kommandozeile schreibe, die auf Änderung des Wertes reagieren.
Anschließend werden Leerzeichen entfernt und die umgewandelten Zeilenumbrüche wieder in "echte" Zeilenumbrüche zurückgewandelt, damit der String auch korrekt im Zertifikate-Objekt geschrieben wird. Denkbar wäre hier noch eine Prüfung, ob die Strings korrekt mit Begin Certificate anfangen und mit End aufhören etc.
Mit getObject hole ich mir die Zertifikate-Config, ändere dort mein Zertifikat und schreibe das Objekt wieder zurück (wichtig: setObject-Kommando muss im JavaScript-Adapter aktiviert sein!).
Hier gibt es aber ein großes Problem, was ich zwar gelöst habe, aber nicht sehr elegant ist.
Mit setObject kann ich das geänderte Objekt wieder in die Datenbank schreiben. Aber nachdem ich diese Funktion aufgerufen habe, ist das aktualisiert Objekt nicht sofort verfügbar.
D.h. wenn ich nach einem setObject direkt danach wieder ein getObject mache, dann steht dort noch die alte Konfiguration vor dem Update drin. Ich habe nun herausgefunden, dass ein wenig Zeit nach setObject vergehen muss, bis man die zurückgeschriebenen Daten wieder mit getObject lesen kann.
Das Problem entsteht dann nämlich durch das Updaten von dem zweiten Zertifikat durch den zweiten Trigger. Die Trigger werden sehr kurz hintereinander ausgeführt. Wenn der erste fertig ist, liest der zweite mit getObject die Zertifikate-Config wieder ein und schreibt das zweite Zertifikat zurück. Das erste ist aber da noch nicht enthalten, da wie gesagt einige Zeit vergehen muss. Dementsprechend ist immer nur das letzte Zertifikat aktualisiert worden.
Gelöst habe ich das mit eine Art Lock-Mechanismus und einer verzögerten Ausführung (timeout), da es in JavaScript ja kein wait oder sleep gibt. Der erste Trigger wird sofort ausgeführt und der zweite verzögert, welcher dann auch das lock zurücksetzt. Nicht schön, geht aber. Ich bin kein JavaScript-Experte, vlt. kann man das auch besser schreiben.
Frage wäre hier: warum ist nach setObject das geändert Object nicht sofort zugreifbar? Ich vermute, das da im Hintergrund was asynchron läuft, bis die Daten tatsächlich geschrieben sind und wieder gelesen werden können. Wäre natürlich toll, wenn setObject das berücksichtigt.
Wenn jemand noch andere Ideen hat, das zu berücksichtigen, bin ich offen dafür.
Mein VBScript startet dann noch den ioBroker neu. Alternative zum gesamten Neustarten des ioBrokers wäre auch das neu-Starten aller betroffenen Adapter.
Das kann in einer kleinen Schleife, die alle Adapter in einem Array z.B. durchgeht und ein Systemaufruf macht passieren. Z.B.:
C:\ioBroker\ioBroker restart web.0
Das Neustarten der Adapter könnte aber auch ein Skript in ioBroker machen:
obj = getObject("system.adapter.web.0"/*web*/); obj.common.enabled = true; setObject("system.adapter.web.0"/*web*/, obj);
Das Ganze ist jetzt der erste Entwurf und wird sicher noch leben.
Für weitere Ideen, Anregungen bin ich offen.
2323_lecertupdate_iobroker.zip -
-
Cool, dass du eine Lösung gefunden hast. Kleiner Tipp:
@daniel_2k:obj = getObject("system.adapter.web.0"/*web*/); obj.common.enabled = true; setObject("system.adapter.web.0"/*web*/, obj); ```` `
geht auch einacher:
extendObject("system.adapter.web.0", {common: {enabled: true}});
-
Ich habe das Update-Script (Windows) mal in Powershell geschrieben.
Da es ein Powershell ACME-Modul als ACME-Client gibt, macht es Sinn es komplett als PS-Skript zu schreiben.
Es aktualisiert über die Let's Encrypt CA das Zertifikat (es wird alle 90 Tage ein neues angefordert), aktualisiert die Bindung in IIS und aktualisiert die Zertifikate in ioBroker.
Das Skript ist noch nicht zu 100% getestet. Der IIS-Teil und ioBroker-Update funktioniert (getestet). Der Let's Encrypt-Part funktioniert prinzipiell auch, allerdings konnte ich bisher noch keinen Test nach Ablauf des Identifiers (30 Tage) machen (muss ich noch warten ). Ich weiß derzeit noch nicht genau, welche Schritt nach der ersten Einrichtung des Vaults noch notwendig sind, um ein neues Zertifikat mit gleichen Eigenschaften wie zuvor zu erstellen.
2323_lecertupdate.ps1.zip -
Update für das PowerShell-Skript.
Nach dem nun die Gültigkeit meines Identifiers abgelaufen ist, konnte ich das Skript mal als Ganzes testen.
Es funktionierte nämlich noch nicht wie gewünscht. Es ist tatsächlich so, das bei Ablauf des Identifiers man die gesamte Prozedur (Identifier erzeugen, Challenge durchlaufen, Zertifikat anfordern) immer neu durchlaufen werden muss.
Daher habe ich das Skript angepasst (inkl. diverse Anpassungen und kleine Fixes) und konnte es auch als Ganzes erfolgreich testen.
2323_lecertupdate_v1.3.zip -
Hat sich etwas an iobroker geändert? Ich finde das entsprechende Objekt nicht und kann selbst mit dem JS-Modul das objekt nicht einlesen:
log(JSON.stringify(getObject('system.certificates')));
resultiert in einem null-value.
-
CLI support ist geplant für js.controller 2.0.0 … https://github.com/ioBroker/ioBroker.js ... issues/222 ... dauert aber noch ein bissl