]> granicus.if.org Git - postgresql/commitdiff
Add system view pg_stat_ssl
authorMagnus Hagander <magnus@hagander.net>
Sun, 12 Apr 2015 17:07:46 +0000 (19:07 +0200)
committerMagnus Hagander <magnus@hagander.net>
Sun, 12 Apr 2015 17:07:46 +0000 (19:07 +0200)
This view shows information about all connections, such as if the
connection is using SSL, which cipher is used, and which client
certificate (if any) is used.

Reviews by Alex Shulgin, Heikki Linnakangas, Andres Freund & Michael Paquier

doc/src/sgml/monitoring.sgml
src/backend/catalog/system_views.sql
src/backend/libpq/be-secure-openssl.c
src/backend/postmaster/pgstat.c
src/backend/utils/adt/pgstatfuncs.c
src/include/catalog/catversion.h
src/include/catalog/pg_proc.h
src/include/libpq/libpq-be.h
src/include/pgstat.h
src/test/regress/expected/rules.out

index 71d06ce513bf90e654e9df0d512aac2ad52fe7f8..e64b7ef0c5fac2ccc172a4a127fce32b0d36f001 100644 (file)
@@ -300,6 +300,14 @@ postgres   27093  0.0  0.0  30096  2752 ?        Ss   11:34   0:00 postgres: ser
       </entry>
      </row>
 
+     <row>
+      <entry><structname>pg_stat_ssl</><indexterm><primary>pg_stat_ssl</primary></indexterm></entry>
+      <entry>One row per connection (regular and replication), showing information about
+       SSL used on this connection.
+       See <xref linkend="pg-stat-ssl-view"> for details.
+      </entry>
+     </row>
+
     </tbody>
    </tgroup>
   </table>
@@ -825,6 +833,75 @@ postgres   27093  0.0  0.0  30096  2752 ?        Ss   11:34   0:00 postgres: ser
    listed; no information is available about downstream standby servers.
   </para>
 
+  <table id="pg-stat-ssl-view" xreflabel="pg_stat_ssl">
+   <title><structname>pg_stat_ssl</structname> View</title>
+   <tgroup cols="3">
+    <thead>
+    <row>
+      <entry>Column</entry>
+      <entry>Type</entry>
+      <entry>Description</entry>
+     </row>
+    </thead>
+
+   <tbody>
+    <row>
+     <entry><structfield>pid</></entry>
+     <entry><type>integer</></entry>
+     <entry>Process ID of a backend or WAL sender process</entry>
+    </row>
+    <row>
+     <entry><structfield>ssl</></entry>
+     <entry><type>boolean</></entry>
+     <entry>True if SSL is used on this connection</entry>
+    </row>
+    <row>
+     <entry><structfield>version</></entry>
+     <entry><type>text</></entry>
+     <entry>Version of SSL in use, or NULL if SSL is not in use
+      on this connection</entry>
+    </row>
+    <row>
+     <entry><structfield>cipher</></entry>
+     <entry><type>text</></entry>
+     <entry>Name of SSL cipher in use, or NULL if SSL is not in use
+      on this connection</entry>
+    </row>
+    <row>
+     <entry><structfield>bits</></entry>
+     <entry><type>integer</></entry>
+     <entry>Number of bits in the encryption algorithm used, or NULL
+     if SSL is not used on this connection</entry>
+    </row>
+    <row>
+     <entry><structfield>compression</></entry>
+     <entry><type>boolean</></entry>
+     <entry>True if SSL compression is in use, false if not,
+      or NULL if SSL is not in use on this connection</entry>
+    </row>
+    <row>
+     <entry><structfield>clientdn</></entry>
+     <entry><type>text</></entry>
+     <entry>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 <symbol>NAMEDATALEN</symbol> (64 characters
+      in a standard build)
+     </entry>
+    </row>
+   </tbody>
+   </tgroup>
+  </table>
+
+  <para>
+   The <structname>pg_stat_ssl</structname> view will contain one row per
+   backend or WAL sender process, showing statistics about SSL usage on
+   this connection. It can be joined to <structname>pg_stat_activity</structname>
+   or <structname>pg_stat_replication</structname> on the
+   <structfield>pid</structfield> column to get more details about the
+   connection.
+  </para>
+
 
   <table id="pg-stat-archiver-view" xreflabel="pg_stat_archiver">
    <title><structname>pg_stat_archiver</structname> View</title>
