From 5acd643aff5a12b8ee2ca3365532f5773c1b02a8 Mon Sep 17 00:00:00 2001 From: Michael Paquier Date: Mon, 26 Apr 2021 11:25:17 +0900 Subject: [PATCH v1] Add authenticated data to pg_stat_activity --- src/include/catalog/pg_proc.dat | 6 +-- src/include/utils/backend_status.h | 16 ++++--- src/backend/catalog/system_views.sql | 1 + src/backend/utils/activity/backend_status.c | 45 +++++++++++++------ src/backend/utils/adt/pgstatfuncs.c | 21 +++++++-- src/backend/utils/misc/guc.c | 11 +++++ src/backend/utils/misc/postgresql.conf.sample | 1 + src/test/ldap/t/001_auth.pl | 4 +- src/test/regress/expected/rules.out | 9 ++-- src/test/ssl/t/001_ssltests.pl | 8 ++-- doc/src/sgml/config.sgml | 18 ++++++++ doc/src/sgml/monitoring.sgml | 10 +++++ 12 files changed, 117 insertions(+), 33 deletions(-) diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index db1abc149c..7217e017a2 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -5279,9 +5279,9 @@ proname => 'pg_stat_get_activity', prorows => '100', proisstrict => 'f', proretset => 't', provolatile => 's', proparallel => 'r', prorettype => 'record', proargtypes => 'int4', - proallargtypes => '{int4,oid,int4,oid,text,text,text,text,text,timestamptz,timestamptz,timestamptz,timestamptz,inet,text,int4,xid,xid,text,bool,text,text,int4,text,numeric,text,bool,text,bool,int4,int8}', - proargmodes => '{i,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o}', - proargnames => '{pid,datid,pid,usesysid,application_name,state,query,wait_event_type,wait_event,xact_start,query_start,backend_start,state_change,client_addr,client_hostname,client_port,backend_xid,backend_xmin,backend_type,ssl,sslversion,sslcipher,sslbits,ssl_client_dn,ssl_client_serial,ssl_issuer_dn,gss_auth,gss_princ,gss_enc,leader_pid,query_id}', + proallargtypes => '{int4,oid,int4,oid,text,text,text,text,text,timestamptz,timestamptz,timestamptz,timestamptz,inet,text,int4,xid,xid,text,bool,text,text,int4,text,numeric,text,bool,text,bool,int4,int8,text}', + proargmodes => '{i,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o}', + proargnames => '{pid,datid,pid,usesysid,application_name,state,query,wait_event_type,wait_event,xact_start,query_start,backend_start,state_change,client_addr,client_hostname,client_port,backend_xid,backend_xmin,backend_type,ssl,sslversion,sslcipher,sslbits,ssl_client_dn,ssl_client_serial,ssl_issuer_dn,gss_auth,gss_princ,gss_enc,leader_pid,query_id,authenticated_id}', prosrc => 'pg_stat_get_activity' }, { oid => '3318', descr => 'statistics: information about progress of backends running maintenance command', diff --git a/src/include/utils/backend_status.h b/src/include/utils/backend_status.h index 0cbcc9c943..d2381f07dd 100644 --- a/src/include/utils/backend_status.h +++ b/src/include/utils/backend_status.h @@ -145,13 +145,16 @@ typedef struct PgBackendStatus char *st_appname; /* - * Current command string; MUST be null-terminated. Note that this string - * possibly is truncated in the middle of a multi-byte character. As - * activity strings are stored more frequently than read, that allows to - * move the cost of correct truncation to the display side. Use - * pgstat_clip_activity() to truncate correctly. + * Current command string and authenticated ID string; MUST be + * null-terminated. Note that those strings possibly are truncated in + * the middle of a multi-byte character. As activity strings are + * stored more frequently than read, that allows to move the cost of + * correct truncation to the display side. Authenticated ID strings + * are stored once at backend startup but the cost is minimal. + * Use pgstat_clip_string() to truncate both of them correctly. */ char *st_activity_raw; + char *st_authn_id; /* * Command progress reporting. Any command which wishes can advertise @@ -267,6 +270,7 @@ typedef struct LocalPgBackendStatus */ extern PGDLLIMPORT bool pgstat_track_activities; extern PGDLLIMPORT int pgstat_track_activity_query_size; +extern PGDLLIMPORT int pgstat_track_activity_authn_size; /* ---------- @@ -315,7 +319,7 @@ extern uint64 pgstat_get_my_query_id(void); extern int pgstat_fetch_stat_numbackends(void); extern PgBackendStatus *pgstat_fetch_stat_beentry(int beid); extern LocalPgBackendStatus *pgstat_fetch_stat_local_beentry(int beid); -extern char *pgstat_clip_activity(const char *raw_activity); +extern char *pgstat_clip_string(const char *raw_activity, int max_size); #endif /* BACKEND_STATUS_H */ diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql index 70e578894f..ef251daf17 100644 --- a/src/backend/catalog/system_views.sql +++ b/src/backend/catalog/system_views.sql @@ -821,6 +821,7 @@ CREATE VIEW pg_stat_activity AS S.usesysid, U.rolname AS usename, S.application_name, + S.authenticated_id, S.client_addr, S.client_hostname, S.client_port, diff --git a/src/backend/utils/activity/backend_status.c b/src/backend/utils/activity/backend_status.c index a368101103..a0f5fc98fd 100644 --- a/src/backend/utils/activity/backend_status.c +++ b/src/backend/utils/activity/backend_status.c @@ -44,6 +44,7 @@ */ bool pgstat_track_activities = false; int pgstat_track_activity_query_size = 1024; +int pgstat_track_activity_authn_size = 128; /* exposed so that progress.c can access it */ @@ -95,7 +96,9 @@ BackendStatusShmemSize(void) mul_size(NAMEDATALEN, NumBackendStatSlots)); /* BackendActivityBuffer: */ size = add_size(size, - mul_size(pgstat_track_activity_query_size, NumBackendStatSlots)); + mul_size(pgstat_track_activity_query_size + + pgstat_track_activity_authn_size, + NumBackendStatSlots)); #ifdef USE_SSL /* BackendSslStatusBuffer: */ size = add_size(size, @@ -171,7 +174,8 @@ CreateSharedBackendStatus(void) } /* Create or attach to the shared activity buffer */ - BackendActivityBufferSize = mul_size(pgstat_track_activity_query_size, + BackendActivityBufferSize = mul_size(pgstat_track_activity_query_size + + pgstat_track_activity_authn_size, NumBackendStatSlots); BackendActivityBuffer = (char *) ShmemInitStruct("Backend Activity Buffer", @@ -182,12 +186,14 @@ CreateSharedBackendStatus(void) { MemSet(BackendActivityBuffer, 0, BackendActivityBufferSize); - /* Initialize st_activity pointers. */ + /* Initialize st_activity and st_authn_id pointers. */ buffer = BackendActivityBuffer; for (i = 0; i < NumBackendStatSlots; i++) { BackendStatusArray[i].st_activity_raw = buffer; buffer += pgstat_track_activity_query_size; + BackendStatusArray[i].st_authn_id = buffer; + buffer += pgstat_track_activity_authn_size; } } @@ -432,10 +438,22 @@ pgstat_bestart(void) else lbeentry.st_clienthostname[0] = '\0'; lbeentry.st_activity_raw[0] = '\0'; + if (MyProcPort && MyProcPort->authn_id) + { + /* Compute the length of the field to store */ + int len = Min(strlen(MyProcPort->authn_id), + pgstat_track_activity_authn_size - 1); + + memcpy(lbeentry.st_authn_id, MyProcPort->authn_id, len); + lbeentry.st_authn_id[len] = '\0'; + } + else + lbeentry.st_authn_id[0] = '\0'; /* Also make sure the last byte in each string area is always 0 */ lbeentry.st_appname[NAMEDATALEN - 1] = '\0'; lbeentry.st_clienthostname[NAMEDATALEN - 1] = '\0'; lbeentry.st_activity_raw[pgstat_track_activity_query_size - 1] = '\0'; + lbeentry.st_authn_id[pgstat_track_activity_authn_size - 1] = '\0'; #ifdef USE_SSL memcpy(lbeentry.st_sslstatus, &lsslstatus, sizeof(PgBackendSSLStatus)); @@ -936,7 +954,8 @@ pgstat_get_backend_current_activity(int pid, bool checkUser) else { /* this'll leak a bit of memory, but that seems acceptable */ - return pgstat_clip_activity(beentry->st_activity_raw); + return pgstat_clip_string(beentry->st_activity_raw, + pgstat_track_activity_query_size); } } @@ -1102,7 +1121,7 @@ pgstat_fetch_stat_numbackends(void) } /* - * Convert a potentially unsafely truncated activity string (see + * Convert a potentially unsafely truncated activity or authn ID string (see * PgBackendStatus.st_activity_raw's documentation) into a correctly truncated * one. * @@ -1110,9 +1129,9 @@ pgstat_fetch_stat_numbackends(void) * freed. */ char * -pgstat_clip_activity(const char *raw_activity) +pgstat_clip_string(const char *raw_string, int max_size) { - char *activity; + char *string; int rawlen; int cliplen; @@ -1124,10 +1143,10 @@ pgstat_clip_activity(const char *raw_activity) * underlying buffer is guaranteed to be pgstat_track_activity_query_size * large. */ - activity = pnstrdup(raw_activity, pgstat_track_activity_query_size - 1); + string = pnstrdup(raw_string, max_size - 1); /* now double-guaranteed to be NUL terminated */ - rawlen = strlen(activity); + rawlen = strlen(string); /* * All supported server-encodings make it possible to determine the length @@ -1137,10 +1156,10 @@ pgstat_clip_activity(const char *raw_activity) * even if the string earlier was truncated in the middle of a multi-byte * character. */ - cliplen = pg_mbcliplen(activity, rawlen, - pgstat_track_activity_query_size - 1); + cliplen = pg_mbcliplen(string, rawlen, + max_size - 1); - activity[cliplen] = '\0'; + string[cliplen] = '\0'; - return activity; + return string; } diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c index 87f02d572e..e4215c46c4 100644 --- a/src/backend/utils/adt/pgstatfuncs.c +++ b/src/backend/utils/adt/pgstatfuncs.c @@ -569,7 +569,7 @@ pg_stat_get_progress_info(PG_FUNCTION_ARGS) Datum pg_stat_get_activity(PG_FUNCTION_ARGS) { -#define PG_STAT_GET_ACTIVITY_COLS 30 +#define PG_STAT_GET_ACTIVITY_COLS 31 int num_backends = pgstat_fetch_stat_numbackends(); int curr_backend; int pid = PG_ARGISNULL(0) ? -1 : PG_GETARG_INT32(0); @@ -703,10 +703,23 @@ pg_stat_get_activity(PG_FUNCTION_ARGS) break; } - clipped_activity = pgstat_clip_activity(beentry->st_activity_raw); + clipped_activity = pgstat_clip_string(beentry->st_activity_raw, + pgstat_track_activity_query_size); values[5] = CStringGetTextDatum(clipped_activity); pfree(clipped_activity); + if (beentry->st_authn_id) + { + char *clipped_authn_id; + + clipped_authn_id = pgstat_clip_string(beentry->st_authn_id, + pgstat_track_activity_authn_size); + values[30] = CStringGetTextDatum(clipped_authn_id); + pfree(clipped_authn_id); + } + else + nulls[30] = true; + /* leader_pid */ nulls[28] = true; @@ -946,6 +959,7 @@ pg_stat_get_activity(PG_FUNCTION_ARGS) nulls[27] = true; nulls[28] = true; nulls[29] = true; + nulls[30] = true; } tuplestore_putvalues(tupstore, tupdesc, values, nulls); @@ -1026,7 +1040,8 @@ pg_stat_get_backend_activity(PG_FUNCTION_ARGS) else activity = beentry->st_activity_raw; - clipped_activity = pgstat_clip_activity(activity); + clipped_activity = pgstat_clip_string(activity, + pgstat_track_activity_query_size); ret = cstring_to_text(activity); pfree(clipped_activity); diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index b130874bdc..def2c21fb8 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -3516,6 +3516,17 @@ static struct config_int ConfigureNamesInt[] = NULL, NULL, NULL }, + { + {"track_activity_authn_size", PGC_POSTMASTER, RESOURCES_MEM, + gettext_noop("Sets the size reserved for pg_stat_activity.authenticated_id, in bytes."), + NULL, + GUC_UNIT_BYTE + }, + &pgstat_track_activity_authn_size, + 128, 100, 1048576, + NULL, NULL, NULL + }, + { {"gin_pending_list_limit", PGC_USERSET, CLIENT_CONN_STATEMENT, gettext_noop("Sets the maximum size of the pending list for GIN index."), diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample index 0f7f49b949..762a9d67c4 100644 --- a/src/backend/utils/misc/postgresql.conf.sample +++ b/src/backend/utils/misc/postgresql.conf.sample @@ -602,6 +602,7 @@ #track_wal_io_timing = off #track_functions = none # none, pl, all #track_activity_query_size = 1024 # (change requires restart) +#track_activity_authn_size = 128 # (change requires restart) #stats_temp_directory = 'pg_stat_tmp' diff --git a/src/test/ldap/t/001_auth.pl b/src/test/ldap/t/001_auth.pl index ec4721234b..17edc33776 100644 --- a/src/test/ldap/t/001_auth.pl +++ b/src/test/ldap/t/001_auth.pl @@ -6,7 +6,7 @@ use Test::More; if ($ENV{with_ldap} eq 'yes') { - plan tests => 28; + plan tests => 29; } else { @@ -199,6 +199,8 @@ $ENV{"PGPASSWORD"} = 'secret1'; test_access( $node, 'test1', 0, 'simple bind authentication succeeds', + sql => 'SELECT authenticated_id FROM pg_stat_activity WHERE pid = pg_backend_pid()', + expected_stdout => qr/uid=test1,dc=example,dc=net/, log_like => [ qr/connection authenticated: identity="uid=test1,dc=example,dc=net" method=ldap/ ],); diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out index 6dff5439e0..0a5ae0bc35 100644 --- a/src/test/regress/expected/rules.out +++ b/src/test/regress/expected/rules.out @@ -1750,6 +1750,7 @@ pg_stat_activity| SELECT s.datid, s.usesysid, u.rolname AS usename, s.application_name, + s.authenticated_id, s.client_addr, s.client_hostname, s.client_port, @@ -1765,7 +1766,7 @@ pg_stat_activity| SELECT s.datid, s.query_id, s.query, s.backend_type - FROM ((pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, leader_pid, query_id) + FROM ((pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, leader_pid, query_id, authenticated_id) LEFT JOIN pg_database d ON ((s.datid = d.oid))) LEFT JOIN pg_authid u ON ((s.usesysid = u.oid))); pg_stat_all_indexes| SELECT c.oid AS relid, @@ -1877,7 +1878,7 @@ pg_stat_gssapi| SELECT s.pid, s.gss_auth AS gss_authenticated, s.gss_princ AS principal, s.gss_enc AS encrypted - FROM pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, leader_pid, query_id) + FROM pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, leader_pid, query_id, authenticated_id) WHERE (s.client_port IS NOT NULL); pg_stat_prefetch_recovery| SELECT s.stats_reset, s.prefetch, @@ -2058,7 +2059,7 @@ pg_stat_replication| SELECT s.pid, w.sync_priority, w.sync_state, w.reply_time - FROM ((pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, leader_pid, query_id) + FROM ((pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, leader_pid, query_id, authenticated_id) JOIN pg_stat_get_wal_senders() w(pid, state, sent_lsn, write_lsn, flush_lsn, replay_lsn, write_lag, flush_lag, replay_lag, sync_priority, sync_state, reply_time) ON ((s.pid = w.pid))) LEFT JOIN pg_authid u ON ((s.usesysid = u.oid))); pg_stat_replication_slots| SELECT s.slot_name, @@ -2090,7 +2091,7 @@ pg_stat_ssl| SELECT s.pid, s.ssl_client_dn AS client_dn, s.ssl_client_serial AS client_serial, s.ssl_issuer_dn AS issuer_dn - FROM pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, leader_pid, query_id) + FROM pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, leader_pid, query_id, authenticated_id) WHERE (s.client_port IS NOT NULL); pg_stat_subscription| SELECT su.oid AS subid, su.subname, diff --git a/src/test/ssl/t/001_ssltests.pl b/src/test/ssl/t/001_ssltests.pl index cc797a5c98..9a6dec7c26 100644 --- a/src/test/ssl/t/001_ssltests.pl +++ b/src/test/ssl/t/001_ssltests.pl @@ -17,7 +17,7 @@ if ($ENV{with_ssl} ne 'openssl') } else { - plan tests => 110; + plan tests => 111; } #### Some configuration @@ -256,7 +256,6 @@ $node->connect_ok( "host name matching with X.509 Subject Alternative Names 2"); $node->connect_ok("$common_connstr host=foo.wildcard.pg-ssltest.test", "host name matching with X.509 Subject Alternative Names wildcard"); - $node->connect_fails( "$common_connstr host=wronghost.alt-name.pg-ssltest.test", "host name not matching with X.509 Subject Alternative Names", @@ -446,10 +445,13 @@ $node->connect_ok( # same thing but using explicit CN $dn_connstr = "$common_connstr dbname=certdb_cn"; +# The full DN should still be used as the authenticated identity, within the +# backend logs and pg_stat_activity. $node->connect_ok( "$dn_connstr user=ssltestuser sslcert=ssl/client-dn.crt sslkey=ssl/client-dn_tmp.key", "certificate authorization succeeds with CN mapping", - # the full DN should still be used as the authenticated identity + sql => 'SELECT authenticated_id FROM pg_stat_activity WHERE pid = pg_backend_pid()', + expected_stdout => qr/CN=ssltestuser-dn,OU=Testing,OU=Engineering,O=PGDG/, log_like => [ qr/connection authenticated: identity="CN=ssltestuser-dn,OU=Testing,OU=Engineering,O=PGDG" method=cert/ ],); diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cf75d913ce..51d040ea9c 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7596,6 +7596,24 @@ COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv; + + track_activity_authn_size (integer) + + track_activity_authn_size configuration parameter + + + + + Specifies the amount of memory reserved to store the text of the + currently executing command for each active session, for the + pg_stat_activity.authenticated_id field. + If this value is specified without units, it is taken as bytes. + The default value is 128 bytes. + This parameter can only be set at server start. + + + + track_counts (boolean) diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml index 886e626be8..dda95cd860 100644 --- a/doc/src/sgml/monitoring.sgml +++ b/doc/src/sgml/monitoring.sgml @@ -756,6 +756,16 @@ postgres 27093 0.0 0.0 30096 2752 ? Ss 11:34 0:00 postgres: ser + + + authenticated_id text + + + Name of the authenticated ID used for this backend login at + authentication + + + client_addr inet -- 2.31.1