Bash Rest API Client

Dienstag, 3. März, 2020

Ich bin an unserem Institut am Neuaufbau des Monitorings mit ICINGA dran. Weil es so empfohlen wird, kommt das Director Modul zum Einsatz. Ach so, bereut habe ich dies nicht.

Das Monitoring funktioniert über passive Checks: jeder Server meldet sich selbst an und sendet seine Monitoring Daten zu Icinga ein.

Jeder unserer Server soll ein Host Objekt anlegen, die bemötigten Service-Objekte und und die Services mit dem Host-Objekt verknüpfen. Dazu “spricht” jeder Server per REST API mit dem Director. Der muss die Konfiguration zum Icinga-Server deployen.

Zum Einsenden der Daten braucht es den bereitgestellten Slot für den Host+Service auf dem Icinga-Service. Der Client spricht zum Einliefern der Monitoring-Daten ebenfalls mit einer REST-API - aber dann mit der des Icinga Daemons.

Zum Abstrahieren der REST-API Zugriffe auf verschiedene APIs habe ich alles rund um Http abstrahiert und eine Bash-Komponente geschrieben, die diverse Funktionen für Http-Zugriffe und Abfragen in Skripten einfacher verfügbar macht. Die Verwendbarkeit in Skripten war der Fokus. Die Funktionen ähneln vom Aussehen Methoden einer Klasse: “http.[Funktion]”.
Macht man einen Http-Request, so bleibt dessen Antwort im RAM. Bis man den nächsten Request macht - oder die Shell / das Skript beendet.
Weil es ein Wrapper von Curl ist, kann man alle Http-Methoden, wie GET, HEAD, POST, PUT, DELETE verwenden.

Um einen ersten Eindruck zu vermitteln:

http.makeRequest GET https://www.axel-hahn.de/

Diese Aktion erzeugt keine Ausgabe.

Man kann dann mit Funktionen den Statuscode abfragen, den Http Response Header, den Body oder aber die Curl Daten (darin sind z.B. Upload- und Download-Speed und Verbindungszeiten).

http.getResponseHeader

… gibt alle Http-Response-Headerzeilen aus.

http.getStatuscode

… zeigt den Statuscode

http.isOk

… testet, ob die Anfrage mit einem 2xx Code zurückkam.

Für Icinga wurde der Zugriff mit Basic Authentication umgesetzt.

GET Abfragen lassen sich für N Sekunden cachen. Darüber hinaus kann man den Response exportieren und wieder importieren. Nach dem Import kann man wiederum den Response mit allen Funktionen erfragen - mit http.getRequestAge holt man sich das Alter der Ausführungszeit des Requests. Man kann ein Debugging aktivieren, um die interne Verarbeitung offenzulegen und nachzuvollziehen.

Und Einiges mehr.

Falls das für euch interessant klingt, schaut euch das Bash-Skript doch einmal an.

weiterführende Links:

  1. Gitlab: iml-open-source/bash-rest-api-client
  2. Icinga Open Source Monitoring Tool
  3. Icingaweb - Director REST API
  4. Icinga: REST API

Fish - SSH-Agent initial starten

Mittwoch, 22. Januar, 2020

Meine beiden Manjaro Instanzen im Büro und daheim habe ich auf fish als Default-Shell umgestellt.
Nun wollte ich in der Konsole beim ersten Aufruf einer Shell den SSH Agent mitsamt meines SSH Keys starten lassen.

(1)
Dazu muss man einmal den “Ersatz” der ~/.bashrc finden - das wäre die Datei ~/.config/fish/config.fish. Wenn man als Newbie mit fish startet und noch gar nichts auskonfigriert hat, so exisitiert diese Datei nicht. Man muss sie anlegen.

(2)
Trick 2 ist die Meisterung des Starts des SSH-Agents. Unter fish wäre es mit

eval (ssh-agent -c)

aufzurufen.

(3)
Was mir beim Schreiben des allerersten 5-Zeilers so auffiel: die Syntax von fish untescheidet sich durchaus zu anderen Shells, wie sh, ksh oder bash: hier gibt es keinen Abschluss mit “fi” oder “esac”, aber davon verabschiedet man sich sicher gern. Und statt $? ist die Variable $status.

