‘git grep’ oder wie finde ich nochmal, was ich noch tun muss?

In meinem laufenden Projekt homekit_python haben sich inzwischen doch 2 oder 3 TODOs angesammelt. Wie findet man am schnellsten die TODOs die sich in Dateien befinden, die von git verwaltet werden?

Die Antwort ist einfacher, wie ich zunächst annahm: git bietet mit dem Kommando git grep bereits alles was ich benötige:

git grep TODO

Die Ausgabe umfasst in dieser einfachsten Form wie von grep gewohnt alle Treffer:

Weitere git-Kommandos findet man im git Reference Manual.

Python 3: Profiling von Skripten

Auf GitHub habe ich in einem Issue die Anfrage bekommen, die Software sei langsam. Ja, gefühlt ist die Software nicht die schnellste. Aber wie langsam genau? Und für die Optimierung wichtiger,  wo geht die Zeit verloren?

Das kann mit Profiling herausfinden. Ich will hier eine Lösung beschreiben, die ich für elegant und schnell halte, wobei es sich besseres/schöneres/… gibt.

Zunächst installiert man gprof2dot und dot:

pip3 install --user gprof2dot
sudo apt install dot

python3 bietet mit cProfile bereits ein Modul an, welches das Profiling eines Python-Skripts übernimmt. Mit folgendem Kommando startet man das Profilen des Skripts put_characteristic.py (mit dessen Parametern). Für das Module cProfile wird nur der Parameter -o genutzt, der die Ausgabedatei festlegt.

python3 -m cProfile -o profile.pstats \
        homekit/put_characteristic.py -f koogeek.json -c 1.8 -v false

Anschließend verwendet man gprof2dot und dot um eine mehr oder weniger übersichtliche Grafik zu erzeugen, welche aufzeigt, wo die meiste Zeit “liegen bleibt”. Das Ergebnis sieht in unserem Beispiel wie folgt aus:

Ergebnis eines Profilinglaufs

Allerdings muss man auch hier berücksichtigen, dass die Ergebnisse eines einzelnen Laufs nicht repräsentativ sind. Hier sollten mehrere Durchläufe hintereinander geprofiled werden. Allerdings kann man auch bei einem Durchlauf erkennen, das hier Zeroconf zum Auffinden des HomeKit Geräts und das Erstellen der Sessionkeys viel Zeit verbrauchen.

Bilder & Videos fürs Blog

Von Zeit zu Zeit will man in einem Blog Bilder und Videos veröffentlichen. Dabei würde ich gerne manches unter Kontrolle behalten. Deshalb dieser kurze Artikel, der einige Tricks verraten soll.

Bilder

Bilder erstelle ich inzwischen eigentlich immer mit einer Handy-Kamera. Dabei werden inzwischen über die Exif-Daten viele Informationen preis gegeben. Mobiltelefone fügen hier auch gerne Felder ein, die nicht jeden etwas angehen:

  • GPS Position der Aufnahme,
  • Informationen über das Gerät und
  • viele weitere

Einsehen kann man die Exif-Daten eines Fotos mit:

identify -verbose $BILD

Die Exif-Daten können (und sollten) mit folgendem Kommando entfernt werden:

convert $IN -strip $OUT

Für das Blog ist es ab und zu notwendig ein Bild in der Größe zu reduzieren (hier auf 50%):

covert $IN -resize 50% $OUT

Alternativ ist auch die Angabe der Größe in Pixeln (kann das Bild vergrößern):

covert $IN -resize 600@ $OUT

In beiden Fällen werden die Seitenverhältnisse bei behalten.

Videos

Da bei meinen Videos der Ton keine Rolle spielt, wird dieser mit folgendem Befehl entfernt:

ffmpeg -i $INPUT -an $OUTPUT

Oft möchte man das Video nach der Aufnahme um 90 Grad im Uhrzeigersinn drehen (transpose=2 für 90 Grad gegen den Uhrzeigersinn):

ffmpeg -i $INPUT -filter:v transpose=1 $OUTPUT

Einzelbilder können mit folgendem Kommando extrahiert werden:

ffmpeg -i $INPUT -ss $second -vframes 1 $OUTPUT.png

