Skip to content

Continuous re-validation of session credentials - #403

Open
pg-hub-mirror[bot] wants to merge 5 commits into
masterfrom
pg-hub/mirror-patch-cdbbedf97fd52f34
Open

pg-hub-mirror[bot] wants to merge 5 commits into
masterfrom
pg-hub/mirror-patch-cdbbedf97fd52f34

Conversation

@pg-hub-mirror

@pg-hub-mirror pg-hub-mirror Bot commented Sep 17, 2026

Copy link
Copy Markdown

Read-only mirror. Reply and review on pgsql-hackers; activity here is not sent upstream.

  • Original author: Ajit Awekar <ajitpostgres(at)gmail(dot)com>
  • Mailing list: pgsql-hackers
  • Message-ID: CAER375N35XAWLhAiokyVs65bFQgq_n_=LgNs61o+ZtYdEmPEqw@mail.gmail.com
  • Original email

Patch files:


Hi Hackers,
Attached is v2 of the series, with three fixes folded into 0001 (no
changes to 0002-0005):

  1. Standby servers
    CheckCredentialValidity() no longer skips validation under
    RecoveryInProgress(). Hot-standby sessions run continuously "in
    recovery", so this was silently disabling the feature for read
    replicas -- all the checks involved are read-only/local, so
    there's no reason to exclude them.
  2. Set Session Authorization
    ValidateRoleValidity() now checks GetAuthenticatedUserId()
    instead of GetSessionUserId(), so a superuser can't mask the
    login role's expiry/removal by switching session authorization.
    This also keeps it consistent with the method-specific
    validators, which always check the fixed login identity. Added
    regression coverage.
  3. Walsenders
    EnableCredentialValidationTimeout() and CheckCredentialValidity()
    now explicitly skip AmWalSenderProcess()
    Request a review.
    Thanks & Best Regards,
    Ajit
    On Wed, 1 Jul 2026 at 17:45, Ajit Awekar <ajitpostgres(at)gmail(dot)com> wrote:

Hi,

Following up on the ongoing work for continuous credential validation, I
would like to propose the next two patches in the series.

These patches extend the continuous validation framework to handle both
GSSAPI/Kerberos and LDAP (Search+Bind) authenticated sessions.
Patch 4/5: Add GSS credential expiry to credential validation

This patch introduces method-specific validation for GSSAPI/Kerberos
sessions (CVT_GSS).The GSS security context established at authentication
carries a lifetime derived from the client's Kerberos ticket. We introduce
a helper be_gssapi_get_context_expired() which utilizes gss_context_time()
on the acceptor context retained on the Port. This is a purely local
check that requires no round-trip to the KDC. If the context lifetime has
elapsed, the session is safely terminated.

postgres=> select system_user;
system_user

gss:test1(at)EXAMPLE(dot)COM
(1 row)

postgres=> select current_user;
current_user

test1

postgres=> select current_time;
FATAL: session credentials have expired
HINT: Please reconnect to establish a new authenticated session.
server closed the connection unexpectedly
This probably means the server terminated abnormally
before or while processing the request.
The connection to the server was lost. Attempting reset: Failed.
The connection to the server was lost. Attempting reset: Failed.

(1 row)

Patch 5/5: Add LDAP account-existence check to credential validation

This patch adds validation support for LDAP sessions (CVT_LDAP).

Unlike token- or certificate-based authentication, an LDAP session does
not retain a credential with an intrinsic expiry date. Instead, "validity"
is defined as the account's continued existence within the directory.

During each validation cycle, the backend connects and re-binds using the
configured search credentials (ldapbinddn / ldapbindpasswd), then re-runs
the configured search filter. If the search yields exactly zero entries,
the account is considered deleted or disabled, and the session is
terminated.

postgres=> SELECT current_user;
current_user

test1
(1 row)

postgres=> select system_user;
system_user

ldap:uid=test1,dc=example,dc=net. <= After this I fired ldapdelete and got validation error on next validation cycle
(1 row)

postgres=> select current_time;
FATAL: session credentials have expired
HINT: Please reconnect to establish a new authenticated session.
server closed the connection unexpectedly
This probably means the server terminated abnormally
before or while processing the request.
The connection to the server was lost. Attempting reset: Failed.
The connection to the server was lost. Attempting reset: Failed.

(1 row)

Thanks & Best regards,

Ajit

On Fri, 26 Jun 2026 at 11:21, Ajit Awekar <ajitpostgres(at)gmail(dot)com> wrote:

Hi,

Attached is v2 of the patch set, rebased on the current master.

The only functional change from the previous version is a fix for the
cfbot failure. The earlier version src/test/modules/test_misc/t/
003_check_guc.pl
due to the tab in the trailing spaces.

Thanks & Best Regards,
Ajit

On Tue, 16 Jun 2026 at 17:44, Ajit Awekar <ajitpostgres(at)gmail(dot)com> wrote:

Hi,

Today PG validates a client's credentials only once, at connection
time. After that the backend runs until the client disconnects, even if
the
basis for the original authentication decision has gone away. In
particular:

  • the role's VALID UNTIL passes, or the role is dropped (ALTER ROLE
    ...
    VALID UNTIL / DROP ROLE);
  • the OAuth bearer token the session authenticated with expires;
  • the TLS client certificate the session authenticated with reaches
    its
    notAfter date.

