]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
Avoid exposing WAL receiver raw conninfo during timeline jumps
authorMichael Paquier <michael@paquier.xyz>
Fri, 22 May 2026 23:10:07 +0000 (08:10 +0900)
committerMichael Paquier <michael@paquier.xyz>
Fri, 22 May 2026 23:10:07 +0000 (08:10 +0900)
When reusing an existing WAL receiver after it has reached
WALRCV_WAITING for new instructions, RequestXLogStreaming() copied
PrimaryConnInfo into WalRcv->conninfo before switching the state to
WALRCV_RESTARTING.  At that point ready_to_display could still be true,
so pg_stat_wal_receiver could expose the raw connection string,
including sensitive fields, but it should only show the user-displayable
version of the connection string.

WALRCV_RESTARTING does not establish a new connection.  The waiting WAL
receiver reuses its existing connection and only needs a new startpoint
and timeline, so there is no need to copy the raw connection string into
shared memory again.  Let's only copy conninfo when launching a new WAL
receiver after WALRCV_STOPPED, not while waiting for instructions.

This commit adds coverage for the case fixed by this commit to the
timeline-switch test by verifying that the WAL receiver conninfo remains
consistent across the jump.

Backpatch all the way down, as this issue is possible since
pg_stat_wal_receiver has been introduced.

Author: Chao Li <li.evan.chao@gmail.com>
Reviewed-by: Michael Paquier <michael@paquier.xyz>
Discussion: https://postgr.es/m/EF91FF76-1E2B-4F3B-9162-290B4DC517FF@gmail.com
Backpatch-through: 14

src/backend/replication/walreceiverfuncs.c
src/test/recovery/t/004_timeline_switch.pl

index a0ed853e2f60ff6406ffd8946e428b782804eb29..ecf510517eb0f7d4e078aa20f0a210c5ced81d76 100644 (file)
@@ -281,11 +281,6 @@ RequestXLogStreaming(TimeLineID tli, XLogRecPtr recptr, const char *conninfo,
        Assert(walrcv->walRcvState == WALRCV_STOPPED ||
                   walrcv->walRcvState == WALRCV_WAITING);
 
-       if (conninfo != NULL)
-               strlcpy(walrcv->conninfo, conninfo, MAXCONNINFO);
-       else
-               walrcv->conninfo[0] = '\0';
-
        /*
         * Use configured replication slot if present, and ignore the value of
         * create_temp_slot as the slot name should be persistent.  Otherwise, use
@@ -303,10 +298,19 @@ RequestXLogStreaming(TimeLineID tli, XLogRecPtr recptr, const char *conninfo,
                walrcv->is_temp_slot = create_temp_slot;
        }
 
+       /*
+        * While waiting for instructions, the WAL receiver uses the same
+        * connection, so do not clobber the user-visible conninfo already saved.
+        */
        if (walrcv->walRcvState == WALRCV_STOPPED)
        {
                launch = true;
                walrcv->walRcvState = WALRCV_STARTING;
+
+               if (conninfo != NULL)
+                       strlcpy(walrcv->conninfo, conninfo, MAXCONNINFO);
+               else
+                       walrcv->conninfo[0] = '\0';
        }
        else
                walrcv->walRcvState = WALRCV_RESTARTING;
index 5afd2f444668463fb62a83daa14985b8500f1554..e0b3851927cd9914a84cd0e93c16615a770a58a5 100644 (file)
@@ -47,11 +47,15 @@ $node_standby_1->psql(
        stdout => \$psql_out);
 is($psql_out, 't', "promotion of standby with pg_promote");
 
-# Switch standby 2 to replay from standby 1
+# Switch standby 2 to replay from standby 1.  During the timeline switch,
+# the WAL receiver process on standby 2 should not be stopped, and the
+# new primary connection string should not be visible
+# in pg_stat_wal_receiver.
+my $secret = 'dont_show_me';
 my $connstr_1 = $node_standby_1->connstr;
 $node_standby_2->append_conf(
        'postgresql.conf', qq(
-primary_conninfo='$connstr_1'
+primary_conninfo='$connstr_1 password=$secret'
 ));
 
 # Rotate logfile before restarting, for the log checks done below.
@@ -93,6 +97,13 @@ my $wr_pid_after_switch = $node_standby_2->safe_psql('postgres',
 is($wr_pid_before_switch, $wr_pid_after_switch,
        'WAL receiver PID matches across timeline jumps');
 
+my $raw_conninfo_count = $node_standby_2->safe_psql('postgres',
+       "SELECT count(*) FROM pg_stat_wal_receiver WHERE conninfo LIKE '%$secret%'"
+);
+
+is($raw_conninfo_count, '0',
+       'pg_stat_wal_receiver.conninfo not updated across timeline jumps');
+
 # Ensure that a standby is able to follow a primary on a newer timeline
 # when WAL archiving is enabled.