Du teilst Deine Downloads mit deinem Antivirus Provider


Ich habe vor kurzem einem Kunden einen Link zu einem Firewall Image (für den Turris MOX Router mit einer Variante von OpenWRT) auf meinem eigenen Webserver geschickt. Das Image enthielt Key-Material für eine OpenVPN Verbindung. Die Datei war in einem versteckten Verzeichnis auf meinem Projekt-Webserver. Ich habe genau geschaut, ob es noch andere Downloads gab, zusätzlich zu dem meines Kunden.

Ich bin mir bewusst, dass es nicht die beste Sicherheitspraxis ist, Key-Material über einen unsicheren Kanal zu übertragen. Und am Ende musste ich das VPN Key-Material das im Image war revoken und meinem Kunden über einen sicheren Kanal einen neuen Schlüssel zukommen lassen.

Nun, ich hatte schon erwähnt, dass ich die Downloads beobachtet hatte. Etwa eine Stunde (!) nachdem mein Kunde das Image geholt hatte (um genau zu sein um 21/Mar/2021:17:35:50) wurde von einer anderen IP darauf zugegriffen:

77.74.177.4 - - [21/Mar/2021:18:43:51 +0100] "GET /turris-image-XXXXXXX.zip HTTP/1.1" 200 77244886 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.92 Safari/537.36"

Wenn man diese IP über whois sucht findet man:

> whois 77.74.177.4
% This is the RIPE Database query service.
...
netname:        KL-NET3
descr:          Kaspersky Lab Internet
country:        RU
...
source:         RIPE
organisation:   ORG-KL28-RIPE
org-name:       Kaspersky Lab AO
country:        RU

Mein Kunde hat die Kaspersky Antivirus-Software installiert. Der Link wurde also vermutlich an Kaspersky über diese installierte Software weitergegeben. Es kann ja einerseits sein, dass der Grund, warum Kaspersky das Image heruntergeladen hat ein harmloser war, möglicherweise um ein Service zu erbringen (wie z.B. der Scan auf Viren) aber in meinem Fall hat das bedeutet dass nicht öffentliche Informationen Dritten bekannt wurden. Auf der anderen Seite könnte es sein, dass diese Informationen auch für andere Zwecke verwendet werden – wir wissen es nicht.

Also beachten Sie dass Ihr Antivirus-Produkt Ihnen beim Download im Web über die Schulter schaut.

Interaktion von libvirt und AppArmor


Ich unterrichte an der Fachhochschule Burgenland in Eisenstadt (Österreich). Dort hatten wir vor kurzem eine Laborübung (die zwar im Labor stattfand aber alle Studenten arbeiteten von zuhause auf den Laborrechnern aus Covid-Gründen) wo die Aufgabe war, mit libvirt eine laufende virtuelle Maschine auf einen anderen Server zu migrieren (wir verwenden die Kommandozeile und virsh).

Bei einer von mehreren Gruppen – alle mit identischen Debian Installationen – schlug die Migration mit einer Fehlermeldung fehl. Das Migrations-Kommando war:

virsh -c qemu+ssh://root@primary/system migrate --live --unsafe \
    debian-1 qemu+ssh://root@secondary/system

Für die Laborübung verwenden wir NFS weil ein besseres verteiltes Filesystem zu viel Zeit kosten würde, daher verwenden wir die --unsafe Option bei der Migration. Die folgende Fehlermeldung war das Resultat (die Meldung ist in mehrere Zeilen zerlegt, die Meldung ist normalerweise alles in einer Zeile):

error: internal error: Process exited prior to exec:
libvirt:  error : unable to set AppArmor profile
'libvirt-d22db7ca-50ca-43bd-b6da-1ccecf5a83e7' for '/usr/bin/kvm':
No such file or directory

Es stellte sich dann heraus, dass es diese Gruppe geschafft hatte, die /var Partition mit Log-Dateien zu füllen. Aber auch nach Aufräumen kam noch die gleiche Fehlermeldung. Das Gefühl sagte, dass hier von AppArmor und/oder libvirt dynamisch erzeugte Dateien nicht erzeugt werden konnten und dass dies der Grund für den Fehlschlag war. Ausserdem stellte sich heraus, dass einige AppArmor Dateien die auf der ersten Maschine korrekt installiert waren auf der zweiten Maschine fehlten.

Eine Neuinstallation von AppArmor und den zugehörigen Dateien mit apt-get mit der Option --reinstall funktionierte nicht, die fehlenden Konfigurationdateien in /etc/apparmor.d wurden nicht neu erzeugt. Daher war deinstallieren der Pakete mit dem purge Kommando (dieses entfernt auch die Konfigurationdateien) und dann Neuinstallation der Ausweg um die fehlenden AppArmor Dateien zu bekommen und so die Migration schlussendlich durchzuführen. Ich habe bis jetzt keine Ahnung, welche Dateien gefehlt haben.

