Provide fix for failing pg_rewind with non-superuser

Issue #829 was opened indicating, with a reproducible, that repmgr
was not able to run `node rejoin` with minimal privileged user. The
main obstacle is that pg_rewind is not able to execute the rewind
operation if the user has REPLICATION privileges, but the user
repmgr uses requires REPLICATION. This is a typical catch22.

The solution provided here adds a --superuser, similar to what
other commands have, to `node rejoin`

AI-assisted development notes:

    The approach was designed and directed by Martín Marqués, who
    also reviewed and refined the output. Code was written by Claude
    (AI), with some additions from Martín in the documentation.

Fixes #829

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Martín Marqués <martin.marques@enterprisedb.com>
This commit is contained in:
Martín Marqués
2026-03-28 15:29:18 +01:00
parent e696e28f43
commit c9c380a022
5 changed files with 75 additions and 3 deletions

View File

@@ -192,6 +192,9 @@
<listitem> <listitem>
<simpara><link linkend="repmgr-node-service">repmgr node service</link> (to execute <command>CHECKPOINT</command> via the <option>--checkpoint</option>; note this is also called by <link linkend="repmgr-standby-switchover">repmgr standby switchover</link>)</simpara> <simpara><link linkend="repmgr-node-service">repmgr node service</link> (to execute <command>CHECKPOINT</command> via the <option>--checkpoint</option>; note this is also called by <link linkend="repmgr-standby-switchover">repmgr standby switchover</link>)</simpara>
</listitem> </listitem>
<listitem>
<simpara><link linkend="repmgr-node-rejoin">repmgr node rejoin</link> (to execute <command>repmgr node rejoin --force-rewind</command>)</simpara>
</listitem>
</itemizedlist> </itemizedlist>
</para> </para>
</sect3> </sect3>

View File

@@ -113,6 +113,29 @@
</varlistentry> </varlistentry>
<varlistentry>
<term><option>-S/--superuser</option></term>
<listitem>
<para>
Specify a superuser to be used by <application>pg_rewind</application>
for its source server connection.
</para>
<para>
<application>pg_rewind</application> requires a normal (non-replication)
connection with <literal>pg_read_server_files</literal> privilege or
superuser rights. If the &repmgr; user has the <literal>REPLICATION</literal>
attribute but lacks these privileges, use this option to specify
a suitably privileged user for the <application>pg_rewind</application>
connection. The superuser's password should be configured in
<filename>.pgpass</filename>.
</para>
<para>
This option is only effective in combination with
<option>--force-rewind</option>.
</para>
</listitem>
</varlistentry>
<varlistentry> <varlistentry>
<term><option>-W/--no-wait</option></term> <term><option>-W/--no-wait</option></term>
<listitem> <listitem>
@@ -281,6 +304,23 @@
a &quot;magic bullet&quot; which can resolve all problematic replication situations. a &quot;magic bullet&quot; which can resolve all problematic replication situations.
</para> </para>
<note>
<para>
<command>pg_rewind</command> requires a normal (non-replication) connection to
the source server. The user for this connection must have superuser rights or
the <literal>pg_read_server_files</literal> role.
</para>
<para>
If the &repmgr; user has the <literal>REPLICATION</literal> attribute but does
not have the privileges required by <command>pg_rewind</command>, use the
<option>-S/--superuser</option> option to specify a suitably privileged user
for the <command>pg_rewind</command> source connection. For example:
<programlisting>
repmgr node rejoin -f /etc/repmgr.conf -d 'host=node3 dbname=repmgr user=repmgr' \
--force-rewind -S postgres</programlisting>
</para>
</note>
<para> <para>
A typical use-case for <command>pg_rewind</command> is when a scenario like the following A typical use-case for <command>pg_rewind</command> is when a scenario like the following
is encountered: is encountered:

View File

@@ -2838,9 +2838,29 @@ do_node_rejoin(void)
appendShellString(&command, appendShellString(&command,
config_file_options.data_directory); config_file_options.data_directory);
appendPQExpBuffer(&command, if (runtime_options.superuser[0] != '\0')
" --source-server='%s'", {
primary_node_record.conninfo); t_conninfo_param_list rewind_conninfo = T_CONNINFO_PARAM_LIST_INITIALIZER;
char *rewind_conninfo_str = NULL;
initialize_conninfo_params(&rewind_conninfo, false);
parse_conninfo_string(primary_node_record.conninfo, &rewind_conninfo, NULL, false);
param_set(&rewind_conninfo, "user", runtime_options.superuser);
rewind_conninfo_str = param_list_to_string(&rewind_conninfo);
appendPQExpBuffer(&command,
" --source-server='%s'",
rewind_conninfo_str);
pfree(rewind_conninfo_str);
free_conninfo_params(&rewind_conninfo);
}
else
{
appendPQExpBuffer(&command,
" --source-server='%s'",
primary_node_record.conninfo);
}
if (runtime_options.dry_run == true) if (runtime_options.dry_run == true)
{ {
@@ -3698,6 +3718,7 @@ do_node_help(void)
printf(_(" --config-archive-dir directory to temporarily store retained configuration files\n" \ printf(_(" --config-archive-dir directory to temporarily store retained configuration files\n" \
" (default: /tmp)\n")); " (default: /tmp)\n"));
printf(_(" -W, --no-wait don't wait for the node to rejoin cluster\n")); printf(_(" -W, --no-wait don't wait for the node to rejoin cluster\n"));
printf(_(" -S, --superuser=USERNAME superuser to use for pg_rewind if repmgr user is not superuser\n"));
puts(""); puts("");
printf(_("NODE SERVICE\n")); printf(_("NODE SERVICE\n"));

View File

@@ -5359,6 +5359,13 @@ do_standby_switchover(void)
} }
appendPQExpBufferChar(&node_rejoin_options, ' '); appendPQExpBufferChar(&node_rejoin_options, ' ');
if (runtime_options.superuser[0] != '\0')
{
appendPQExpBuffer(&node_rejoin_options,
"--superuser=%s ",
runtime_options.superuser);
}
} }
key_value_list_free(&remote_config_files); key_value_list_free(&remote_config_files);

View File

@@ -1761,6 +1761,7 @@ check_cli_parameters(const int action)
case STANDBY_SWITCHOVER: case STANDBY_SWITCHOVER:
case NODE_CHECK: case NODE_CHECK:
case NODE_SERVICE: case NODE_SERVICE:
case NODE_REJOIN:
break; break;
default: default:
item_list_append_format(&cli_warnings, item_list_append_format(&cli_warnings,