Einen Ausschnitt aus einem Video kann man mit folgendem Kommando erstellen:

ffmpeg -i $INPUT -filter:v crop=$WIDTH:$HEIGHT:$X:$Y $OUTPUT

Anschließend verwandelt man das Video in webm mit einer Bandbreite von 1 Mbit/s  (welches in WordPress direkt abgespielt werden kann):

ffmpeg -i $INPUT -vcodec libvpx-vp9 -b:v 1M $OUTPUT$.webm

 

Brother DCP-9017CDW und Linux

Das alte Faxgerät meines Vater ging leider kaputt und wurde zuletzt eh nur noch als Kopierer verwendet. Als Ersatz wurde ein Brother DCP-9017CDW auserkoren, da dieses Gerät:

  1. ein duplexfähigen Farblaser zur Ausgabe verwendet (trocknet einfach nicht ein),
  2. einen moderaten Anschaffungspreis besitzt,
  3. per WLAN auch mit Linux als Drucker verwendet werden kann und
  4. über ein einfaches Touch Display bedient werden kann.

Kurz um, das Gerät scheint das richtige zu sein, zumal auch eine Funktion “Scan2Email” auf der Funktionsliste steht. Leider sagt das Kleingedruckte “Benötigt Brother-Software” und verweist auf einen optionalen kostenlosen Download für Linux aus dem Brother Solutions Center. Ebenso leider existiert dann kein Download für Linux im Brother Solutions Center (Stand 25.12.2017). Ergo kein offizieller Weg für Scan2Email. Na danke.

Ohne Brother Software kann das Gerät allerdings auf (lokale) FTP-Server scannen. Lokal deshalb, da FTP als unsicher einzustufen ist (Link, Link, …).

Die Idee ist nun: vom Brother DCP-9017CDW wird in ein FTP-Verzeichnis pro Email-Adresse gescannt. Auf dem FTP-Server wird auf eingehende Dateien reagiert und diese per Mail versendet.

Folgende Software wurde ausgewählt:

  • vsftpd als FTP-Server,
  • mpack zum Versand der Dateien als Anhang,
  • exim4 mit Smarthost zum Versand der Mails und
  • incron zum Reagieren auf neue Dateien.

Zunächst erlaubt man dem vsftpd in seiner Konfiguration das Schreiben von Dateien (write_enable=YES) sowie lokale User (local_enable=YES). Somit kann auf dem DCP-9017CDW testweise auf den FTP-Server gescannt werden.

Anschließend exim so einrichten, dass er Mails nach außen versenden kann. Das passiert in diesem Szenario mit einem Smarthost. Dann sollte man testen, ob mpack auch Anhänge versenden kann:

mpack -s SUBJECT /PATH/TO/FILE MAILADRESSE

Kurz danach sollte die Datei als Anhang eintreffen.

Abschließend noch incron konfigurieren, damit auf neue Dateien reagiert werden kann:

  • den Nutzernamen, unter dem per FTP die Dateien abgelegt werden, in der Datei /etc/incron.allow vermerken
  • mit incrontab -e pro Verzeichnis bzw. Email Adresse eine Zeile der Form
    $PFAD IN_CLOSE_WRITE $Script $@/$# $MAIL

    eintragen

Das Script übergibt mpack die Datei und die Mailadresse und löscht die Datei nach dem Versand.

Et voilà, Scan2Mail funktioniert doch.

Thread Dumps – auch für Python

Thread Dumps sind Momentaufnahmen des Aufruf-Stacks für jeden Thread einer laufenden multi-thread Anwendung. Dies ist in vielen Situationen zur Analyse hilfreich. Diese gestaltet sich gerade bei multi-thread Anwendung meist komplizierter.

Für Java gibt es das Programm jstack (u.a. mit jps im JRE enthalten) um Thread Dumps zu erzeugen.

Unter Python kann ein ähnliches Verhalten (getestet unter Linux) mit einem kleinen Umweg über einen Signal-Handler erreicht werden:

...
import faulthandler 
import signal
import os
...

def handler(signum, frame):
  file ='/tmp/threads'
  print('Writing thread dump to ', file)
  with open(file, 'w') as f:
    faulthandler.dump_traceback(file=f, all_threads=True)

