Skip to content

Switch to pgx, add SSL/cert and GSSAPI auth support#4

Merged
vyruss merged 10 commits into
mainfrom
feat/auth-methods
Apr 1, 2026
Merged

Switch to pgx, add SSL/cert and GSSAPI auth support#4
vyruss merged 10 commits into
mainfrom
feat/auth-methods

Conversation

@vyruss
Copy link
Copy Markdown
Collaborator

@vyruss vyruss commented Mar 31, 2026

Summary

  • Switch PostgreSQL driver from lib/pq to pgx
  • Switch sslmode to prefer
  • Add --sslmode, --sslcert, --sslkey, --sslrootcert flags and PGSSLMODE/PGSSLCERT/PGSSLKEY/PGSSLROOTCERT env vars
  • Unify connection string building

Checklist

  • Unit tests (sslmode validation, cert validation, env fallbacks, connection string quoting)
  • Integration tests (scenario 5: cert auth, scenario 6: GSSAPI/Kerberos)
  • Docs/README updated (flags, env vars, authentication methods section)
  • Integration test impact (2 new scenarios in local CI + GitHub CI)
  • Security checks (cert file existence validation, cert/key pairing, sslmode whitelist)
  • PR links to a tracking issue/ticket

Tracking: STAFF-9

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 31, 2026

📝 Walkthrough

Walkthrough

Adds PostgreSQL SSL/TLS and GSSAPI/Kerberos support: swaps pq for pgx driver, introduces SSL CLI flags/env and validation, changes connection-string behavior to include SSL parameters, extends CI with cert- and GSSAPI-based integration jobs, updates Dockerfile, docs, tests, and integration scripts.

Changes

