Ein Lese-Zugang für BI-Tools, Reporting-Skripte oder Read-Replicas gehört zu den häufigsten Aufgaben in der DB-Administration. PostgreSQL bietet zwei sinnvolle Wege: kurz mit pg_read_all_data oder explizit per GRANT. Welcher passt, hängt davon ab, wie viel Kontrolle du über die Schemas brauchst.
Variante 1 — pg_read_all_data (PG 14+)
Die kurze Variante:
CREATE ROLE reporting LOGIN PASSWORD 'changeme'
CONNECTION LIMIT 5;
GRANT pg_read_all_data TO reporting;
GRANT CONNECT ON DATABASE myapp TO reporting;pg_read_all_data ist eine Predefined Role, die SELECT auf alle Tabellen, Views, Sequenzen und Materialized Views in allen Schemas der Datenbank umfasst — auch zukünftige.
Vorteile:
- Wirklich nur drei Zeilen.
- Keine
ALTER DEFAULT PRIVILEGESnötig. - Funktioniert auch in zukünftigen Schemas.
Nachteile:
- Wirklich alle Schemas. Wenn morgen jemand ein
internal_admin-Schema mit Sensitive-Daten anlegt, kann der Reporting-User auch das lesen. - Funktioniert erst ab PG 14.
Faustregel: für interne Datenbanken mit klar getrennter Funktion (Webapp-DB, Analytics-DB) — gut. Für Multi-Tenant-Setups oder Datenbanken mit gemischtem Inhalt — lieber Variante 2.
Variante 2 — Explizite GRANTs
-- 1. Login-Rolle anlegen
CREATE ROLE reporting LOGIN PASSWORD 'changeme'
CONNECTION LIMIT 5;
GRANT CONNECT ON DATABASE myapp TO reporting;
-- 2. Pro Schema, das gelesen werden soll:
GRANT USAGE ON SCHEMA app TO reporting;
GRANT SELECT ON ALL TABLES IN SCHEMA app TO reporting;
GRANT SELECT ON ALL SEQUENCES IN SCHEMA app TO reporting;
-- 3. Default-Privileges fuer ZUKUENFTIGE Objekte:
ALTER DEFAULT PRIVILEGES FOR ROLE app_owner IN SCHEMA app
GRANT SELECT ON TABLES TO reporting;
ALTER DEFAULT PRIVILEGES FOR ROLE app_owner IN SCHEMA app
GRANT SELECT ON SEQUENCES TO reporting;
-- 4. Search-path fuer den User
ALTER ROLE reporting SET search_path = app, public;Wichtig: das FOR ROLE app_owner muss zur Rolle passen, die deine Migrationen ausführt. Wenn migrator neue Tabellen anlegt, brauchst du einen zusätzlichen Eintrag ALTER DEFAULT PRIVILEGES FOR ROLE migrator IN SCHEMA app GRANT SELECT ON TABLES TO reporting;. Sonst greifen die Defaults nicht (siehe Default Privileges).
Welche Variante wann?
| Situation | Empfehlung |
|---|---|
| Einfache App-DB, ein Schema, PG 14+ | Variante 1 (pg_read_all_data) |
| Multi-Tenant-DB, Schema-pro-Tenant | Variante 2 — explizit pro Tenant-Schema |
| DB mit gemischten Sensitivitäts-Levels | Variante 2 |
| Read-Replica für Drittanbieter-Tools | Variante 2 mit eingeschränkten Schemas |
| Quick-and-dirty für lokales Tooling | Variante 1 |
| PG 13 oder älter | Variante 2 (Variante 1 nicht verfügbar) |
Verifikation
Nach dem Anlegen prüfen, dass der User wirklich nur lesen kann:
$ psql -h localhost -U reporting -d myapp
Password for user reporting:myapp=> SELECT count(*) FROM orders;
count
-------
42
myapp=> INSERT INTO orders (customer_id, total) VALUES (1, 100);
ERROR: permission denied for table orders
myapp=> DROP TABLE orders;
ERROR: must be owner of table ordersSchreibversuche und DDL werden sauber abgewiesen.
Sicherheits-Erweiterungen
default_transaction_read_only
Zusätzlich pro Rolle setzbar:
ALTER ROLE reporting SET default_transaction_read_only = on;Damit beginnt jede Transaktion implizit als BEGIN READ ONLY. Falls reporting doch mal versehentlich Schreib-Rechte bekommt, können trotzdem keine Schreibvorgänge stattfinden — Defense in Depth.
statement_timeout
Reporting-Queries sind oft groß. Damit ein versehentlicher Cross-Join nicht den ganzen Server blockiert:
ALTER ROLE reporting SET statement_timeout = '5min';Queries, die länger als 5 Minuten laufen, werden automatisch abgebrochen.
idle_in_transaction_session_timeout
Verhindert, dass eine BI-Tool-Session ihre Transaktion stundenlang offen lässt und dadurch VACUUM blockiert:
ALTER ROLE reporting SET idle_in_transaction_session_timeout = '5min';FAQ
Sieht der Read-Only-User auch System-Tabellen?
Ja — pg_catalog-Tabellen sind grundsätzlich von jedem lesbar (das ist Postgres-Architektur). Mit pg_read_all_data kommen auch sonst eingeschränkte pg_stat_*-Views dazu. Wer das nicht will, sollte stattdessen Variante 2 mit explizit aufgezählten Schemas nutzen.
Was ist mit Functions — darf der User die ausfuehren?
pg_read_all_data umfasst kein EXECUTE auf Funktionen. In Variante 2 musst du explizit GRANT EXECUTE ON FUNCTION … TO reporting setzen, falls die Reporting-Tools das brauchen. Klassisch nicht nötig, aber bei BI-Tools wie Metabase, die manchmal Helper-Functions nutzen, kann es relevant werden.
Wie blockiere ich, dass der User mit pg_dump die ganze DB rauszieht?
Gar nicht zuverlässig. Wer SELECT auf alles hat, kann die Daten in irgendeiner Form exportieren — über pg_dump, eigene Scripts, Copy. Wenn das ein Problem ist, hilft nur Daten-Minimierung: Views, die nur die nötigen Spalten/Zeilen exponieren, und SELECT-Privilegien auf diese Views statt auf die Roh-Tabellen.
Wie verteile ich das Passwort an mehrere BI-Tools?
Nicht — sondern lieber pro Tool einen eigenen User anlegen, alle Mitglied einer NOLOGIN-Gruppe reporting_group. Damit kannst du Einzelne deaktivieren oder rotieren, ohne andere zu treffen. Im Zweifel sogar pro Mensch ein eigener User mit GRANT reporting_group TO ….
Reicht das fuer einen pg_dump-Backup-User?
Fast — aber pg_dump braucht zusätzlich Privilegien auf manche Sequenzen und ggf. auf Views, die er logisch rekonstruieren will. Für reine Backups ist ein dedizierter Backup-User mit pg_read_all_data plus REPLICATION der saubere Weg, siehe Backup-Benutzer.
Weiterführende Ressourcen
Externe Quellen
- Predefined Roles – PostgreSQL Documentation
- GRANT – PostgreSQL Documentation
- ALTER DEFAULT PRIVILEGES
- default_transaction_read_only – Run-time Configuration
- statement_timeout – Run-time Configuration