...
if __name__ == '__main__':
  with open('/tmp/pid', 'w') as f:
    f.write(str(os.getpid()))
  signal.signal(signal.SIGUSR1, handler)
  ...

Nach dem Starten des Programms kann man über die PID aus /tmp/pid einen Thread Dump erzeugen:

kill -USR1 `cat/tmp/pid`

Dieser sieht exemplarisch so aus (extra in kleinerer Schriftart um die Zeilen ohne Umbrüche zu erhalten):

Thread 0xb3ccb460 (most recent call first):
 File "/home/jlusiardi/.local/lib/python3.4/site-packages/zeroconf.py", line 1102 in run
 File "/usr/lib/python3.4/threading.py", line 920 in _bootstrap_inner
 File "/usr/lib/python3.4/threading.py", line 888 in _bootstrap

Thread 0xb44ff460 (most recent call first):
 File "/usr/lib/python3.4/socketserver.py", line 154 in _eintr_retry
 File "/usr/lib/python3.4/socketserver.py", line 236 in serve_forever
 File "/home/jlusiardi/sources/smarthome/homekit_link/__init__.py", line 142 in run
 File "/usr/lib/python3.4/threading.py", line 920 in _bootstrap_inner
 File "/usr/lib/python3.4/threading.py", line 888 in _bootstrap

Thread 0xb4cff460 (most recent call first):
 File "/home/jlusiardi/sources/smarthome/influxdb/__init__.py", line 109 in run
 File "/usr/lib/python3.4/threading.py", line 920 in _bootstrap_inner
 File "/usr/lib/python3.4/threading.py", line 888 in _bootstrap

Thread 0xb6025460 (most recent call first):
 File "/usr/lib/python3.4/socketserver.py", line 154 in _eintr_retry
 File "/usr/lib/python3.4/socketserver.py", line 236 in serve_forever
 File "/home/jlusiardi/sources/smarthome/homematic/callback.py", line 42 in run
 File "/usr/lib/python3.4/threading.py", line 920 in _bootstrap_inner
 File "/usr/lib/python3.4/threading.py", line 888 in _bootstrap

Current thread 0xb6fb6300 (most recent call first):
 File "./main.py", line 19 in handler
 File "/usr/lib/python3.4/cmd.py", line 126 in cmdloop
 File "/home/jlusiardi/sources/smarthome/cli/__init__.py", line 32 in cmdloop
 File "./main.py", line 50 in <module>

Auch hier bieten sich verbesserte Analysemöglichkeiten analog zu Java.

Spielchen mit RSA-Schlüsseln

Als Einstiegshürde für ein CTF (Capture the Flag) dient gerne mal ein RSA Schlüsselpaar, von dem nur der öffentliche Teil bekannt ist. Also kein unrealistischer Ansatz, lediglich die Schlüssellängen sollten entsprechend klein sein.

Vorbereiten der Challenge

Diese Schritte werden vom Spielleitder durchgeführt. Wichtig ist eine geeignete, nicht zu lange Schlüssellänge zu wählen. Hier wählen wir 256 Bit als Schlüssellänge. Das sollte die Teilnehmer vor keine Probleme stellen.

Generieren des Schlüsselpaars

Der wesentliche Schritt bei der Erzeugung eines RSA-Schlüsselpaars ist die Wahl von 2 Primzahlen p und q, deren Produkt n (der RSA-Modulus) die gewünschte Schlüssellänge besitzt.

Dank OpenSSL ist es einfach ein RSA-Schlüsselpaar zu erzeugen:

openssl genrsa -des3 -out private.pem 256
openssl rsa -in private.pem -outform PEM -pubout -out public.pem

Daraus ergibt sich als Public Key beispielsweise:

-----BEGIN PUBLIC KEY-----
MDwwDQYJKoZIhvcNAQEBBQADKwAwKAIhALRUOO0rvPEA282wqL4VF5GibnA1kcux
LHdIOcT807+DAgMBAAE=
-----END PUBLIC KEY-----

Den privaten Schlüssel private.pem können wir nun löschen, da wir diesen zum Einen nicht mehr benötigen und zum Anderen ja rekonstruieren wollen.

