ADMIN

2021

02

2021-02-01T12:00:00

Sichere Virtualisierung

PRAXIS

046

Datenbank

Sicherheit

Datenbanken mit immudb betreiben

Fälschungssicher

von Dennis Zimmer

Veröffentlicht in Ausgabe 02/2021 - PRAXIS

Um schützenswerte Informationen wie Kontonummern oder Ausweisdaten vor Manipulation zu bewahren, ist eine fälschungssichere Datenablage unabdingbar. In unserem Workshop stellen wir die Open-Source-Datenbank immudb vor, die Sensibles fälschungssicher abspeichert und dabei sehr leistungsstark und auch ohne Entwicklerkenntisse recht administrationsfreundlich ist.

Bei immudb handelt es sich um eine leistungsfähige, schlanke NoSQL-Datenbank, die mit jedem Eintrag zugleich eine kryptografisch abgesicherte Verifizierung aufzeichnet und damit die komplette Datenhistorie aufrechterhält. Die Software besitzt vier Hauptvorteile:
1. Die Daten sind unveränderlich: Es lassen sich nur Datensätze hinzufügen, niemals aber bestehende Datensätze ändern oder löschen. Die Datensatzhistorie bleibt zu jeder Zeit erhalten.
2. Gespeicherte Informationen sind kryptographisch kohärent und überprüfbar, wie aus der Blockchain-Technologie bekannt – allerdings ohne deren Komplexität und dabei hochperformant.
Bei immudb handelt es sich um eine leistungsfähige, schlanke NoSQL-Datenbank, die mit jedem Eintrag zugleich eine kryptografisch abgesicherte Verifizierung aufzeichnet und damit die komplette Datenhistorie aufrechterhält. Die Software besitzt vier Hauptvorteile:
1. Die Daten sind unveränderlich: Es lassen sich nur Datensätze hinzufügen, niemals aber bestehende Datensätze ändern oder löschen. Die Datensatzhistorie bleibt zu jeder Zeit erhalten.
2. Gespeicherte Informationen sind kryptographisch kohärent und überprüfbar, wie aus der Blockchain-Technologie bekannt – allerdings ohne deren Komplexität und dabei hochperformant.
3. Die Datenbank ist innerhalb von Minuten gestartet und betriebsbereit. Zudem stehen SDKs für node.js, Java, Python, Golang und .Net zur Verfügung. Mittels immugw lässt sich auch eine RESTful API nutzen.
4. immudb ist Open Source. Sie können es ohne Lizenzkosten on-premises oder auf Cloudplattformen betreiben. Die Datenbank unterliegt der Lizenz Apache 2.0 und ist auf GitHub gehostet.
Die Software ist unter Linux, FreeBSD, Windows sowie macOS getestet und unterstützt x86-, x64-, ARM-, s390x- und RISC-V-Architekturen. Um kryptografische Fälschungssicherheit zu gewährleisten, setzt sie auf eine Technologie, die von Blockchain und anderen verteilten Ledger-Systemen bekannt ist: den Merkle Tree.
Verkettete Hash-Werte
Ein Merkle Tree ist ein Baum aus Hash-Werten von Datenblöcken und dient dazu, die Integrität von Informationen sicherzustellen und diese kryptografisch überprüfbar zu machen. Jeder Dateneintrag führt automatisch zu einem neuen Root-Hash, der kryptografisch durch Hashes der Hashes gebildet wird und mit dem sich die komplette Datenkonsistenz verifizieren lässt. immudb nutzt den Merkle-Tree-Mechanismus, um für jeden Key/Value-Eintrag einen korrespondierenden Hash-(SHA-256)-Eintrag im Hash-Baum zu generieren. Die Daten und die Hash-Werte sind dabei unveränderbar miteinander verknüpft. Key-Value-Daten bestehen immer aus Schlüssel (Key) und Wert (Value) und die Datensuche findet auf Basis des Schlüssels statt.
Schauen wir uns dies exemplarisch auf der Zeitachse in Bild 1 von t0 nach t3 an, sind immer neue Einträge zu sehen, die den Root-Hash verändern. Dieser ist für jeden Zeitpunkt rot markiert. Der neue Eintrag und der neue Hash sind jeweils grün und die grauen Punkte stellen bestehende Hashes dar. Bei t0 führt ein erster Key-Value-Eintrag k0 in immudb zu einem einzelnen Hash (H0), der zugleich zum Root-Hash wird. Bei t1 gibt es einen zweiten Wert v1 desselben Keys, der zu einem zweiten Hash führt. Der Root-Hash berechnet sich aus dem Hash der beiden Hashes darunter. Zu diesem Zeitpunkt ist bereits ersichtlich, dass sich die einzelnen Daten nur dann manipulieren ließen, wenn sowohl der Daten-Hash (H0, H1) als auch der Root-Hash (H01) neu berechnet und ersetzt würde. Die Key-Historie ist also bereits intakt und beide Werte lassen sich mit dem Erstellungszeitpunkt auslesen und verifizieren.
Bild 1: Der Aufbau des Hash-Baums von immudb verändert sich über die Zeit hinweg.
Bei t2 wird dann ein neuer Key-Value-Eintrag k1 erstellt. Es bildet sich der erste Knoten (H01), vormals Root-Hash in t1, und ein neuer Root-Hash entsteht (H012). Bei t3 generiert sich durch eine weitere Wertanpassung (v3) von k0 ein zweiter Knoten (H23) und der Root-Hash wird aus H0123 gebildet. Zwei Dinge sind an unserem Beispiel bereits gut zu erkennen: Zum einen steigt der Aufwand, Daten zu fälschen, exponentiell mit der Anzahl der Dateneinträge. Zudem schützen nicht Server, sondern die Clients den Merkle Tree und können Manipulationen durch kryptografische Berechnungen selbst erkennen.
Denn während der Server den kompletten Hash-Baum und damit die Datenintegrität prüfen und Manipulationen feststellen kann, ist dieser nicht vor einem lokalen Datenaustausch der gesamten Datenbank inklusive des Hash-Baums geschützt. Der Client oder Auditor hat daher eine sehr wichtige Rolle, da er über zwei äußerst mächtige Methoden verfügt, um jegliche Fälschung oder Manipulation zu erkennen und bei Bedarf Alarm zu schlagen:
1. Signed Root: In diesem Fall wird auf dem Server ein Zertifikat abgelegt, mit dem der Server den aktuellen Root-Hash signiert. Dadurch kann der Client feststellen, ob der korrekte Server antwortet.
2. Speicherung des Root-Hash bei jeder Kommunikation: Dadurch kann die Verifizierung eines einzelnen Elements und die kryptografische Verifizierung des Merkle Tree als Ganzes erfolgen.
Übrigens ist ein Client jeder Prozess, der auf immudb zugreift – das heißt jedes SDK (verfügbar für Go, Java, Python, .net und Node.js) sowie der immuclient-Auditor.
Einsatz als primäre Datenbank
Die Anwendungsfälle für immudb sind vielfältig und lassen sich grob in primäre und sekundäre Datenbankzugriffe einteilen. Sehen wir uns zunächst den ersten Fall an, bei dem immudb als primärer und gegebenenfalls einziger Speicherort für Daten zum Einsatz kommt, die von der Key-Value-Datenstruktur profitieren. Die Nutzungsmöglichkeiten von reinen Key-Value-Daten sind deutlich höher als zunächst vermutet, da Sie damit etwa Kundeninformationen oder Transaktionsdaten abbilden können.
Für eine Kundendatenbank etwa entspricht der Schlüssel einer eindeutigen Kundennummer, nach der in der Anwendung auch gesucht wird. Der Wert hingegen ist die JSON-Struktur, die die Felder Vor- und Nachname, Adresse, Telefonnummer und vieles mehr beinhaltet. Analog dazu wäre bei einer Rechnungsablage der Schlüssel die eindeutige Rechnungsnummer, während der Wert wiederum eine JSON-Struktur ist, die Informationen zur Rechnung enthält sowie eine Base64-enkodierte PDF-Datei. Base64 bietet sich bei komplexen Datenstrukturen an, da Sie damit Probleme mit Sonderzeichen et cetera sauber umgehen. Die Beispielstruktur einer Rechnung entnehmen Sie dem Listing-Kasten.
Listing: Beispiel für Key-Value-basierte Rechnung
Key: INV001221 Value: {       "DueDate": "31.01.2021",       "Balance": 330,00,       "InvoiceNumber": "INV001221",       "Status": "Open",       "Customer": {                      "value": "CST002",                      "name": "Customer 2"       },       "AccountingReference": {                      "value": "0245557",                      "name": "Sales"       },       "Document": {                      "value": "<BASE64-PDF>",                      "name": "INV001221"       } }
Bei beiden Beispielen ist das Abspeichern in immudb wichtig, da sich so eine Veränderung des Schlüssels verhindern lässt und die Historie der Werte immer erhalten bleibt. Es wird stets ein neuer Eintrag mit dem neuen Wert und dessen kryptografischen Hashes zur Verifizierung geschrieben. Dies ermöglicht eine fälschungssichere Ablage der Datenhistorie.
Sekundäre Datenbank für relationale Daten
Benötigen Sie eine relationale Datenstruktur, bei der eine Suche über komplexere SQL-Abfragen stattfinden soll, wäre immudb eher als sekundäre Datenbank zu nutzen. Auch hierfür zeigt sich die Datenbank gut gerüstet, da sie über eine sehr gute Performance verfügt. Sie unterstützt selbst auf langsameren Systemen mehrere hunderttausend Transaktionen und erreicht in Laborumgebungen sogar bis zu zehn Millionen Transaktionen pro Server. Dabei lässt sie im Gegensatz zu Archivsystemen die Daten weiterhin aktiv im Zugriff.
Daher können Sie Änderungen in bestehenden Datenbanken mittels Change Data Capture – Anpassungen und Änderungen von Datenbanktabellen werden durch CDC für kurze Zeit mitprotokolliert – sehr gut in immudb dauerhaft und fälschungssicher protokollieren. Diese Daten sind jederzeit von Anwendungen für Vergleichsoperationen nutzbar, um zum Beispiel Anpassungen von Kontodaten innerhalb eines Zeitraums nachzuprüfen und gegebenenfalls Überweisungen zu stoppen. Ein weiterer typischer Fall ist die fälschungssichere Ablage von Syslogs und anderen Protokolldaten, um diese konform zum BSI-IT-Grundschutz abzulegen. Es gibt hier bereits einige Softwarewerkzeuge, die Syslogs, Dokumente, Change Data Capture und DevOps-Daten sammeln und in immudb ablegen.
Aufbau, Installation und erster Start
"immudb" ist die Server-Binärdatei, die standardmäßig auf Port 3322 erreichbar ist und eine gRPC-Schnittstelle (Remote Procedure Calls) bereitstellt. "immu­admin" fungiert als Admin-CLI für immudb. Es dient zur einfachen Verwaltung der Datenbanken sowie der Benutzer. Bei "immuclient" handelt es sich um die Client-CLI für immudb. Mit ihr können Sie Daten in immudb ohne Programmierkenntnisse schreiben und auslesen. Beide Komponenten sind im offiziellen GitHub-Repository [1] zu finden.
In diesem finden Sie regelmäßig neue Versionen der kompilierten Software. Beim Verfassen dieses Artikels stand immudb in Version 0.8 zum Download [2] bereit. Selbstverständlich können Sie immudb auf dem eigenen System auch mit Go selbst kompilieren. Sollten Sie Docker verwenden, laden Sie entweder die fertigen Images von Docker Hub [3] herunter oder erzeugen die Container-Images selbst. Beispielkommandos, um die Container selbst zu bauen, nachdem Sie vorher mit git clone das Repository heruntergeladen haben, könnten dann so aussehen:
docker build -t myown/immudb:latest -f Dockerfile .
 
