Skip to content

feat: make Postgres sslmode and TLS material configurable - #379

Open
mrofreP wants to merge 2 commits into
UndernetIRC:masterfrom
mrofreP:feat/database-ssl-config
Open

mrofreP wants to merge 2 commits into
UndernetIRC:masterfrom
mrofreP:feat/database-ssl-config

Conversation

@mrofreP

@mrofreP mrofreP commented Jul 18, 2026

Copy link
Copy Markdown

Two related changes to GetDbURI, as separate commits:

feat: make Postgres sslmode and TLS material configurable
GetDbURI hardcoded ?sslmode=disable, so there was no way to run against a TLS-only Postgres. Adds database.ssl_mode plus optional ssl_root_cert / ssl_cert / ssl_key (ssl_mode defaults to disable, so existing deployments are unchanged); cert paths are appended only when set. Documents the CSERVICE_DATABASE_SSL_* env vars and require's MITM caveat in the README and config.yml.example.

fix: escape userinfo when building the database DSN
GetDbURI interpolated the username/password raw, so an unescaped / in a base64 password (per the README's openssl rand -base64 recipe) truncated the host under url.Parse and broke startup for ~half of generated passwords. Rebuilds the DSN with url.URL / url.UserPassword / net.JoinHostPort (RFC-3986 userinfo escaping, plus IPv6 bracketing). The default DSN is byte-for-byte unchanged.

Testing

  • TestGetDbURI covers default (byte-for-byte), verify-full with certs, require + root cert, and base64 / URI-delimiter passwords, each asserting a url.Parse round-trip.
  • go build, go test, go vet, and make lint all pass; both commits build independently.

mrofreP added 2 commits July 18, 2026 02:29
GetDbURI hardcoded "?sslmode=disable", so there was no way to run the
service against a TLS-only Postgres. Add four config keys --
database.ssl_mode plus optional ssl_root_cert / ssl_cert / ssl_key --
with ssl_mode defaulting to "disable" so existing deployments are
unchanged. The cert paths are appended to the DSN only when set, so
libpq/pgx can locate the CA and client material for require and stricter
modes.

Document the four CSERVICE_DATABASE_SSL_* environment variables in the
README and config.yml.example, and state plainly that "require" encrypts
without verifying the server certificate -- it does not stop an active
man-in-the-middle; verify-full is the safe choice when the server's
chain is available.

TestGetDbURI, refactored to a struct table, covers the default
(byte-for-byte unchanged), verify-full with all cert paths URL-encoded,
and require with only a root cert set.
GetDbURI built the connection string with fmt.Sprintf and interpolated
the username and password raw ("postgres://%s:%s@..."). pgx parses
postgres:// DSNs with url.Parse, which splits the authority on the first
"/", so an unescaped "/" in the password truncates the host and the
service fails to start with a misleading "invalid port" error.

This is not hypothetical: the README tells operators to generate the
password with "openssl rand -base64 32", and the base64 alphabet includes
"/", so roughly half of the passwords produced by the documented recipe
break startup. "@", ":", "?" and "#" fail the same way.

Build the URL with url.URL and url.UserPassword, which escapes userinfo
per RFC 3986 (and net.JoinHostPort, which also brackets IPv6 hosts). The
default DSN is unchanged byte-for-byte -- url.URL.String() escapes nothing
in the default credentials -- so the existing regression case still holds.
Add table cases for base64 and URI-delimiter passwords, each asserting the
DSN survives a url.Parse round-trip with the password intact.
@codacy-production

Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

🟢 Metrics 0 complexity · 0 duplication

Metric Results
Complexity 0
Duplication 0

View in Codacy

NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.

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