Verschlüsseln der Probedatei

Eine Probedatei lesbar.txt wird mit dem öffentlichen Schlüssel mit OpenSSL verschlüsselt:

openssl rsautl -encrypt -inkey public.pem -pubin -in lesbar.txt -out geheim.txt

Das Ergebnis der Verschlüsselung ist eine Datei, die nur unlesbare binäre Daten enthält. Sie kann hier heruntergeladen werden: geheim.txt. Diese Datei und der öffentliche Schlüssel gehen an die Teilnehmer.

Lösen der Challenge

Diese Schritte werden durch die Teilnehmer durchgeführt.

Knacken der Schlüssel

Zunächst müssen wir aus dem öffentlichen Schlüssel die beiden Parameter  n (Modulus) und e (Exponent) extrahieren:

from Crypto.PublicKey import RSA

public_key = RSA.importKey(open('public.pem', 'r').read())
print 'n =', public_key.n
print 'e =', public_key.e

Dazu verwenden wir die Bibliothek pycrypto (Debian: python-crypto). Ausgegeben werden die beiden oben genannten Werte, in unserem Beispiel also:

n = 815651207903382691105573612057017618852
    64115611526254943458542612275141001091
e = 65537

Nun muss der Modulus faktorisiert werden (n ist das Produkt der beiden Primzahlen p und q). Dazu kann das msieve von Jason Papadopoulos verwendet werden.

Für unsere Zahl n liefert msieve auf einem Core2Duo T7400 nach knapp 10 Minuten die beiden Primfaktoren:

p = 264446414907090989475638551039611206507
q = 308437234133027923039636551098640509513

Alternativ können wir auch factordb.com verwenden, wenn die Zahl dort bereits faktorisiert vorliegt.

Nun müssen wir aus p und q den privaten Exponenten d berechnen:

d = 1/e mod (p-1)*(q-1)

oder mit Python und dem Modul gmpy2:

import gmpy2

p = 264446414907090989475638551039611206507
q = 308437234133027923039636551098640509513
e = 65537
d = gmpy2.invert(e,(p-1)*(q-1))

Aus den Werten n,e und d wird nun der private Schlüssel erstellt und ausgegeben:

from Crypto.PublicKey import RSA
key = RSA.construct((n,e,d))
print key.exportKey()

In unserem Beispiel ergibt das:

-----BEGIN RSA PRIVATE KEY-----
MIGrAgEAAiEAtFQ47Su88QDbzbCovhUXkaJucDWRy7Esd0g5xPzTv4MCAwEAAQIh
ALFDhX4nG6FxbaChww6vnyzjasWei1L/rUg3aG/c6MaxAhEA6ArZu41/pzoKRlXH
MfYqSQIRAMbyhuPv26I/S0+zokene2sCEDxkfpDK3huHBp+RubtuJ0kCECAEWecG
6+7Rhto9y4kCkB8CEQCSSbMXMjOybMpz3gCOQYeS
-----END RSA PRIVATE KEY-----

Wir speichern den privaten Schlüssel wieder in der Datei private.pem ab.

Entschlüsseln der Probedatei

Wurde alles richtig durchgeführt, so kann man mit folgender OpenSSL Anweisung die Probedatei entschlüsseln:

openssl rsautl -decrypt -inkey private.pem -in geheim.txt -out lesbar.txt

Der genaue Inhalt von lesbar.txt wird hier jedoch nicht verraten.

Abschluss