index 2800f73fb6e6adcbd65abaeed40b93fc1545c160..a4fd88fa98d7edc9674240d00f6de06e7f600a1b 100644 (file)
@@ -646,6 +646,17 @@ CREATE VIEW pg_stat_replication AS
     WHERE S.usesysid = U.oid AND
             S.pid = W.pid;
 
+CREATE VIEW pg_stat_ssl AS
+    SELECT
+            S.pid,
+            S.ssl,
+            S.sslversion AS version,
+            S.sslcipher AS cipher,
+            S.sslbits AS bits,
+            S.sslcompression AS compression,
+            S.sslclientdn AS clientdn
+    FROM pg_stat_get_activity(NULL) AS S;
+
 CREATE VIEW pg_replication_slots AS
     SELECT
             L.slot_name,
index b06f987b3fd2c14bae57809fdd85913e6900f1b5..f7f6618bc21143c1474eafe4a6c0ba44de29ad17 100644 (file)
@@ -90,6 +90,8 @@ static void info_cb(const SSL *ssl, int type, int args);
 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,3 +1042,105 @@ SSLerrmessage(void)
        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;
+}
index 0e7c1544ec0ee75ec2284cd124582e9731c94b96..1e6073abca4995f8866791e6664964e05568be8b 100644 (file)
@@ -2482,6 +2482,9 @@ static char *BackendClientHostnameBuffer = NULL;
 static char *BackendAppnameBuffer = NULL;
 static char *BackendActivityBuffer = NULL;
 static Size BackendActivityBufferSize = 0;
+#ifdef USE_SSL
+static PgBackendSSLStatus *BackendSslStatusBuffer = NULL;
+#endif
 
 
 /*
@@ -2563,6 +2566,26 @@ CreateSharedBackendStatus(void)
                }
        }
 
+#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_sslstatus pointers. */
+               buffer = (char *) BackendSslStatusBuffer;
+               for (i = 0; i < MaxBackends; i++)
+               {
+                       BackendStatusArray[i].st_sslstatus = (PgBackendSSLStatus *)buffer;
+                       buffer += sizeof(PgBackendSSLStatus);
+               }
+       }
+#endif
+
        /* Create or attach to the shared activity buffer */
        BackendActivityBufferSize = mul_size(pgstat_track_activity_query_size,
                                                                                 MaxBackends);
@@ -2672,6 +2695,23 @@ pgstat_bestart(void)
                                NAMEDATALEN);
        else
                beentry->st_clienthostname[0] = '\0';
+#ifdef USE_SSL
+       if (MyProcPort && MyProcPort->ssl != NULL)
+       {
+               beentry->st_ssl = true;
+               beentry->st_sslstatus->ssl_bits = be_tls_get_cipher_bits(MyProcPort);
+               beentry->st_sslstatus->ssl_compression = be_tls_get_compression(MyProcPort);
+               be_tls_get_version(MyProcPort, beentry->st_sslstatus->ssl_version, NAMEDATALEN);
+               be_tls_get_cipher(MyProcPort, beentry->st_sslstatus->ssl_cipher, NAMEDATALEN);
+               be_tls_get_peerdn_name(MyProcPort, beentry->st_sslstatus->ssl_clientdn, NAMEDATALEN);
+       }
+       else
+       {
+               beentry->st_ssl = false;
+       }
+#else
+       beentry->st_ssl = false;
+#endif
        beentry->st_waiting = false;
        beentry->st_state = STATE_UNDEFINED;
        beentry->st_appname[0] = '\0';
@@ -2892,6 +2932,9 @@ pgstat_read_current_status(void)
        volatile PgBackendStatus *beentry;
        LocalPgBackendStatus *localtable;
        LocalPgBackendStatus *localentry;
+#ifdef USE_SSL
+       PgBackendSSLStatus *localsslstatus;
+#endif
        char       *localappname,
                           *localactivity;
        int                     i;
@@ -2908,6 +2951,12 @@ pgstat_read_current_status(void)
        localappname = (char *)
                MemoryContextAlloc(pgStatLocalContext,
                                                   NAMEDATALEN * MaxBackends);
+#ifdef USE_SSL
+       localsslstatus = (PgBackendSSLStatus *)
+               MemoryContextAlloc(pgStatLocalContext,
+                                                  sizeof(PgBackendSSLStatus) * MaxBackends);
+#endif
+
        localactivity = (char *)
                MemoryContextAlloc(pgStatLocalContext,
                                                   pgstat_track_activity_query_size * MaxBackends);
