Skip to content

Add GSSAPI (Kerberos) authentication and GSS encryption - #12

Merged
quinnj merged 6 commits into
mainfrom
gssapi-auth-encryption
Sep 18, 2026
Merged

quinnj merged 6 commits into
mainfrom
gssapi-auth-encryption

Conversation

@quinnj

@quinnj quinnj commented Sep 18, 2026

Copy link
Copy Markdown
Member

Summary

Kerberos support for Postgres.jl through the operating system's GSSAPI library (the same library libpq uses), with no new binary dependency. Mechanism bindings come from registered SASLAuth 1.1.0; this PR is the protocol side.

GSSAPI authentication (auth codes 7, 8, 9): the gss_init_sec_context token exchange with GSSResponse messages, mutual authentication required (an AuthenticationOk before the context completes is refused). New options krbsrvname (default postgres) and gssdelegation, plus PGKRBSRVNAME / PGGSSDELEGATION.

GSS encryption (gssencmode: disable (default), prefer, require; PGGSSENCMODE): GSSENCRequest before SSLRequest, only when a ticket can be acquired; framed handshake; every message gss_wraped and chunked under the 16 KiB packet limit through a new API.GSSConn transport; N falls through to sslmode under prefer; a GSS attempt the server accepted but that then fails is retried once without GSS under prefer, as libpq does. cancel_query! uses GSS encryption when the connection it cancels does. Message flow, limits, and error texts mirror libpq's fe-auth.c / fe-secure-gssapi.c / fe-connect.c.

CVE-2024-10977 pattern: the server's ErrorResponse to SSLRequest (existing path) or GSSENCRequest (new) is no longer read or shown; the connection fails with libpq's fixed message.

Also: the keyword constructor now defaults user to the OS login name (PGUSER / USER / USERNAME), as the DSN path already did, so Postgres.Connection(host=...) works for ticket holders.

Design notes

  • gssencmode defaults to disable rather than libpq's prefer: prefer would silently replace TLS with GSS encryption for anyone holding a ticket. Documented in the support policy.
  • API.GSSConn.ctx is the concrete SASLAuth.GSSAPI.Context, as requested in review. The scripted protocol tests replace native entry points temporarily and use real Context objects; they restore system-library lookup before the live Kerberos fixtures.
  • CI and documentation use the registered SASLAuth package. All temporary branch installs are removed; the compatibility floor is 1.1.0.
  • The merge with main preserves startup options across plain, TLS, and GSS connections and reconnects. The release version is 2.1.0.

Test plan

  • test/gssapi.jl: scripted native GSS entry points plus a scripted server on a loopback port cover G/N/E negotiation for prefer and require, the framed handshake, chunked framing both ways, the prefer re-dial, startup errors over GSS, no-credential behavior, auth codes 7/8/9 incl. premature AuthenticationOk, server rejection, duplicate request, cancel over GSS, wait_for_notification deadlines on the GSS transport, and the SSLRequest E path. Runs on every CI platform.
  • Kerberos Fixture (Linux Docker jobs): an MIT KDC inside the PostgreSQL container, hostgssenc and hostnogssenc rules, real GSSAPI authentication and GSS encryption verified through pg_stat_gssapi, 100 KB results and 70 KB parameters across packets, cancel over GSS, and unknown-principal failures. Passed locally on macOS (Heimdal client) against Docker.
  • Full suite passed locally on Julia 1.12 with Docker integration required: 6,226 checks, including live Kerberos, TLS, startup options, and the concrete-context regression. The SASLAuth source tree matches the registered release.
  • All 16 checks passed on c9c190d: Julia/platform matrix, PostgreSQL 14–18, both authentication modes, documentation, and coverage. CI uses registered SASLAuth 1.1.0.

🤖 Generated with Claude Code

Co-authored by Codex

quinnj and others added 3 commits September 17, 2026 19:44
Phase 1: GSSAPI authentication. Auth codes 7 and 9 run the gss_init_sec_context
token exchange through SASLAuth.GSSAPI (the system Kerberos library, as libpq),
sending GSSResponse messages and feeding back code-8 continuations. Options
krbsrvname (default postgres) and gssdelegation, with PGKRBSRVNAME and
PGGSSDELEGATION.

Phase 2: GSS encryption (gssencmode, default disable). GSSENCRequest is sent
before SSLRequest when a ticket can be acquired; on G the framed handshake
runs and every message is gss_wrap'ed under the 16 KiB packet limit through a
new GSSConn transport; N falls through to sslmode under prefer and fails under
require; a GSS attempt the server accepted but that then fails is retried once
without GSS under prefer, as libpq does. Cancel requests use GSS encryption
when the connection they cancel does.

Side finding (CVE-2024-10977 pattern): the server's ErrorResponse to SSLRequest
or GSSENCRequest is no longer read or displayed; the connection fails with
libpq's fixed message instead.

Tests: a scripted GSS mechanism and scripted server cover negotiation, chunked
framing, cancel, deadlines, authentication, and every error path on all CI
platforms; a Kerberos fixture runs an MIT KDC inside the PostgreSQL container
for a real round trip on Linux.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…rder

The bind-mounted temp dir is 0700 on Linux, so the postgres user could not
read the keytab and hba file from it; keep the server's files in a
container-local directory and export only the ticket cache. In the docs job,
pin SASLAuth before developing the package so the resolve succeeds.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Comment thread src/api/gss.jl Outdated
@quinnj
quinnj marked this pull request as ready for review September 18, 2026 16:18
@quinnj
quinnj merged commit 2df1248 into main Sep 18, 2026
16 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant