Skip to content

Commit 80ff224

Browse files
committed
merge: resolve v3.0 conflict and add pgsql_servers_ssl_params cluster sync
v3.0 added pgsql_servers_ssl_params table. Integrate into pgsql cluster sync path: - Add CLUSTER_QUERY_PGSQL_SERVERS_SSL_PARAMS to Admin_Handler interception block alongside other pgsql cluster queries - Add ssl_params fetch query to pull_pgsql_servers_v2_from_peer - Add update_pgsql_servers_ssl_params() for applying synced data - Update convert_pgsql_servers_resultsets to handle 5 result sets - Include ssl_params in v2 checksum computation (4 tables)
2 parents f8dcf88 + 7f14bee commit 80ff224

41 files changed

Lines changed: 676119 additions & 242 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

deps/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -370,6 +370,7 @@ postgresql/postgresql/src/interfaces/libpq/libpq.a:
370370
cd postgresql/postgresql && patch -p0 < ../fmt_err_msg.patch
371371
cd postgresql/postgresql && patch -p0 < ../bind_fmt_text.patch
372372
cd postgresql/postgresql && patch -p0 < ../pqsendpipelinesync.patch
373+
cd postgresql/postgresql && patch -p0 < ../sslkeylogfile.patch
373374
ifeq ($(UNAME_S),Darwin)
374375
cd postgresql/postgresql && LDFLAGS="-L$$(brew --prefix icu4c)/lib" CPPFLAGS="-I$$(brew --prefix icu4c)/include" PKG_CONFIG_PATH="$$(brew --prefix icu4c)/lib/pkgconfig:$$PKG_CONFIG_PATH" DYLD_LIBRARY_PATH="$(SSL_LDIR):$$DYLD_LIBRARY_PATH" ./configure --with-ssl=openssl --with-includes="$(SSL_IDIR)" --with-libraries="$(SSL_LDIR)" --without-readline --with-icu
375376
else
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
diff -ruN ../tmp/src/interfaces/libpq/fe-secure-openssl.c ./src/interfaces/libpq/fe-secure-openssl.c
2+
--- ../tmp/src/interfaces/libpq/fe-secure-openssl.c 2025-08-11 21:06:43.000000000 +0000
3+
+++ ./src/interfaces/libpq/fe-secure-openssl.c 2026-04-03 00:00:00.000000000 +0000
4+
@@ -97,6 +97,8 @@
5+
6+
static PQsslKeyPassHook_OpenSSL_type PQsslKeyPassHook = NULL;
7+
static int ssl_protocol_version_to_openssl(const char *protocol);
8+
+
9+
+static PQsslKeyLogCallback_type PQsslKeyLogCB = NULL;
10+
11+
/* ------------------------------------------------------------ */
12+
/* Procedures common to all secure sessions */
13+
@@ -972,6 +974,10 @@
14+
/* Disable old protocol versions */
15+
SSL_CTX_set_options(SSL_context, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
16+
17+
+ /* Set SSL keylog callback if configured (for TLS traffic decryption) */
18+
+ if (PQsslKeyLogCB)
19+
+ SSL_CTX_set_keylog_callback(SSL_context, (void(*)(const SSL*, const char*))PQsslKeyLogCB);
20+
+
21+
/* Set the minimum and maximum protocol versions if necessary */
22+
if (conn->ssl_min_protocol_version &&
23+
strlen(conn->ssl_min_protocol_version) != 0)
24+
@@ -1758,6 +1764,24 @@
25+
return NULL;
26+
}
27+
28+
+/*
29+
+ * SSL Key Log callback support
30+
+ *
31+
+ * Global callback for writing TLS secrets to a keylog file.
32+
+ * Follows the same pattern as PQsslKeyPassHook.
33+
+ */
34+
+PQsslKeyLogCallback_type
35+
+PQgetSSLKeyLogCallback(void)
36+
+{
37+
+ return PQsslKeyLogCB;
38+
+}
39+
+
40+
+void
41+
+PQsetSSLKeyLogCallback(PQsslKeyLogCallback_type cb)
42+
+{
43+
+ PQsslKeyLogCB = cb;
44+
+}
45+
+
46+
const char *const *
47+
PQsslAttributeNames(PGconn *conn)
48+
{
49+
diff -ruN ../tmp/src/interfaces/libpq/libpq-fe.h ./src/interfaces/libpq/libpq-fe.h
50+
--- ../tmp/src/interfaces/libpq/libpq-fe.h 2025-08-11 21:06:43.000000000 +0000
51+
+++ ./src/interfaces/libpq/libpq-fe.h 2026-04-03 00:00:00.000000000 +0000
52+
@@ -669,6 +669,11 @@
53+
extern void PQsetSSLKeyPassHook_OpenSSL(PQsslKeyPassHook_OpenSSL_type hook);
54+
extern int PQdefaultSSLKeyPassHook_OpenSSL(char *buf, int size, PGconn *conn);
55+
56+
+/* Support for SSL key log callback (TLS traffic decryption) */
57+
+typedef void (*PQsslKeyLogCallback_type)(const void *ssl, const char *line);
58+
+extern PQsslKeyLogCallback_type PQgetSSLKeyLogCallback(void);
59+
+extern void PQsetSSLKeyLogCallback(PQsslKeyLogCallback_type cb);
60+
+
61+
#ifdef __cplusplus
62+
}
63+
#endif