Hier mein kleines Snippet:

cat ~/.config/fish/config.fish
# ----------------------------------------------------------------------
# Start SSH agent with my default key
# ----------------------------------------------------------------------
ps -ef | grep -v grep | grep "ssh-agent -c" >/dev/null
if test $status -ne 0
     echo "SSH-Agent wird gestartet..."
     eval (ssh-agent -c)
     ssh-add ~/.ssh/id_rsa
end

# ----------------------------------------------------------------------

Update:

  • Nach dem Holen der Prozessliste wurde noch ein
    grep -v grep

    eingefügt.

weiterführende Links:

  1. fishshell.com - Webseite
  2. Docs

Linux: bei Änderung einer / mehrerer Dateien ein Kommando starten

Mittwoch, 16. Oktober, 2019

Ich brauchte da mal was für unsere Systemlandschaft: der Webservice soll neu gestartet werden, wenn sich eine vordefinierte Datei ändert. Wenn es ein Update der Web-Applikation gibt, dann kann jenes Update ein touch [Dateiname] auslösen.

Wenn das Check-Skript mit einem User läuft, der den Webservice starten darf - sei es mit root oder einem User, dem sudo auf systemctl erlaubt ist - dann kann ein Entwickler niedrige Applikationsrechte haben, ihn aber eine spezifische höhere Rechte erforderliche Aktion ausführen lassen, ohne ihm selbst root oder sudo Rechte geben zu müssen.

Mein Skript habe ich onfilechange.sh genannt.
Es benötigt minimal Parameter zum Erfassen der zu überwachenden Trigger-Dateien und das auszuführende Kommando.
Mit einem “-v” schaltet man den Verbose-Modus ein - dann sieht man mehr, was das Skript gerade macht.

Parameter:

-c [command]
    command to execute on a file change
-f [filename(s)]
    filenames to watch; separate multiple files with space and put all in quotes
-h
    show this help
-i
    force inotifywait command
-s
    force stat command
-v
    verbose mode; enable showing debug output
-w [integer]
    for stat mode: wait time in seconds betweeen each test or on missing file; default: 5 sec

Ein erstes einfaches Beispiel:
Es wird der Text Hallo ausgegeben, sobald man (z.B. in einem 2. Terminal) die Datei /tmp/mytestfile toucht oder beschreibt.

onfilechange.sh -f "/tmp/mytestfile" -c 'echo "Hallo" '

Mit einem zusätzlichen Parameter -v kann man etwas genauer reinschauen, wie das Skript onfilechange.sh arbeitet.

Auf der Kommandozeile macht das Skript wenig Sinn. Um es permanent laufen zu lassen, sollte man es als Dienst einrichten. Wie man mit dem Skript einen Systemd Service einrichtet, ist in der Readme enthalten. Folge ganz unten dem Link zum Sourcecode.

Lizenz: Free software; GNU GPL 3.0

Und noch etwas zu den Interna, wie man die Datei-Überwachung bewerkstelligen kann:

(1)
Das gibt es zum einen inotifywait . Das Tool erhält man in vielen Distros mit Installation der “inotify-tools”.

Nachteile:
- Die Datei / alle zu prüfende Dateien müssen immer existieren. Wenn es einen Prozess gibt, der das Löschen der Trigger-Datei zulässt, ist diese Kommando ungünstig
- Installation der “inotify-tools” ist erforderlich

Vorteile:
+ Man kann mehrere Dateien zum Überwachen andocken
+ Man kann spezifisch definieren, welche Events man überwachen möchte
+ Ohne einen Loop zu verwenden, kann man nach Beendigung mit Returncode 0 ein Kommando starten. Die Ausführung erfolgt sofort

(2)
Das Kommando stat liefert etliche Datei-Attribute, wie Namen, letzer Zugriff, letzte Änderung, Rechte, …

Nachteile
- man kann nur 1 Datei prüfen. Bei mehreren muss man sich einen Loop über n Dateien bauen
- es braucht eine while Schleife und ein sleep N, um zyklisch einen Zustand zu überwachen
- der Kommando-Aufruf erfolgt nicht sofort, sondern ist entspr. sleep in dessen Wartezeiten etwas verzögert.

