Docker Hub Pull-Statistiken selbst auswerten

In diesem Artikel möchte ich beschreiben, wie man mit dem „zusammenstecken“ einiger Komponenten eine eigene grafische Auswertung über den Pull Verlauf von Docker Hub Images erstellen kann, die so aussehen könnte:

Der eigentliche Kniff ist nur, die Daten bei Docker Hub abzugreifen über die folgende URL:

https://hub.docker.com/v2/repositories/<namespace>/<name>

Architektur

Wie man in dem Schaubild sieht, wird ein Job zyklisch die Metriken von Docker Hub lesen und in einen MQTT Server pushen. Ein Dienst wird auf die entsprechenden Topics hören, ankommende Daten abgreifen und in einer Opensearch (oder Elasticsearch) Datenbank abspeichern. Ab da kann man diese bequem mit Grafana auslesen.

In meinem Tutorial werde ich Opensearch verwenden, aber die Verwendung von Elasticsearch ist analog möglich wird aber im nachfolgenden Tutorial nicht weiter erwähnt.

Warum so kompliziert? Nun ja, ich habe einen zentralen MQTT Server im Einsatz, der bei mir zu Hause dreh und angelpunkt für Datenaustausch darstellt, daher habe ich das entsprechend entkoppelt. Zudem gibt es anderen Diensten die Möglichkeit gleichzeitig die gesammelten Daten zu verarbeiten.

Umsetzung

Für die exemplartische Umsetzung verwende ich einen Linux Rechner mit vorinstalliertem Docker. Bitte achtet auf genügend Arbeitsspeicher, da die Opensearch Datenbank einiges benötigt. 4 GB sollten aber genügen.

Job

Um die Metriken abzugreifen, habe ich ein kleines Script geschrieben, welches mir die Metriken der Docker Hub Repositories abgreift und in einen MQTT Topic published. Das Script findet Ihr auch in meinem GitHub Repo: https://github.com/cybcon/docker.dockerhubstats2mqtt/blob/main/src/entrypoint.sh. Aus dem Repo heraus erstelle ich einen Docker Container, den Ihr hier findet: https://hub.docker.com/r/oitc/dockerhubstats2mqtt.Dieser ist einfach über Umgebungsvariablen zu konfigurieren.

Die Variable „REPOSITORIES“ enthält die Container Image Repositorie Namen auf die es einem ankommt bei der Visualisierung. Also von diesen werden die Daten gesammelt.Hierzu gibt man der Variable eine, mit Leerzeichen getrennte Liste mit der relevanten Repositories. Jeweils mit dem Docker Hub Namespace und dem eigentlichen Image Namen.

Docker-Compose Beispiel:

services:
  dockerhubstats2mqtt:
    restart: "no"
    user: 5241:5241
    image: oitc/dockerhubstats2mqtt:latest
    environment:
      MQTT_SERVER: test.mosquitto.org
      MQTT_PORT: 1883
      MQTT_TOPIC: com/docker/hub/repositories/metrics
      REPOSITORIES: oitc/dockerhubstats2mqtt oitc/mqtt2elasticsearch
      MQTT_RETAIN: true
      MQTT_TOPIC_REPO_EXTENSION: true

Beim starten des Containers wird das Skript die Daten sammeln und in den MQTT_TOPC publishen und sich dann wieder beenden. Das ganze triggere ich dann via Cron zyklisch, z.B. einmal am Tag:

0  1  *  *  * /usr/bin/docker compose -f /pathto/docker-compose.yml run --rm dockerhubstats2mqtt >/dev/null 2>&1

MQTT

Als MQTT Server nehm ich, zur einfachheit halber, in diesem Tutorial den Testserver von Eclipse Mosquitto. Dieser ist erreichbar auf test.mosquitto.org:1883.

In einem echten Szenario sollte man hier einen eigenen Server aufsetzen.

Service

Um Daten aus einem MQTT Topic auszulesen und in eine Opensearch Datenbank zu speichern, habe ich auch schon vor einiger Zeit ein kleines Python Skript geschrieben, dass genau das macht. Ihr findet das Skript in meinem GitHub Repo: https://github.com/cybcon/docker.mqtt2elasticsearch/blob/main/src/app/bin/mqtt2elasticsearch.py. Auch aus diesem Repo heraus erstelle ich einen Docker Container den Ihr hier findet: https://hub.docker.com/r/oitc/mqtt2elasticsearch.