Wenn man nach der obigen Fehlermeldung googled, findet man einen Debian Bug-Report wo eine der dynamisch erzeugten Dateien in /etc/apparmor.d/libvirt die Länge Null hatte. Dies war jedoch nicht das Problem in unserem Fall, zeigt aber dass AppArmor nicht besonders sorgfältig die Fehler prüft, z.B. wenn das Dateisystem voll ist. Es gibt also vermutlich noch andere Dateien die dynamisch erzeugt werden die in unserem Fall das Problem waren.

Die folgende Abfolge von Deinstallations und Re-Installations Kommandos hat das Problem in unserem Fall behoben. Es sei darauf hingewiesen dass das Entfernen der beiden Dateien wie im Debian Bug-Report das Problem in unserem Fall nicht gelöst hat:

dpkg --purge apparmor-utils apparmor-profiles
dpkg --purge apparmor
rm -rf /var/cache/apparmor
apt-get install apparmor apparmor-utils apparmor-profiles
dpkg --purge libvirt-daemon-system
apt-get install libvirt-daemon-system
systemctl restart libvirtd.service
systemctl restart virtlogd.service
systemctl restart virtlogd.socket

Ich bin nicht sicher dass man alle Services wirklich neu starten muss, aber es gab ein Problem dass libvirt nicht auf den virtlog Socket verbinden konnte und das ließ sich durch ein Restart von virtlog.{service,socket} beheben.

Dynamisches DNS mit dem bind DNS server


Der bekannte DNS server bind erlaubt Konfigurationen die es ermöglichen, dass clients ihre DNS Einträge selbst verändern können. Weil einige öffentliche dynamische DNS Services zu einem Bezahlmodell gewechselt sind und weil ich sowieso meinen eigenen Mail- und Webserver bei einem Hoster betreibe, suchte ich nach einer Möglichkeit, meinen eigenen dynamischen DNS Server zu verwenden. Das liegt schon ein paar Jahre zurück, aber weil das von mir damals verwendete Howto vom Netz (oder zumindest aus meiner Google-Bubble) verschwunden ist, dokumentiere ich hier wie ich das gemacht habe.

Die Software läuft bei mir unter Debian Buster zur Zeit dieses Blog-Eintrags, die Details können sich also bei anderen Systemen ändern. In den folgenden Beispielen nenne ich die Subdomain für die dynamischen DNS Einträge dyn.example.com.

Die Konfigurations-Datei von bind braucht ein zusätzliches include um die Konfigurations-Datei für die dynamische Domain einzubinden. In meiner Konfiguration heisst diese Datei named.conf.handedited. In dieser Datei braucht es einen Entrag für jeden dynamischen DNS Client wie folgt:

zone "dyn.example.com" {
        type master;
        allow-transfer {none;};
        file "/etc/bind/slave/pri.dyn.example.com";
        update-policy {
            grant h1.dyn.example.com. name h1.dyn.example.com. A TXT;
            grant h2.dyn.example.com. name h2.dyn.example.com. A TXT;
            [...]
        };
};

In diesem Beispiel dürfen die Hosts h1 und h2 (und möglicherweise weitere) ihre eigenen DNS Einträge ändern. Ich erlaube hier dass sie den A und TXT Record ändern. Für IPv6 möchte man hier noch AAAA dazugeben.

Die Konfigurations-Datei /etc/bind/slave/pri.dyn.example.com enthält dann:

dyn.example.com         IN SOA  ns1.example.com. admin.example.com. (
                                2020080100 ; serial
                                120      ; refresh (2 minutes)
                                120      ; retry (2 minutes)
                                120      ; expire (2 minutes)
                                120      ; minimum (2 minutes)
                                )
                        NS      ns1.example.com.
h1                      A       127.0.0.1
                        KEY     512 3 10 (
                                AwEAAdEvnGmGO4ku+xms4w1c5RWh5BvugiZ4ty9tkIes
                                <more gibberish lines>
                                ); alg = RSASHA512 ; key id = <number>

Die Einträge in spitzen Klammern sind Kommentare und sollten durch die korrekten Werte in der Zielinstallation ersetzt werden. Die Einträge A und KEY werden von Hand für jeden neuen Host der seine eigene IP-Adresse setzen können soll eingetragen. Der KEY ist der öffentliche Schlüssel (public key) der weiter unten erzeugt wird. Nach meiner Erfahrung braucht es den A-Eintrag damit das ganze funktioniert. Ich setze hier eine localhost Adresse weil der Client diese später sowieso überschreiben wird. Es ist üblich die Admin Email-Adresse (wo @ durch einen Punkt ersetzt ist) im SOA-Eintrag an die Stelle zu setzen wo ich admin.example.com. eingetragen habe.

