Non-superuser subscription owners

First seen: 2021-10-20 18:40:39+00:00 · Messages: 246 · Participants: 15

Latest Update

2026-06-01 · claude-opus-4-6

Non-superuser Subscription Owners: Deep Technical Analysis

Core Problem

PostgreSQL's logical replication system was designed with the assumption that subscriptions would always be owned and operated by superusers. This created two fundamental problems:

  1. Security vulnerability: Subscription apply workers execute with superuser privileges, meaning any table owner on a subscriber node can escalate to superuser by attaching malicious triggers, default expressions, or index expressions to replicated tables. The apply worker will execute this code as the subscription owner (superuser), giving the attacker full control.

  2. Usability limitation: There is no way to delegate subscription management to non-superusers, which is particularly problematic for cloud database providers (AWS, Azure, EDB) who need to allow customers to manage replication without granting full superuser access.

The thread reveals that this is not merely a convenience issue but a serious architectural security defect. As Robert Haas dramatically put it: if a non-superuser owns a table that is part of a subscription, they can "instantly hack superuser" — making logical replication fundamentally insecure in any multi-tenant environment.

Evolution of the Solution

Phase 1: Permission Checks on Apply Workers (v15, committed Jan 2022)

Mark Dilger's initial work (committed by Jeff Davis) made apply workers respect the privileges of the subscription owner rather than always acting as superuser. Key changes:

This closed the gap where revoking superuser from a subscription owner had no effect on the apply worker's actual privileges.

Phase 2: pg_create_subscription Predefined Role (v16, committed Mar 2023)

Robert Haas's patch introduced pg_create_subscription, allowing non-superusers with this role to create and manage subscriptions. Key design decisions:

Phase 3: Apply-as-Table-Owner (proposed, separate thread)

Robert Haas proposed running replication as the table owner rather than the subscription owner. This addresses the trigger/expression code execution vulnerability by ensuring that code attached to tables runs with the table owner's privileges (who is already exposed to that risk via REINDEX, VACUUM, etc.).

Key Technical Design Decisions and Tradeoffs

Transaction Boundary vs. Per-Row Permission Checking

A significant debate occurred over when to detect privilege changes:

The consensus was transaction-boundary detection, with documentation noting the behavior.

Connection String Security Model

Three competing approaches emerged:

  1. Password requirement (implemented): Mirror postgres_fdw's approach — require password-based authentication unless superuser or password_required=false. Blocks loopback-to-superuser attacks.

  2. Separate connection object (Jeff Davis's proposal): Introduce CREATE SUBSCRIPTION ... SERVER using existing foreign server infrastructure. Separates "who can create subscriptions" from "who can specify connection strings." Would use existing USAGE grants on SERVER objects.

  3. Reverse pg_hba.conf (Robert Haas's proposal): A configuration file controlling outbound connections, analogous to how pg_hba.conf controls inbound connections. Would allow subnet-based filtering, authentication method requirements, and per-component rules.

  4. require_auth libpq option (Jacob Champion/Andres Freund): Force specific authentication methods from the client side, preventing ambient authentication from being used. Landed as a separate feature in PG16.

The password requirement was implemented as the pragmatic choice, with acknowledgment that it's imperfect (blocks legitimate passwordless use cases while not covering all attack vectors like GSS/Kerberos credential theft).

Predefined Role Granularity

Jeff Davis argued strongly for two separate roles:

Robert Haas rejected this, arguing that having two roles both required to do a single thing with no independent utility is confusing. He committed with only pg_create_subscription, noting that pg_create_connection should be introduced alongside whatever patch adds CREATE SUBSCRIPTION ... SERVER.

SECURITY INVOKER vs. SECURITY DEFINER Debate

A major tangent (but architecturally important) explored how code attached to tables executes:

Provenances Mechanism (2026 proposal)

Robert Haas's most ambitious proposal introduces a Provenances object tracking the chain of custody for code execution:

Unresolved Issues

  1. RLS enforcement gap: COPY FROM (used in table sync) doesn't support RLS. The current solution errors out for RLS-enabled tables with non-superuser subscription owners.

  2. Partition routing: ExecUpdate converts updates to DELETE+INSERT when partition constraints fail; subscriber-side handling is incomplete.

  3. Connection security completeness: The password_required mechanism doesn't address GSS/Kerberos credential forwarding, peer/ident authentication ambient authority, or file access via service files.

  4. Trigger security in replication: Any table owner can still compromise the subscription owner by attaching malicious triggers, unless apply-as-table-owner is adopted.

  5. The provenances infrastructure: Requires massive code changes touching nearly every expression evaluation path. Verification is extremely difficult. The 2026 discussion shows this remains work-in-progress.

Bugs Found and Fixed