Matomo-Javascript Tracking Code auslagern
Matomo empfiehlt zum Tracken einer Webseite den Einbau des Codeschnipsels innerhalb des HTML Dokuments … im Head Bereich. [1]
Aber: das Hineinschreiben von Javascript Code in das HTML Dokument ist nicht so recht günstig, wenn man im CSP Security Header das Attribut script-src sicher und ohne unsafe-inline konfigurieren will. [3]
Die Abhilfe besteht darin, dass man [2]
- das Snippet in eine Javascript Datei auslagert.
- den Aufruf des Trackens im Onload Event einfügt - der ebenfalls in der Javascript-Datei ist und nicht im HTML-Code einer Webseite.
Hier einmal ist die Funktion embedTrackingCode benannt:
function embedTrackingCode() { var _paq = window._paq = window._paq || []; _paq.push(["trackPageView"]); _paq.push(["enableLinkTracking"]); var u="/matomo/"; _paq.push(['setTrackerUrl', u+'matomo.php']); _paq.push(['setSiteId', 1]); var d=document, g=d.createElement("script"), s=d.getElementsByTagName("script")[0]; g.type="text/javascript"; g.defer=true; g.async=true; g.src=u+"matomo.js"; s.parentNode.insertBefore(g,s); }
… welche bei Abschluss des Ladevorgangs initialisiert wird:
window.onload = (event) => { embedTrackingCode(); };
Hinweis:
Wenn der Tracking Code in einer Javascript-Datei ist, muss man das Http-Caching der JS Datei bei einem expires= im Http Response Header beachten. Man ist u.U. nicht mehr so flexibel, wenn man den Code anpassen wollte, weil Browser das Javascript aus dem Cache nehmen, statt frisch vom Server die angepasste Version zu holen. Man kann per ETag cachen lassen … oder kann die JS-Datei mit Versionsnummer benennen.
weiterführende Links:
Linux Bash - Zufallspasswort für Mysql-Monitoring User
Ich möchte Mysql/ MariaDB Server monitoren. Dazu legt man einen Monitoring-User in der Datenbank an, der dann Select Rechte auf mysql.* bekommt, um aus der Datenbank Statusinformationen zu lesen. Der User soll ein langes Zufallspasswort bekommen. Die Installation muss mit dem root User auf der Datenbank erfolgen.
Im Monitoring Modus will ich kein Passwort als Parameter übergeben, damit es nicht in der Prozessliste erscheinen kann. Dies lässt sich mit einer .my.cnf im HOME Verzeichnis bewerkstelligen.
Schritt 0: Vorbereitung
Initial setze ich mein HOME und die Variable cfgfile auf die .my.cnf:
# --- set HOME HOME=/etc/icinga2-passive-client # --- other vars... cfgfile=$HOME/.my.cnf myuser=icingamonitor datafile=/tmp/mysql-status.txt
Schritt 1: Ein Zufalls-Passwort erzeugen.
Ich will nur parametriesieren können, wie lang das zu erstellende Passwort ist … daher die Variable vorab.
Das Passwort kommt durch eine Ausgabe des Zufallsgenerators /dev/urandom zustande - wobei mittels tr nur Ziffern und Buchstaben gefiltert werden.
pwlength=64 mypw=$( head /dev/urandom | tr -dc A-Za-z0-9 | head -c $pwlength )
Schritt 2: Mysql-User mit Rechten anlegen.
Dieser Aufruf funktioniert nur mit dem root (oder anderen zusätzl. angelegten Admin-) User.
Wenn Linux-Root passwortlosen Zugriff auf den Datenbank-Root-User hat, muss man das HOME auf /root setzen, damit dessen .my.cnf gefunden wird.
HOME=/root mysql -e "CREATE USER $myuser@localhost IDENTIFIED BY '$mypw';" if [ $? -ne 0 ]; then echo "ERROR: mysql command to create user failed." exit 1 fi echo "- grant SELECT on mysql tables ..." mysql -e "GRANT SELECT ON mysql.* TO $myuser@localhost;" if [ $? -ne 0 ]; then echo "ERROR: mysql command to grant permissions failed." exit 1 fi echo "- flush privileges ..." mysql -e "FLUSH PRIVILEGES;"
OK, damit habe ich nun meinen Datenbank-User. Dessen Passwort ich selbst nicht kenne, aber ich weiss, dass es 64 Zufalls-Zeichen besitzt und “halbwegs” safe sein dürfte. Damit erspare ich mir bei zig-zig lokalen Mysql/ MariaDb Services die Verwaltung der Kennwörter für den Monitoring-User.
Schritt 3: Mysql-User-Konfigurationsdatei anlegen.
Die Konfigurationsdatei ist in einem Verzeichnis des Monitoring-Users anzulegen. Zum Erzeugen der Datei wird hier nur auf $cfgfile zurückgegriffen.
cat >$cfgfile <<EOF # # generated on `date` # [client] user=$myuser host=localhost password=$mypw EOF ls -l $cfgfile if [ $? -ne 0 ]; then echo "ERROR: creation of config file failed." exit 1 fi
Nachdem Schritte 1..3 als root erfolgten, sollte bei Aufruf des Skripts mit dem Monitoring User das $HOME wie in Schritt 0 umgebogen sein, damit die soeben erzeugte .my.cnf angezogen wird. Und voila: dann hat der Monitoring Aufruf einen passwortlosen Zugriff.
Man kann den Status lesen, dies in eine Datei umleiten … und dann die gewünschten Variablen per grep lesen.
function _mysqlreadvars(){ mysql -e "SHOW GLOBAL VARIABLES ;" --skip-column-names >$datafile mysql -e "SHOW STATUS ;" --skip-column-names >>$datafile } function _mysqlgetvar() { local sVarname=$1 grep "^$sVarname[^_a-z]" ${datafile} | awk '{ print $2 }' } # init _mysqlreadvars _mysqlgetvar max_connections _mysqlgetvar Max_used_connections # cleanup rm -f $datafile
Die Codeschnipsel sind zur Veranschaulichung aus dem Gesamtkontext herausgerissen. Das komplette Skript ist unten verlinkt.
weiterführende Links:
Sourcen für Icinga Checks und Graphen mit Graphite
An “meinem” Institut geht ohne Open Source eigentlich nichts. Damit wir nicht nur nehmen, geben wir an und wann auch etwas als Open Source zurück. Für dieses Thema wird auf unserer Instituts-Webseite kein Platz eingeräumt, daher publiziere ich es hier in meinem privaten Blog.
Ich habe seit Anfang des Jahres eine Icinga Instanz bei uns aufgebaut. Als Icinga-Client auf unseren Servern verwenden wir ein Bash-Skript, das die REST API des Icinga-Servers als auch des Directors anspricht.
Es gibt es diverse mit Bash geschriebene Checks, die die Standard-Nagios Checks ergänzen (und nur teilw. ersetzen). Zur Abstraktion diverser Funktionen, wie z.B.
- Prüfung auf notwendige Binaries im Check
- Schreiben von Performance-Daten
- Handhabe von Exitcodes
… gibt es eine Include Datei, die gemeinsame Funktionen beherbergt. Oder anders: wer einen der Checks verwenden möchte, muss neben dem Check auch einmal die Include-Datei übernehmen.
Die Sourcecodes der Checks sind nun publiziert in [1].
Viele Plugins schreiben Performance-Daten, die wir mit dem Icinga-Graphite-Modul visualisieren. Die unsrigen Ini-Dateien der Graph-Templates liegen in einem separaten Repository [2]. Beim Schreiben der Ini-Dateien der Graph-Templates kam ich immer wieder ans Limit und es gibt (noch?) nicht so wirklich viele dokumentierte Vorlagen. Und so manche in Graphite dokumentierte Funktionen greifen (scheinbar?) nicht in den Inis für Icinga Graphite.
Ich hoffe, dass trotz ausbaufähiger Dokumentation der ein oder andere doch entwas mitnehmen kann.
Ich kann mir gut vorstellen, den ein oder anderen Check noch einmal im Detail hier im Blog vorzustellen.
weiterführende Links: (en)
Bash: Ausführungszeit eines Kommandos in Millisekunden messen
Wenn man im Monitoring einen Check schreiben will, der die Antwort-Zeit einer Aktion oder Response eines Servers messen will, sind sekundengenaue Angaben zu grob. Mit dem Kommando
time [Kommando]
kann man sehen, wie lange das jeweilige Kommando brauchte:
$ time ls [... Liste von Dateien ...] real 0m0,022s user 0m0,000s sys 0m0,015s
Die Gesamtzeit ist in der Zeile real enthalten. Angegeben sind die Minuten, ein “m” und danach die Sekunden mit 3 Nachkommastellen. Wobei die Tausendstel je nach System/ Sprache mit Punkt oder Komma getrennt sein könnten.
Aha, nun muss man “nur” noch die Zeile mit der Angabe “real” in den letzten 3 Zeilen der gesamten Ausgabe suchen und das Ganze parsen.
Als kleines Demo anbei einmal mundgerecht als Funktion (es läuft unter Linux und mit CYGWIN unter MS Windows):
#!/usr/bin/env bash # ------ FUNCTION # measure time in ms # @param string command to execute / measure function getExecTime(){ local sCommand=$1 local tmpfile=$( mktemp ) ( time eval $sCommand ) >$tmpfile 2>&1 local sRealtime=`cat $tmpfile | tail -3 | grep "^real" | awk '{ print $2 }'` rm -f $tmpfile local iMin=`echo $sRealtime | cut -f 1 -d "m" ` local iMillisec=`echo $sRealtime | cut -f 2 -d "m" | sed "s#[.,s]##g" | sed "s#^0*##g" ` typeset -i local iTime=$iMin*60000+$iMillisec echo $iTime } # ------ MAIN echo echo "ZEITMESSUNG IN MILLISEKUNDEN" echo mytime=`getExecTime 'ls -ltr'` echo Dauer: ${mytime} ms
Manjaro: Update-Skript
Ein kleines Snippet von mir zum Updaten der Linux-Distro Manjaro.
Es aktualisiert die Softwarepakete der Distribution (Kernel, Programme u.s.w.) - danach aus den AUR (Arch User Repository) installierte Programme. Zum Schluss werden nicht benötigte Pakete entfernt.
Es werden noch Zwischenabfragen gestellt, die mit Yes oder No zu beantworten sind. Mit Auskommentieren der Zeile answer=Y kann man es teilautomatisieren, jedoch pamac wird immer eine Interaktion bedürfen und das Passwort erfragen.
#!/usr/bin/env bash answer= # answer=Y # ------------------------------------------------------------ # FUNCTIONS # ------------------------------------------------------------ function header(){ echo; echo; echo "========== `date` - $*"; echo } # ------------------------------------------------------------ # MAIN # ------------------------------------------------------------ echo "______________________________________________________________________" echo echo echo "Axels Manjaro update script" echo echo "______________________________________________________________________" header "SYSTEM UPDATE" echo $answer | sudo pacman -Syyuu header "Update incl. AUR..." pamac upgrade -a header "CLEANUP - remove unneeded packages" echo $answer | pacman -Qdtq && sudo pacman -Rs $(pacman -Qdtq) header "DONE." # ------------------------------------------------------------
Nachtrag:
Wer es ein wenig bunter mag, der kann in der /etc/pacman.conf in der Sektion [options] diese 2 Einstellungen aktivieren:
[options] (...) Color ILoveCandy (...)
UPDATE:
- pamac does not run as root but in user context. So sudo was removed.
weiterführende Links:
- manjaro.org Startseite der Linux Distribution
- wiki.manjaro.org: AUR
- wiki.manjaro.org: pacman
- wiki.manjaro.org: Pamac
Bash Snippet: Alter einer Datei
Ein Mini-Schnipsel fürs Shell-Skripting. Nach Angabe einer Datei bekommt man mit nachfolgender Funktion das Alter in Sekunden.
# get age of a file in sec # param string filename to test # function _getFileAge(){ echo $(($(date +%s) - $(date +%s -r "$1"))) }
Als Beispiel:
myfile=/tmp/whatever.txt typeset -i iAgeLastGet=`_getFileAge "${myfile}"` echo "INFO: The file ${myfile} is ${iAgeLastGet} sec old ..."
Bash Rest API Client
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:
- Gitlab: iml-open-source/bash-rest-api-client
- Icinga Open Source Monitoring Tool
- Icingaweb - Director REST API
- Icinga: REST API
Fish - SSH-Agent initial starten
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:
Linux: bei Änderung einer / mehrerer Dateien ein Kommando starten
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:
- Download/ Readme: git-repo.iml.unibe.ch: onfilechange
- Docs: os-docs.iml.unibe.ch/onfilechange/
Xampp: Port 3306 belegt - durch Firefox
Schon komisch: ein Programm krallt sich einen Port. Ich dachte, das wäre die Domäne von Skype.
Abhilfe:
- Firefox beenden
- Mysql im Xampp starten
- Firefox starten