docker build -t myown/immuadmin:latest -f Dockerfile.immuadmin .
Der Befehl immudb startet den Datenbankserver im Vordergrund, immudb -d im Hintergrund. Danach erreichen Sie den Dienst auf Port 3322. Die Konfiguration von immudb erfolgt entweder mit einer Konfigurationsdatei oder mittels Umgebungsvariablen. Sämtliche Konfigurationsparameter sehen Sie mit immudb -help ein. Die Onlinedokumentation ist unter [4] zu finden.
Bevor Sie immudb als Dienst installieren, stellen Sie sicher, dass Sie die Komponente "immudb" heruntergeladen haben, da diese sich selbst als Dienst unter Linux und Windows registrieren kann.
Für die Installation als Linux-Dienst geben Sie einfach sudo ./immudb service install ein. immudb wird dann automatisch als Dienst registriert und mit folgender Konfiguration gestartet:
- Benutzer: immu
- Gruppe: immu
- Konfiguration: /etc/immudb/immudb.toml
- Datenablage: /var/lib/immudb
- Protokolle: /var/log/immudb/immudb.log
- Port: TCP 3322
- Prometheus-Port (Monitoring): 9497
Für die Installation als Windows-Dienst führen Sie einfach immudb service install in einer Admin-Kommandozeile aus. Die Konfiguration ist dann in "C:\ProgramData\Immudb\config\immudb.toml" gespeichert und die Datenablage erfolgt in "C:\ProgramData\Immudb".
Liegt keine anderslautende Konfiguration vor, wird beim ersten Start von immudb der Benutzer "immudb" mit dem Passwort "immudb" als Admin angelegt. Dies können Sie mittels der Umgebungsvariable "IMMUDB_ADMIN_PASSWORD" vor dem allerersten Start von immudb anpassen. Falls Sie Docker verwenden und die Daten gleich dauerhaft speichern möchten, starten Sie den immudb-Server mit folgendem Befehl:
docker run -it -d -p 3322:3322 -p 9497:9497 -v immudb:/var/lib/immudb --env \
--name immudb codenotary/immudb:latest
Administration der Datenbank
Sobald immudb gestartet ist, nutzen Sie das immuadmin-Kommandozeilentool, um die Benutzer- oder die Datenbanken zu verwalten. immudb unterstützt aktuell drei verschiedene Rollen und kann mehrere tausend Datenbanken mit einer Instanz verwalten: Mit "read" lassen sich Daten lesen und verifizieren – die ideale Rolle für einen Auditor. Sie lässt sich für jede Datenbank einzeln vergeben. Mit "readwrite" sind Informationen les- und verifizierbar. Außerdem darf diese Rolle neue Daten schreiben. Auch sie lässt sich für jede Datenbank einzeln vergeben. "admin" schließlich ist die administrative Rolle mit Vollzugriff auf Benutzer- und Datenbankverwaltung.
Immuadmin verfügt über eine Benutzerhilfe, in der Sie mit immuadmin help alle nun folgenden Befehle nachschlagen können. Um immudb zu konfigurieren, muss sich immuadmin mit immuadmin login <Benutzername> zunächst anmelden. Der vollständige Vorgang sieht so aus:
immuadmin login immudb # <Initialer Benutzer>
Die erste Anmeldung verlangt nach einer Änderung des Passworts, falls Sie das initiale Kennwort nicht schon vorab geändert haben. Danach melden Sie sich noch einmal mit dem neuen Passwort an und können immudb nun administrieren – es sollte eine Bestätigung "logged in" auf dem Bildschirm erscheinen.
Um den Status des immudb-Servers zu prüfen, existieren die Befehle immuadmin status (Test, ob immudb auf dem Port antwortet) und immuadmin stats (visuelle Performancedarstellung). Benutzer erstellen Sie mit immuadmin user create <Benutzer> <Berechtigung> [<Datenbank>], beispielhaft also etwa
immuadmin user create auditor read
Bevor Sie eine neue Datenbank anlegen, sollten Sie mittels immuadmin database list erst überprüfen, welche Datenbanken bereits existieren. Danach legen Sie zum Beispiel mit immuadmin database create test eine Datenbank namens "test" sowie mit immuadmin user create testuser readwrite test einen eigenen Benutzer nur für diese Datenbank an.
Ein Tipp: Vor allem wenn Sie mit sehr vielen Datenbanken unter Linux arbeiten (Windows setzt die Grenze automatisch deutlich höher), sollten Sie die Limitierung der offenen Dateien beachten. Diese lässt sich allerdings leicht anpassen. Unter Ubuntu etwa editieren Sie die Datei "/etc/ systemd/system.conf". Kommentieren Sie "DefaultLimitNOFILE" aus, setzen Sie den Wert auf 65535 und speichern Sie die Datei. Vergessen Sie den nötigen Neustart nicht. Unter Windows müssen Sie wiederum den Speicherplatz beachten, da jede Datenbankdatei automatisch mit einer Größe von 2 GByte initialisiert wird.
Daten schreiben und lesen
Für Entwickler stehen SDKs für Go, Java, .Net, Python und Node.js zur Verfügung, um immudb in bestehende oder neue Anwendungen zu integrieren. Als Startpunkt bietet sich die Developer-Jumpstart-Dokumentation an [5]. Allerdings lässt sich immudb auch von Nicht-Entwicklern sehr einfach nutzen, um etwa Dokumentationen, öffentliche Zertifikate oder andere wichtige Informationen abzuspeichern, die entweder veränderbar sein sollen oder wo eine vollständige und unveränderbare Datenhistorie ein Muss ist. Hierzu bietet sich das Kommandozeilenprogramm immuclient an.
Um Daten in immudb zu schreiben, bietet immuclient zwei verschiedene Möglichkeiten an: direkte Kommandos und eine interaktive Shell, die automatisch startet, wenn keine Parameter mitgegeben werden. In den folgenden Beispielen nutzen wir die direkten Kommandos, da sich diese auch in Skripte integrieren lassen.
In jedem Fall müssen Sie sich mit immuclient login immudb zuerst anmelden. Die Befehlsreferenz lautet immuclient login <Benutzer> und falls immudb auf einem anderen System läuft immuclient -a <immudb-server-ip> login <Benutzer>. Sind mehrere Datenbanken angelegt, müssen Sie diese mit immuclient use <Datenbank> initial angeben.
Ist all dies erledigt, dienen die beiden Befehle set und safeset zum Schreiben der Key-Value-Daten, wobei safeset zusätzlich die Integrität von immudb prüft. Daher ist es langsamer, findet mit immuclient safeset <key> <value> aber in der Regel nur sporadisch Verwendung. Ein einfacher Wert ließe sich also zum Beispiel so schreiben:
immuclient safeset ip-SAP-prod01 192.168.10.150
Wie Bild 2 zeigt, weist jeder Eintrag einen Index, einen Zeitstempel und ein Hash auf. Geben Sie jetzt immuclient current ein und veranlassen so die Ausgabe des aktuellen Merkle-Tree-Root-Hash, erhalten Sie die gleichen Hash- und Index-Angaben, da beim ersten Eintrag Root-Hash und Key-Value-Hash übereinstimmen.
Bild 2: Das Schreiben eines Werts mittels safeset bewirkt automatisch eine Ausgabe mit wichtigen Parametern.
Als Nächstes speichern wir ein Dockerfile in immudb. Damit wir uns Probleme mit Sonderzeichen ersparen, werden wir den Dateinamen als Key nutzen und als Wert den Base64-kodierten Dateiinhalt. Die PowerShell macht uns das Leben hier deutlich einfacher:
$b64 = [Convert]::ToBase64String ([System.Text.Encoding]::UTF8.GetBytes((get-content .\Dockerfile -Encoding UTF8 -Raw)))
 