Um einen neuen Client Host anzulegen geht man wie folgt vor:

  • Erzeugen eines Schlüsselpaares (öffentlicher / privater Schlüssel, public/private key), bevorzugt passiert das beim Client und dieser sendet nur den öffentlichen Schlüssel an den DNS-Administrator um Sicherheitsprobleme bei der Schlüssel-Übermittlung zu vermeiden:

    dnssec-keygen -T key -a RSASHA512 -b 2048 -n HOST newhost.dyn.example.com
    
  • Das erzeugt einen privaten und einen öffentlichen Schlüssel. Zu beachten: Der Client braucht beide Schlüssel, sowohl den öffentlichen als auch den privaten, obwohl nur der private Schlüssel dem dynamischen DNS Client übergeben wird!

  • Das letzte Mal als ich einen neuen Schlüssel erzeugt habe, konnte das Kommando keine Schlüssel mit mehr als 2048 bits erzeugen, obwohl der verwendete Hash-Algorithmus SHA2 modern ist und eine große Bitlänge unterstützt.

  • Damit bind seine Datenbank auf eine Datei schreibt (und man diese editieren darf) muss man bind für diese Domain einfrieren:

    rndc freeze dyn.example.com
    
  • Jetzt darf man das Konfigurationsfile ändern und einen neuen Absatz für einen neuen Host eintragen. Nicht vergessen die serial Nummer zu erhöhen:

    $EDITOR /etc/bind/slave/pri.dyn.example.com
    
  • Dann nicht vergessen, die Domain wieder aufzutauen:

    rndc unfreeze dyn.example.com
    
  • Und schließlich nicht vergessen, dem neuen Host die nötigen Berechtigungen in named.conf.handedited zu geben.

  • Zuletzt muss man wohl dem bind ein reload geben:

    systemctl reload bind9.service
    

Auf der Client-Seite heisst das Programm um bind mitzuteilen dass sich die IP-Adresse des Client geändert hat nsupdate. Man findet diese Programm für viele Betriebssysteme.

Ich verwende ein simples Script am Client das die Änderung der dynamischen IP Adresse bemerkt und eine Adressänderung bei bind anstößt wenn sich die IP Adresse geändert hat. Nachdem wir unseren eigenen DNS-Server betreiben sind die Chancen groß, dass es dort auch einen eigenen Webserver gibt. Das folgende einfache Script erlaubt es dem Client, seine eigene IP-Adresse rauszufinden (Clients sind oft hinter einer NAT Firewall und wir wollen ja nicht wieder auf ein anderes öffentliches Service angewiesen sein, wenn wir gerade öffentliche dynamische DNS Services losgeworden sind, oder?):

#!/bin/sh
echo Content-Type: text/plain
echo ""
echo $REMOTE_ADDR

Dieses Script installiert man in ein cgi-bin Verzeichnis auf einem Webserver. Es liefert dem anfragenden Client seine eigene IP-Adresse (in Text Format) zurück. Das script heisst bei mir ip.cgi und ist dann unter der URL http://example.com/cgi-bin/ip.cgi in unserem Beispiel verfügbar. Die URL muss dann unten im Client-Script geändert werden.

Mein bind Script (einige Variablen müssen geändert werden) schaut aus wie folgt (bitte beachten: Das Script geht davon aus dass obiges IP-Adressen Script auf der Domain example.com läuft, das muss natürlich auch geändert werden):

#!/bin/sh
ZONE=dyn.example.com
DOMAIN=h1.dyn.example.com
DNSKEY=/etc/nsupdate/Kh1.dyn.example.com.+010+04711.private
NS="ns1.example.com"