Vorteile:
+ Man kann obige Nachteile weitestgehend umgehen - dann ist die Prüfung mit stat auch die zuverlässigere Variante
+ Es ist in jeder Distribution in der Standard-Installation enthalten

weiterführende Links:

  1. Download/ Readme: git-repo.iml.unibe.ch: onfilechange
  2. Docs: os-docs.iml.unibe.ch/onfilechange/

Xampp: Port 3306 belegt - durch Firefox

Mittwoch, 2. Oktober, 2019

Schon komisch: ein Programm krallt sich einen Port. Ich dachte, das wäre die Domäne von Skype.

2019-10-02-xampp-port-3306-durch-firefox-belegt.png

Abhilfe:

  1. Firefox beenden
  2. Mysql im Xampp starten
  3. Firefox starten

preDispatcher - speed up my slow Concrete5 website

Dienstag, 20. August, 2019

Meine Concrete5 Webseite wurde langsamer und langsamer. Sie wird auf einem Shared Hosting betrieben (was ohnehin nicht die schnellste Option sein kann) - hier kann ich zudem keinen Varnish oder auch nur irgendeinen anderen Caching Dienst installieren und davorschalten, sondern muss mich mit .htaccess und Standard-PHP-Mitteln behelfen können.

Das Initialisieren eines Requests in Concrete5 (Bootstrapping) braucht scheinbar sehr lange. Selbst statische Inhalts-Seiten, die ein aktives Fullpage Cache haben, benötigen für die Verarbeitung am Server mehr als 200 ms. Hinzu kommen für einen kompletten Request dann noch Netzwerkzeiten für Namensauflösung (DNS), Anfrage zum Server senden … und das Ergebnis zum Browser zurücksenden, was der Browser dann rendert. Meine schnellsten Seiten aus dem CMS waren bei 300 ms. Andere brauchten 3 Sekunden und mehr. Concrete5 ist einfach langsam.

Ich habe einen Cache geschrieben, der Concrete5 vorgeschaltet ist und im Filesystem seine Caching-Daten ablegt. Kurz er hat minimalste Anforderungen und arbeitet mit einem Shared Hoster. Dieses “Vorschalten” umgeht die lange Initialisierungszeit von Concrete5 - dies macht die Seite schnell.

Vor einigen Tagen habe ich eine erste Version meines preDispatchers eingespielt. Erwartungsgemäss muss man ein paar Kinderkrankheiten ausmerzen. Aber erste Ergebnisse in der Webseiten-Statistik (Matomo) zeigen, dass es eine gute Entscheidung war: die mittleren Seiten-Generierungs-Zeiten sanken von über 2s auf unter 0.4s

2019-08-20-predispatcher-generation-time-in-matomo.png

Der PreDispatcher besitzt eine Debugging-Ausgabe, die die Arbeitsweise veranschaulicht und den Unterschied deutlich macht. Das ist ein ungecachter Aufruf einer Seite mit Fullpage Cache in C5 - also inkl. Bootstrapping + Auslieferung einer in C5 gecachten Seite:

2019-08-20-predispatcher-uncached-concrete5-request-of-fullpage-cached-page.png

Der PreDispatcher speichert den Request, der nicht im Cache ist (oder dessen Caching Zeit abgelaufen ist). Der nächste Request auf dieselbe Seite wird dann schnell. Richtig schnell:

2019-08-20-predispatcher-cached-request.png

Wie lange eine Seite aus einem Cache kommt? Man definiert einen Default für alle Seiten. Mit einer Liste von Regex für aufzurufende URLS kann man die Standard-Vorgabe übersteuern und einen neuen Wert zuweisen.

Zudem gibt es eine Erkennung, was nicht gecacht werden darf - oder wo ein Cache verworfen werden muss. Ich kann mich im C5 Backend einloggen - und der Cache jeder mit Login aufgerufenen Seite wird verworfen.

Ich bin wohl auf dem richtigen Weg. Aber ein wenig Feintuning braucht es noch. Bei Interesse, schaut auf Github … oder fragt mich an :-)

Der PreDispatcher ist Freie Software - unter GNU GPL 3!

weiterführende Links:

  1. Github: pre-dispatcher for more speed
  2. Concrete5 webseite

