Skip to content
Draft
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
5be45c3
feat(ansible): add pgBackRest tasks and configurations
jchancojr Oct 29, 2025
32ffede
fix(setup-pgbackrest.yml): update nix install path
jchancojr Oct 29, 2025
16c6474
fix(setup-pgbackrest.yml): fix file module
jchancojr Oct 29, 2025
9bb0eb8
fix(setup-pgbackrest.yml): errant indentation fix
jchancojr Oct 29, 2025
e9b3216
Merge remote-tracking branch 'origin/PSQL-773' into PSQL-773
hunleyd Oct 30, 2025
56e3d22
fix(setup-pgbackrest): adjust as per Sam
hunleyd Oct 30, 2025
6b71d69
style(setup-pgbackrest.yml): ansible-lint
jchancojr Oct 30, 2025
7345b6a
refactor(setup-pgbackrest.yml): refactor as per Sam
jchancojr Oct 30, 2025
b191228
fix(setup-pgbackrest): Sanitize pgbackrest wrapper script arguments
hunleyd Nov 12, 2025
365fa48
Merge branch 'develop' into PSQL-773
hunleyd Nov 12, 2025
32c047e
Merge branch 'develop' into PSQL-773
jchancojr Nov 14, 2025
b223518
feat(setup-pgbackrest.yml): add pgbackrest to sudoers
jchancojr Nov 14, 2025
fb412ba
Update ansible/files/pgbackrest_config/pgbackrest.conf
jchancojr Nov 14, 2025
7a50d3b
Update ansible/playbook.yml
jchancojr Nov 14, 2025
865da58
Apply suggestions from code review
jchancojr Nov 14, 2025
6d74ce5
fix(setup-pgbackrest.yml): allow postgres user to run pgbackrest cmds
jchancojr Nov 14, 2025
dc709b7
fix(setup-pgbackrest.yml): add /usr/bin/bash to sudoers task
jchancojr Nov 14, 2025
f7b9c60
Update ansible/tasks/setup-pgbackrest.yml
hunleyd Nov 14, 2025
1d62a99
Update ansible/tasks/setup-pgbackrest.yml
hunleyd Nov 14, 2025
7d4e051
Merge branch 'develop' into PSQL-773
hunleyd Nov 14, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions ansible/files/pgbackrest_config/computed_globals.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[global]
# process-max = 1

[archive-get]
# process-max = 1

[archive-push]
# process-max = 1
17 changes: 17 additions & 0 deletions ansible/files/pgbackrest_config/pgbackrest.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
[global]
archive-async = n
archive-copy = y
backup-standby = prefer
compress-type = zst
delta = y
expire-auto = n
link-all = y
log-level-console = info
log-level-file = detail
log-subprocess = y
resume = n
start-fast = y
[supabase]
pg1-path = /var/lib/postgresql/data
pg1-socket-path = /run/postgresql
pg1-user = supabase_admin
14 changes: 14 additions & 0 deletions ansible/files/pgbackrest_config/repo1.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[supabase]
repo1-block = y
repo1-bundle = y
# repo1-path = <foo>
repo1-retention-diff = 1
repo1-retention-full = 28
repo1-retention-full-type = time
repo1-retention-history = 0
# repo1-s3-bucket= <foo>
# repo1-s3-endpoint= <foo>
repo1-s3-key-type = auto
# repo1-s3-region = <foo>
repo1-storage-upload-chunk-size = 10MiB
repo1-type = s3
2 changes: 2 additions & 0 deletions ansible/files/pgbackrest_config/repo1_async.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[supabase]
# archive-async = y
3 changes: 3 additions & 0 deletions ansible/files/pgbackrest_config/repo1_encrypted.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[supabase]
# repo-cipher-pass = {{ generated pass }}
# repo-cipher-type = aes-256-cbc
4 changes: 4 additions & 0 deletions ansible/playbook.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@
import_tasks: tasks/setup-wal-g.yml
when: debpkg_mode or nixpkg_mode or stage2_nix

- name: Install pgBackRest
import_tasks: tasks/setup-pgbackrest.yml
when: debpkg_mode or nixpkg_mode or stage2_nix

- name: Install Gotrue
import_tasks: tasks/setup-gotrue.yml
tags:
Expand Down
75 changes: 75 additions & 0 deletions ansible/tasks/setup-pgbackrest.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
- name: Create pgBackRest group
group:
name: pgbackrest
state: present
when: nixpkg_mode

- name: Create pgBackRest user
user:
name: pgbackrest
comment: pgBackRest user
group: pgbackrest
groups: pgbackrest, postgres
shell: /sbin/nologin
system: true
home: /var/lib/pgbackrest
when: nixpkg_mode

- name: Install pgBackRest
become: true
become_user: pgbackrest
shell: |
sudo -u pgbackrest bash -c ". /nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh && nix profile install github:supabase/postgres/{{ git_commit_sha }}#pg-backrest"
when: stage2_nix

