Skip to content

Commit c7842e7

Browse files
committed
Ensure Postgres data directories have group-read permission
Postgres has allowed group-read on its data directories since v11. This permission enables more avenues for data recovery when storage misbehaves. Issue: PGO-300
1 parent 11666e9 commit c7842e7

File tree

7 files changed

+17
-55
lines changed

7 files changed

+17
-55
lines changed

internal/controller/pgupgrade/jobs.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ func upgradeCommand(spec *v1beta1.PGUpgradeSettings, fetchKeyCommand string) []s
9898
// proper permissions have to be set on the old pgdata directory and the
9999
// preload library settings must be copied over.
100100
`echo -e "\nStep 3: Setting the expected permissions on the old pgdata directory...\n"`,
101-
`chmod 700 /pgdata/pg"${old_version}"`,
101+
`chmod 750 /pgdata/pg"${old_version}"`,
102102
`echo -e "Step 4: Copying shared_preload_libraries setting to new postgresql.conf file...\n"`,
103103
`echo "shared_preload_libraries = '$(/usr/pgsql-"""${old_version}"""/bin/postgres -D \`,
104104
`/pgdata/pg"""${old_version}""" -C shared_preload_libraries)'" >> /pgdata/pg"${new_version}"/postgresql.conf`,

internal/controller/pgupgrade/jobs_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@ spec:
214214
echo -e "Step 2: Initializing new pgdata directory...\n"
215215
/usr/pgsql-"${new_version}"/bin/initdb -k -D /pgdata/pg"${new_version}"
216216
echo -e "\nStep 3: Setting the expected permissions on the old pgdata directory...\n"
217-
chmod 700 /pgdata/pg"${old_version}"
217+
chmod 750 /pgdata/pg"${old_version}"
218218
echo -e "Step 4: Copying shared_preload_libraries setting to new postgresql.conf file...\n"
219219
echo "shared_preload_libraries = '$(/usr/pgsql-"""${old_version}"""/bin/postgres -D \
220220
/pgdata/pg"""${old_version}""" -C shared_preload_libraries)'" >> /pgdata/pg"${new_version}"/postgresql.conf

internal/patroni/config.go

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -518,22 +518,7 @@ func instanceYAML(
518518
if command := pgbackrestReplicaCreateCommand; len(command) > 0 {
519519

520520
// Regardless of the "keep_data" setting below, Patroni deletes the
521-
// data directory when all methods fail. pgBackRest will not restore
522-
// when the data directory is missing, so create it before running the
523-
// command. PostgreSQL requires that the directory is writable by only
524-
// itself.
525-
// - https://github.com/zalando/patroni/blob/v2.0.2/patroni/ha.py#L249
526-
// - https://github.com/pgbackrest/pgbackrest/issues/1445
527-
// - https://git.postgresql.org/gitweb/?p=postgresql.git;f=src/backend/utils/init/miscinit.c;hb=REL_13_0#l319
528-
//
529-
// NOTE(cbandy): The "PATRONI_POSTGRESQL_DATA_DIR" environment variable
530-
// is defined in this package, but it is removed by Patroni at runtime.
531-
command = append([]string{
532-
"bash", "-ceu", "--",
533-
`install --directory --mode=0700 "${PGDATA?}" && exec "$@"`,
534-
"-",
535-
}, command...)
536-
521+
// data directory when all methods fail.
537522
postgresql[pgBackRestCreateReplicaMethod] = map[string]any{
538523
"command": strings.Join(shell.QuoteWords(command...), " "),
539524
"keep_data": true, // Use the data directory from a prior method.

internal/patroni/config_test.go

Lines changed: 4 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -720,8 +720,7 @@ postgresql:
720720
- pgbackrest
721721
- basebackup
722722
pgbackrest:
723-
command: '''bash'' ''-ceu'' ''--'' ''install --directory --mode=0700 "${PGDATA?}"
724-
&& exec "$@"'' ''-'' ''some'' ''backrest'' ''cmd'''
723+
command: '''some'' ''backrest'' ''cmd'''
725724
keep_data: true
726725
no_leader: true
727726
no_params: true
@@ -786,40 +785,17 @@ func TestPGBackRestCreateReplicaCommand(t *testing.T) {
786785
}
787786
assert.NilError(t, yaml.Unmarshal([]byte(data), &parsed))
788787

789-
dir := t.TempDir()
788+
assert.Equal(t, parsed.PostgreSQL.PGBackRest.Command, `'some' 'backrest' 'cmd'`)
790789

791790
// The command should be compatible with any shell.
792791
{
793-
command := parsed.PostgreSQL.PGBackRest.Command
794-
file := filepath.Join(dir, "command.sh")
795-
assert.NilError(t, os.WriteFile(file, []byte(command), 0o600))
792+
file := filepath.Join(t.TempDir(), "command.sh")
793+
assert.NilError(t, os.WriteFile(file, []byte(parsed.PostgreSQL.PGBackRest.Command), 0o600))
796794

797795
cmd := exec.CommandContext(t.Context(), shellcheck, "--enable=all", "--shell=sh", file)
798796
output, err := cmd.CombinedOutput()
799797
assert.NilError(t, err, "%q\n%s", cmd.Args, output)
800798
}
801-
802-
// Naive parsing of shell words...
803-
command := strings.Split(strings.Trim(parsed.PostgreSQL.PGBackRest.Command, "'"), "' '")
804-
805-
// Expect a bash command with an inline script.
806-
assert.DeepEqual(t, command[:3], []string{"bash", "-ceu", "--"})
807-
assert.Assert(t, len(command) > 3)
808-
script := command[3]
809-
810-
// It should call the pgBackRest command.
811-
assert.Assert(t, strings.HasSuffix(script, ` exec "$@"`))
812-
assert.DeepEqual(t, command[len(command)-3:], []string{"some", "backrest", "cmd"})
813-
814-
// It should pass shellcheck.
815-
{
816-
file := filepath.Join(dir, "script.bash")
817-
assert.NilError(t, os.WriteFile(file, []byte(script), 0o600))
818-
819-
cmd := exec.CommandContext(t.Context(), shellcheck, "--enable=all", file)
820-
output, err := cmd.CombinedOutput()
821-
assert.NilError(t, err, "%q\n%s", cmd.Args, output)
822-
}
823799
}
824800

825801
func TestProbeTiming(t *testing.T) {

internal/pgbackrest/config.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -356,7 +356,7 @@ func DedicatedSnapshotVolumeRestoreCommand(pgdata string, args ...string) []stri
356356
BACKUP_LABEL=$([[ ! -e "${pgdata}/backup_label" ]] || md5sum "${pgdata}/backup_label")
357357
echo "Starting pgBackRest delta restore"
358358
359-
install --directory --mode=0700 "${pgdata}"
359+
install --directory --mode=0750 "${pgdata}"
360360
rm -f "${pgdata}/postmaster.pid"
361361
bash -xc "pgbackrest restore ${opts}"
362362
rm -f "${pgdata}/patroni.dynamic.json"

internal/postgres/config.go

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -341,9 +341,9 @@ func startupCommand(
341341
// and so we add the subdirectory `data` in order to set the permissions.
342342
checkInstallRecreateCmd := strings.Join([]string{
343343
`if [[ ! -e "${tablespace_dir}" || -O "${tablespace_dir}" ]]; then`,
344-
`install --directory --mode=0700 "${tablespace_dir}"`,
344+
`install --directory --mode=0750 "${tablespace_dir}"`,
345345
`elif [[ -w "${tablespace_dir}" && -g "${tablespace_dir}" ]]; then`,
346-
`recreate "${tablespace_dir}" '0700'`,
346+
`recreate "${tablespace_dir}" '0750'`,
347347
`else (halt Permissions!); fi ||`,
348348
`halt "$(permissions "${tablespace_dir}" ||:)"`,
349349
}, "\n")
@@ -419,23 +419,24 @@ chmod +x /tmp/pg_rewind_tde.sh
419419
// PostgreSQL requires its directory to be writable by only itself.
420420
// Pod "securityContext.fsGroup" sets g+w on directories for *some*
421421
// storage providers. Ensure the current user owns the directory, and
422-
// remove group permissions.
422+
// remove group-write permission.
423423
// - https://www.postgresql.org/docs/current/creating-cluster.html
424424
// - https://git.postgresql.org/gitweb/?p=postgresql.git;f=src/backend/postmaster/postmaster.c;hb=REL_10_0#l1522
425-
// - https://git.postgresql.org/gitweb/?p=postgresql.git;f=src/backend/utils/init/miscinit.c;hb=REL_14_0#l349
425+
// - https://git.postgresql.org/gitweb/?p=postgresql.git;f=src/backend/utils/init/miscinit.c;hb=REL_11_0#l142
426+
// - https://git.postgresql.org/gitweb/?p=postgresql.git;f=src/backend/utils/init/miscinit.c;hb=REL_17_0#l386
426427
// - https://issue.k8s.io/93802#issuecomment-717646167
427428
//
428429
// When the directory does not exist, create it with the correct permissions.
429430
// When the directory has the correct owner, set the correct permissions.
430431
`if [[ ! -e "${postgres_data_directory}" || -O "${postgres_data_directory}" ]]; then`,
431-
`install --directory --mode=0700 "${postgres_data_directory}"`,
432+
`install --directory --mode=0750 "${postgres_data_directory}"`,
432433
//
433434
// The directory exists but its owner is wrong. When it is writable,
434435
// the set-group-ID bit indicates that "fsGroup" probably ran on its
435436
// contents making them safe to use. In this case, we can make a new
436437
// directory (owned by this user) and refill it.
437438
`elif [[ -w "${postgres_data_directory}" && -g "${postgres_data_directory}" ]]; then`,
438-
`recreate "${postgres_data_directory}" '0700'`,
439+
`recreate "${postgres_data_directory}" '0750'`,
439440
//
440441
// The directory exists, its owner is wrong, and it is not writable.
441442
`else (halt Permissions!); fi ||`,

internal/postgres/reconcile_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -263,9 +263,9 @@ initContainers:
263263
[[ -d "${bootstrap_dir}" ]] && results 'bootstrap directory' "${bootstrap_dir}"
264264
[[ -d "${bootstrap_dir}" ]] && postgres_data_directory="${bootstrap_dir}"
265265
if [[ ! -e "${postgres_data_directory}" || -O "${postgres_data_directory}" ]]; then
266-
install --directory --mode=0700 "${postgres_data_directory}"
266+
install --directory --mode=0750 "${postgres_data_directory}"
267267
elif [[ -w "${postgres_data_directory}" && -g "${postgres_data_directory}" ]]; then
268-
recreate "${postgres_data_directory}" '0700'
268+
recreate "${postgres_data_directory}" '0750'
269269
else (halt Permissions!); fi ||
270270
halt "$(permissions "${postgres_data_directory}" ||:)"
271271
(mkdir -p '/pgdata/pgbackrest/log' && { chmod 0775 '/pgdata/pgbackrest/log' '/pgdata/pgbackrest' || :; }) ||

0 commit comments

Comments
 (0)