PostgreSQL behandelt Benutzer und Gruppen einheitlich — es gibt nur Rollen. Eine Rolle kann sich anmelden (dann ist sie ein „User“), Mitglieder enthalten (dann ist sie eine „Group“) oder beides gleichzeitig. Wer das verinnerlicht hat, versteht die meisten anderen Sachen rund um Berechtigungen sehr schnell.
Eine Rolle, zwei Hüte
Klassisch trennen Datenbanken zwischen Users (jemand, der sich einloggt) und Groups (eine Sammlung von Users, an die Rechte vergeben werden). Postgres hat das früher auch getan — und 2007 mit der Einführung von „Rollen“ zusammengelegt.
In Postgres gilt:
- Eine Rolle ist die Einheit, an die Rechte vergeben werden.
- Eine Rolle mit dem Attribut
LOGINdarf sich anmelden — das ist, was klassisch ein „User“ wäre. - Eine Rolle ohne
LOGIN(NOLOGIN) ist ein reiner Container — das ist, was klassisch eine „Group“ wäre. - Rollen können andere Rollen als Mitglieder haben (Membership). Rechte fließen über die Mitgliedschaft.
Daraus ergibt sich, was Postgres an Befehlen anbietet:
| Befehl | Was er tut |
|---|---|
CREATE ROLE name | Erstellt eine Rolle. Default: NOLOGIN. |
CREATE USER name | Alias für CREATE ROLE name LOGIN. |
CREATE GROUP name | Alias für CREATE ROLE name NOLOGIN (Legacy, selten genutzt). |
Es gibt also nur einen Befehl mit Substanz — CREATE ROLE. Die anderen sind reine Bequemlichkeits-Aliasse.
Erstes Beispiel
-- Eine reine Container-Rolle (Gruppe):
CREATE ROLE devs;
-- Eine Login-Rolle (User):
CREATE ROLE alice WITH LOGIN PASSWORD 'changeme';
-- Identisch zum vorherigen, nur kuerzer:
CREATE USER bob WITH PASSWORD 'changeme';
-- Bob und Alice in die devs-Gruppe aufnehmen:
GRANT devs TO alice, bob;Was hier passiert ist:
devsist eine Rolle ohne Login. Niemand kann sich als „devs“ anmelden.aliceundbobsind Login-Rollen. Sie können sich verbinden.GRANT devs TO alice, bobmacht beide zu Mitgliedern der Rolledevs— alle Rechte, diedevsjetzt oder später bekommt, gelten automatisch für die Mitglieder.
Anzeigen lässt sich das mit \du in psql:
myapp=# \du
List of roles
Role name | Attributes | Member of
-----------+------------------------------------------------------------+-----------
alice | | {devs}
bob | | {devs}
devs | Cannot login | {}
postgres | Superuser, Create role, Create DB, Replication, Bypass RLS | {}Was sind Privilegien?
Eine Rolle existiert isoliert. Damit sie etwas tun kann, brauchen wir Privilegien auf konkrete Datenbankobjekte. Postgres kennt einen festen Katalog davon — die wichtigsten:
| Objekt | Mögliche Privilegien |
|---|---|
| Tabelle / View | SELECT, INSERT, UPDATE, DELETE, TRUNCATE, REFERENCES, TRIGGER |
| Spalte | SELECT, INSERT, UPDATE, REFERENCES (granular pro Spalte) |
| Sequence | USAGE, SELECT, UPDATE |
| Schema | USAGE, CREATE |
| Datenbank | CONNECT, CREATE, TEMPORARY |
| Function / Procedure | EXECUTE |
| Type / Domain | USAGE |
| Foreign Data Wrapper | USAGE |
Vergeben wird über GRANT, entzogen über REVOKE:
GRANT USAGE ON SCHEMA public TO devs;
GRANT SELECT, INSERT, UPDATE ON TABLE orders TO devs;
GRANT EXECUTE ON FUNCTION calculate_tax(numeric) TO devs;Sobald devs diese Rechte hat, dürfen alice und bob (per Mitgliedschaft) damit arbeiten — ohne dass man die GRANTs an die Einzelpersonen gibt.
Owner ist nochmal etwas anderes
Eine wichtige Abgrenzung gleich vorweg: der Owner eines Objekts (Tabelle, Schema, Datenbank, Function) ist nicht über GRANT zu bekommen. Owner ist ein eigener Status, gesetzt beim CREATE … (Default: der erstellende User) oder über ALTER … OWNER TO ….
Owner haben implizit alle Privilegien auf ihre Objekte und sind als einzige berechtigt, das Objekt zu droppen oder umzubenennen. Wer \d table macht, sieht den Owner separat — er taucht nicht in der Access privileges-Spalte auf.
Mehr dazu im Artikel Ownership und Besitz.
Wie man das Modell typischerweise anwendet
In der Praxis hat sich ein Drei-Ebenen-Pattern etabliert:
- Schema-Owner: eine NOLOGIN-Rolle, die alle Objekte einer Anwendung besitzt (
app_owner). Migrationen laufen unter diesem User. - App-Rolle (Group): eine NOLOGIN-Rolle, an die Read-/Write-Privilegien auf die Anwendungstabellen vergeben werden (
app_rw). - Service-Login: eine LOGIN-Rolle, die die Anwendung in der Connection-String benutzt (
app_user), Mitglied vonapp_rw.
Damit ist sauber getrennt: Schema-Änderungen (DDL) und Datenoperationen (DML) gehören zu unterschiedlichen Rollen. Die Anwendung kann nichts droppen oder migrieren, was sie nicht soll.
Das konkrete Setup zeigen wir im Rezept-Tier — siehe Webapp-Benutzer mit Least-Privilege.
Abgrenzung zu MySQL/MariaDB und Oracle
Wer von einem anderen System kommt, ist es oft anders gewohnt:
- MySQL/MariaDB: trennt User und Grant. Es gibt
CREATE USER, dannGRANT … ON db.table TO user. Es gibt keine Rollen-Vererbung mit Rechtefluss wie in Postgres — Rollen wurden erst spät nachgereicht und sind weniger zentral. - Oracle: kennt Rollen schon lange, aber das
LOGIN-Konzept als Attribut der Rolle ist Postgres-eigen. In Oracle ist „User“ und „Role“ syntaktisch klar getrennt. - MSSQL: trennt zwischen
LOGIN(Server-weit, für Authentifizierung) undUSER(Datenbank-spezifisch). Postgres hat diese Zwei-Stufen-Trennung nicht: eine Rolle gilt cluster-weit; was sie in einer Datenbank darf, regelt nur das Privileg-System.
Für Umsteiger ist die wichtigste Erkenntnis: in Postgres ist das Modell einheitlich und cluster-weit. Ein User existiert einmal pro Cluster, nicht einmal pro Datenbank.
Interessantes
CREATE USER und CREATE GROUP sind reine Aliasse.
Beide laufen intern auf CREATE ROLE mit unterschiedlichen Defaults. Wenn du in einem Skript Konsistenz willst, schreib einfach immer CREATE ROLE aus und ergänze die Attribute, die du brauchst. Das ist auch in der offiziellen Doku der bevorzugte Weg.
Rollen sind cluster-weit, nicht datenbank-weit.
Wenn du einen User app anlegst, existiert er für alle Datenbanken im Cluster. Das ist anders als bei MSSQL. Was die Rolle in einer bestimmten Datenbank darf, wird über CONNECT-Privileg auf der Datenbank und USAGE/Objekt-Privilegien innerhalb der Datenbank gesteuert.
Es gibt keine „User ohne Login“.
Das klingt nach einer Spezialform, ist aber nichts anderes als „Rolle mit NOLOGIN“. Das Vokabular „User“ und „Group“ ist in Postgres nur Komfort — intern existiert nur die Rolle.
Postgres bringt vorgefertigte Rollen mit.
Seit PG 14 gibt es pg_read_all_data, pg_write_all_data, pg_monitor, pg_signal_backend u. a. — fertige Rollen, an die du deine User per GRANT pg_read_all_data TO … hängst, statt selbst alle Tabellen zu enumerieren. Eigener Artikel: Predefined Roles.
postgres ist nur ein Default-Superuser, nicht magisch.
Beim initdb legt Postgres einen Superuser an, üblicherweise mit dem Namen des OS-Users, der initdb aufruft (also oft postgres). Du kannst weitere Superuser anlegen oder den Default umbenennen. Magische Sonderbehandlung gibt es nicht — postgres ist einfach der vorgegebene Name.
Weiterführende Ressourcen
Externe Quellen
- Database Roles – PostgreSQL Documentation
- CREATE ROLE – PostgreSQL Documentation
- CREATE USER – PostgreSQL Documentation
- GRANT – PostgreSQL Documentation
- Privileges – PostgreSQL Documentation