In all of these cases a long-lived session keeps running with
credentials that
would be rejected on a fresh connection. For environments with
short-lived
credentials (OAuth tokens, short-lived certs, time-boxed accounts) this
is a
real gap: revoking access has no effect on already-established sessions.

This patch series adds an optional mechanism to periodically re-validate
the
credentials of an active session and terminate it (FATAL) once they are
no
longer valid.

A per-backend timer (CREDENTIAL_VALIDATION_TIMEOUT) fires every
credential_validation_interval seconds and sets a pending flag.

Validation has two layers:
- A baseline, auth-method-independent check (role still exists and
has not
passed rolvaliduntil), applied to every authenticated session.
- Optional method-specific validators, registered via
RegisterCredentialValidator(), for OAuth token expiry and client
certificate expiry.

The framework is deliberately extensible: adding re-validation for a new
authentication method needs only three small steps -- add a
CredentialValidationType, implement a "bool validator(void)" callback
that
returns whether the credential is still valid, and register it with
RegisterCredentialValidator(). The certificate and OAuth validators in
0002
and 0003 are themselves examples of plugging into the framework this
way,
so methods such as LDAP, RADIUS, or GSSAPI/Kerberos credential
lifetimes
could be added later without touching the core.

Two GUCs :
credential_validation_enabled (bool, default off)
credential_validation_interval (int, 5..3600 s, default 60)

Patch series

0001 Framework + baseline role-validity check (rolvaliduntil / role
existence). Useful on its own for password/md5/scram sessions.
0002 TLS client certificate expiry. The cert presented at connect
time is
retained on the Port, so its notAfter is re-checked locally with
no
network round-trip.
0003 OAuth token expiry. This adds an optional expire_cb callback to
OAuthValidatorCallbacks; to stay ABI-compatible the validator
magic is
versioned (V1 = existing layout, V2 = adds expire_cb), and the
server
accepts both.

Request a review.

Thanks & Best Regards,
Ajit

Introduce a mechanism that periodically re-validates the credentials of an
active session and terminates the session if they are no longer valid.  A
per-backend timer (CREDENTIAL_VALIDATION_TIMEOUT) fires at a configurable
interval; the pending flag is acted on at the next command boundary in the
main loop, where the check runs inside a short-lived transaction (reusing the
session's open transaction if one exists, so a long-running transaction block
is still validated at each command boundary).

This commit adds the framework and the baseline, auth-method-independent
check: the authenticated (login) role must still exist and must not have
passed its rolvaliduntil expiration.  This is checked against
GetAuthenticatedUserId(), not the current session or effective role, so
SET SESSION AUTHORIZATION / SET ROLE cannot mask the original login role's
expiry or removal.  Method-specific validators plug in via
RegisterCredentialValidator() and are added in following commits.

Two GUCs control the feature: credential_validation_enabled (default off) and
credential_validation_interval (5..3600 seconds, default 60).
Register a method-specific validator for certificate-authenticated sessions
(CVT_CERT).  The client certificate presented at connection time is retained
on the Port, so its notAfter date can be re-checked locally with no network
round-trip; once the certificate has expired the session is terminated at the
next validation cycle.

be_tls_get_peer_cert_expired() reports whether the peer certificate's validity
period has passed, conservatively treating an absent or unparsable notAfter as
not expired.
Register a method-specific validator for OAuth-authenticated sessions
(CVT_OAUTH) that asks the loaded OAuth validator module whether the bearer
token has expired.

This requires a new optional callback, expire_cb, in OAuthValidatorCallbacks.
To add it without breaking existing modules, the validator ABI magic is
versioned: PG_OAUTH_VALIDATOR_MAGIC_V1 (the original layout, without
expire_cb) and PG_OAUTH_VALIDATOR_MAGIC_V2 (which adds expire_cb).  The server
accepts both; expire_cb is only consulted for V2 modules that provide it, and
a token is assumed still valid otherwise.
Register a method-specific validator for GSSAPI/Kerberos sessions (CVT_GSS).
The new helper be_gssapi_get_context_expired() uses gss_context_time() on the
acceptor context retained on the Port to report, with no round-trip to the KDC,
whether the context lifetime derived from the client's Kerberos ticket has
elapsed.  A short-lived-ticket TAP test (009_gss_continuous_validation.pl)
confirms an idle GSS session is terminated once the credential lapses;
PostgreSQL::Test::Kerberos gains clockskew and ticket-lifetime parameters and
namespaces its KDC files by test script name.
For LDAP-authenticated sessions, re-run the configured search filter on each
validation cycle and terminate the session once the account no longer exists
in the directory.  Search+bind only; simple-bind sessions are treated as valid.
@pg-hub-mirror pg-hub-mirror Bot added source:pgsql-hackers Mirrored from pgsql-hackers type:patch Mail thread contains a PostgreSQL patch area:testing Tests and buildfarm area:security Authentication, authorization, or security area:wal Write-ahead logging and recovery labels Sep 17, 2026
@pg-hub-mirror pg-hub-mirror Bot locked and limited conversation to collaborators Sep 17, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area:security Authentication, authorization, or security area:testing Tests and buildfarm area:wal Write-ahead logging and recovery source:pgsql-hackers Mirrored from pgsql-hackers type:patch Mail thread contains a PostgreSQL patch

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant