*** a/doc/src/sgml/monitoring.sgml
--- b/doc/src/sgml/monitoring.sgml
***************
*** 300,305 **** postgres 27093 0.0 0.0 30096 2752 ? Ss 11:34 0:00 postgres: ser
--- 300,313 ----
+
+ pg_stat_ssl>pg_stat_ssl
+ One row per connection (regular and replication), showing information about
+ SSL used on this connection.
+ See for details.
+
+
+
***************
*** 825,830 **** postgres 27093 0.0 0.0 30096 2752 ? Ss 11:34 0:00 postgres: ser
--- 833,907 ----
listed; no information is available about downstream standby servers.
+
+ pg_stat_ssl View
+
+
+
+ Column
+ Type
+ Description
+
+
+
+
+
+ pid>
+ integer>
+ Process ID of a backend or WAL sender process
+
+
+ ssl>
+ boolean>
+ True if SSL is used on this connection
+
+
+ version>
+ text>
+ Version of SSL in use, or NULL if SSL is not in use
+ on this connection
+
+
+ cipher>
+ text>
+ Name of SSL cipher in use, or NULL if SSL is not in use
+ on this connection
+
+
+ bits>
+ integer>
+ Number of bits in the encryption algorithm used, or NULL
+ if SSL is not used on this connection
+
+
+ compression>
+ boolean>
+ True if SSL compression is in use, false if not,
+ or NULL if SSL is not in use on this connection
+
+
+ clientdn>
+ text>
+ Distinguished Name (DN) field from the client certificate
+ used, or NULL if no client certificate was supplied or if SSL
+ is not in use on this connection. This field is truncated if the
+ DN field is longer than NAMEDATALEN (64 characters
+ in a standard build)
+
+
+
+
+
+
+
+ The pg_stat_ssl view will contain one row per
+ backend or WAL sender process, showing statistics about SSL usage on
+ this connection. It can be joined to pg_stat_activity
+ or pg_stat_replication on the
+ pid column to get more details about the
+ connection.
+
+
pg_stat_archiver View
*** a/src/backend/catalog/system_views.sql
--- b/src/backend/catalog/system_views.sql
***************
*** 646,651 **** CREATE VIEW pg_stat_replication AS
--- 646,662 ----
WHERE S.usesysid = U.oid AND
S.pid = W.pid;
+ CREATE VIEW pg_stat_ssl AS
+ SELECT
+ I.pid,
+ I.ssl,
+ I.version,
+ I.cipher,
+ I.bits,
+ I.compression,
+ I.clientdn
+ FROM pg_stat_get_sslstatus() AS I;
+
CREATE VIEW pg_replication_slots AS
SELECT
L.slot_name,
*** a/src/backend/libpq/be-secure-openssl.c
--- b/src/backend/libpq/be-secure-openssl.c
***************
*** 90,95 **** static void info_cb(const SSL *ssl, int type, int args);
--- 90,97 ----
static void initialize_ecdh(void);
static const char *SSLerrmessage(void);
+ static char *X509_NAME_to_cstring(X509_NAME *name);
+
/* are we in the middle of a renegotiation? */
static bool in_ssl_renegotiation = false;
***************
*** 1040,1042 **** SSLerrmessage(void)
--- 1042,1146 ----
snprintf(errbuf, sizeof(errbuf), _("SSL error code %lu"), errcode);
return errbuf;
}
+
+ /*
+ * Return information about the SSL connection
+ */
+ int
+ be_tls_get_cipher_bits(Port *port)
+ {
+ int bits;
+
+ if (port->ssl)
+ {
+ SSL_get_cipher_bits(port->ssl, &bits);
+ return bits;
+ }
+ else
+ return 0;
+ }
+
+ bool
+ be_tls_get_compression(Port *port)
+ {
+ if (port->ssl)
+ return (SSL_get_current_compression(port->ssl) != NULL);
+ else
+ return false;
+ }
+
+ void
+ be_tls_get_version(Port *port, char *ptr, size_t len)
+ {
+ if (port->ssl)
+ strlcpy(ptr, SSL_get_version(port->ssl), len);
+ else
+ ptr[0] = '\0';
+ }
+
+ void
+ be_tls_get_cipher(Port *port, char *ptr, size_t len)
+ {
+ if (port->ssl)
+ strlcpy(ptr, SSL_get_cipher(port->ssl), len);
+ else
+ ptr[0] = '\0';
+ }
+
+ void
+ be_tls_get_peerdn_name(Port *port, char *ptr, size_t len)
+ {
+ if (port->peer)
+ strlcpy(ptr, X509_NAME_to_cstring(X509_get_subject_name(port->peer)), len);
+ else
+ ptr[0] = '\0';
+ }
+
+ /*
+ * Convert an X509 subject name to a cstring.
+ *
+ */
+ static char *
+ X509_NAME_to_cstring(X509_NAME *name)
+ {
+ BIO *membuf = BIO_new(BIO_s_mem());
+ int i,
+ nid,
+ count = X509_NAME_entry_count(name);
+ X509_NAME_ENTRY *e;
+ ASN1_STRING *v;
+ const char *field_name;
+ size_t size;
+ char nullterm;
+ char *sp;
+ char *dp;
+ char *result;
+
+ (void) BIO_set_close(membuf, BIO_CLOSE);
+ for (i = 0; i < count; i++)
+ {
+ e = X509_NAME_get_entry(name, i);
+ nid = OBJ_obj2nid(X509_NAME_ENTRY_get_object(e));
+ v = X509_NAME_ENTRY_get_data(e);
+ field_name = OBJ_nid2sn(nid);
+ if (!field_name)
+ field_name = OBJ_nid2ln(nid);
+ BIO_printf(membuf, "/%s=", field_name);
+ ASN1_STRING_print_ex(membuf, v,
+ ((ASN1_STRFLGS_RFC2253 & ~ASN1_STRFLGS_ESC_MSB)
+ | ASN1_STRFLGS_UTF8_CONVERT));
+ }
+
+ /* ensure null termination of the BIO's content */
+ nullterm = '\0';
+ BIO_write(membuf, &nullterm, 1);
+ size = BIO_get_mem_data(membuf, &sp);
+ dp = pg_any_to_server(sp, size - 1, PG_UTF8);
+
+ result = pstrdup(dp);
+ if (dp != sp)
+ pfree(dp);
+ BIO_free(membuf);
+
+ return result;
+ }
*** a/src/backend/postmaster/pgstat.c
--- b/src/backend/postmaster/pgstat.c
***************
*** 2479,2484 **** pgstat_fetch_global(void)
--- 2479,2485 ----
static PgBackendStatus *BackendStatusArray = NULL;
static PgBackendStatus *MyBEEntry = NULL;
static char *BackendClientHostnameBuffer = NULL;
+ static PgBackendSSLStatus *BackendSslStatusBuffer = NULL;
static char *BackendAppnameBuffer = NULL;
static char *BackendActivityBuffer = NULL;
static Size BackendActivityBufferSize = 0;
***************
*** 2563,2568 **** CreateSharedBackendStatus(void)
--- 2564,2591 ----
}
}
+ #ifdef USE_SSL
+ /* Create or attach to the shared SSL status buffer */
+ size = mul_size(sizeof(PgBackendSSLStatus), MaxBackends);
+ BackendSslStatusBuffer = (PgBackendSSLStatus *)
+ ShmemInitStruct("Backend SSL Status Buffer", size, &found);
+
+ if (!found)
+ {
+ MemSet(BackendSslStatusBuffer, 0, size);
+
+ /* Initialize st_ssl pointers. */
+ buffer = (char *) BackendSslStatusBuffer;
+ for (i = 0; i < MaxBackends; i++)
+ {
+ BackendStatusArray[i].st_ssl = (PgBackendSSLStatus *)buffer;
+ buffer += sizeof(PgBackendSSLStatus);
+ }
+ }
+
+ /* In the non-SSL case, st_ssl is already initialized to NULL by the MemSet */
+ #endif
+
/* Create or attach to the shared activity buffer */
BackendActivityBufferSize = mul_size(pgstat_track_activity_query_size,
MaxBackends);
***************
*** 2672,2677 **** pgstat_bestart(void)
--- 2695,2716 ----
NAMEDATALEN);
else
beentry->st_clienthostname[0] = '\0';
+ #ifdef USE_SSL
+ if (MyProcPort && MyProcPort->ssl != NULL)
+ {
+ beentry->st_ssl->ssl_bits = be_tls_get_cipher_bits(MyProcPort);
+ beentry->st_ssl->ssl_compression = be_tls_get_compression(MyProcPort);
+ be_tls_get_version(MyProcPort, beentry->st_ssl->ssl_version, NAMEDATALEN);
+ be_tls_get_cipher(MyProcPort, beentry->st_ssl->ssl_cipher, NAMEDATALEN);
+ be_tls_get_peerdn_name(MyProcPort, beentry->st_ssl->ssl_clientdn, NAMEDATALEN);
+ }
+ else
+ {
+ beentry->st_ssl = NULL;
+ }
+ #else
+ beentry->st_ssl = NULL;
+ #endif
beentry->st_waiting = false;
beentry->st_state = STATE_UNDEFINED;
beentry->st_appname[0] = '\0';
***************
*** 2892,2897 **** pgstat_read_current_status(void)
--- 2931,2937 ----
volatile PgBackendStatus *beentry;
LocalPgBackendStatus *localtable;
LocalPgBackendStatus *localentry;
+ PgBackendSSLStatus *localsslstatus;
char *localappname,
*localactivity;
int i;
***************
*** 2908,2913 **** pgstat_read_current_status(void)
--- 2948,2956 ----
localappname = (char *)
MemoryContextAlloc(pgStatLocalContext,
NAMEDATALEN * MaxBackends);
+ localsslstatus = (PgBackendSSLStatus *)
+ MemoryContextAlloc(pgStatLocalContext,
+ sizeof(PgBackendSSLStatus) * MaxBackends);
localactivity = (char *)
MemoryContextAlloc(pgStatLocalContext,
pgstat_track_activity_query_size * MaxBackends);
***************
*** 2944,2949 **** pgstat_read_current_status(void)
--- 2987,2999 ----
localentry->backendStatus.st_appname = localappname;
strcpy(localactivity, (char *) beentry->st_activity);
localentry->backendStatus.st_activity = localactivity;
+ if (beentry->st_ssl)
+ {
+ memcpy(localsslstatus, beentry->st_ssl, sizeof(PgBackendSSLStatus));
+ localentry->backendStatus.st_ssl = localsslstatus;
+ }
+ else
+ localentry->backendStatus.st_ssl = NULL;
}
pgstat_save_changecount_after(beentry, after_changecount);
***************
*** 2966,2971 **** pgstat_read_current_status(void)
--- 3016,3022 ----
localentry++;
localappname += NAMEDATALEN;
localactivity += pgstat_track_activity_query_size;
+ localsslstatus += sizeof(PgBackendSSLStatus);
localNumBackends++;
}
}
*** a/src/backend/utils/adt/pgstatfuncs.c
--- b/src/backend/utils/adt/pgstatfuncs.c
***************
*** 53,58 **** extern Datum pg_stat_get_function_self_time(PG_FUNCTION_ARGS);
--- 53,59 ----
extern Datum pg_stat_get_backend_idset(PG_FUNCTION_ARGS);
extern Datum pg_stat_get_activity(PG_FUNCTION_ARGS);
+ extern Datum pg_stat_get_sslstatus(PG_FUNCTION_ARGS);
extern Datum pg_backend_pid(PG_FUNCTION_ARGS);
extern Datum pg_stat_get_backend_pid(PG_FUNCTION_ARGS);
extern Datum pg_stat_get_backend_dbid(PG_FUNCTION_ARGS);
***************
*** 823,828 **** pg_stat_get_activity(PG_FUNCTION_ARGS)
--- 824,907 ----
}
}
+ /*
+ * Returns SSL information for all connections, both regular backend and
+ * WAL senders.
+ */
+ Datum
+ pg_stat_get_sslstatus(PG_FUNCTION_ARGS)
+ {
+ #define PG_STAT_GET_SSLSTATUS_COLS 7
+ ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
+ TupleDesc tupdesc;
+ Tuplestorestate *tupstore;
+ MemoryContext per_query_ctx;
+ MemoryContext oldcontext;
+ int i;
+
+ /* check to see if caller supports us returning a tuplestore */
+ if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("set-valued function called in context that cannot accept a set")));
+ if (!(rsinfo->allowedModes & SFRM_Materialize))
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("materialize mode required, but it is not " \
+ "allowed in this context")));
+
+ /* Build a tuple descriptor for our result type */
+ if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
+ elog(ERROR, "return type must be a row type");
+
+ per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
+ oldcontext = MemoryContextSwitchTo(per_query_ctx);
+
+ tupstore = tuplestore_begin_heap(true, false, work_mem);
+ rsinfo->returnMode = SFRM_Materialize;
+ rsinfo->setResult = tupstore;
+ rsinfo->setDesc = tupdesc;
+
+ MemoryContextSwitchTo(oldcontext);
+
+ for (i = 0; i < pgstat_fetch_stat_numbackends(); i++)
+ {
+ Datum values[PG_STAT_GET_SSLSTATUS_COLS];
+ bool nulls[PG_STAT_GET_SSLSTATUS_COLS];
+ PgBackendStatus *beentry = &pgstat_fetch_stat_local_beentry(i+1)->backendStatus;
+
+ if (beentry->st_procpid == 0)
+ continue;
+
+ MemSet(nulls, 0, sizeof(nulls));
+
+ values[0] = Int32GetDatum(beentry->st_procpid);
+ values[1] = BoolGetDatum(beentry->st_ssl != NULL);
+ if (beentry->st_ssl)
+ {
+ values[2] = CStringGetTextDatum(beentry->st_ssl->ssl_version);
+ values[3] = CStringGetTextDatum(beentry->st_ssl->ssl_cipher);
+ values[4] = Int32GetDatum(beentry->st_ssl->ssl_bits);
+ values[5] = BoolGetDatum(beentry->st_ssl->ssl_compression);
+ values[6] = CStringGetTextDatum(beentry->st_ssl->ssl_clientdn);
+ }
+ else
+ {
+ nulls[2] = true; /* version */
+ nulls[3] = true; /* cipher */
+ nulls[4] = true; /* bits */
+ nulls[5] = true; /* compression */
+ nulls[6] = true; /* clientdn */
+ }
+
+ tuplestore_putvalues(tupstore, tupdesc, values, nulls);
+ }
+
+ /* clean up and return the tuplestore */
+ tuplestore_donestoring(tupstore);
+
+ return (Datum) 0;
+ }
Datum
pg_backend_pid(PG_FUNCTION_ARGS)
*** a/src/include/catalog/pg_proc.h
--- b/src/include/catalog/pg_proc.h
***************
*** 2760,2765 **** DATA(insert OID = 2022 ( pg_stat_get_activity PGNSP PGUID 12 1 100 0 0 f f f
--- 2760,2767 ----
DESCR("statistics: information about currently active backends");
DATA(insert OID = 3099 ( pg_stat_get_wal_senders PGNSP PGUID 12 1 10 0 0 f f f f f t s 0 0 2249 "" "{23,25,3220,3220,3220,3220,23,25}" "{o,o,o,o,o,o,o,o}" "{pid,state,sent_location,write_location,flush_location,replay_location,sync_priority,sync_state}" _null_ pg_stat_get_wal_senders _null_ _null_ _null_ ));
DESCR("statistics: information about currently active replication");
+ DATA(insert OID = 3284 ( pg_stat_get_sslstatus PGNSP PGUID 12 1 10 0 0 f f f f f t s 0 0 2249 "" "{23,16,25,25,23,16,25}" "{o,o,o,o,o,o,o}" "{pid,ssl,version,cipher,bits,compression,clientdn}" _null_ pg_stat_get_sslstatus _null_ _null_ _null_ ));
+ DESCR("statistics: information about SSL connections");
DATA(insert OID = 2026 ( pg_backend_pid PGNSP PGUID 12 1 0 0 0 f f f f t f s 0 0 23 "" _null_ _null_ _null_ _null_ pg_backend_pid _null_ _null_ _null_ ));
DESCR("statistics: current backend PID");
DATA(insert OID = 1937 ( pg_stat_get_backend_pid PGNSP PGUID 12 1 0 0 0 f f f f t f s 1 0 23 "23" _null_ _null_ _null_ _null_ pg_stat_get_backend_pid _null_ _null_ _null_ ));
*** a/src/include/libpq/libpq-be.h
--- b/src/include/libpq/libpq-be.h
***************
*** 212,217 **** extern void be_tls_close(Port *port);
--- 212,222 ----
extern ssize_t be_tls_read(Port *port, void *ptr, size_t len, int *waitfor);
extern ssize_t be_tls_write(Port *port, void *ptr, size_t len, int *waitfor);
+ extern int be_tls_get_cipher_bits(Port *port);
+ extern bool be_tls_get_compression(Port *port);
+ extern void be_tls_get_version(Port *port, char *ptr, size_t len);
+ extern void be_tls_get_cipher(Port *port, char *ptr, size_t len);
+ extern void be_tls_get_peerdn_name(Port *port, char *ptr, size_t len);
#endif
extern ProtocolVersion FrontendProtocol;
*** a/src/include/pgstat.h
--- b/src/include/pgstat.h
***************
*** 701,706 **** typedef enum BackendState
--- 701,723 ----
*/
+ /*
+ * PgBackendSSLStatus
+ *
+ * For each backend, we keep the SSL status in a separate struct, that
+ * is only filled in if SSL is enabled.
+ */
+ typedef struct PgBackendSSLStatus
+ {
+ /* Information about SSL connection */
+ int ssl_bits;
+ bool ssl_compression;
+ char ssl_version[NAMEDATALEN]; /* MUST be null-terminated */
+ char ssl_cipher[NAMEDATALEN]; /* MUST be null-terminated */
+ char ssl_clientdn[NAMEDATALEN]; /* MUST be null-terminated */
+ } PgBackendSSLStatus;
+
+
/* ----------
* PgBackendStatus
*
***************
*** 744,749 **** typedef struct PgBackendStatus
--- 761,769 ----
SockAddr st_clientaddr;
char *st_clienthostname; /* MUST be null-terminated */
+ /* Information about SSL connection */
+ PgBackendSSLStatus *st_ssl;
+
/* Is backend currently waiting on an lmgr lock? */
bool st_waiting;
*** a/src/test/regress/expected/rules.out
--- b/src/test/regress/expected/rules.out
***************
*** 1743,1748 **** pg_stat_replication| SELECT s.pid,
--- 1743,1756 ----
pg_authid u,
pg_stat_get_wal_senders() w(pid, state, sent_location, write_location, flush_location, replay_location, sync_priority, sync_state)
WHERE ((s.usesysid = u.oid) AND (s.pid = w.pid));
+ pg_stat_ssl| SELECT i.pid,
+ i.ssl,
+ i.bits,
+ i.compression,
+ i.version,
+ i.cipher,
+ i.clientdn
+ FROM pg_stat_get_sslstatus() i(pid, ssl, bits, compression, version, cipher, clientdn);
pg_stat_sys_indexes| SELECT pg_stat_all_indexes.relid,
pg_stat_all_indexes.indexrelid,
pg_stat_all_indexes.schemaname,