Das Skipt selbst wird gesteuert durch 2 Konfigurationsdateien. Die „mqtt2elasticsearch.json“ enthält dabei die Verbindungsdaten zu den jeweiligen Servern. Also dem MQTT Server von dem es die Daten bekommt und die Opensearch Datebank, in den es die Daten schrieben soll. Die andere Datei „mqtt2elasticsearch-mappings.json“ dagegen enthält die eigentliche Mapping von MQTT Daten zu Opensearch Index. Einstieg ist immer der MQTT Topic auf den das Tool hören soll und darin wird das Ziel spezifiziert, wohin die Daten geschrieben werden sollen und wie dies dann im Detail aussieht. Dieser „elasticBody“ Bereich ist aber optional. Er ist dafür da, der Datenbank die Entscheidung abzunehmen was es denn für Felder gibt und was für Daten darin enthalten sein werden. Wenn man das weglässt. entscheidet die Datenbak das selbst anhand der Daten.

Die Konfigurationsdatei für die Verbindungsinformationen sieht z.B. so aus (mqtt2elasticsearch.json):

{
"DEBUG": true,
"removeIndex": false,
"opensearch": {
  "hosts": [
     {
       "host": "opensearch-node1",
       "port": 9200
     }
  ],
  "tls": true,
  "verify_certs": false,
  "username": "admin",
  "password": "FooBarBaz-123"
  },
"mqtt": {
  "server": "test.mosquitto.org",
  "port": 1883,
  "tls": false,
  "protocol_version": 3
  }
}

Die Konfigurationsdatei für das Mapping von MQTT Topic und die Opensearch Datenbank sieht z.B. so aus (mqtt2elasticsearch-mappings.json):

{
  "com/docker/hub/repositories/metrics/#": {
    "elasticIndex": "dockerhub-repositories-metrics-{Y}-{m}",
    "elasticBody": {
      "settings": {
        "index": {
          "number_of_shards": 5,
          "number_of_replicas": 0
        }
      },
      "mappings": {
        "properties": {
          "timestamp": { "type": "date" },
          "user": { "type": "keyword" },
          "name": { "type": "keyword" },
          "repository_type": { "type": "keyword" },
          "status": { "type": "integer" },
          "status_description": { "type": "keyword" },
          "description": { "type": "text" },
          "is_private": { "type": "boolean" },
          "is_automated": { "type": "boolean" },
          "star_count": { "type": "integer" },
          "pull_count": { "type": "integer" },
          "last_updated": { "type": "date" },
          "last_modified": { "type": "date" },
          "date_registered": { "type": "date" },
          "collaborator_count": { "type": "integer" },
          "affiliation": { "type": "keyword" },
          "hub_user": { "type": "keyword" },
          "has_starred": { "type": "boolean" },
          "permissions": { "type": "object" },
          "media_types": { "type": "text" },
          "content_types": { "type": "text" },
          "categories": { "type": "object" },
          "immutable_tags": { "type": "boolean" },
          "immutable_tags_rules": { "type": "keyword" },
          "storage_size": { "type": "integer" }
        }
      }
    }
  }
}

Diese beiden Konfigurationsdateien legt man dann im Dateisystem ab und mounted diese als Volume in den Container.

Docker-Compose Beispiel:


services:
  mqtt2elasticsearch:
    restart: unless-stopped
    image: oitc/mqtt2elasticsearch:latest
    container_name: mqtt2elasticsearch
    volumes:
      - /path/to/mqtt2elasticsearch/etc:/app/etc:ro
    depends_on:
      - opensearch-node1

Opensearch / Elasticsearch

Ich glaube, die Opensearch Datenbank zu betreiben ist das kompizierteste an allem, da die ein wenig „Liebe“ benötigt und genügend Ressourcen (RAM). 😀

Docker-Compose Beispiel:

services:
  opensearch-node1:
    restart: unless-stopped
    image: opensearchproject/opensearch:3.4.0
    container_name: opensearch-node1
    environment:
      node.name: opensearch-node1
      discovery.type: single-node
      discovery.seed_hosts: opensearch-node1
      OPENSEARCH_INITIAL_ADMIN_PASSWORD: FooBarBaz-123
      bootstrap.memory_lock: true  # along with the memlock settings below, disables swapping
      OPENSEARCH_JAVA_OPTS: -Xms512m -Xmx512m # minimum and maximum Java heap size, recommend setting both to 50% of system RAM
    ulimits:
      memlock:
        soft: -1
        hard: -1
      nofile:
        soft: 65536
        hard: 65536