- name: Create needed directories for pgBackRest
file:
path: "{{ backrest_dir }}"
state: directory
owner: pgbackrest
group: postgres
mode: '0770'
loop:
- /etc/pgbackrest/conf.d
- /var/lib/pgbackrest
- /var/spool/pgbackrest
- /var/log/pgbackrest
loop_control:
loop_var: backrest_dir
when: nixpkg_mode

- name: Symlink pgbackrest.conf
file:
path: /etc/pgbackrest/pgbackrest.conf
src: /etc/pgbackrest.conf
state: link
force: true

- name: Move pgBackRest files to /etc/pgbackrest
copy:
dest: "/etc/pgbackrest/{{ conf_item['path'] }}/{{ conf_item['name'] }}"
group: postgres
mode: '0644'
owner: pgbackrest
src: "files/pgbackrest_config/{{ conf_item['name'] }}"
loop:
- {name: computed_globals.conf, path: conf.d}
- {name: pgbackrest.conf, path: ''}
- {name: repo1_async.conf, path: conf.d}
- {name: repo1_encrypted.conf, path: conf.d}
- {name: repo1.conf, path: conf.d}
loop_control:
loop_var: conf_item
when: stage2_nix

- name: Symlink pgBackRest binary
file:
path: /usr/bin/pgbackrest
src: /var/lib/pgbackrest/.nix-profile/bin/pgbackrest
state: link

- name: Sticky bit the pgBackRest binary
file:
path: /var/lib/pgbackrest/.nix-profile/bin/pgbackrest
mode: '4755'
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Setting setuid won't work for multiple reasons:

  • /var/lib/pgbackrest/.nix-profile/bin/pgbackrest is a symlink to a binary in the store, and the kernel ignores setuid bits on symlinks.
  • We cannot modify the binary file in the Nix store as it is mounted with ro
  • Binaries with setuid in the Nix store won't work as it is mounted with nosuid

I see two possible paths:

  • Use sudo/doas
  • Create a wrapper in /usr/local/bin (or /usr/bin) that execs the original pgbackrest in the store and set the setuid bit on that wrapper

I find the sudo approach safer and more maintainable than using a setuid wrapper.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something like this could work

- name: Configure sudoers for pgBackRest
  lineinfile:
    path: /etc/sudoers.d/pgbackrest
    line: "postgres ALL=(pgbackrest) NOPASSWD: /var/lib/pgbackrest/.nix-profile/bin/pgbackrest"
    create: yes
    mode: '0440'
    validate: 'visudo -cf %s'

- name: Create pgBackRest wrapper script
  copy:
    content: |
      #!/bin/bash
      exec sudo -u pgbackrest /var/lib/pgbackrest/.nix-profile/bin/pgbackrest "$@"
    dest: /usr/bin/pgbackrest
    mode: '0755'
    owner: root
    group: root

if not root/root could be some user/group such that pgbackrest cannot change permissions of it's own script

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@staaldraad might want to take a peek at this just to make sure you're happy with this kind of approach too when you have a moment

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there are a few arguments that look a little scary

  • --cmd
  • --ssh-cmd
  • --repo-host-cmd

The --config variants may also allow access to the above arguments.

The wrapper would feel safer, although less flexible, if we had fixed arguments per command in the wrapper, and then allow sudo on the wrapper.

Something like:

#!/bin/bash

if [[ "$1" == "archive-get" ]]; then
  exec /var/lib/pgbackrest/.nix-profile/bin/pgbackrest archive-get --stanza="$2"
else
  echo "Error: not allowed"
  exit 1
fi
- name: Configure sudoers for pgBackRest
  lineinfile:
    path: /etc/sudoers.d/pgbackrest
    line: "postgres ALL=(pgbackrest) NOPASSWD:  /usr/bin/pgbackrest"
    create: yes
    mode: '0440'
    validate: 'visudo -cf %s'

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the backrest binary can only be called if you're already on the box, or by changing the archive_command in the postgresql.conf, which we disallow already. so it kinda feels like by the time someone could exploit those args, we'd already have bigger problems, no? @staaldraad

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, in the ideal world. And that is what #1879 brings us closer to. Up until now, there was still avenue for superuser --> COPY (select 1) FROM PROGRAM '.../pgbackrest' (or first getting a more powerful shell and then having "local" access).

Fully agree that we are protecting against "bigger problems", however it is good to think of layered defense. As we start narrowing how big a problem local access is (if the likeliest route is to get shell as the unprivileged postgres user). With things like https://github.com/supabase/postgres/pull/1846/files , we really squeeze down what is possible to do, even if someone manages to change archive_command or get shell via postgres. Definitely trying to think in chains of small bugs here, in isolation one bug or protection might not be much, but chained together small bugs can be powerful. And the same goes for small protections

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep. This makes sense. Thanks very much will get this going

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

an attempt at addressing your concern is now in this PR @staaldraad . Please let us know what you think?

become: true
Loading