7 Minuten Lesezeit (1354 Worte)

„Hör gut zu!“. PostgreSQL Auditing mit PGAudit

An vielen Stellen müssen Zugriffe auf Daten bzw. Datenbanken auditiert werden. Die Open Source Datenbank PostgreSQL bietet für diesen Zweck die Erweiterung (Extension) PGAudit. Sie ermöglicht es Session- und / oder Objekt-basiert solche Informationen zu erheben. Im Folgenden möchten wir Ihnen die Installation und Funktionsweise dieser Erweiterung kurz vorstellen.

„Achtung Aufnahme!"

Bevor mit der Erhebung von Auditdaten begonnen werden kann, muss die Erweiterung installiert und konfiguriert werden. Auf einem Ubuntu-Server haben wir das aktuelle PostgreSQL-Repository (Version 14) eingebunden und den Cluster bereits installiert und gestartet. Lediglich die PGAudit-Erweiterung ist noch nicht vorhanden. Die Nachinstallation der Extension ist an sich jedoch recht einfach:

root@pg14:/# apt install postgresql-14-pgaudit
Reading package lists... Done
Building dependency tree
…
0 upgraded, 1 newly installed, 0 to remove and 0 not upgraded.
 

Nachdem die Extension nun verfügbar ist, muss sie in den Cluster eingebunden und dieser neu gestartet werden. Dazu muss PGAudit als zu ladende Bibliothek über die zentrale Konfigurationsdatei „postgresql.conf" spezifiziert werden. 

postgres@pg14:~/14/pgdata$ cat postgresql.conf | grep shared_preload
shared_preload_libraries = 'pgaudit'	# (change requires restart)
 

Im Vorfeld wurde bereits eine Test-Datenbank mit „pgbench" erstellt, anhand derer die folgenden Schritte verdeutlicht werden sollen: 

postgres@pg14:~/14/pgdata$ pgbench -i sampledb
dropping old tables...
NOTICE:  table "pgbench_accounts" does not exist, skipping
NOTICE:  table "pgbench_branches" does not exist, skipping
NOTICE:  table "pgbench_history" does not exist, skipping
NOTICE:  table "pgbench_tellers" does not exist, skipping
creating tables...
generating data (client-side)...
100000 of 100000 tuples (100%) done (elapsed 0.21 s, remaining 0.00 s)
vacuuming...
creating primary keys...
done in 0.48 s (drop tables 0.01 s, create tables 0.02 s, client-side generate 0.30 s, vacuum 0.08 s, primary keys 0.08 s).
 

Zusätzlich wurden im Nachgang zwei User angelegt, die nur lesende Rechte (SampleRO) und sowie schreibende Rechte (SampleRW) auf diese vier Tabellen haben. 

„Aufnahme läuft?!"

Um das Auditing innerhalb unserer Datenbank nutzen zu können, muss die Extension dort erzeugt (besser eingebunden) werden: 

postgres@pg14:~/14/pgdata$ psql -d sampledb
psql (14.2 (Ubuntu 14.2-1.pgdg20.04+1))
Type "help" for help.

sampledb=# create extension pgaudit;
CREATE EXTENSION

sampledb=# select extname, extversion from pg_extension;
 extname | extversion
---------+------------
 plpgsql | 1.0
 pgaudit | 1.6.1
(2 rows)
 
Im ersten Schritt aktivieren wir das Auditing Session-spezifisch und so zwar so, dass alle schreibenden Aktionen protokolliert werden sollen. Der Wert „write" beinhaltet, die folgenden SQL-Kommandos:
  • INSERT
  • UPDATE
  • DELETE
  • TRUNCATE
  • COPY

Eine vollständige Übersicht dieses und anderer PGAudit Parameter ist in der entsprechenden Dokumentation zu finden:
https://github.com/pgaudit/pgaudit/blob/master/README.md

sampledb=# set pgaudit.log = 'write';
sampledb=# \c sampledb SampleRW
You are now connected to database "sampledb" as user "SampleRW".
sampledb=> insert into pgbench_accounts values ( 100001, 1, 0);
INSERT 0 1
 

Dieses Kommando (INSERT) führt konsequenterweise zu einem Eintrag im Logfile (Parameter: log_filename) von PostgreSQL 

postgres@pg14:~$ tail -f /var/lib/postgresql/logfile &
2022-02-25 09:40:11.447 CET [3004] LOG:  AUDIT: SESSION,2,1,WRITE,INSERT,,,"insert into pgbench_accounts values ( 100001, 1, 0);",<not logged>
 