Grafana

Nachdem nun alle Vorbereitungen erledigt sind, widmen wir uns der eigentlichen graifschen Auswertung. Hierfür verwende ich Grafana. Das Tool wird häufig auch im Unternehmensumfeld eingesetzt und kann zwischenzeitlich mit vielen Datenquellen interagieren.

Docker Compose Beispiel:

services:
  grafana:
    container_name: grafana
    image: grafana/grafana:12.3-ubuntu
    restart: unless-stopped
    ports:
      - 3000:3000

Nach dem Starten von Grafana, kann man sich mit dem Browser auf http://<server>:3000 verbinden und mit dem Benutzername admin und dem Passwort admin anmelden.

Zunächst brauchen richten wir die Datenquelle ein, also unsere Opensearch Datenbank. Hierfür gibt es im Grafana die Möglichkeit ein passendes Datenbank Plugin zu installieren. wir navigieren im Grafan im Menü zu „Connections“ > „Add new connection“. suchen dort nach „Opensearch“, wählen das Plugin aus und installieren dieses über den „Install“ Button.

Nach der Installation des Plugins können wir die Datenbankverbindung selbst anlegen. Hierzu klicken wir entweder auf den Button „Add a new datasource“ nach der Plugin Installation oder Navigieren im Menü zu „Connections“ > „Data sources“ und wählen dort „Add data source“ und wählen hier „Opensearch“. Hier machen wir folgende Konfigurationen:

Namedockerhub-repositories-metrics
HTTP: URLhttps://opensearch-node1:9200
Auth: Basic autheinschalten
Auth: With Credentialseinschalten
Auth: Skip TLS Verifyeinschalten
Auth: Basic Auth Details: Useradmin
Auth: Basic Auth Details: PasswordFooBarBaz-123
OpenSearch details: Index namedockerhub-repositories-metrics-*
OpenSearch details: Time field nametimestamp (ohne das @ Zeichen)
OpenSearch details: VersionAuf „Get Version and Save“ klicken sollte die Version entsprechend finden. (hier OpenSearch 3.4.0)
Grafana Konfiguration für Opensearch Datenquelle

Nach Eingabe der Daten, sollte über Klick auf „Save & test“ alles gün sein.

Danach richten wir das Dashboard ein. Hierzu navigieren wir im Grafana im Menü nach „Dashboards“ und Klicken hier auf „Create dashboard“. Nun haben wir die Wahl, ob wir komplett frisch anfangen wollen oder ob wir ein fertiges Dashboard importieren möchten. Das passende Dashboard habe ich bei Grafan hochgeladen und kann über ID: 24696 einfach importiert werden. Hier auch der Link zum Dashboard: https://grafana.com/grafana/dashboards/24696-docker-image-pulls/. Daher klicken wir im Bereich „Import a dashboard“ auf den Button „mport dashboard“.

Auf dem nächsten Screen können wir die Dashboard ID (24696) eingeben und per Klick auf „Load“ das Dashboard laden.

Auf dem daraufhin folgenden Screen müssen wir nur noch unsere vorher angelegte Datenquelle (dockerhub-repositories-metrics) auswählen und auf „Import“ klicken.

Dann sollte einem das Dashboard mit den Daten angezeigt werden. Sollten hier noch keine Daten auftauchen, kann man:

  1. das Tool dockerhubstats2mqtt einmal manuell aufrufen
  2. Den Zeitraum des Dashboards vom Default (30 Tage) auf zum Beispiel „Last 15 minutes“ einstellen.

Die gesamte Docker Compose Konfiguration

Da dies im Verlauf ein wenig zerstückelt war, hier nochmals die gesamt Docker Compose Konfiguration. Bitte beachtet, dass Ihr die beiden Konfigurationsdateien vom mqtt2ealsticsearch passend platzieren müsst und den Pfad bei „volumes“ dann richtig angeben müsst.