jsclasses.org - Top user rated classes

Donnerstag, 16. Mai, 2019

Oh, was für eine kleine nette Überraschung: Das da bin ja ich :-)

2019-05-16-jsclasses-top-rated-classes.png

Mit dieser Klasse wird bei einem Passwort-Feld ein Div eingeblendet. Darin wird die Erfüllung der vorgegebenen Bedingungen (z.B. Anzahl Zeichen, Gross-/ Kleinschreibung, Anzahl Sonderzeichen) dargestellt - on the fly während der Passworteingabe.

Diese Klasse ist Freie Software und Open Source.

weiterführende Links:

  1. Axels Webseite: ahPwcheck
  2. Docs: Password checker (en)
  3. jsclasses.org: AH JavaScript Password Strength Check: Calculate and display the strength of a password (en)

PHP: komisch grosse Zeiten in Curl

Mittwoch, 8. Mai, 2019

Ich hatte wirklich seltsame Antwortzeiten mit der Curl-Bibliothek in PHP in meiner eigenen Monitoring-Anwendung.
Bei Verwendung von curl_getinfo($curl) ist der Wert von “total_time” ein Wert in Sekunden. Eigentlich. Bei einer Reaktionszeit von 16ms sollte es 0,016 sein, ich erhielt aber 16000.

Das Problem ist, dass sich die Werte der Curl-Funktion bei Verwendung von PHP mit mod_php korrekt verhalten - nur im FCGI Modus gibt es Zahlenwerte, die Faktor 1 Mio. zu gross sind.

Bei einer Antwort-Zeit von 16 ms sollte der Wert im Feld total_time 0.016 sein - unter FCGI kommt aber 16000 zurück.

2019-05-08-curl-time-values.png

Nach einer kurzen Suche, fand ich einen bestehenden Bugreport, wo ich meine Daten ergänzte. Mal schauen, ob da wohl was geht …

weiterführende Links:

  1. bugs.php.net- 76438: wrong curl_getinfo time values
  2. php.net: Funktion curl_getinfo()
  3. Github: IML AppMonitor

Virtualbox: Protocol error beim Mounten der Shared Folders

Mittwoch, 17. April, 2019

In meiner Virtualbox 6.0(.x) Instanz habe ich ein lokales Webentwicklungsverzeichnis mit mehreren Domains von Entwicklungsumgebungen als Shared Folder in eine CentOS VM geteilt. In jener sind die Verzeichnisse als Apache Webroots via /var/www/[Domainname]/public_html/ angesprochen.

Und wie das so bei Computern ist: von einem Tag auf den anderen funktioniert irgendwas nicht mehr. Heute: das Mounten der Verzeichnisse.

Versuch 1:

Ein Reboot der VM … brachte nichts.

Versuch 2:

Shared Folders funktionieren nur, wenn die Gästetools installiert sind. Es hätte ja durch ein OS-Update der VM einen neuen Kernel gegeben haben. Diese habe ich nochmals installiert. Es wurde ein neuer Kernel compiliert… was einen weiteren Reboot brauchte … es geht aber nicht.

Versuch 3:

So kommt man dem Problem dann näher:
Man öffnet die Logs - das geht via dem Icon rechts einer VM (oh, ich hatte beim Umstellung von Virtualbox 5 auf V 6 lange die Sicherungspunkte gesucht :-)) - hier Logs auswählen. Dann erscheinen mehrere Tabs - das erste davon brauchen wir - die VBox.log.

2019-04-17-virtualbox-01-show-logs.png

Darin taucht ein Fehler auf:

00:36:51.618960 ASSERT_GUEST_LOGREL F:\tinderbox\win-rel\src\VBox\HostServices\SharedFolders\mappings.cpp(762) 
... int __cdecl vbsfMapFolder(struct _SHFLCLIENTDATA *,struct _SHFLSTRING *,unsigned short,bool,unsigned int *): 
... pFolderMapping->cMappings == 0 || pFolderMapping->fGuestCaseSensitive == fCaseSensitive
00:36:51.618981 Incompatible case sensitivity setting: C:\data\htdocs: 2 mappings, insenitive, requested senitive!
00:36:51.619183 VMMDev: Guest Log: 06:57:31.461002 automount Error: vbsvcAutomounterMountIt: Failed to mount 
... 'htdocs' on '/media/sf_htdocs': Protocol error (-1,71)