registered=$(host $DOMAIN $NS 2> /dev/null | grep 'has address' | tail -n1 | cut -d' '  -f4)
current=$(wget -q -O- http://example.com/cgi-bin/ip.cgi)
[ -n "$current" \
-a "(" "$current" != "$registered" ")" \
] && {
 nsupdate -d -v -k $DNSKEY << EOF
server $NS
zone $ZONE
update delete $DOMAIN A
update add $DOMAIN 60 A $current
send
EOF
 logger -t dyndns -p daemon.notice "Updated dyndns: $registered -> $current"
} > /dev/null 2>&1

exit 0

Es sei nochmal darauf hingewiesen, dass der private Schlüssel (in obigem Beispiel in /etc/nsupdate/Kh1.dyn.example.com.+010+04711.private nicht ausreicht, das Programm nsupdate braucht auch den öffentlichen Schlüssel im selben Verzeichnis wie das des privaten Schlüssels.

Hitachi HD44780 Text Anzeigemodul unter Linux


/content/2021/display.jpg

Für ein Projekt verwende ich ein Text Anzeigmodul mit dem Hitachi HD44780 Chip. Diese Displays gibt es in verschiedenen Größen, üblich sind 2 Zeilen mit 16 Zeichen oder 4 Zeilen mit 20 Zeichen. Die letztere Variante wird auch unter dem Namen 2004a vertrieben. Diese Displays arbeiten mit 5V. Weil heutzutage die meisten Microcontroller und CPUs mit 3.3V oder weniger betrieben werden, wird so ein Display oft an einen I²C-Bus über eine I/O Erweiterung (ein sogenannter GPIO Extender) angeschlossen, die meisten davon mit einem PCF8574 Chip. Man kann die Displays und GPIO Extender separat oder zusammen kaufen, auflöten muss man das extender-Modul in jedem Fall selbst.

Das korrekte Vorgehen, so ein Display an einen 3.3V I²C Bus anzuschließen wäre über einen Pegelwandler. Es gibt aber einen Hardware-Hack wo man einfach die Pull-Up Widerstände auf dem PCF8574 Board entfernt (diese hängen direkt an +5V). Diese Änderung macht den GPIO Extender kompatibel mit 3.3V Installationen: Der im Datenblatt des PCF8574 angegebene minimale High-Pegel ist zwar 0.7 * VCC (was 3.5V bedeuten würde), in der Praxis funktioniert er aber einwandfrei mit 3.3V. Es sei darauf hingewiesen, dass die Nummern der Widerstände wie im Hardware-Hack angegeben (R5/R6) je nach Variante des PCF8574 Boards variieren. Ich habe die Nummern R8 und R9 gesehen sowie auch gar keine Beschriftung der Widerstände. Die Widerstände sind 4.7 kΩ, üblicherweise beschriftet mit 472 in der SMD Variante.

Dann habe ich untersucht, ob es einen Linux-Treiber für diese Displays gibt und habe einen in drivers/auxdisplay/hd44780.c im Kernel gefunden. Auf den ersten Blick schaut es so aus als ob dieser Treiber die Ansteuerung mit I²C über den PCF8574 I/O Extender nicht unterstützt. Also habe ich einen Treiber geschrieben und als Kernel-Patch eingereicht.

In der Diskussion (danke an Geert) hat sich dann herausgestellt, dass es einen Treiber für den PCF8574 im Kernel gibt (das hatte ich schon gesehen) und dass man den HD44780 Treiber mit der nötigen Device-Tree Magie so konfigurieren kann dass der PCF8574 Treiber für den I/O Extender verwendet wird. Die folgenden Device-Tree Beschwörungsformeln definieren ein Overlay das den PCF8574 mit seiner Standard I²C-Adress 0x27 konfiguriert und diesen dann für die I/Os des hd44780 Treibers verwendet:

// Note that on most boards another fragment must enable the I2C-1 Bus

/dts-v1/;
/plugin/;

/ {
        fragment@0 {
                target = <&i2c1>;
                __overlay__ {
                        #address-cells = <1>;
                        #size-cells = <0>;

                        pcf8574: pcf8574@27 {
                                compatible = "nxp,pcf8574";
                                reg = <0x27>;
                                gpio-controller;
                                #gpio-cells = <2>;
                        };

                };
        };

        fragment@1 {
                target-path = "/";
                __overlay__ {
                        hd44780 {
                                compatible = "hit,hd44780";
                                display-height-chars = <2>;
                                display-width-chars  = <16>;
                                data-gpios = <&pcf8574 4 0>,
                                             <&pcf8574 5 0>,
                                             <&pcf8574 6 0>,
                                             <&pcf8574 7 0>;
                                enable-gpios = <&pcf8574 2 0>;
                                rs-gpios = <&pcf8574 0 0>;
                                rw-gpios = <&pcf8574 1 0>;
                                backlight-gpios = <&pcf8574 3 0>;
                        };
                };
        };
};

Weil das nicht nur für mich nicht offensichtlich ist (in meiner Nachforschung habe ich mindestens zwei Linux-Treiber Implementierungen für die Kombination von HD44780 und PCF8574 ausserhalb des Linux-Kernels gefunden) habe ich einen Dokumentations-Patch eingereicht um das besser zu dokumentieren für Menschen die auch einen Linux-Treiber für diese Kombination suchen.

Es sei noch angemerkt, dass der Treiber für das Display Escape-Sequenzen verwendet um Spezialfunktionen des Displays (z.B. Ein/Ausschalten der Hintergrundbeleuchtung, löschen des Bildschirms, oder Benutzerdefinierte Bitmap-Zeichen) anzusteuern. Ich glaube diese sind nur im Source-Code in drivers/auxdisplay/charlcd.c dokumentiert.