services:
  dockerhubstats2mqtt:
    restart: "no"
    user: 5241:5241
    image: oitc/dockerhubstats2mqtt:latest
    environment:
      MQTT_SERVER: test.mosquitto.org
      MQTT_PORT: 1883
      MQTT_TOPIC: com/docker/hub/repositories/metrics
      REPOSITORIES: oitc/dockerhubstats2mqtt oitc/mqtt2elasticsearch
      MQTT_RETAIN: true
      MQTT_TOPIC_REPO_EXTENSION: true

  mqtt2elasticsearch:
    restart: unless-stopped
    image: oitc/mqtt2elasticsearch:latest
    container_name: mqtt2elasticsearch
    volumes:
      - /path/to/mqtt2elasticsearch/etc:/app/etc:ro
    depends_on:
      - opensearch-node1

  opensearch-node1:
    restart: unless-stopped
    image: opensearchproject/opensearch:3.4.0
    container_name: opensearch-node1
    environment:
      node.name: opensearch-node1
      discovery.type: single-node
      discovery.seed_hosts: opensearch-node1
      OPENSEARCH_INITIAL_ADMIN_PASSWORD: FooBarBaz-123
      bootstrap.memory_lock: true  # along with the memlock settings below, disables swapping
      OPENSEARCH_JAVA_OPTS: -Xms512m -Xmx512m # minimum and maximum Java heap size, recommend setting both to 50% of system RAM
    ulimits:
      memlock:
        soft: -1
        hard: -1
      nofile:
        soft: 65536
        hard: 65536

  grafana:
    container_name: grafana
    image: grafana/grafana:12.3-ubuntu
    restart: unless-stopped
    ports:
      - 3000:3000

Hinweise

datenpersitenz

Dieses kleine Tutorial verwendet keinerlei Persistenz. D.b. wenn Ihr die Docker Container wegwerft oder erneuert, dann sind die gesammelten historischen Daten auch weg.

  • Bei Grafana sind die Daten im Container Image an der Stelle „/var/lib/grafana„.
  • Bei Opensearch sind die Daten im Container Image an der Stelle „/usr/share/opensearch/data

Passwörter und Zugänge

Das sind ganz einfache Beispiel, bitte verwendet richtige Passwörter. Im Unternehmenskontext sollte man auch nicht mit dem „admin“ user arbeiten sondern dedizierte Accounts anlegen die eben auch nur die Rechte haben, die benötigt werden.

TLS Zertifikate

Die Verbindungen sind alle unverschlüsselt bis auf die Verbindung zur Opensearch Datenbank. Und Opensearch verwendet ein selbst generiertes Zertifikat. In einer „echten“ Umgebung im Unternehmenskontext verwenden die Tools auch „echte“ Zertifikate um den Netzwerkverkehr abzusichern. Hier wird auch die TLS Validierung nicht deaktiviert.

Links

MCP2200 auf Ubuntu 24.04 LTS (Kernel 6.8)

Wie ich am 29. August geschrieben habe, hatte ich diverse Probleme mit „Ubuntu Server 24.04 LTS und HID Devices mit MCP2200 Chipsatz„.
Zwischenzeitlich war ich aber in der Lage das Problem weiter einzukreisen und zu Lösen.

Vermutung

Ende August gab es eine Erweiterung bei den Treibern im Linux Kernel. Folgende Diskussion habe ich hierzu gefunden: https://lore.kernel.org/lkml/202308180056.nB1KSUap-lkp@intel.com/T/.
Ende November ist es dann in den Quellcode eingeflossen: https://github.com/torvalds/linux/blob/master/drivers/hid/hid-mcp2200.c.

Diese Änderung floss dann vermutlich in Kernel 6.8, der mit Ubuntu 24.04 ausgeliefert wurde und dazu führte, dass das Relaisboard (USB Relay Module 4 Channels, for Home Automation – v2) der Firma Denkovi Assembly Electronics LTD, sobald as am USB Port angeschlossen wurde nun mit dem NEUEN Treiber versorgt wird und nicht mehr mit dem generischen HID Treiber.
Mit dem alten Treiber wurde im Devicetree von Linux das Gerät /dev/usb/hiddev0 angelegt. Das ist auch das Gerät, welches man in den Docker Container linken muss (--device=/dev/usb/hiddev0) damit das Relaisboard von dort aus angesprochen werden kann.

Das sieht man, wenn man sich den Ladevorgang via dmesg anschaut und vergleicht:

Ubuntu 22.04 dmesg