Der Log-Eintrag setzt sich dabei (in der Reihenfolge) aus den folgenden Komponenten zusammen:

  1. AUDIT_TYPE "SESSION" oder "OBJECT"
  2. STATEMENT_ID Jedes Statement verfügt über eine eindeutige ID
  3. SUBSTATEMENT_ID ID des verschalteten Kommandos (z.B. bei Funktionen, Prozeduren)
  4. CLASS z.B. „WRITE" (wie in unserem Beispiel)
  5. COMMAND z.B. „INSERT" als Element der Klasse (CLASS) „WRITE"
  6. OBJECT_TYPE z.B. „TABLE" oder "VIEW" (AUDIT_TYPE = OBJECT)
  7. OBJECT_NAME Der Name des Objektes (AUDIT_TYPE = OBJECT)
  8. STATEMENT Das ausgeführte Kommando
  9. PARAMETER Übergebenes Parameter (z.B. bei Prozeduren) können optional als CSV-Liste mitprotokolliert werden

Auffällig ist, dass der verursachende User (Session) nicht augenfällig im Protokoll erscheint. Dies kann über den Parameter „log_line_prefix" optimiert werden. Wir setzten ihn in der Konfiguration wie folgt:

log_line_prefix = '%m %u [%p] '

Neben dem Zeitstempel (%m) wird der Benutzername (%u) und seine Prozess-ID (%p) nun in das Logfile geschrieben. Der obenstehende Log-Eintrag verändert sich wie folgt:

2022-02-25 10:25:42.240 CET SampleRW [3310]LOG:  AUDIT: SESSION,1,1,WRITE,INSERT,,,"insert into pgbench_accounts values ( 100001, 1, 0);",<not logged> 

Um noch mehr Übersicht zu haben, möchten wir die Audit-Informationen in eine separate Datei schreiben. Dazu installieren (1) wir eine weitere Extension „pgauditlogtofile", laden diese über die „postgresql.conf" (2) und konfigurieren (3) sie in der Datenbank.

#1
root@pg14:/pgaudit# apt install postgresql-14-pgauditlogtofile

#2
postgres@pg14:~/14/log$ cat /var/lib/postgresql/14/pgdata/postgresql.conf | grep preload_lib
shared_preload_libraries = 'pgaudit,pgauditlogtofile'	# (change requires restart)

#3 
You are now connected to database "sampledb" as user "postgres".
sampledb=# create extension pgauditlogtofile;

postgres=# select name, setting from pg_settings where name like 'pgaudit%';
            name            |          setting
----------------------------+----------------------------
 pgaudit.log                | write
 pgaudit.log_catalog        | on
 pgaudit.log_client         | off
 pgaudit.log_directory      | /var/lib/postgresql/14/log
 pgaudit.log_filename       | audit-%Y%m%d_%H%M.log
 pgaudit.log_level          | log
 pgaudit.log_parameter      | off
 pgaudit.log_relation       | off
 pgaudit.log_rotation_age   | 1440
 pgaudit.log_rows           | off
 pgaudit.log_statement      | on
 pgaudit.log_statement_once | off
 pgaudit.role               |
(13 rows)
 

Zuhören ist eine Kunst! 

Okay, generell sind wir in der Lage zu auditieren und ein paar Konfigurationen vorzunehmen (z.B. über pgaudit.log). In der Praxis wollen wir aber sehr zielgerichtet „hinhören". In unserem Datenmodell gibt es ggfs. ein paar Tabellen mit vertraulichen Daten, die in wenigen Spalten verwahrt werden. Und genau hier lohnt es sich für uns, ganz genau zu protokollieren. Aus diesem Grund schalten wir das Session-spezifische Auditing ab und konzentrieren uns auf bestimmte Objekte.

sampledb=# alter system set pgaudit.log = 'NONE';
ALTER SYSTEM
sampledb=# alter system set pgaudit.log_catalog = off;
ALTER SYSTEM
 

Nachdem wir das Auditing für die Sessions generell deaktiviert haben, richten wir nun die Objekt-basierte Auditierung ein. Dazu definieren wir uns eine Rolle, welche unsere Audit-Regeln sammeln, bzw. umsetzen soll. Diese Rolle wird dann als Regelwert der Erweiterung PGAudit übergeben: 

sampledb=# create role auditor;
CREATE ROLE
GRANT
sampledb=# set pgaudit.role='auditor';
SET
 