doc/ssl_keylog/ssl_keylog_user_guide.md

Lines changed: 44 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ SSL/TLS key logging is a debugging feature that allows ProxySQL to write TLS enc
88

99
This feature is primarily useful for:
1010

11-
- **Debugging TLS connection issues** between clients and ProxySQL
12-
- **Analyzing encrypted traffic** without modifying application code
11+
- **Debugging TLS connection issues** between clients and ProxySQL, or between ProxySQL and MySQL/PostgreSQL backends
12+
- **Analyzing encrypted backend traffic** to MySQL and PostgreSQL servers without modifying application code
1313
- **Troubleshooting TLS handshake problems**
1414
- **Performance analysis** of TLS connections
1515
- **Security auditing** of TLS configurations
@@ -222,11 +222,16 @@ In production environments, you typically don't run Wireshark directly on the se
222222
On the ProxySQL server, capture network traffic to a pcap file:
223223

224224
```bash
225-
# Capture on the interface ProxySQL is listening on (e.g., eth0)
226-
# Replace 6033 with your ProxySQL MySQL port
225+
# Capture MySQL frontend traffic (client → ProxySQL)
227226
sudo tcpdump -i eth0 -w /tmp/proxysql_debug.pcap port 6033
228227

229-
# Or capture traffic between specific hosts
228+
# Capture PgSQL frontend traffic (client → ProxySQL)
229+
sudo tcpdump -i eth0 -w /tmp/proxysql_debug.pcap port 6133
230+
231+
# Capture PgSQL backend traffic (ProxySQL → PostgreSQL server)
232+
sudo tcpdump -i eth0 -w /tmp/proxysql_debug.pcap port 5432
233+
234+
# Capture traffic between specific hosts
230235
sudo tcpdump -i eth0 -w /tmp/proxysql_debug.pcap host client_ip and host proxysql_ip
231236

232237
# Run for a specific duration
@@ -271,6 +276,9 @@ On your analysis system with Wireshark installed:
271276
# Show only MySQL packets
272277
mysql
273278
279+
# Show only PostgreSQL packets
280+
pgsql
281+
274282
# Show TLS handshake
275283
tls.handshake.type == 1
276284
@@ -407,11 +415,21 @@ mysql_variables=
407415
**Solutions:**
408416
1. Verify TLS is actually being used:
409417
```sql
410-
-- Check if connections are using TLS
418+
-- Check MySQL backend connections
411419
SELECT * FROM stats_mysql_connection_pool;
420+
-- Check PgSQL backend connections
421+
SELECT * FROM stats_pgsql_connection_pool;
422+
```
423+
2. For PgSQL backends, ensure `use_ssl=1` is set on the servers:
424+
```sql
425+
SELECT hostgroup_id, hostname, port, use_ssl FROM pgsql_servers;
426+
```
427+
3. Keylog entries are only written during **new** SSL handshakes. Existing pooled connections won't generate entries. To force new handshakes, reload servers:
428+
```sql
429+
LOAD PGSQL SERVERS TO RUNTIME;
412430
```
413-
2. Make sure clients are connecting with SSL/TLS
414-
3. Check that `admin-ssl_keylog_file` is loaded into runtime:
431+
4. Make sure clients are connecting with SSL/TLS
432+
5. Check that `admin-ssl_keylog_file` is loaded into runtime:
415433
```sql
416434
LOAD ADMIN VARIABLES TO RUNTIME;
417435
```
@@ -476,9 +494,26 @@ sudo tcpdump -i eth0 -w /tmp/capture.pcap port 6033
476494

477495
---
478496

497+
## Supported Connection Types
498+
499+
When `admin-ssl_keylog_file` is configured, TLS secrets are captured from **all** SSL/TLS connection types:
500+
501+
| Connection Type | Direction | Protocol |
502+
|----------------|-----------|----------|
503+
| Frontend (client) | Client → ProxySQL | MySQL |
504+
| Frontend (client) | Client → ProxySQL | PostgreSQL |
505+
| Backend | ProxySQL → MySQL server | MySQL |
506+
| Backend | ProxySQL → PostgreSQL server | PostgreSQL |
507+
| Monitor | ProxySQL → MySQL server | MySQL |
508+
| Monitor | ProxySQL → PostgreSQL server | PostgreSQL |
509+
| Cluster | ProxySQL → ProxySQL peer | MySQL |
510+
511+
No additional configuration is needed per connection type — the single `admin-ssl_keylog_file` variable enables logging for all types.
512+
513+
---
514+
479515
## Additional Resources
480516

481-
- **Developer Documentation:** See `ssl_keylog_developer_guide.md` for implementation details
482517
- **NSS Key Log Format:** https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS/Key_Log_Format
483518
- **Wireshark TLS Decryption:** https://wiki.wireshark.org/TLS
484519
- **tshark Manual:** `man tshark` or https://www.wireshark.org/docs/man-pages/tshark.html

docs/pgsql_servers_ssl_params.md

Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
# pgsql_servers_ssl_params
2+
3+
## Overview
4+
5+
The `pgsql_servers_ssl_params` table allows per-server SSL configuration for PostgreSQL backend connections. This enables different SSL certificates, keys, and TLS version restrictions for each backend server, overriding the global `pgsql-ssl_p2s_*` variables.
6+
7+
This is the PostgreSQL equivalent of MySQL's `mysql_servers_ssl_params` table.
8+
9+
## Table Schema
10+
11+
```sql
12+
CREATE TABLE pgsql_servers_ssl_params (
13+
hostname VARCHAR NOT NULL,
14+
port INT CHECK (port >= 0 AND port <= 65535) NOT NULL DEFAULT 5432,
15+
username VARCHAR NOT NULL DEFAULT '',
16+
ssl_ca VARCHAR NOT NULL DEFAULT '',
17+
ssl_cert VARCHAR NOT NULL DEFAULT '',
18+
ssl_key VARCHAR NOT NULL DEFAULT '',
19+
ssl_crl VARCHAR NOT NULL DEFAULT '',
20+
ssl_crlpath VARCHAR NOT NULL DEFAULT '',
21+
ssl_protocol_version_range VARCHAR NOT NULL DEFAULT '',
22+
comment VARCHAR NOT NULL DEFAULT '',
23+
PRIMARY KEY (hostname, port, username)
24+
)
25+
```
26+
27+
## Column Reference
28+
29+
| Column | Description |
30+
|--------|-------------|
31+
| `hostname` | Backend server hostname. Must match the `hostname` in `pgsql_servers`. |
32+
| `port` | Backend server port. Default: `5432`. Must match the `port` in `pgsql_servers`. |
33+
| `username` | ProxySQL username. Empty string `''` acts as a wildcard fallback (see Lookup Hierarchy). |
34+
| `ssl_ca` | Path to the CA certificate file (PEM). May contain multiple concatenated CA certs. Maps to libpq `sslrootcert`. |
35+
| `ssl_cert` | Path to the client certificate file. Maps to libpq `sslcert`. |
36+
| `ssl_key` | Path to the client private key file. Maps to libpq `sslkey`. |
37+
| `ssl_crl` | Path to the certificate revocation list file. Maps to libpq `sslcrl`. |
38+
| `ssl_crlpath` | Path to directory containing CRL files. Maps to libpq `sslcrldir` (PostgreSQL 14+). |
39+
| `ssl_protocol_version_range` | TLS protocol version constraint. See format below. |
40+
| `comment` | Free-form comment. |
41+
42+
## ssl_protocol_version_range
43+
44+
Controls which TLS protocol versions are allowed for backend connections. This maps to libpq's `ssl_min_protocol_version` and `ssl_max_protocol_version` parameters.
45+
46+
### Format
47+
48+
**Range:** `<min_version>-<max_version>`
49+
50+
Allows connections using any TLS version from `min_version` to `max_version` inclusive.
51+
52+
**Min-only:** `<min_version>-`
53+
54+
Sets a minimum TLS version with no upper bound (libpq default max applies).
55+
56+
**Max-only:** `-<max_version>`
57+
58+
Sets a maximum TLS version. The minimum defaults to libpq's built-in default (`TLSv1.2`).
59+
60+
**Single version (pin):** `<version>`
61+
62+
Pins to exactly that TLS version. Both min and max are set to the same value.
63+
64+
**Empty string:** `''`
65+
66+
Uses libpq defaults (no restriction).
67+
68+
A bare `-` is treated as malformed and ignored (a warning is logged).
69+
70+
### Valid Version Tokens
71+
72+
`TLSv1`, `TLSv1.1`, `TLSv1.2`, `TLSv1.3`
73+
74+
> **Note:** `TLSv1` and `TLSv1.1` are disabled in most modern PostgreSQL deployments. Attempting to use them will result in connection failures.
75+
76+
### Examples
77+
78+
| Value | Meaning |
79+
|-------|---------|
80+
| `TLSv1.2-TLSv1.3` | Allow TLS 1.2 and TLS 1.3 |
81+
| `TLSv1.3` | Pin to TLS 1.3 only |
82+
| `TLSv1.2-TLSv1.2` | Pin to TLS 1.2 only (equivalent to `TLSv1.2`) |
83+
| `TLSv1.2-` | Require at least TLS 1.2 (max defaults to highest OpenSSL supports) |
84+
| `-TLSv1.3` | Allow up to TLS 1.3 (min defaults to libpq's built-in `TLSv1.2`) |
85+
| `''` (empty) | Use libpq defaults |
86+
87+
## Lookup Hierarchy
88+
89+
When ProxySQL opens a new connection to a PostgreSQL backend, it looks up SSL parameters in this order:
90+
91+
1. **Exact match:** `(hostname, port, username)` — if an entry exists for the specific server and the ProxySQL user making the connection, use it.
92+
2. **Wildcard fallback:** `(hostname, port, '')` — if no exact match, check for an entry with empty username.
93+
3. **Global fallback:** If no match found, use the global `pgsql-ssl_p2s_*` variables.
94+
95+
This allows you to set a default SSL configuration for a server (empty username) while overriding it for specific users.
96+
97+
> **Important — matching is all-or-nothing.** Once a row in `pgsql_servers_ssl_params` matches (either at step 1 or step 2), ProxySQL uses **only** the SSL fields from that row. Empty columns in the matched row are passed through as empty (libpq defaults), they are **not** silently filled in from `pgsql-ssl_p2s_*`. The global variables are consulted **only** when no row matches at all (step 3). If you want a per-server row to inherit some defaults from the globals, you must copy those values into the row explicitly.
98+
99+
## Usage
100+
101+
### Basic: Same SSL cert for all users connecting to a server
102+
103+
```sql
104+
INSERT INTO pgsql_servers_ssl_params
105+
(hostname, port, ssl_ca, ssl_cert, ssl_key)
106+
VALUES
107+
('db1.example.com', 5432, '/certs/ca.crt', '/certs/client.crt', '/certs/client.key');
108+
109+
LOAD PGSQL SERVERS TO RUNTIME;
110+
SAVE PGSQL SERVERS TO DISK;
111+
```
112+
113+
### Per-user: Different certs for different applications
114+
115+
```sql
116+
-- Default for all users connecting to db1
117+
INSERT INTO pgsql_servers_ssl_params
118+
(hostname, port, username, ssl_ca, ssl_cert, ssl_key)
119+
VALUES
120+
('db1.example.com', 5432, '', '/certs/ca.crt', '/certs/default.crt', '/certs/default.key');
121+
122+
-- Override for 'billing_app' user
123+
INSERT INTO pgsql_servers_ssl_params
124+
(hostname, port, username, ssl_ca, ssl_cert, ssl_key)
125+
VALUES
126+
('db1.example.com', 5432, 'billing_app', '/certs/ca.crt', '/certs/billing.crt', '/certs/billing.key');
127+
128+
LOAD PGSQL SERVERS TO RUNTIME;
129+
SAVE PGSQL SERVERS TO DISK;
130+
```
131+
132+
### TLS version restriction
133+
134+
```sql
135+
-- Require TLS 1.3 for a specific server
136+
INSERT INTO pgsql_servers_ssl_params
137+
(hostname, port, ssl_ca, ssl_cert, ssl_key, ssl_protocol_version_range)
138+
VALUES
139+
('secure-db.example.com', 5432, '/certs/ca.crt', '/certs/client.crt', '/certs/client.key', 'TLSv1.3');
140+
141+
-- Allow TLS 1.2 or 1.3 for another server
142+
INSERT INTO pgsql_servers_ssl_params
143+
(hostname, port, ssl_ca, ssl_cert, ssl_key, ssl_protocol_version_range)
144+
VALUES
145+
('db2.example.com', 5432, '/certs/ca.crt', '/certs/client.crt', '/certs/client.key', 'TLSv1.2-TLSv1.3');
146+
147+
LOAD PGSQL SERVERS TO RUNTIME;
148+
SAVE PGSQL SERVERS TO DISK;
149+
```
150+
151+
### Multiple servers with different SSL configs
152+
153+
```sql
154+
-- Server A: uses company CA and TLS 1.3
155+
INSERT INTO pgsql_servers_ssl_params
156+
(hostname, port, ssl_ca, ssl_cert, ssl_key, ssl_protocol_version_range, comment)
157+
VALUES
158+
('db-a.internal', 5432, '/certs/company-ca.crt', '/certs/a-client.crt', '/certs/a-client.key', 'TLSv1.3', 'Internal DB');
159+
160+
-- Server B: uses AWS RDS CA bundle
161+
INSERT INTO pgsql_servers_ssl_params
162+
(hostname, port, ssl_ca, comment)
163+
VALUES
164+
('mydb.us-east-1.rds.amazonaws.com', 5432, '/certs/rds-combined-ca-bundle.pem', 'AWS RDS');
165+
166+
LOAD PGSQL SERVERS TO RUNTIME;
167+
SAVE PGSQL SERVERS TO DISK;
168+
```
169+
170+
## Viewing Configuration
171+
172+
```sql
173+
-- View configured SSL params
174+
SELECT * FROM pgsql_servers_ssl_params;
175+
176+
-- View active runtime SSL params
177+
SELECT * FROM runtime_pgsql_servers_ssl_params;
178+
```
179+
180+
## Admin Commands
181+
182+
| Command | Effect |
183+
|---------|--------|
184+
| `LOAD PGSQL SERVERS TO RUNTIME` | Promotes `pgsql_servers_ssl_params` to runtime (activates the config) |
185+
| `SAVE PGSQL SERVERS TO DISK` | Persists `pgsql_servers_ssl_params` to the on-disk database |
186+
| `LOAD PGSQL SERVERS FROM DISK` | Restores `pgsql_servers_ssl_params` from the on-disk database |
187+
188+
## Prerequisites
189+
190+
Per-server SSL parameters only take effect when `use_ssl=1` is set for the corresponding server in `pgsql_servers`:
191+
192+
```sql
193+
UPDATE pgsql_servers SET use_ssl=1 WHERE hostname='db1.example.com';
194+
LOAD PGSQL SERVERS TO RUNTIME;
195+
```
196+
197+
If `use_ssl=0`, the backend connection does not use SSL regardless of `pgsql_servers_ssl_params` entries.
198+
199+
## Notes
200+
201+
- Empty fields in `pgsql_servers_ssl_params` are omitted from the libpq connection string (libpq defaults apply for those fields).
202+
- Per-server SSL params only affect **new** backend connections. Existing pooled connections continue using their original SSL settings. Use the `/* create_new_connection=1 */` query annotation to force ProxySQL to create a new backend connection.
203+
- Per-server SSL params apply on the data path (`PgSQL_Connection`), the monitor path (`PgSQL_Monitor`), and the cancel/terminate path (`PgSQL_Backend_Kill_Args`).

0 commit comments

Comments
 (0)