[ 2.437680] usb 1-2: new full-speed USB device number 2 using xhci_hcd
[ 2.591719] usb 1-2: New USB device found, idVendor=04d8, idProduct=00df, bcdDevice= 1.01
[ 2.591756] usb 1-2: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[ 2.591770] usb 1-2: Product: MCP2200 USB Serial Port Emulator
[ 2.591783] usb 1-2: Manufacturer: Microchip Technology Inc.
[ 2.591794] usb 1-2: SerialNumber: 0002460031
[ 2.884575] hid: raw HID events driver (C) Jiri Kosina
[ 2.889410] usbcore: registered new interface driver usbhid
[ 2.889425] usbhid: USB HID core driver
[ 2.893929] hid-generic 0003:04D8:00DF.0001: hiddev0,hidraw0: USB HID v1.11 Device [Microchip Technology Inc. MCP2200 USB Serial Port Emulator] on usb-0000:00:15.0-2/input2

Ubuntu 24.04 dmesg

[ 184.330464] usb 1-2: new full-speed USB device number 3 using ohci-pci
[ 184.857213] usb 1-2: New USB device found, idVendor=04d8, idProduct=00df, bcdDevice= 1.01
[ 184.857220] usb 1-2: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[ 184.857221] usb 1-2: Product: MCP2200 USB Serial Port Emulator
[ 184.857223] usb 1-2: Manufacturer: Microchip Technology Inc.
[ 184.857224] usb 1-2: SerialNumber: 0001693160
[ 184.877118] hid-generic 0003:04D8:00DF.0002: hiddev0,hidraw1: USB HID v1.11 Device [Microchip Technology Inc. MCP2200 USB Serial Port Emulator] on usb-0000:00:06.0-2/input2
[ 184.901800] mcp2200 0003:04D8:00DF.0002: USB HID v1.11 Device [Microchip Technology Inc. MCP2200 USB Serial Port Emulator] on usb-0000:00:06.0-2/input2
[ 184.906240] cdc_acm 1-2:1.0: ttyACM0: USB ACM device
[ 184.906268] usbcore: registered new interface driver cdc_acm
[ 184.906270] cdc_acm: USB Abstract Control Model driver for USB modems and ISDN adapters
[ 448.466402] usb 1-2: USB disconnect, device number 3
[ 457.088963] usb 1-2: new full-speed USB device number 4 using ohci-pci
[ 457.624849] usb 1-2: New USB device found, idVendor=04d8, idProduct=00df, bcdDevice= 1.01
[ 457.624855] usb 1-2: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[ 457.624857] usb 1-2: Product: MCP2200 USB Serial Port Emulator
[ 457.624858] usb 1-2: Manufacturer: Microchip Technology Inc.
[ 457.624860] usb 1-2: SerialNumber: 0001693160
[ 457.636478] cdc_acm 1-2:1.0: ttyACM0: USB ACM device
[ 457.661624] mcp2200 0003:04D8:00DF.0003: USB HID v1.11 Device [Microchip Technology Inc. MCP2200 USB Serial Port Emulator] on usb-0000:00:06.0-2/input2

Schnelle Lösung

Es mag hier vermutlich eine elegante Lösung geben, aber die quick ’n‘ dirty Variante ist, den MCP2200 Kernel Treiber zu deaktivieren.

Eine temporäre Aktivierung (zum Testen) macht man mit dem Befehl:

modprobe -r hid_mcp2200

Die permanente Deaktivierung erfolgt dann, indem man einen Blacklist Eintrag in der Datei: „/etc/modprobe.d/blacklist.conf“ vornimmt:

# The MCP2200 kernel driver doesn't support the "USB Relay Module 4 Channels,
# for Home Automation – v2" from "Denkovi Assembly Electronics LTD".
# if the /dev/usb/hiddev* device is missing, you need to deactivate the kernel
# driver to fallback to the generic HID kernel driver.
blacklist hid_mcp2200

ACS Containerisierung

Wir sind aktuell dabei unsere Fenster im Haus zu tauschen, was dazu führte, dass wir die Wand am eingangsbereich neu machen mussten (Holz weg, richtig dämmen, verputzen, streichen).
Da wir von Anfang an den RFID Leser im Eingangsbereich nur „provisorisch“ installiert haben, habe ich mir nun ein schickes Edelstahlgehäuse anfertigen lassen.

Lesereinbau vorher/nachher

Der Leser war wärend der Zeit der Renovierung leider offline.

Reboot mit Problemen

Der Server für das Zugangskontrollsystem ist das Letzte Artefakt, das ich noch nicht Containerisiert habe. Ich habe zwar dran gearbeitete, aber es gab hier noch diverse Probleme und die Integrationstests haben letztlich auch gefehlt. Ich wollte dies in aller Ruhe im sommerurlaub mal angehen. einen alten gebrauchten Testrechner habe ich dafür auch schon installiert gehabt, aber zu diesem Zeitpunkt war das System nicht einsatzbereit.