Nach jenem

automount Error: vbsvcAutomounterMountIt: Failed to mount ‘htdocs’ on ‘/media/sf_htdocs’: Protocol error (-1,71)

kann man im Internet micht der Suchmaschine seiner Wahl suchen. Leider wurde ich trotz des Fehlercodes und Wortlaut der Meldung nicht fündig. Das fand ich unlustig. Ich bin also der erste Mensch auf der Welt, der dieses Problem hat :-)

Schauen wir bei den gemeinsamen Ordnern - das hatte ich eingestellt:

2019-04-17-virtualbox-02-shared-folder.png

Was aber klappte:

Ich habe die Shared Folder Defintion gelöscht … rechts das Order Symbol mit dem X … im Log erschien

00:39:53.656135 SharedFolders host service: Removing host mapping 'htdocs'
00:39:53.743943 GUI: UIMediumEnumerator: Medium-enumeration finished!

Dann wurde das zuvor Gelöschte 1:1 wieder eingerichtet.

00:40:26.398568 SharedFolders host service: Adding host mapping
00:40:26.398589     Host path 'C:\data\htdocs', map name 'htdocs', writable, automount=true, automntpnt=, create_symlinks=false, missing=false
00:40:26.452420 GUI: UIMediumEnumerator: Medium-enumeration finished!
00:40:27.403966 VMMDev: Guest Log: 07:01:07.246159 automount vbsvcAutomounterMountIt: Successfully mounted 'htdocs' on '/media/sf_htdocs'

Dies hat er eingerichtet - und sofort war es auch in der VM ohne weiteres Zutun gemountet.
Last but not least habe ich die VM nochmals rebootet, um zu schauen, ob es auch nach einem Neustart noch funktioniert … yep, auch dies ging.

Flatpress lebt weiter

Samstag, 23. Februar, 2019

Nachdem bei Flatpress über Jahre nicht mehr viel weiterging, habe ich Anpassungen (die in dessen Forum genannt wurden) für mich gemacht und gar angefangen ein anderes Blog Tool zu suchen. In meinem Blog sind Inhalte seit 2008. Die Inhalte zu übernehmen, ist so eine gewisse Hürde, die mich abschreckt.

Aber all das entfällt ja nun :-) Am 22. Februar gab es eine neue Version, die gar PHP 7.3 komptibel ist.

Ein Dankeschön an Arvid und seine Helfer!

Anbei die Beschreibung des Updates von 1.0x auf 1.1 mit Übernahme des Inhalts. Bei der Variante “Überschreiben der bestehenden Dateien” bleibt tendeziell Unnützes über.
Anm.: dies ist inoffiziell - es ist die Beschreibung des Vorgehens auf meiner Installation.

  1. Backup: das Verzeichnis des Blogs “/blog/” wurde umbenannt nach “/blog_OLD/”
  2. Unzip der version 1.1 nach “/blog/”
  3. Beiträge und Bilder: Kopieren des Verzeichnisses /blog/fp-content/
  4. Theme: Kopieren des Verzeichnisses /blog/fp-interface/themes/[theme]
  5. Plugins: gehe in das Backend des Blogs blog/admin.php?p=plugin … wenn ein fehlendes Plugin gemeldet wird: Kopieren des Verzeichhisses /blog/folder fp-plugins/[pluginname] (Kontrolle mit Reload im Browser)
  6. Die Toolbar des Editors habe ich mir erweitert - im BBCode plugin wurden einige weitere Buttons hinzugefügt /blog/fp-plugins/bbcode/tpls/toolbar.tpl
  7. Pretty-Urls waren aktiv? dann die /blog/.htaccess übertragen
  8. Flatpress-Index neu erstellen: Dazu anmelden, Wartung anklicken und den Link anklicken [Den Flatpress Index neu erstellen]
  9. Einmal den Browsercache löschen (Ctrl+Shift+Delete), damit Javascript- und CSS Elemente von der neuen Version geladen werden

UPDATE:
3.3.2019 - Dank an Matthias (Laborix): Neuerstellen des Index wurde hinzugefügt.

