Skip to content

Conversation

@mbj
Copy link
Contributor

@mbj mbj commented Nov 12, 2025

  • Before hostaddr was used to just be an alternative way to set host. But this is not how libpq actually behaves or intents hostaddr to be used.
  • Instead hostaddr is meant to contain a numeric IP address to connect to, bypassing DNS lookups. This can be useful if the DNS lookups are slow, or if the DNS lookup has to be bypassed as the port is proxied to localhost for ifrastructure reasons. The TLS verification is still always done against the host parameter. Basically it allows to do verify-full on proxied ports.

Libpq docs:

https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-PARAMKEYWORDS

Does your PR solve an issue?

Yes but issue was not reported yet, the original implementation of hostaddr parameters only misleading/bad semantics.

Is this a breaking change?

Can be argued both ways. Its definitively a bugfix, but could be that the bug is loadbearing. I'm fine either way, going to use a fork till release.

@mbj mbj force-pushed the fix/hostaddr-semantics branch 3 times, most recently from 743da8b to df9a233 Compare November 12, 2025 19:02
@mbj mbj force-pushed the fix/hostaddr-semantics branch 3 times, most recently from 4dcf7d1 to 96a356c Compare November 12, 2025 19:10
* Since 3.x extensions in the CSR are not automatically carried * Needed as rustls does not use the CN to verify hostnames it uses subject alternative DNS names only.
@mbj mbj force-pushed the fix/hostaddr-semantics branch from 96a356c to 207c342 Compare November 12, 2025 21:52
25iUuef8MB8GA1UdIwQYMBaAFAsNqVaRis9sZCI21R2IdC583ZFuMAUGAytlcANB
AKyosmZvuCIrWkvb4QN8k2Fwf09LICCNjh571XwNxp9eUEEwJOjl956o6SFxDlgK
Cr1llASvz5cPm6jUV2wlaQc=
-----END CERTIFICATE-----
Copy link
Contributor Author

@mbj mbj Nov 12, 2025

Choose a reason for hiding this comment

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

Before:

Certificate: Data: Version: 3 (0x2) Serial Number: b6:c7:1f:4b:11:c0:a1:8f Signature Algorithm: ED25519 Issuer: C=us, ST=california, O=Internet Widgits Pty Ltd, CN=Austin Bonander Validity Not Before: Jul 1 05:21:56 2025 GMT Not After : Jun 29 05:21:56 2035 GMT Subject: C=us, ST=california, O=SQLx.rs, CN=sqlx.rs Subject Public Key Info: Public Key Algorithm: ED25519 ED25519 Public-Key: pub: 0d:f7:4b:6a:ac:aa:96:6c:b1:47:18:ae:99:5e:31: 70:e3:e7:f9:a1:6f:9e:87:3d:a3:b7:08:82:20:63: 97:3c X509v3 extensions: X509v3 Subject Key Identifier: 3D:4A:67:F7:91:87:16:E3:1E:EF:ED:A9:1B:9A:DB:98:94:B9:E7:FC X509v3 Authority Key Identifier: 0B:0D:A9:56:91:8A:CF:6C:64:22:36:D5:1D:88:74:2E:7C:DD:91:6E Signature Algorithm: ED25519 Signature Value: 13:11:0e:cd:af:48:ad:27:21:a1:0b:35:37:03:f1:7c:27:5f: 5c:c1:e2:5e:9b:20:31:38:43:ba:28:3e:f6:16:ae:5b:e6:f5: 2a:50:51:dd:97:96:f0:03:88:c1:ac:cc:85:db:2d:04:53:a1: ba:66:96:3d:0f:f0:64:d5:24:0a 

After:

$ openssl x509 -text -noout -in server.crt Certificate: Data: Version: 3 (0x2) Serial Number: 2c:da:80:96:d8:8e:10:fe:5f:79:0a:e3:20:15:d8:03:a6:02:d1:f5 Signature Algorithm: ED25519 Issuer: C=us, ST=california, O=Internet Widgits Pty Ltd, CN=Austin Bonander Validity Not Before: Nov 12 21:49:58 2025 GMT Not After : Nov 10 21:49:58 2035 GMT Subject: C=us, ST=california, O=SQLx.rs, CN=sqlx.rs Subject Public Key Info: Public Key Algorithm: ED25519 ED25519 Public-Key: pub: 0d:f7:4b:6a:ac:aa:96:6c:b1:47:18:ae:99:5e:31: 70:e3:e7:f9:a1:6f:9e:87:3d:a3:b7:08:82:20:63: 97:3c X509v3 extensions: X509v3 Subject Alternative Name: DNS:sqlx.rs X509v3 Subject Key Identifier: 3D:4A:67:F7:91:87:16:E3:1E:EF:ED:A9:1B:9A:DB:98:94:B9:E7:FC X509v3 Authority Key Identifier: 0B:0D:A9:56:91:8A:CF:6C:64:22:36:D5:1D:88:74:2E:7C:DD:91:6E Signature Algorithm: ED25519 Signature Value: ac:a8:b2:66:6f:b8:22:2b:5a:4b:db:e1:03:7c:93:61:70:7f: 4f:4b:20:20:8d:8e:1e:7b:d5:7c:0d:c6:9f:5e:50:41:30:24: e8:e5:f7:9e:a8:e9:21:71:0e:58:0a:0a:bd:65:94:04:af:cf: 97:0f:9b:a8:d4:57:6c:25:69:07 

Note the subject alternative name is now present (was absent before)!


```
openssl x509 -req -CA ca.crt -CAkey keys/ca.key -in server.csr -out server.crt -days 3650 -CAcreateserial
openssl x509 -req -CA ca.crt -CAkey keys/ca.key -in server.csr -out server.crt -days 3650 -CAcreateserial -copy_extensions copy
Copy link
Contributor Author

Choose a reason for hiding this comment

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

OT: I consider it a terrible design descision to silently truncate extensions, instead they should have bailed "unacknowledged extension A, B, C present".

Copy link
Contributor Author

Choose a reason for hiding this comment

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

context: rustls does not use the CN field to populate present hostnames for hostname checks. Instead it exclusively looks at the subject alternative DNS names. All modern CAs always emit subject alternative names, so we need to properly create one to allow verify-full in tests (followup commit does so after fixing the hostaddr bug).

@mbj mbj force-pushed the fix/hostaddr-semantics branch from 1d469ca to b3c90e1 Compare November 12, 2025 22:11
@mbj mbj marked this pull request as ready for review November 12, 2025 22:11
.unwrap_or_else(|| default_host(port));
let host = var("PGHOST").ok().unwrap_or_else(|| default_host(port));

let host_addr = var("PGHOSTADDR").ok();
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Now not all strings are valid here, only numeric IPs are. But we are not doing any validation on the strings at all for all the sibling fields, so I did not start to do that here also. In fact there is not even an error path in this API.

That I do not agree to the defaulting to ENVs in the first place was discussed in previous PRs, for the moment I consider the bugfix more important than improving tghe API so I just keep it "as wrong in my eyes as the siblings" while fixing host != hostaddr.

None => net::connect_tcp(&options.host, options.port, MaybeUpgradeTls(options)).await?,
None => {
net::connect_tcp(
options.host_addr.as_ref().unwrap_or(&options.host),
Copy link
Contributor Author

@mbj mbj Nov 12, 2025

Choose a reason for hiding this comment

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

I've modified the integration tests to cover this behavior, they now:

  • instruct the ssl tests to connect to a host: sqlx.rs that does not resolve to a postgresql instance on the public DNS.
  • but provide the hostaddr to point it to 127.0.0.1 bypassing DNS lookups
  • proof that cert validation fully works in this scenario in upgrading to verify-full from verify-ca.

I use sqlx.rs as hostname for this test as this is the hostname present in the test certificates.

* Before hostaddr was used to just be an alternative way to set `host`. But this is not how libpq actually behaves or intents hostaddr to be used. * Instead `hostaddr` is meant to contain a numeric IP address to connect to, bypassing DNS lookups. This can be useful if the DNS lookups are slow, or if the DNS lookup has to be bypassed as the port is proxied to localhost for ifrastructure reasons. The TLS verification is still always done against the `host` parameter. Basically it allows to do `verify-full` on proxied ports.
@mbj mbj force-pushed the fix/hostaddr-semantics branch from b3c90e1 to f795fe9 Compare November 13, 2025 00:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

1 participant