Im ersten Schritt möchten wir nur eine einzige Spalte („abalance") in der Tabelle „pgbench_accounts" auf Zugriffe „SELECTs" überwachen. Dazu formulieren wir unsere Audit-Regel als „GRANT"-Anweisung.

sampledb=# grant select (abalance) on public.pgbench_accounts to auditor;

Nun führen wir mit einem unserer User (SampleRO) ein paar lesende Zugriffe auf die überwachte Tabelle durch:

sampledb=> select * from pgbench_accounts where aid <= 1 /* TEST 1 */;
 aid | bid | abalance |                                        filler
-----+-----+----------+--------------------------------------------------------------------------------------
   1 |   1 |        0 |
(1 row)

sampledb=> select aid, bid from pgbench_accounts where aid <= 1 /* TEST 2 */;
 aid | bid
-----+-----
   1 |   1
(1 row)

sampledb=> select abalance from pgbench_accounts where aid <= 1 /* TEST 3 */;
 abalance
----------
        0
(1 row)
 

Das Ergebnis ist erwartungsgemäß. Statement „1" und „3" haben auf die Spalte „abalance" zugegriffen. Exakt diesen beiden Statements sind daher auch im Audit-Protokoll zu finden. 

2022-02-25 11:07:15.837 CET,SampleRW,sampledb,3523,[local],6218a9d3.dc3,6,SELECT,2022-02-25 11:05:07 CET,3/974,0,00000,OBJECT,6,1,READ,SELECT,TABLE,public.pgbench_accounts,select * from pgbench_accounts where aid <= 1 /* TEST 1 */;,<not logged>,,,,,,,,,psql

2022-02-25 11:07:39.660 CET,SampleRW,sampledb,3523,[local],6218a9d3.dc3,7,SELECT,2022-02-25 11:05:07 CET,3/976,0,00000,OBJECT,7,1,READ,SELECT,TABLE,public.pgbench_accounts,select abalance from pgbench_accounts where aid <= 1 /* TEST 3 */;,<not logged>,,,,,,,,,psql
 

Der User "SampleRW" verfügt über Schreibrechte auf alle Tabellen. Wir starten daher einen weiteren Test mit einem „INSERT". 

sampledb=> insert into pgbench_accounts (aid, bid) values ( 100009 , 1) /* Test 4 */;
INSERT 0 1 

Im Logfile ist kein Eintrag zu finden. Das macht Sinn, da es ja noch keine Regel zur Überwachung von „INSERTs" auf dieser Tabelle gibt. Legen wird diese nun an und wiederholen den Test, ändert sich natürlich das Ergebnis. 

sampledb=# grant insert  on public.pgbench_accounts to auditor;
GRANT

sampledb=> insert into pgbench_accounts (aid, bid) values ( 100010 , 1) /* Test 5 */;
INSERT 0 1

postgres@pg14:~/14/log$ tail -f audit-20220225_0000.log
…
2022-02-25 11:25:01.651 CET,SampleRO,sampledb,3585,[local],6218ae79.e01,1,SELECT,2022-02-25 11:24:57 CET,3/987,0,00000,OBJECT,1,1,READ,SELECT,TABLE,public.pgbench_accounts,select abalance from pgbench_accounts where aid <= 1 /* TEST 3 */;,<not logged>,,,,,,,,,psql
2022-02-25 11:30:35.042 CET,SampleRW,sampledb,3597,[local],6218af44.e0d,1,INSERT,2022-02-25 11:28:20 CET,3/996,0,00000,OBJECT,1,1,WRITE,INSERT,TABLE,public.pgbench_accounts,"insert into pgbench_accounts (aid, bid) values ( 100010 , 1) /* Test 5 */;",<not logged>,,,,,,,,,psql
 

Fazit: Nun höre und lerne.

Die Einrichtung eines Auditings ist in PostgreSQL kein Hexenwerk. Die Erweiterungen lassen sich meist schnell und problemlos konfigurieren. Lediglich die Konfiguration ist im ersten Augenblick nicht immer gleich verständlich. Die Parameter haben nicht immer die treffendsten Namen und können an der einen oder anderen Stelle leicht zur Verwirrung beitragen. Hier hilft es, sich einfach ein paar Testszenarien auszudenken und auszuprobieren. So kommt schnell Licht ins Dunkel.

Sie haben Fragen rund um den Betrieb von PostgreSQL? Dann sprechen Sie uns an. 

Seminarempfehlung

Principal Consultant bei ORDIX

 

Kommentare

Derzeit gibt es keine Kommentare. Schreibe den ersten Kommentar!
Samstag, 27. Juli 2024

Sicherheitscode (Captcha)

×
Informiert bleiben!

Bei Updates im Blog, informieren wir per E-Mail.

Weitere Artikel in der Kategorie