Man erkennt hier am Zeitaufwand zur Faktorisierung eines 256 Bit Schlüssels bereits, dass die Sicherheit von RSA auf genau dieser Schwierigkeit basiert. In der Praxis verwendet man daher Schlüssellängen von mindestens 2000 Bit (siehe dazu die Technische Richtilinie des BSI: Kryptographische Verfahren: Empfehlungen und Schlüssellängen (Seite 39, Kapitel 3.5 – RSA).

Dynamische IP – Reagieren auf Änderungen

Als DSL Nutzer kommt man hierzulande meist nicht in den Genuss einer statischen IPv4 Adresse sondern bekommt regelmäßig eine andere IPv4 von seinem Provider zugewiesen. Als Nebeneffekt wird dadurch das Anbieten von Serverdiensten an Heimanschlüssen oder auch der Betrieb eines IPv6 Tunnels erschwert.

Die FRITZ!Box bietet Unterstützung für einige DDNS-Provider, manchmal möchte man aber auch einfach ein Skript ausführen. Den möglichen Anwendungen sind kaum Grenzen gesetzt.

Über Dienste wie https://icanhazip.com/ kann man sich seine öffentliche IP-Adresse, die zum Abrufen der Seite verwendet wurde, einfach anzeigen lassen und auch in Skripten verwenden. Möchte man die Aktualisierungen wirklich nur dann ausführen, wenn sich die IP geändert hat, so kann das Skript react_to_ip_change.sh helfen. Zu finden ist es im GIT.

Im wesentlichen Skript vergleicht das Skript eine gespeicherte IP mit der aktuell von https://icanhazip.com/ zurück gelieferten. Unterscheiden die beiden sich, so werden alle Skripte in /usr/local/etc/ipchange.d/ ausgeführt.

Kombiniert man das mit Cron, so aktualisiert man die eigene IP zeitnah in allen relevanten Systemen:

# m h dom mon dow command
* * * * * /usr/local/bin/react_to_ip_change.sh

Noch mal der Link zum GIT: https://code.nerd2nerd.org/n0ob/react_to_ip_changes

Mounten von Partitionen aus Image-Dateien

Ist das Backup einer SD-Karte nur als Image-Datei vorhanden und keine Zeit erst wieder auf SD-Karte zu schreiben? Dateimanager kann Image-Dateien nicht direkt einbinden? Hier die Lösung:

  1. Auslesen der Partitionen und ihrer Startsektoren:
    fdisk -l ${QUELLDATEI}

    (Hier in der Spalte Start nach dem Anfang der Partition suchen, unter Units steht die Größe der Sektoren)

  2. Einbinden der gewählten Partition:
    mount ${QUELLDATEI} /mnt -o offset=$((${ANFANGSSEKTOR}*${SEKTORGRÖßE}
  3. Und fertig!

 

Programme mit tmux beim Systemstart automatisch starten

Da ich für das neuste Projekt (ClimateRecording) für den Linux-Client auf der Arietta Hardware erstmal noch kein Init-Script wollte, musste eine alternative Lösung gefunden werden.

Nachdem der Client bisher manuell in tmux gestartet wurde, bot sich als Lösung die Skriptbarkeit von tmux an:

tmux new-session -d -s client_tmux
tmux select-pane -t client_tmux
tmux send-keys "cd /$PATH/ClimateRecording/linuxClient" C-m
tmux send-keys "/$PATH/ClimateRecording/linuxClient/client.py" C-m

Trägt man diese Zeilen in der Datei /etc/rc.local ein, so wird bei jedem Systemstart eine Session für tmux gestartet und darin der Linux-Client ausgeführt.

Mit

tmux a -t client_tmux

kann man dann nach Login auf der Arietta die tmux-Session aktivieren.

 

jq – Kommandozeilenbasiertes Tool für JSON

Viele Web-Dienste basieren auf REST und liefern die Resultate häufig als JSON. Das auf der Kommandozeile auszuwerten gestaltet sich wegen der verschachtelten Struktur als etwas schwerer.

EIne Lösung stellt jq dar, jq erlaubt das gezielte Abfragen der einzelnen Einträge in einem JSON String.

Beispiel:

docker inspect openvpn_server | jq '.[0].NetworkSettings.Ports | keys '

inspiziert zunächst einen Docker-Container, fragt die Ports aus den Networksettings ab und extrahiert die Keys des Objects. Das Ergebnis ist:

[
  "1194/tcp",
  "1194/udp"
]

Ein Tutorial bietet das Projekt hier: http://stedolan.github.io/jq/tutorial/. Das ausführliche Handbuch hier: http://stedolan.github.io/jq/manual/.

Update

curl -s http://api.bitcoincharts.com/v1/markets.json | jq '.[] | select(.symbol == "btcdeEUR" )'

fragt eine JSON-Datei (mit Bitcoin Informationen) und extrahiert aus dem Array das Objekt, welches den Wert btcdeEUR als symbol besitzt.