weiterführende Links:

  1. www.flatpress.org

Unterbinden von Load_file() in Mysql und MariaDB

Sonntag, 17. Februar, 2019

Ich benötige zum Betreiben von Applikationen keine Dateizugriffe via Mysql. Wenn es weder Dateien vom Server noch vom Client braucht, fühle ich mich wohler, wenn die Funktion komplett deaktiviert ist, weil das ein offenes Scheunentor sein kann.

Um das Laden von Dateien innerhalb SQL zu vermeiden, gibt es in Mysql die Möglichkeit, in der Konfiguration den Dateizugriff zu unterbinden

[mysqld]
local-infile = 0|1 oder ON|OFF
secure_file_priv = [Wert]

Kurz in der Dokumentation die Werte nachlesen, ins Puppet kippen und auf alle Server verteilen.

Als Test, ob ich erfolgreich bin, dient der Aufruf … dieser muss NULL ausgeben und keinen Dateiinhalt.

#  mysql -e "SELECT  load_file('/etc/passwd');"
+--------------------------+
| load_file('/etc/passwd') |
+--------------------------+
| NULL                     |
+--------------------------+

Das war die Idee. Eigentlich klingt es einfach.
Leider verhalten sich aber die Einstellungen von Mysql und MariaDB unterschiedlich.

Der Wert für local-infile muss bei beiden 0 oder OFF sein. local-infile ist aber eine dynamische Variable - das ist aber lediglich der Startwert, der mit einem SQL Befehl in der laufenden Instanz neu gesetzt werden kann.

Heisst: es braucht zwingend den Wert für secure_file_priv, der sich wiederum NICHT zur Laufzeit verändern lässt.

(1)
Variante für Mysql:

[mysqld]
(...)
local-infile = 0
secure_file_priv = NULL
(...)

Danach einmal den Service neu starten systemctl restart mysql … dann habe ich das Verhalten wie in meinem Testaufruf.
Dieses Setup funktioniert nicht auf MariaDB - es wird mit einem Fehler in der Variable secure_file_priv beendet.

Die Dokumentation sagt: wenn secure_file_priv fehlt, würde der Dateizugriff verhindert werden…

Description: LOAD DATA, SELECT … INTO and LOAD FILE() will only work with files in the specified path. If not set, the default, the statements will work with any files that can be accessed.

Aber das ist nicht korrekt! Wenn ich secure_file_priv nicht setze, und es aus der laufenden Datenbank abfrage, ist der Wert leer

# mysql -e "SHOW VARIABLES LIKE 'secure_file_priv';"
+------------------+------------+
| Variable_name    | Value      |
+------------------+------------+
| secure_file_priv |            |
+------------------+------------+

… und ein SELECT load_file(’/etc/passwd’); zeigt mir den Dateiinhalt an!
Wer Percona oder einen anderen Mysql-Abkömmling verwendet, sollte das Verhalten austesten.

MariaDB erwartet einen existierenden Verzeichnisnamen. Ich habe mich entschieden, /dev/null zu nehmen, weil es sowohl existiert als auch ein User hier keine Datei ablegen kann:

(2)
Variante für MariaDB:

[mysqld]
(...)
local-infile = 0
secure_file_priv = /dev/null
(...)

(3)
Bliebe noch die Software-Verteilung… wenn in Puppet die Mysql-Klasse aufgerufen wird, muss diese von irgendwoher wissen, ob das Produkt am Zielsystem MariaDB oder Mysql ist. Die Lazy Variante wäre, auf das OS zu reagieren, weil Defaults in den Repos beispielsweise von Debian oder CentOS sich unterscheiden. Besser und sicherer ist natürlich ein echtes Flag.

Update zu (3):

Meine Variante für ein vom Mysql Daemon nicht verwendbares, aber stets existierendes Verzeichnis für beide Mysql-Varianten ist … “/root”

[mysqld]
(...)
local-infile = 0
secure_file_priv = /root
(...)

weiterführende Links:

  1. Golem: Lange bekannte MySQL-Lücke führt zu Angriffen (27.1.2019)
  2. secure_file_priv - mysql - Docs (en)
  3. secure_file_priv - mariaDB - Docs (en)