Auf dem alten Rechner den Docker zum Laufen bringen klappt nicht, da das darunterliegende OS (Ubuntu 16.04 LTS) zu alt war für die Java Requirements. D.b. ich muss den Server komplett neu aufsetzen und ein „Schwenk“ zu einem Container wäre nicht so schnell machbar gewesen. Wäre aber wünschenswert, dann hätte man den Container einfach hin und her schieben können um den Server in Ruhe auf den neuesten Stand updaten zu können.

Aber da der Rechner ja eh schon unten war (aufgrund der Renovierungsarbeiten) wollte ich zumindest mal ein OS Update fahren um die ganzen aktuell anstehenden Patches in Ubuntu 16.04 LTS nachzuziehen. Dabei kam dann der Rechner nach dem Reboot nicht wieder hoch. Scheinbar hat nach der langen Uptime der Rechner (den LESv2 der Thomas Krenn AG) den Reboot nicht verkraftet. Fakt ist, es kommt kein Bild, keine Ausgabe, nichts.

Tja, da half dann nur noch Flucht nach vorne. Ich habe den kleinen alten Testrechner ein Intel NUC) in den Serverschrank gepackt und frisch das OS draufgepackt. Erst ein Ubuntu Server 24.04 LTS, und, aufgrund von Betriebsproblemen (siehe unten), einen Tag später ein Ubuntu Server 22.04 LTS.
Danach binnen 2 Tagen den ACS-Server als Container so fertig bekommen, dass er läuft und alle Funktionalitäten wieder abdeckt (Mail, Pushover, MQTT, Relais ansteuern).
Zwischenzeitlich läuft der Server sogar nicht priviligiert als NonRoot im readonly Modus, das war schon seit Anfang an ein Problem, dass ich aber im Laufe der letzten Tage lösen konnte.

Ubuntu Server 24.04 LTS und HID Devices mit MCP2200 Chipsatz

Ubuntu Server 24.04 LTS ist das aktuelle Ubuntu Betriebssystem. Daher war es naheliegend dieses auch zu verwenden. Ich verwende für das Öffnen der Türe aktuell ein per USB angeschlossenes Relaisboard (USB Relay Module 4 Channels, for Home Automation – v2) der Firma Denkovi Assembly Electronics LTD. Das Board verwendet einen MCP2200 Chip und gibt sich als HiD Device aus. In der Regel wird im Linux unter den Devices dann ein /dev/hidraw0 und ein /dev/usb/hiddev0 angelegt. Leider funktionierte das nur bis Ubuntu Server 22.04 LTS. In Ubuntu Server 24.04 LTS hat sich das System strikt geweigert das Relais so anzulegen wie ich es erwartet hätte. Leider habe ich nach 1 Tag udev Regeln anlegen dann aufgegeben und bin auf Ubuntu Server 22.04 LTS gegangen.

Das Verhalten muss ich bei Zeiten mal genauer untersuchen bzw. wenn es mir hier nicht gelingt eine Lösung zu finden eine Relais Alternative zu suchen. Denkovi hat hier ja diverse alternative Möglichkeiten. Ggf. switche ich ja zu einer IP basierten Lösung.

ACS Komponenten im Container

Es laufen alle Komponenten des Zugangskontrollsystems nun auf einem Container. Damit werden Migrationsszenarien deutlich einfacher werden. Die Java Komponenten laufen in einem Debian Basiscontainer mit JRE21. Alle anderen Komponenten basieren auf schlanken Alpine Basiscontainern.

Alle Container sind mit Trivy sicherheitsüberprüft. Alle erkannten Vulnerabilities werden im Buildprozess dann entsprechend behandelt.

KomponenteBasisStand
ACS ServerDebian + JRE21Containerisiert
ACS ManagerAlpine + Nginx + PHP 8.1Containerisiert
ACS Tag ManagerDebian + JRE21Containerisiert
ACS Pushover ServiceAlpine + Python 3.11Containerisiert
REDISAlpineContainerisiert
LDAPAlpineContainerisiert
MQTTAlpineContainerisiert
ACS, Stand der Containerisierung

