diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml index bd7bb77..8a95f42 100644 --- a/doc/src/sgml/monitoring.sgml +++ b/doc/src/sgml/monitoring.sgml @@ -1302,6 +1302,13 @@ SELECT pid, wait_event_type, wait_event FROM pg_stat_activity WHERE wait_event i text Replication slot name used by this WAL receiver + + conn_info + text + Connection string used by this WAL receiver. This connection + string obfuscates any sensitive parameters like password values. + + diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql index 272c02f..f52de3a 100644 --- a/src/backend/catalog/system_views.sql +++ b/src/backend/catalog/system_views.sql @@ -681,7 +681,8 @@ CREATE VIEW pg_stat_wal_receiver AS s.last_msg_receipt_time, s.latest_end_lsn, s.latest_end_time, - s.slot_name + s.slot_name, + s.conn_info FROM pg_stat_get_wal_receiver() s WHERE s.pid IS NOT NULL; diff --git a/src/backend/replication/libpqwalreceiver/libpqwalreceiver.c b/src/backend/replication/libpqwalreceiver/libpqwalreceiver.c index b61e39d..6f9d087 100644 --- a/src/backend/replication/libpqwalreceiver/libpqwalreceiver.c +++ b/src/backend/replication/libpqwalreceiver/libpqwalreceiver.c @@ -47,6 +47,7 @@ static char *recvBuf = NULL; /* Prototypes for interface functions */ static void libpqrcv_connect(char *conninfo); +static void libpqrcv_get_conninfo(char **safe_connstr); static void libpqrcv_identify_system(TimeLineID *primary_tli); static void libpqrcv_readtimelinehistoryfile(TimeLineID tli, char **filename, char **content, int *len); static bool libpqrcv_startstreaming(TimeLineID tli, XLogRecPtr startpoint, @@ -74,6 +75,7 @@ _PG_init(void) walrcv_disconnect != NULL) elog(ERROR, "libpqwalreceiver already loaded"); walrcv_connect = libpqrcv_connect; + walrcv_get_conninfo = libpqrcv_get_conninfo; walrcv_identify_system = libpqrcv_identify_system; walrcv_readtimelinehistoryfile = libpqrcv_readtimelinehistoryfile; walrcv_startstreaming = libpqrcv_startstreaming; @@ -118,6 +120,66 @@ libpqrcv_connect(char *conninfo) } /* + * Return an obfuscated connection string to caller, this field is aimed at + * being made visible to users and hides any sensitive data like password + * data. + */ +static void +libpqrcv_get_conninfo(char **safe_conninfo) +{ + PQconninfoOption *conn_opts = NULL; + PQconninfoOption *conn_opt; + + Assert(streamConn != NULL); + + *safe_conninfo = NULL; + + conn_opts = PQconninfo(streamConn); + + if (conn_opts == NULL) + ereport(ERROR, + (errmsg("could not parse connection string: %s", + PQerrorMessage(streamConn)))); + + /* Rebuild the connection string */ + for (conn_opt = conn_opts; conn_opt->keyword != NULL; conn_opt++) + { + bool obfuscate = false; + + /* Skip any user-sensitive data */ + if (strchr(conn_opt->dispchar, '*') || strchr(conn_opt->dispchar, 'D')) + obfuscate = true; + + if (conn_opt->val != NULL && conn_opt->val[0] != '\0') + { + int len = strlen(conn_opt->val) + strlen(conn_opt->keyword) + 1; + bool first_loop = *safe_conninfo == NULL; + + if (first_loop) + { + len++; /* '=' */ + *safe_conninfo = (char *) palloc(len); + sprintf(*safe_conninfo, "%s=%s", + conn_opt->keyword, + obfuscate ? "*******" : conn_opt->val); + } + else + { + len += 2; /* '=' and space */ + *safe_conninfo = (char *) repalloc(*safe_conninfo, + strlen(*safe_conninfo) + len); + sprintf(*safe_conninfo, "%s %s=%s", + *safe_conninfo, + conn_opt->keyword, + obfuscate ? "*******" : conn_opt->val); + } + } + } + + PQconninfoFree(conn_opts); +} + +/* * Check that primary's system identifier matches ours, and fetch the current * timeline ID of the primary. */ diff --git a/src/backend/replication/walreceiver.c b/src/backend/replication/walreceiver.c index ce311cb..699ce33 100644 --- a/src/backend/replication/walreceiver.c +++ b/src/backend/replication/walreceiver.c @@ -75,6 +75,7 @@ bool hot_standby_feedback; /* libpqreceiver hooks to these when loaded */ walrcv_connect_type walrcv_connect = NULL; +walrcv_get_conninfo_type walrcv_get_conninfo = NULL; walrcv_identify_system_type walrcv_identify_system = NULL; walrcv_startstreaming_type walrcv_startstreaming = NULL; walrcv_endstreaming_type walrcv_endstreaming = NULL; @@ -192,6 +193,7 @@ void WalReceiverMain(void) { char conninfo[MAXCONNINFO]; + char *safe_conninfo; char slotname[NAMEDATALEN]; XLogRecPtr startpoint; TimeLineID startpointTLI; @@ -282,7 +284,9 @@ WalReceiverMain(void) /* Load the libpq-specific functions */ load_file("libpqwalreceiver", false); - if (walrcv_connect == NULL || walrcv_startstreaming == NULL || + if (walrcv_connect == NULL || + walrcv_get_conninfo == NULL || + walrcv_startstreaming == NULL || walrcv_endstreaming == NULL || walrcv_identify_system == NULL || walrcv_readtimelinehistoryfile == NULL || @@ -304,6 +308,13 @@ WalReceiverMain(void) walrcv_connect(conninfo); DisableWalRcvImmediateExit(); + /* Save connection string visible to user */ + walrcv_get_conninfo(&safe_conninfo); + SpinLockAcquire(&walrcv->mutex); + strlcpy((char *) walrcv->safe_conninfo, safe_conninfo, MAXCONNINFO); + SpinLockRelease(&walrcv->mutex); + pfree(safe_conninfo); + first_stream = true; for (;;) { @@ -1308,7 +1319,7 @@ WalRcvGetStateString(WalRcvState state) Datum pg_stat_get_wal_receiver(PG_FUNCTION_ARGS) { -#define PG_STAT_GET_WAL_RECEIVER_COLS 11 +#define PG_STAT_GET_WAL_RECEIVER_COLS 12 TupleDesc tupdesc; Datum values[PG_STAT_GET_WAL_RECEIVER_COLS]; bool nulls[PG_STAT_GET_WAL_RECEIVER_COLS]; @@ -1323,6 +1334,7 @@ pg_stat_get_wal_receiver(PG_FUNCTION_ARGS) XLogRecPtr latest_end_lsn; TimestampTz latest_end_time; char *slotname; + char *conninfo; /* No WAL receiver, just return a tuple with NULL values */ if (walrcv->pid == 0) @@ -1356,6 +1368,8 @@ pg_stat_get_wal_receiver(PG_FUNCTION_ARGS) TIMESTAMPTZOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 11, "slot_name", TEXTOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 12, "conn_info", + TEXTOID, -1, 0); BlessTupleDesc(tupdesc); @@ -1371,6 +1385,7 @@ pg_stat_get_wal_receiver(PG_FUNCTION_ARGS) latest_end_lsn = walrcv->latestWalEnd; latest_end_time = walrcv->latestWalEndTime; slotname = pstrdup(walrcv->slotname); + conninfo = pstrdup(walrcv->safe_conninfo); SpinLockRelease(&walrcv->mutex); /* Fetch values */ @@ -1418,6 +1433,10 @@ pg_stat_get_wal_receiver(PG_FUNCTION_ARGS) nulls[10] = true; else values[10] = CStringGetTextDatum(slotname); + if (*conninfo == '\0') + nulls[11] = true; + else + values[11] = CStringGetTextDatum(conninfo); } /* Returns the record as Datum */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index f441025..d92c05e 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -2746,7 +2746,7 @@ DATA(insert OID = 3318 ( pg_stat_get_progress_info PGNSP PGUID 12 1 100 0 0 DESCR("statistics: information about progress of backends running maintenance command"); DATA(insert OID = 3099 ( pg_stat_get_wal_senders PGNSP PGUID 12 1 10 0 0 f f f f f t s r 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_ _null_ pg_stat_get_wal_senders _null_ _null_ _null_ )); DESCR("statistics: information about currently active replication"); -DATA(insert OID = 3317 ( pg_stat_get_wal_receiver PGNSP PGUID 12 1 0 0 0 f f f f f f s r 0 0 2249 "" "{23,25,3220,23,3220,23,1184,1184,3220,1184,25}" "{o,o,o,o,o,o,o,o,o,o,o}" "{pid,status,receive_start_lsn,receive_start_tli,received_lsn,received_tli,last_msg_send_time,last_msg_receipt_time,latest_end_lsn,latest_end_time,slot_name}" _null_ _null_ pg_stat_get_wal_receiver _null_ _null_ _null_ )); +DATA(insert OID = 3317 ( pg_stat_get_wal_receiver PGNSP PGUID 12 1 0 0 0 f f f f f f s r 0 0 2249 "" "{23,25,3220,23,3220,23,1184,1184,3220,1184,25,25}" "{o,o,o,o,o,o,o,o,o,o,o,o}" "{pid,status,receive_start_lsn,receive_start_tli,received_lsn,received_tli,last_msg_send_time,last_msg_receipt_time,latest_end_lsn,latest_end_time,slot_name,conn_info}" _null_ _null_ pg_stat_get_wal_receiver _null_ _null_ _null_ )); DESCR("statistics: information about WAL receiver"); DATA(insert OID = 2026 ( pg_backend_pid PGNSP PGUID 12 1 0 0 0 f f f f t f s r 0 0 23 "" _null_ _null_ _null_ _null_ _null_ pg_backend_pid _null_ _null_ _null_ )); DESCR("statistics: current backend PID"); diff --git a/src/include/replication/walreceiver.h b/src/include/replication/walreceiver.h index c87e7a8..7e793b0 100644 --- a/src/include/replication/walreceiver.h +++ b/src/include/replication/walreceiver.h @@ -105,6 +105,12 @@ typedef struct char conninfo[MAXCONNINFO]; /* + * Obfuscated connection string; is made visible to users and hides any + * sensitive data. + */ + char safe_conninfo[MAXCONNINFO]; + + /* * replication slot name; is also used for walreceiver to connect with the * primary */ @@ -133,6 +139,9 @@ extern WalRcvData *WalRcv; typedef void (*walrcv_connect_type) (char *conninfo); extern PGDLLIMPORT walrcv_connect_type walrcv_connect; +typedef void (*walrcv_get_conninfo_type) (char **safe_conninfo); +extern PGDLLIMPORT walrcv_get_conninfo_type walrcv_get_conninfo; + typedef void (*walrcv_identify_system_type) (TimeLineID *primary_tli); extern PGDLLIMPORT walrcv_identify_system_type walrcv_identify_system;