You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
PostgreSQL buildDSN does not URL-encode cfg.User — usernames containing ":" (e.g. Vault dynamic credentials) break authentication #12192
The Temporal server should connect to PostgreSQL regardless of URI-reserved characters in the username. In particular, a username containing : (colon) — the shape produced by HashiCorp
Vault's database secrets engine for dynamic/per-lease credentials, e.g. entity:serv-xxxx —
must authenticate correctly.
Actual Behavior
Authentication fails when the username contains :. The colon is interpreted as the user:password separator inside the connection URL userinfo component, so the username is
truncated at the colon and PostgreSQL rejects the login (password authentication failed / no such user).
The credentials themselves are valid — they authenticate correctly when the username does not
contain URI-reserved characters, or when the username is percent-encoded before building the DSN.
Root cause (in the source)
common/persistence/sql/sqlplugin/postgresql/session/session.go, function buildDSN:
constdsnFmt="postgres://%v:%v@%v/%v?%v"// ...returnfmt.Sprintf(
dsnFmt,
cfg.User, // <-- username interpolated RAW (not encoded)url.QueryEscape(password), // <-- password IS escapedresolvedAddr,
cfg.DatabaseName,
tlsAttrs.Encode(),
), nil
cfg.User is interpolated raw into the postgres://<user>:<password>@... URL, while the
password is passed through url.QueryEscape. So any URI-reserved character in the username
(:, /, @, ?, #) corrupts the DSN. For entity:serv-xxxx the effective parsed username
becomes entity and serv-xxxx is misread as the start of the password.
Two observations:
The username is not encoded at all — this is the primary bug.
url.QueryEscape is also not strictly the correct encoder for the userinfo component
(it is query-string encoding, e.g. it turns a space into +, and does not match RFC 3986
userinfo rules). Building the userinfo via net/url (e.g. url.User(cfg.User) / url.UserPassword(cfg.User, password) and letting url.URL.String() render it) would encode
both fields correctly. This is likely the same class of issue behind the password-side report Temporal fails to connect to Google Cloud SQL Postgres when password contains certain characters #5729.
Suggested fix
Compose the DSN with net/url so both username and password are correctly percent-encoded for
the userinfo component, instead of interpolating raw values into dsnFmt. For example, build a url.URL{ Scheme: "postgres", User: url.UserPassword(cfg.User, password), Host: resolvedAddr, Path: cfg.DatabaseName, RawQuery: tlsAttrs.Encode() } and use its String(). At minimum,
percent-encode cfg.User the same way the password is handled.
Steps to Reproduce the Problem
Create a PostgreSQL login role whose name contains a colon, e.g. entity:serv-abcd, with a
known password and access to the Temporal database.
Configure the sql datastore with user: "entity:serv-abcd", the matching password, and
the postgres plugin (pgx).
Start the server → password authentication failed for user "entity" (username truncated at
the colon).
Rename the role to remove the colon → identical setup works. This isolates the failure to DSN
composition, not the credentials.
This is the exact scenario when delivering DB credentials via Vault dynamic secrets (per-lease
usernames of the form entity:serv-...), synced into the pod by the Vault Secrets Operator.
Specifications
Version: v1.31.2 (also present on main — the buildDSN code above is current).
Plugin/driver: postgres (pgx).
Platform: Kubernetes; credentials delivered as a native Secret (username + password), rotated
per Vault lease.
Reproduced on current main (server 1.32.0, postgres12 plugin).
Setup: Postgres role "entity:serv-abcd" with password s3cret, both postgres-default and postgres-visibility pointed at that user. Starting the server fails during the schema version check:
pq: password authentication failed for user "entity" (28P01) sql schema version compatibility check failed: unable to read DB schema version keyspace/database: temporal error: no usable database connection found
The same password works when the role name has no colon. A percent-encoded username (entity%3Aserv-abcd) also authenticates as entity:serv-abcd, so the credentials are valid and the failure is in buildDSN.
I'd like to work on this. Plan is to build the DSN with url.UserPassword so the username and password are both encoded for the userinfo component, and add a unit test for a colon in the username.
Expected Behavior
The Temporal server should connect to PostgreSQL regardless of URI-reserved characters in the
username. In particular, a username containing
:(colon) — the shape produced by HashiCorpVault's database secrets engine for dynamic/per-lease credentials, e.g.
entity:serv-xxxx—must authenticate correctly.
Actual Behavior
Authentication fails when the username contains
:. The colon is interpreted as theuser:passwordseparator inside the connection URL userinfo component, so the username istruncated at the colon and PostgreSQL rejects the login (
password authentication failed/no such user).The credentials themselves are valid — they authenticate correctly when the username does not
contain URI-reserved characters, or when the username is percent-encoded before building the DSN.
Root cause (in the source)
common/persistence/sql/sqlplugin/postgresql/session/session.go, functionbuildDSN:cfg.Useris interpolated raw into thepostgres://<user>:<password>@...URL, while thepassword is passed through
url.QueryEscape. So any URI-reserved character in the username(
:,/,@,?,#) corrupts the DSN. Forentity:serv-xxxxthe effective parsed usernamebecomes
entityandserv-xxxxis misread as the start of the password.Two observations:
url.QueryEscapeis also not strictly the correct encoder for the userinfo component(it is query-string encoding, e.g. it turns a space into
+, and does not match RFC 3986userinfo rules). Building the userinfo via
net/url(e.g.url.User(cfg.User)/url.UserPassword(cfg.User, password)and lettingurl.URL.String()render it) would encodeboth fields correctly. This is likely the same class of issue behind the password-side report
Temporal fails to connect to Google Cloud SQL Postgres when password contains certain characters #5729.
Suggested fix
Compose the DSN with
net/urlso both username and password are correctly percent-encoded forthe userinfo component, instead of interpolating raw values into
dsnFmt. For example, build aurl.URL{ Scheme: "postgres", User: url.UserPassword(cfg.User, password), Host: resolvedAddr, Path: cfg.DatabaseName, RawQuery: tlsAttrs.Encode() }and use itsString(). At minimum,percent-encode
cfg.Userthe same way the password is handled.Steps to Reproduce the Problem
entity:serv-abcd, with aknown password and access to the Temporal database.
sqldatastore withuser: "entity:serv-abcd", the matchingpassword, andthe
postgresplugin (pgx).password authentication failed for user "entity"(username truncated atthe colon).
composition, not the credentials.
This is the exact scenario when delivering DB credentials via Vault dynamic secrets (per-lease
usernames of the form
entity:serv-...), synced into the pod by the Vault Secrets Operator.Specifications
main— thebuildDSNcode above is current).postgres(pgx).per Vault lease.
Related
applies
url.QueryEscapeto the password only; the username (cfg.User) is stillinterpolated raw, which is what this issue is about.