Offene Punkte

  1. Hausinterne DNS Probleme in den Griff bekommen: Entweder die Namensauflösung im ACS Server anpassen (herkömmlich statt den Ubunt Standard mit resolverd) oder, da es ein generelles Problem ist, hier mal eine echte Lösung finden.
  2. Erhöhung der RFID Leser Sensitivität, durch Durchbrechen des Faradayschen Käfigs. Leider schirmt der Edelstahlkasten die elektromagnetische Strahlung sehr ab. Ich versuche heute mla mit der Flex den Käfig mit einem Schnitt zu unterbrechen.
  3. RFID Leser mit Ubuntu Server 24.04 LTS ans Laufen bringen (siehe oben).

Code Refresh beim Zugangskontrollsystem und Containerisierung

Was in letzter Zeit passiert ist

Die letzten Wochen habe ich nun dazu genutzt das Zugangskontrollsystem etwas zu überarbeiten. Nicht die interne Codebasis, aber das drum herum.
Zum einen werden die Einzelteile immer mehr in Container Images (Docker) gepackt. Dies wird zukünftig dabei helfen, die Skalierbarkeit zu verbessern und auch die Ausfallsicherheit weiter zu erhöhen. Der Schritt ist noch nicht entgültig abgeschlossen aber ich bin auf einem guten Weg.

Dieser Schritt hat nun vorausgesetzt, die aktuelle FEIG SDK (Generation 2) (OBIDISC4J v5.6.3) zu verwenden. Leider haben wir ja gelernt, dass die aktuellste Generation 3 der SDK nicht mehr (oder auch noch nicht) verschlüsselt mit dem RFID Leser kommuniziert. Das folglich ein Ausschlußkriterium darstellt für den Betrieb als Zugangskontrollsystem.
Also bleiben wir vorerst auf der Generation 2 des SDKs.

Weiterhin setze ich nun Java 17 ein, da es im Docker Container doch deutlich besser performt und weniger Speicherprobleme hat als die älteren Java Versionen. Das hatte aber zur Folge, alle bislang vewendeten Java Extensions (javax Klassen) zu den Neuen (jakarta Klassen) zu migrieren (Danke Oracle).
Das war zum Glück nicht so aufwändig und konnte ohne größere Schwierigkeiten erledigt werden. Hier hing dann aber auch der gesamte Build-Prozess dran und da musste ich auch noch einiges gerade rücken. So ist das eben mit dem „Rattenschwanz“.
Vorteil ist, ich bin wieder up-to-date mit den Maven Repos und der Build-Prozess ist nun ausgelagert in eine Azure DevOps Pipeline.

Docker

Allgemein

Nicht alle Probleme kann man mit Docker lösen, aber es gibt uns durchaus Möglichkeiten einige der Probleme in den Griff zu bekommen.
Zum einen wird das „Shipping“ einfacher, da alles im besagen Container liegt was für die Laufzeit benötigt wird. Die FEIG Bibliotheken sind ja „closed source“ und die Java Libraries sind nur Wrapper rund um diese Linux Bibliotheken die im System zu installieren sind. Das macht es schwer die Software „mal eben schnell“ wo anders einzusetzen.
Zum anderen, wenn nun aber alles in handliche Päckchen (Container Images) gepackt wurde, kann man eben mal die Version tauschen und ratzfatz zurückrollen. Was Upgrades deutlich einfacher macht und auch wenn man mal die Hardware darunter tauschen möchte.

ACS Tag-Manager

Hier plane ich auch bereits seit längerem den ACS TAG Manager umzubauen. Diese Anwendung ist dafür verantwortlich, die RFID Tags zu beschreiben. Die Kernfunktionalität soll hier zukünftig als REST API in einem Docker Container laufen. Das Ganze mit API first Ansatz mt der aktuellen OpenAPI Spezifikation.
Das wird es deutlich einfacher machen, die Kommunikation zwischen Anwendung und RFID TAG zu abstrahieren.
Am Ende steht dann die Integration in den ACS-Manager. Diese Anwendung ist in PHP geschrieben und würde die REST API verwenden, um die Tags direkt aus der Management Anwendung heraus zu beschreiben.

Aber bis dahin ist es noch ein langer Weg. Kurzfristig muss nun die zentrale ACS-Server Komponente erfolgreich in den Docker Container gepackt werden, damit ist dann schon viel gewonnen.

Docker Basiscontainer

