1
0
mirror of https://github.com/pgbackrest/pgbackrest.git synced 2024-12-04 09:43:08 +02:00

Fix permissions when restore run as root user.

When restore was run as the root user the pg_control file would end up with root permissions. This bug was introduced in e634fd8. Fix this by directly overwriting the pg_control temp file rather than doing an atomic write that updates permissions. Also update other parameters to more closely match similar calls.

There was also an adjacent error where restore as the root user would fail if the base path did not exist. Fix this by ignoring the missing path since it will be created later and this logic is just trying to find an alternate user for permissions if the user in the manifest does not exist.
This commit is contained in:
David Steele 2024-08-13 11:43:05 +08:00 committed by GitHub
parent 5766353649
commit ed9b0c260a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 67 additions and 5 deletions

View File

@ -1,5 +1,20 @@
<release date="XXXX-XX-XX" version="2.54dev" title="UNDER DEVELOPMENT">
<release-core-list>
<release-bug-list>
<release-item>
<github-issue id="2407"/>
<github-pull-request id="2416"/>
<release-item-contributor-list>
<release-item-ideator id="will.m"/>
<release-item-contributor id="david.steele"/>
<release-item-reviewer id="stefan.fercot"/>
</release-item-contributor-list>
<p>Fix permissions when <cmd>restore</cmd> run as root user.</p>
</release-item>
</release-bug-list>
<release-improvement-list>
<release-item>
<github-pull-request id="2399"/>

View File

@ -1098,6 +1098,10 @@
<contributor-id type="github">vthriller</contributor-id>
</contributor>
<contributor id="will.m">
<contributor-name-display>Will M</contributor-name-display>
</contributor>
<contributor id="william.cox">
<contributor-name-display>William Cox</contributor-name-display>
<contributor-id type="github">mydimension</contributor-id>

View File

@ -778,7 +778,7 @@ restoreManifestOwner(const Manifest *const manifest, const String **const rootRe
if (userRoot())
{
// Get user/group info from data directory to use for invalid user/groups
StorageInfo pathInfo = storageInfoP(storagePg(), manifestTargetBase(manifest)->path);
StorageInfo pathInfo = storageInfoP(storagePg(), manifestTargetBase(manifest)->path, .ignoreMissing = true);
// If user/group is null then set it to root
if (pathInfo.user == NULL) // {vm_covered}
@ -2618,7 +2618,7 @@ cmdRestore(void)
storagePutP(
storageNewWriteP(
storagePgWrite(), STRDEF(PG_PATH_GLOBAL "/" PG_FILE_PGCONTROL "." STORAGE_FILE_TEMP_EXT),
.modeFile = pgControlFile.mode, .timeModified = pgControlFile.timestamp),
.timeModified = pgControlFile.timestamp, .noAtomic = true, .noCreatePath = true, .noSyncPath = true),
pgControlBuffer);
}

View File

@ -266,6 +266,7 @@ hrnHostExecBr(HrnHost *const this, const char *const command, const HrnHostExecB
FUNCTION_HARNESS_BEGIN();
FUNCTION_HARNESS_PARAM(HRN_HOST, this);
FUNCTION_HARNESS_PARAM(STRINGZ, command);
FUNCTION_HARNESS_PARAM(STRINGZ, param.user);
FUNCTION_HARNESS_PARAM(STRINGZ, param.option);
FUNCTION_HARNESS_PARAM(STRINGZ, param.param);
FUNCTION_HARNESS_PARAM(INT, param.resultExpect);
@ -278,6 +279,7 @@ hrnHostExecBr(HrnHost *const this, const char *const command, const HrnHostExecB
MEM_CONTEXT_TEMP_BEGIN()
{
const String *const user = param.user == NULL ? NULL : STR(param.user);
String *const commandStr = strCatFmt(strNew(), "pgbackrest --stanza=" HRN_STANZA);
if (param.option != NULL)
@ -288,7 +290,7 @@ hrnHostExecBr(HrnHost *const this, const char *const command, const HrnHostExecB
if (param.param != NULL)
strCatFmt(commandStr, " %s", param.param);
strCat(result, hrnHostExecP(this, commandStr, .resultExpect = param.resultExpect));
strCat(result, hrnHostExecP(this, commandStr, .user = user, .resultExpect = param.resultExpect));
}
MEM_CONTEXT_TEMP_END();

View File

@ -315,6 +315,7 @@ String *hrnHostExec(HrnHost *this, const String *command, HrnHostExecParam param
typedef struct HrnHostExecBrParam
{
VAR_PARAM_HEADER;
const char *user; // User to execute command
const char *option; // Options
const char *param; // Parameters
int resultExpect; // Expected result, if not 0

View File

@ -117,8 +117,48 @@ testRun(void)
// -------------------------------------------------------------------------------------------------------------------------
TEST_TITLE("standby restore");
{
TEST_HOST_BR(
pg2, CFGCMD_RESTORE, .option = zNewFmt("--type=standby --tablespace-map=ts1='%s'", strZ(hrnHostPgTsPath(pg2))));
// If the repo is running on pg2 then switch the user to root to test restoring as root. Use this configuration so the
// repo is local and root does not need to be configured for TLS or SSH.
const char *user = NULL;
if (pg2 == repo)
{
user = "root";
// Create the pg/ts directory so it will not be created with root permissions
HRN_STORAGE_PATH_CREATE(hrnHostDataStorage(pg2), strZ(hrnHostPgTsPath(pg2)), .mode = 0700);
// Create the restore log file so it will not be created with root permissions
HRN_STORAGE_PUT_EMPTY(hrnHostDataStorage(pg2), zNewFmt("%s/test-restore.log", strZ(hrnHostLogPath(pg2))));
}
// Run restore
const char *option = zNewFmt("--type=standby --tablespace-map=ts1='%s'", strZ(hrnHostPgTsPath(pg2)));
TEST_HOST_BR(pg2, CFGCMD_RESTORE, .option = option, .user = user);
// If the repo is running on pg2 then perform additional ownership tests
if (pg2 == repo)
{
// Update some ownership to root
hrnHostExecP(
pg2,
strNewFmt(
"chown %s:%s %s &&"
"chown %s:%s %s/" PG_PATH_GLOBAL " &&"
"chown %s:%s %s/" PG_PATH_GLOBAL "/" PG_FILE_PGCONTROL,
user, user, strZ(hrnHostPgDataPath(pg2)), user, user, strZ(hrnHostPgDataPath(pg2)), user, user,
strZ(hrnHostPgDataPath(pg2))),
.user = STRDEF(user));
// Expect an error when running without root since permissions cannot be updated
TEST_HOST_BR(
pg2, CFGCMD_RESTORE, .option = zNewFmt("--delta %s", option), .user = NULL,
.resultExpect = errorTypeCode(&FileOpenError));
// Running as root fixes the ownership
TEST_HOST_BR(pg2, CFGCMD_RESTORE, .option = zNewFmt("--delta %s", option), .user = user);
}
HRN_HOST_PG_START(pg2);
// Check standby