.\immuclient.exe set Dockerfile $b64
Der Zusatz "-Encoding UTF8 -Raw" ist wichtig, damit die neuen Zeilen sauber übernommen werden. Der Index erhöht sich dann um eine Stelle. Der Root-Hash ist nun der Hash der Hashes von Index 0 und Index 1.
Bild 3: Der gewünschte Wert konnte ausgelesen und verifiziert werden.
Das Auslesen der Daten ist mit immuclient ebenfalls sehr einfach möglich, entweder mit get oder safeget, wobei auch hier safeget zusätzlich die Prüfung der Integrität der immudb übernimmt. immuclient safeget ip-SAP-prod01 etwa liest den Key "ip-SAP-prod01"W aus, zeigt die IP-Adresse im Wert an und prüft, ob sich der Wert kryptografisch verifizieren lässt.
Um unseren Base64-Wert der Dockerfile-Datei wieder im Klartext darzustellen, ist folgender Befehl in der PowerShell notwendig:
$entry=.\immuclient.exe get Dockerfile --value-only
[System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($entry))
Link-Codes
[3] Docker-Image für immudb: https://hub.docker.com/r/codenotary/immudb
[4] Onlinedokumentation für immudb: https://docs.immudb.io/
[5] Developer Jumpstart für immudb: https://docs.immudb.io/0.9.0/#further-readings
[6] Grafana-Dashboard für immudb: https://grafana.com/grafana/dashboards/12026/
Auditor und Monitoring
Der Auditor ist eine wichtige Komponente zur Absicherung eines immudb-Servers, da er auch eine physische Manipulation der Datenbanken erkennen kann. Daher sollten Sie ihn nicht auf dem gleichen System wie den immudb-Server betreiben.
Je mehr Clients und Auditoren auf einen immudb-Server zugreifen, desto besser ist dieser abgesichert, da ein Angreifer neben dem Server auch alle Clients zur gleichen Zeit manipulieren müsste. Den Auditor installieren Sie sehr einfach unter Linux oder Windows (als Administrator):
immuclient audit-mode install
     --audit-username <Benutzername>
     --audit-password <Passwort>
     -a <immudb-Server-Adresse>
Wir haben bereits den Port 9497 erwähnt, der die Performancemetriken des immudb-Servers für Prometheus bereitstellt. Um einen bestehenden Prometheus-Server zur Abfrage von immudb zu konfigurieren, integrieren Sie folgenden Job in die Konfigurationsdatei:
- job_name: 'immudbmetrics'
    scrape_interval: 60s
    static_configs:
    - targets: ['my-immudb-server:9497']
Das dazugehörige Dashboard für Grafana finden Sie unter [6].
Fazit
WORM-Medien, Hash-Ketten und andere Archivlösungen sind eher für die nachträgliche Forensik gedacht, wenn bereits ein Datenklau erfolgt ist. Eine verteilte Blockchain ist zwar eine spannende Technologie, allerdings recht langsam und äußerst komplex. Mit immudb ist eine Open-Source-Datenbank verfügbar, die sich in bestehende oder neue Projekte und Produkte integrieren lässt, um eine fälschungssichere Datenablage inklusive kompletter Datenhistorie zu realisieren.
 (ln)