2021 hatte ich versucht mit Alpine Linux als Basiscontainer zu arbeiten. Damals noch mit OpenJRE 11. Dort scheiterte es dann erst an der FEIG Bibliothek v4 (Gen 2) und danach an einer nicht kompatiblen libc Bibliothek. Daher musste ich auf Debian als Basis ausweichen mit dem Upgrade auf die FEIG Bibliothek v6 (Gen 3).
Dann kam allerdings die Gen 3/Gen 2 Thematik und das Fehlen der Verschlüsselung was mich so frustrierte, dass ich dann zunächst nicht weiter daran gearbeitet habe.

Nun, 2 Jahre später, versuche ich mal einen Neuanfang. Zunächst auf Basis Debian. Problem ist hier aber, dass der Debian Container schon so viele ungepatchte Sicherheitslücken enthält, dass ich da ein ungutes Gefühl habe, dass so zu betreiben.
Allerdings darf man sich da nichts vor machen, denn wenn man Debian auch so als OS laufen hat, hat man ja auch die selben Lücken. Aber beim Containerbau hat man ja so schicke Tools, die einem das gleich zeigen. Bei einer Bare-Metal Installation fällt das ja erst mal nicht auf.

Wichtig ist aber erstmal, dass es überhaupt funktioniert, dann kann man sich um die Optimierung kümmern. Da fallen mir schon noch Dinge dazu ein. Man könnte z.B. die Teile löschen die eine Sicherheitslücke haben (soweit diese nicht benötigt werden) oder rman nähert sich doch von Alpine Linux und versucht die fehlenden Dinge zu integrieren.

Stand der Containerisierung

Die einzelnen Bestandteile des Zugangskontrollsystems haben folgenden Stand was die Containerisierung angeht:

KomponenteStand
ACS ServerContainerisiert (aber ungetestet)
ACS ManagerContainerisiert
ACS Tag Manageroffen
ACS Pushover ServiceContainerisiert
REDISContainerisiert
LDAPContainerisiert
MQTTContainerisiert
ACS, Stand der Containerisierung

Coderefresh

Seit dem letzten Firmwareupdate hat sich nicht viel getan. Seit dem sind 2,5 Jahre vergangen und das System leistet anstandslos seinen Dienst ohne jegliche Ausfälle.

Vor 3 Wochen habe ich begonnen den Quellcode nach Azure DevOps umzuziehen. Via Azure Pipelines werden nun die einzelenen Java Bibliotheken voll automatisiert gebaut.

Vor 2 Wochen habe ich angefangen mich nun endlich um das Management System zu kümmern. Dieses wird als PHP WebAnwendung umgesetzt und als Docker Container bereitgestellt. Mit start dieses Projekts kam nun auch die Idee, die Java Anwendungen als Docker Container zur Verfügung zu stellen. Die ersten Tests mit der FEIG Bibliothek OBIDISC4J 4.8.0 waren allerdings nicht erfolgreich. Bei start des Tagmanagers (zur Initialisierung der RFID Medien) beendet dieses sich nach dem Start direkt mit einem Coredump. Ein Ad-Hoc upgrade auf die Version 5.5.2 (die aktuelle Version) endet mit einer Fehlermeldung, dass die Feig Crypto Componente sich nicht initialisieren lässt. Daraufhin habe ich mich mal an den Support der Firma FEIG ELECTRONIC GmbH gewendet.

Die Firma arbeitet allerdings aktuell an einer neuen Generation der Java Bibliotheken und mir wurde empfohlen gleich auf die dritte Generation upzugraten, allerdings wird hier einiges an Codingaufwand auf mich zukommen. Der 18. Januar 2021 ist offizielles Releasedatum des ersten Release Candidate der neuen Java Bibliothek. Allerdings noch ohne Crypto Componente.

Ich habe mich dazu entschlossen das Angebot anzunehmen und die Anwendung(en) entsprechend umzuschreiben. Ich werde diese Gelegenheit nutzen um folgende Optimierungen durchzuführen:

  • Überführung der Konfiguration aus der Konfigurationsdatei in den LDAP
  • Dynamische Ermittlung der Zugangspunkte (aus dem LDAP) und erstellen der Listener, damit soll es möglich sein mehrere Leser mit einer Serveranwendung zu versorgen.

Vorbereitungen für den Coderefresh, wie ein Upgrade auf Java 11, das aktualisieren der anderen eingebundenen Bibliotheken und die eEinbindung der neuesten Maven Plugins, habe ich heute abgeschlossen.

Da ich jetzt wieder Betatester spiele, wird es wohl auch wieder mehr Updates hier im Blog geben.