Cohort / File(s) Summary
CI & Integration Tests
.github/workflows/ci.yml
Adds cert-auth-test and gssapi-auth-test jobs that download the artifact, build/run a Debian test image, run containerized integration scripts for certificate and GSSAPI scenarios, and always attempt image cleanup (`docker rmi ...
Container Image / Test Tools
Dockerfile
Installs additional packages for TLS and Kerberos testing: openssl, krb5-kdc, krb5-admin-server, krb5-user.
CLI, Config, and Driver Migration
radar.go, postgres.go
Replaces github.com/lib/pq with github.com/jackc/pgx/v5/stdlib; adds SSLMode, SSLCert, SSLKey, SSLRootCert to Config; adds CLI flags/env fallbacks and validation (allowed modes, required combos, file existence); changes ConnectionString()ConnectionString(dbname string) and includes escaped ssl parameters.
Tests & Integration Script
radar_test.go, test-radar.sh
Updates unit tests for SSL parsing/validation and connection-string quoting; adjusts tests to default sslmode=prefer; extends test-radar.sh with polling via pg_isready, Scenario 5 (certificate auth) and Scenario 6 (GSSAPI/Kerberos), and updates success messaging.
Documentation & README
README.md, docs/index.md
Adds CLI flags --sslmode, --sslcert, --sslkey, --sslrootcert; documents env vars PGSSLMODE, PGSSLCERT, PGSSLKEY, PGSSLROOTCERT, PGDATABASE; adds "Authentication Methods" section describing Password, LDAP, Certificate, and GSSAPI flows with examples.
Go module / Dependencies
go.mod
Bumps Go directive to go 1.24.13; moves to grouped require blocks, replaces github.com/lib/pq with github.com/jackc/pgx/v5 v5.8.0, and adds/organizes test/dev dependencies (go-sqlmock, pgx ecosystem entries).

Poem

🐇
I chewed a tiny cert beneath the moon,
stitched keys and tickets with a carrot tune,
pgx hopped in, tunnels snug and tight,
Kerberos hummed through silver night,
the archive zips — we thumped with delight!

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The pull request title clearly and concisely summarizes the main changes: switching from lib/pq to pgx driver and adding SSL/certificate and GSSAPI authentication support.
Description check ✅ Passed The description is directly related to the changeset, providing a comprehensive summary of the key changes, implementation details, and completed checklist items.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/auth-methods

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@codacy-production
Copy link
Copy Markdown

codacy-production Bot commented Mar 31, 2026

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

🟢 Metrics 22 complexity . 10 duplication

Metric Results
Complexity 22
Duplication 10

View in Codacy

TIP This summary will be updated as you push new changes. Give us feedback

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
test-radar.sh (1)

2-7: ⚠️ Potential issue | 🟡 Minor

Outdated comment: says 4 scenarios but script now has 6.

The header comment should be updated to reflect the additional scenarios.

📝 Suggested fix
 #!/bin/bash
-# Test script for radar in a Debian container with PostgreSQL 18
-# Tests all 4 permission scenarios:
+# Test script for radar in a Debian container with PostgreSQL 18.
+# Tests all 6 scenarios:
 # 1. Root + superuser
 # 2. Root + pg_monitor
 # 3. Non-root + superuser
 # 4. Non-root + pg_monitor
+# 5. Certificate authentication
+# 6. GSSAPI/Kerberos authentication
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test-radar.sh` around lines 2 - 7, Update the header comment at the top of
test-radar.sh to reflect that the script now covers 6 scenarios (change "Tests
all 4 permission scenarios" to "Tests all 6 permission scenarios") and expand
the numbered list to include the two additional scenarios that were added to the
script so the comment matches the actual tests present in the file.
🧹 Nitpick comments (2)
radar_test.go (1)

849-850: Cleanup pattern could leak file handles on error.

The cleanup functions call closeErrCheck(file, ...) followed by os.Remove(file.Name()). However, if os.CreateTemp succeeds but a later operation fails before cleanup runs, the file handle might not be closed properly. Consider using defer certFile.Close() immediately after creation.

💡 Alternative pattern
certFile, err := os.CreateTemp("", "cert*.pem")
if err != nil {
    t.Fatal(err)
}
defer certFile.Close()
t.Cleanup(func() { os.Remove(certFile.Name()) })

Also applies to: 855-856, 861-862

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@radar_test.go` around lines 849 - 850, After creating any temp file with
os.CreateTemp (e.g., certFile), call defer <file>.Close() immediately after the
successful creation to ensure the handle is closed on error paths, and change
the t.Cleanup closures to only remove the file (os.Remove(file.Name())) instead
of calling closeErrCheck; remove the closeErrCheck usage in the cleanup for
certFile and the other temp files created in the same test so cleanup only
removes the file and the file descriptor is closed by the immediate defer.
test-radar.sh (1)

191-193: Consider using pg_isready loop instead of sleep 3.

Hardcoded sleeps after PostgreSQL restart can be flaky. Earlier in the script (lines 31-33), a simpler sleep 3 is used, but for robustness consider a pg_isready loop, especially since the server configuration changes significantly between restarts.

Also applies to: 268-270

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test-radar.sh` around lines 191 - 193, Replace the simple "sleep 3" after the
pg_ctl restart command with a loop that calls pg_isready until PostgreSQL
reports ready (or timeout) to avoid flaky starts; specifically update the block
that runs su - postgres -c "/usr/lib/postgresql/18/bin/pg_ctl -D $PGDATA restart
-l /var/lib/postgresql/18/logfile" to poll pg_isready (checking $PGDATA/$PGPORT
as appropriate) with a short sleep and a max-wait, and make the same change for
the other occurrence currently using "sleep 3" (the block around lines 268-270).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.github/workflows/ci.yml:
- Around line 341-343: The hardcoded "sleep 3" after restarting Postgres can
cause flaky CI; replace it with the same pg_isready-style wait loop used
elsewhere in this workflow: after running sudo -u postgres
/usr/lib/postgresql/18/bin/pg_ctl -D "$PGDATA" restart -l /tmp/postgres.log,
poll postgres with pg_isready (using the same host/port/env vars as the other
jobs) in a loop with a timeout and short sleep between attempts, and fail the
job if pg_isready does not report ready within the timeout.
- Around line 474-476: The hardcoded "sleep 3" after restarting PostgreSQL
should be replaced with a readiness loop using pg_isready to avoid timing
issues; in the block that runs "sudo -u postgres
/usr/lib/postgresql/18/bin/pg_ctl -D \"$PGDATA\" restart -l /tmp/postgres.log"
remove "sleep 3" and add a loop that repeatedly calls "pg_isready -d postgres -h
/var/run/postgresql" (or simply "pg_isready -q -d postgres" as appropriate) with
a short sleep between attempts and an overall timeout (e.g., 30s) to fail early
if the server never becomes ready; ensure the loop runs as the same user (sudo
-u postgres) or prefixes the pg_isready call appropriately so the GSSAPI test
waits for an actual ready state rather than a fixed delay.

In `@docs/index.md`:
- Line 136: The docs show inconsistent flag notation: update all occurrences of
the SSL-related flags to a single consistent form (prefer using double-dash for
long flags) so examples and Usage match; specifically change `-sslmode`,
`-sslcert`, `-sslkey`, `-sslrootcert` in the Usage section to `--sslmode`,
`--sslcert`, `--sslkey`, `--sslrootcert` (and ensure the example line that
already uses `--sslmode --sslcert client.crt --sslkey client.key --sslrootcert
ca.crt` remains unchanged), and scan the rest of the document for any other
`-ssl*` instances to normalize them to the chosen double-dash form.

In `@radar.go`:
- Around line 332-379: The TLS env/validation logic (the sslmodeFlagSet check,
reading PGSSLMODE/PGSSLCERT/PGSSLKEY/PGSSLROOTCERT, validSSLModes validation,
cert/key pairing check, verify-ca/verify-full root cert requirement, and file
existence checks) should be skipped when cfg.SkipPostgres is true; wrap the
entire block starting at the sslmodeFlagSet logic through the cert file os.Stat
loop in if !cfg.SkipPostgres { ... } so none of those environment reads or
validations run for system-only runs, preserving existing behavior when
SkipPostgres is false.
- Around line 402-409: The inline quoting helper q currently only checks for
literal space and backslash/quote and fails to quote other whitespace and empty
strings; update q (the anonymous function) to detect any unicode whitespace
(e.g., use unicode.IsSpace or strings.ContainsAny with "\t\n\r\v\f") and treat
empty string as "''", escape backslashes and single quotes as now, and wrap the
value in single quotes when it contains any whitespace or is empty so fields
like dbname, user, password, and cert paths are correctly quoted in the
connection string.

In `@README.md`:
- Line 159: Update the flag notation in the Certificate example to match the
Usage section by replacing the long form `--sslmode` with the single-dash form
`-sslmode` so it is consistent with the rest of the docs; change the snippet
containing `--sslmode verify-full --sslcert client.crt --sslkey client.key
--sslrootcert ca.crt` to use `-sslmode verify-full` (keep the other flags as-is)
so both `--sslmode` and `-sslmode` are not mixed across docs.

---

Outside diff comments:
In `@test-radar.sh`:
- Around line 2-7: Update the header comment at the top of test-radar.sh to
reflect that the script now covers 6 scenarios (change "Tests all 4 permission
scenarios" to "Tests all 6 permission scenarios") and expand the numbered list
to include the two additional scenarios that were added to the script so the
comment matches the actual tests present in the file.

---

Nitpick comments:
In `@radar_test.go`:
- Around line 849-850: After creating any temp file with os.CreateTemp (e.g.,
certFile), call defer <file>.Close() immediately after the successful creation
to ensure the handle is closed on error paths, and change the t.Cleanup closures
to only remove the file (os.Remove(file.Name())) instead of calling
closeErrCheck; remove the closeErrCheck usage in the cleanup for certFile and
the other temp files created in the same test so cleanup only removes the file
and the file descriptor is closed by the immediate defer.

In `@test-radar.sh`:
- Around line 191-193: Replace the simple "sleep 3" after the pg_ctl restart
command with a loop that calls pg_isready until PostgreSQL reports ready (or
timeout) to avoid flaky starts; specifically update the block that runs su -
postgres -c "/usr/lib/postgresql/18/bin/pg_ctl -D $PGDATA restart -l
/var/lib/postgresql/18/logfile" to poll pg_isready (checking $PGDATA/$PGPORT as
appropriate) with a short sleep and a max-wait, and make the same change for the
other occurrence currently using "sleep 3" (the block around lines 268-270).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 4f963b50-f568-44b1-9b4b-f9420f1da684

📥 Commits

Reviewing files that changed from the base of the PR and between d68dc32 and 19f51ac.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (9)
  • .github/workflows/ci.yml
  • Dockerfile
  • README.md
  • docs/index.md
  • go.mod
  • postgres.go
  • radar.go
  • radar_test.go
  • test-radar.sh

Comment thread .github/workflows/ci.yml
Comment thread .github/workflows/ci.yml
Comment thread docs/index.md Outdated
Comment thread radar.go Outdated
Comment thread radar.go
Comment thread README.md Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

♻️ Duplicate comments (1)
.github/workflows/ci.yml (1)

470-486: ⚠️ Potential issue | 🟠 Major

Remove the fallback trust rules to ensure the test actually validates GSSAPI authentication.

The hostgssenc rule matches only GSSAPI-encrypted connections, while the host rules below match any TCP connection. If GSSAPI encryption negotiation is skipped or fails, the connection falls back to the host rule with trust authentication, allowing the test to pass without actually proving Kerberos auth works. Since kinit provides valid credentials and libpq defaults to attempting GSSAPI encryption when credentials are present, removing the fallback rules will force the connection to either use GSSAPI or fail, ensuring the test genuinely validates the feature.

🔧 Proposed fix
                   cat > "$PGDATA/pg_hba.conf" <<HBA
                   local   all             all                                     trust
                   hostgssenc testdb       testuser        127.0.0.1/32            gss include_realm=0
-                  host    all             all             127.0.0.1/32            trust
-                  host    all             all             ::1/128                 trust
+                  hostgssenc testdb       testuser        ::1/128                 gss include_realm=0
                   HBA
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/ci.yml around lines 470 - 486, The pg_hba.conf creation
block currently adds fallback "host" lines that use "trust", which lets TCP
connections bypass GSSAPI; edit the here-doc that writes "$PGDATA/pg_hba.conf"
to remove the two fallback host rules (the lines starting with "host    all     
all             127.0.0.1/32            trust" and "host    all             all 
::1/128                 trust") so only the GSSAPI-specific rule ("hostgssenc
testdb       testuser        127.0.0.1/32            gss include_realm=0") is
used for TCP, ensuring the test actually requires GSSAPI auth; keep the existing
local socket rule if needed and leave the surrounding restart/kinit/radar
commands unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.github/workflows/ci.yml:
- Around line 334-349: The IPv6 loopback (::1) is allowed by a generic trust
rule so the certificate-auth test can be bypassed; update the pg_hba.conf HBA
block so the IPv6 address family has the same certificate-only rule as IPv4 by
adding an entry mirroring "hostssl testdb testuser 127.0.0.1/32 cert" for
"::1/128" and remove the existing "host    all    all    ::1/128    trust" line;
locate the HBA heredoc (the cat > "$PGDATA/pg_hba.conf" <<HBA ... HBA block) and
modify it accordingly so only the new IPv6 hostssl rule permits access for
testdb/testuser.

In `@radar.go`:
- Around line 371-379: The SSL file validation loop currently only checks
existence via os.Stat and should also reject directories; update the loop that
iterates over the slice containing {"--sslcert", cfg.SSLCert}, {"--sslkey",
cfg.SSLKey}, {"--sslrootcert", cfg.SSLRootCert} to call os.Stat, then check
fi.IsDir() and return a clear error (e.g., fmt.Errorf("%s: is a directory",
f.flag) or wrap the error) when a path is a directory so directory paths are
rejected early instead of passing validation.

In `@test-radar.sh`:
- Around line 186-203: The IPv6 trust rule in the generated pg_hba.conf lets
connections to ::1 bypass the intended cert requirement when radar connects to
"localhost"; update the pg_hba.conf generation so IPv6 localhost uses the same
cert-only rule as IPv4: replace or precede the line "host    all             all
::1/128                 trust" with a hostssl entry for the testdb/testuser
(e.g. "hostssl testdb testuser ::1/128 cert") and ensure this hostssl rule
appears before any broader trust rules so libpq IPv6 attempts cannot match a
permissive trust entry first; then restart PostgreSQL and re-run the radar
invocation that uses localhost.
- Around line 266-285: The pg_hba.conf here-document in test-radar.sh currently
contains broad "host ... trust" fallback lines that will match before/instead of
GSSAPI; edit the here-doc that writes "$PGDATA/pg_hba.conf" (the block
containing hostgssenc, hostssl, host ... trust) to remove the fallback "host all
... 127.0.0.1/32 trust" and "host all ... ::1/128 trust" lines and instead add
explicit hostgssenc entries for the IPv4/IPv6 addresses (or ensure hostgssenc
for testdb/testuser precedes any generic host rules) so GSSAPI-encrypted
connections are required when running ./radar -h localhost -d testdb -U testuser
-sslmode disable; keep or adjust other entries (local and hostssl) as needed.

---

Duplicate comments:
In @.github/workflows/ci.yml:
- Around line 470-486: The pg_hba.conf creation block currently adds fallback
"host" lines that use "trust", which lets TCP connections bypass GSSAPI; edit
the here-doc that writes "$PGDATA/pg_hba.conf" to remove the two fallback host
rules (the lines starting with "host    all             all            
127.0.0.1/32            trust" and "host    all             all            
::1/128                 trust") so only the GSSAPI-specific rule ("hostgssenc
testdb       testuser        127.0.0.1/32            gss include_realm=0") is
used for TCP, ensuring the test actually requires GSSAPI auth; keep the existing
local socket rule if needed and leave the surrounding restart/kinit/radar
commands unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 7b9e8fd2-bab1-41ab-ad03-715f3ba07207

📥 Commits

Reviewing files that changed from the base of the PR and between da60c1a and c73418c.

📒 Files selected for processing (5)
  • .github/workflows/ci.yml
  • README.md
  • docs/index.md
  • radar.go
  • test-radar.sh
🚧 Files skipped from review as they are similar to previous changes (2)
  • docs/index.md
  • README.md

Comment thread .github/workflows/ci.yml
Comment thread radar.go
Comment thread test-radar.sh
Comment thread test-radar.sh
@vyruss vyruss requested a review from bonesmoses March 31, 2026 18:07
Copy link
Copy Markdown
Member

@bonesmoses bonesmoses left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good addition. No notes.

@vyruss vyruss merged commit 1f4cd85 into main Apr 1, 2026
10 checks passed
@vyruss vyruss deleted the feat/auth-methods branch April 1, 2026 12:31
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.

2 participants