@@ -2944,6 +2993,14 @@ pgstat_read_current_status(void)
                                localentry->backendStatus.st_appname = localappname;
                                strcpy(localactivity, (char *) beentry->st_activity);
                                localentry->backendStatus.st_activity = localactivity;
+                               localentry->backendStatus.st_ssl = beentry->st_ssl;
+#ifdef USE_SSL
+                               if (beentry->st_ssl)
+                               {
+                                       memcpy(localsslstatus, beentry->st_sslstatus, sizeof(PgBackendSSLStatus));
+                                       localentry->backendStatus.st_sslstatus = localsslstatus;
+                               }
+#endif
                        }
 
                        pgstat_save_changecount_after(beentry, after_changecount);
@@ -2966,6 +3023,9 @@ pgstat_read_current_status(void)
                        localentry++;
                        localappname += NAMEDATALEN;
                        localactivity += pgstat_track_activity_query_size;
+#ifdef USE_SSL
+                       localsslstatus += sizeof(PgBackendSSLStatus);
+#endif
                        localNumBackends++;
                }
        }
index 78adb2d853cbf7b61fbdd9512b5b113a2c648b5b..bbe94c34a15976ea76bb54a9673edfd928c5333d 100644 (file)
@@ -538,7 +538,7 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
 
                oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
 
-               tupdesc = CreateTemplateTupleDesc(16, false);
+               tupdesc = CreateTemplateTupleDesc(22, false);
                TupleDescInitEntry(tupdesc, (AttrNumber) 1, "datid",
                                                   OIDOID, -1, 0);
                TupleDescInitEntry(tupdesc, (AttrNumber) 2, "pid",
@@ -571,6 +571,18 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
                                                   XIDOID, -1, 0);
                TupleDescInitEntry(tupdesc, (AttrNumber) 16, "backend_xmin",
                                                   XIDOID, -1, 0);
+               TupleDescInitEntry(tupdesc, (AttrNumber) 17, "ssl",
+                                                  BOOLOID, -1, 0);
+               TupleDescInitEntry(tupdesc, (AttrNumber) 18, "sslversion",
+                                                  TEXTOID, -1, 0);
+               TupleDescInitEntry(tupdesc, (AttrNumber) 19, "sslcipher",
+                                                  TEXTOID, -1, 0);
+               TupleDescInitEntry(tupdesc, (AttrNumber) 20, "sslbits",
+                                                  INT4OID, -1, 0);
+               TupleDescInitEntry(tupdesc, (AttrNumber) 21, "sslcompression",
+                                                  BOOLOID, -1, 0);
+               TupleDescInitEntry(tupdesc, (AttrNumber) 22, "sslclientdn",
+                                                  TEXTOID, -1, 0);
 
                funcctx->tuple_desc = BlessTupleDesc(tupdesc);
 
