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 LOGIN darf 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:

BefehlWas er tut
CREATE ROLE nameErstellt eine Rolle. Default: NOLOGIN.
CREATE USER nameAlias für CREATE ROLE name LOGIN.
CREATE GROUP nameAlias 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

SQL Rolle, User und Gruppe anlegen
-- 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:

  1. devs ist eine Rolle ohne Login. Niemand kann sich als „devs“ anmelden.
  2. alice und bob sind Login-Rollen. Sie können sich verbinden.
  3. GRANT devs TO alice, bob macht beide zu Mitgliedern der Rolle devs — alle Rechte, die devs jetzt oder später bekommt, gelten automatisch für die Mitglieder.

Anzeigen lässt sich das mit \du in psql:

SQL
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:

ObjektMögliche Privilegien
Tabelle / ViewSELECT, INSERT, UPDATE, DELETE, TRUNCATE, REFERENCES, TRIGGER
SpalteSELECT, INSERT, UPDATE, REFERENCES (granular pro Spalte)
SequenceUSAGE, SELECT, UPDATE
SchemaUSAGE, CREATE
DatenbankCONNECT, CREATE, TEMPORARY
Function / ProcedureEXECUTE
Type / DomainUSAGE
Foreign Data WrapperUSAGE

Vergeben wird über GRANT, entzogen über REVOKE:

SQL Privilegien vergeben
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:

  1. Schema-Owner: eine NOLOGIN-Rolle, die alle Objekte einer Anwendung besitzt (app_owner). Migrationen laufen unter diesem User.
  2. App-Rolle (Group): eine NOLOGIN-Rolle, an die Read-/Write-Privilegien auf die Anwendungstabellen vergeben werden (app_rw).
  3. Service-Login: eine LOGIN-Rolle, die die Anwendung in der Connection-String benutzt (app_user), Mitglied von app_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, dann GRANT … 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) und USER (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

/ Weiter

Zurück zu Server-Administration

Zur Übersicht