| From: | Renaud Métrich <rmetrich(at)redhat(dot)com> |
|---|---|
| To: | pgsql-hackers(at)lists(dot)postgresql(dot)org |
| Subject: | [PATCH v1] Add ssl_alt_cert_file/ssl_alt_key_file for dual RSA+ECDSA certificate support |
| Date: | 2026-06-12 10:05:24 |
| Message-ID: | 0cabf744-6d58-4bc4-817a-413c804cf61d@redhat.com |
| Views: | Whole Thread | Raw Message | Download mbox | Resend email |
| Thread: | |
| Lists: | pgsql-hackers |
Hi hackers,
PostgreSQL currently supports only one SSL certificate per server instance
(via ssl_cert_file/ssl_key_file). Web servers like httpd, nginx, and
haproxy have long supported serving multiple certificates of different key
types (e.g., RSA and ECDSA simultaneously), letting OpenSSL select the
appropriate one during the TLS handshake based on the negotiated cipher
suite. PostgreSQL cannot do this today, and there is no viable workaround
— TLS-terminating proxies don't work because PostgreSQL uses an
in-protocol SSL upgrade rather than raw TLS connections.
This patch adds two new GUC parameters, ssl_alt_cert_file and
ssl_alt_key_file, which allow loading an alternate certificate alongside
the primary one. OpenSSL natively supports one certificate per key type
in a single SSL_CTX; we just need to call SSL_CTX_use_certificate_file()
and SSL_CTX_use_PrivateKey_file() a second time for the alternate key
type.
The main challenge was the SNI architecture introduced in PG 19. The
ssl_update_ssl() function copies the certificate from the per-host
SSL_CTX to the per-connection SSL object, but the original code calls
SSL_CTX_get0_certificate() only once — which returns a single cert —
and SSL_use_cert_and_key() with override=1, wiping any additional certs.
The fix iterates all certificate types in the SSL_CTX using
SSL_CTX_set_current_cert(SSL_CERT_SET_FIRST/NEXT) and loads each onto
the SSL object with override=1 for the first and override=0 for
subsequent ones.
A secondary issue was that be_tls_get_server_cert_types() originally
accessed SSL_hosts->default_host->ssl_ctx at query time, but SSL_hosts
is allocated in PostmasterContext which child backends delete after
InitPostgres(). This caused a segfault. The fix caches the cert types
string in a static variable during be_tls_init() while the SSL_CTX is
still valid.
For observability, the patch adds ssl_server_cert_type() and
ssl_server_cert_types() to the sslinfo extension (bumped to v1.3):
SELECT ssl_server_cert_type(); -- 'ECDSA' (per-connection)
SELECT ssl_server_cert_types(); -- 'RSA, ECDSA' (server-wide)
Usage is straightforward:
# postgresql.conf
ssl = on
ssl_cert_file = 'server.crt' # RSA certificate
ssl_key_file = 'server.key'
ssl_alt_cert_file = 'server-ecdsa.crt' # ECDSA certificate
ssl_alt_key_file = 'server-ecdsa.key'
Both parameters must be set together; setting one without the other
produces a configuration error. When neither is set, behavior is
identical to an unpatched server.
The patch targets master (currently 19beta1) and is intended for
application — it is not WIP. It compiles cleanly, and the full SSL
test suite passes without regressions: 443/443 tests (001_ssltests,
002_scram, 003_sslinfo, 004_sni, 004_ssl_alt_cert). The new test
004_ssl_alt_cert.pl covers 20 subtests: basic connectivity, GUC
verification, cipher-specific cert selection via openssl s_client,
configuration mismatch validation, and the sslinfo observability
functions.
Testing was done on RHEL 9.8 (x86_64) with OpenSSL 3.5.5. There are
no platform-specific items; the patch uses only standard OpenSSL APIs
available since OpenSSL 1.1.0.
Performance impact is negligible — the only added overhead is one extra
SSL_CTX_use_certificate_file()/SSL_CTX_use_PrivateKey_file() call during
server startup or SIGHUP reload, and the SSL_CTX_set_current_cert()
iteration in ssl_update_ssl() which adds one loop iteration per
additional key type (typically just one: ECDSA alongside RSA).
The patch includes documentation updates (config.sgml, runtime.sgml,
sslinfo.sgml) and regression tests.
I chose to use a separate ssl_alt_cert_file/ssl_alt_key_file GUC pair
rather than a list-based approach (e.g., ssl_cert_file accepting
multiple values) to keep the interface simple and backward-compatible.
In practice, the only two key types in widespread use today are RSA and
ECDSA — EdDSA (Ed25519/Ed448) server certificates are still not
commonly issued by public CAs and have limited client support. A single
alternate pair therefore covers the real-world need. Should EdDSA
adoption grow in the future, the ssl_update_ssl() fix in this patch
already handles an arbitrary number of key types in the SSL_CTX, so
extending the interface would be straightforward.
The attached patch is:
v1-0001-Add-ssl_alt_cert_file-ssl_alt_key_file-for-dual-R.patch
16 files changed, 591 insertions(+), 22 deletions(-)
Thanks,
Renaud Métrich
Red Hat
| Attachment | Content-Type | Size |
|---|---|---|
| v1-0001-Add-ssl_alt_cert_file-ssl_alt_key_file-for-dual-R.patch | text/x-patch | 29.2 KB |
| From | Date | Subject | |
|---|---|---|---|
| Next Message | Ashutosh Sharma | 2026-06-12 10:10:23 | Re: synchronized_standby_slots behavior inconsistent with quorum-based synchronous replication |
| Previous Message | Matthias van de Meent | 2026-06-12 10:03:55 | Re: Commit Sequence Numbers and Visibility |