@@ -622,8 +634,8 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
        if (funcctx->call_cntr < funcctx->max_calls)
        {
                /* for each row */
-               Datum           values[16];
-               bool            nulls[16];
+               Datum           values[22];
+               bool            nulls[22];
                HeapTuple       tuple;
                LocalPgBackendStatus *local_beentry;
                PgBackendStatus *beentry;
@@ -676,6 +688,21 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
                else
                        nulls[15] = true;
 
+               if (beentry->st_ssl)
+               {
+                       values[16] = BoolGetDatum(true); /* ssl */
+                       values[17] = CStringGetTextDatum(beentry->st_sslstatus->ssl_version);
+                       values[18] = CStringGetTextDatum(beentry->st_sslstatus->ssl_cipher);
+                       values[19] = Int32GetDatum(beentry->st_sslstatus->ssl_bits);
+                       values[20] = BoolGetDatum(beentry->st_sslstatus->ssl_compression);
+                       values[21] = CStringGetTextDatum(beentry->st_sslstatus->ssl_clientdn);
+               }
+               else
+               {
+                       values[16] = BoolGetDatum(false); /* ssl */
+                       nulls[17] = nulls[18] = nulls[19] = nulls[20] = nulls[21] = true;
+               }
+
                /* Values only available to role member */
                if (has_privs_of_role(GetUserId(), beentry->st_userid))
                {
index 5191526bef7ee2cee7a9af3ec2d9f3fb3eb9a620..8ecd5fd9af81b8aa3ce0083cdc018da30322c64d 100644 (file)
@@ -53,6 +53,6 @@
  */
 
 /*                                                     yyyymmddN */
-#define CATALOG_VERSION_NO     201504061
+#define CATALOG_VERSION_NO     201504121
 
 #endif
index 62187fb27415f3bd0b58f00607ed7fa3f6caff7b..9caa0960d50a23746dda45d0c218e3fa3714993c 100644 (file)
@@ -2756,7 +2756,7 @@ DATA(insert OID = 3057 ( pg_stat_get_autoanalyze_count PGNSP PGUID 12 1 0 0 0 f
 DESCR("statistics: number of auto analyzes for a table");
 DATA(insert OID = 1936 (  pg_stat_get_backend_idset            PGNSP PGUID 12 1 100 0 0 f f f f t t s 0 0 23 "" _null_ _null_ _null_ _null_ pg_stat_get_backend_idset _null_ _null_ _null_ ));
 DESCR("statistics: currently active backend IDs");
-DATA(insert OID = 2022 (  pg_stat_get_activity                 PGNSP PGUID 12 1 100 0 0 f f f f f t s 1 0 2249 "23" "{23,26,23,26,25,25,25,16,1184,1184,1184,1184,869,25,23,28,28}" "{i,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o}" "{pid,datid,pid,usesysid,application_name,state,query,waiting,xact_start,query_start,backend_start,state_change,client_addr,client_hostname,client_port,backend_xid,backend_xmin}" _null_ pg_stat_get_activity _null_ _null_ _null_ ));
+DATA(insert OID = 2022 (  pg_stat_get_activity                 PGNSP PGUID 12 1 100 0 0 f f f f f t s 1 0 2249 "23" "{23,26,23,26,25,25,25,16,1184,1184,1184,1184,869,25,23,28,28,16,25,25,23,16,25}" "{i,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o}" "{pid,datid,pid,usesysid,application_name,state,query,waiting,xact_start,query_start,backend_start,state_change,client_addr,client_hostname,client_port,backend_xid,backend_xmin,ssl,sslversion,sslcipher,sslbits,sslcompression,sslclientdn}" _null_ pg_stat_get_activity _null_ _null_ _null_ ));
 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");
index cf520f545d916cea83bba5c6ef3953e61862ef4b..f323ed87106162a75c757262d61de0424bfa408c 100644 (file)
@@ -212,6 +212,11 @@ extern void be_tls_close(Port *port);
 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;
index f2b2257a115927244ed1e4b602b7141418a3e3c5..e3fe06e95be644b3ccf36f42905d91530dec43e2 100644 (file)
@@ -701,6 +701,23 @@ typedef enum BackendState
  */
 
 
+/*
+ * 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,6 +761,10 @@ typedef struct PgBackendStatus
        SockAddr        st_clientaddr;
        char       *st_clienthostname;          /* MUST be null-terminated */
 
+       /* Information about SSL connection */
+       bool            st_ssl;
+       PgBackendSSLStatus *st_sslstatus;
+
        /* Is backend currently waiting on an lmgr lock? */
        bool            st_waiting;
 
index 17882704d48ab6ae8fb16c28eacf38022c32ee8e..71fa44a6beaa359642f9067efb97c6fcdf87d996 100644 (file)
@@ -1633,7 +1633,7 @@ pg_stat_activity| SELECT s.datid,
     s.backend_xmin,
     s.query
    FROM pg_database d,
-    pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, waiting, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin),
+    pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, waiting, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, ssl, sslversion, sslcipher, sslbits, sslcompression, sslclientdn),
     pg_authid u
   WHERE ((s.datid = d.oid) AND (s.usesysid = u.oid));
 pg_stat_all_indexes| SELECT c.oid AS relid,
@@ -1739,10 +1739,18 @@ pg_stat_replication| SELECT s.pid,
     w.replay_location,
     w.sync_priority,
     w.sync_state
-   FROM pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, waiting, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin),
+   FROM pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, waiting, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, ssl, sslversion, sslcipher, sslbits, sslcompression, sslclientdn),
     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 s.pid,
+    s.ssl,
+    s.sslversion AS version,
+    s.sslcipher AS cipher,
+    s.sslbits AS bits,
+    s.sslcompression AS compression,
+    s.sslclientdn AS clientdn
+   FROM pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, waiting, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, ssl, sslversion, sslcipher, sslbits, sslcompression, sslclientdn);
 pg_stat_sys_indexes| SELECT pg_stat_all_indexes.relid,
     pg_stat_all_indexes.indexrelid,
     pg_stat_all_indexes.schemaname,