From 45abbe3301f7d8b0ee4294eabac9e0c88a654014 Mon Sep 17 00:00:00 2001 From: Douglas J Hunley Date: Thu, 3 Sep 2026 17:51:44 -0400 Subject: [PATCH] feat(sudoers): let supabase-admin-agent invoke pgbackrest reconcile as root The salt subcommand's systemd service runs as the unprivileged supabase-admin-agent OS user, which can't write to /etc/pgbackrest/conf.d (owned pgbackrest:postgres 02770). Adds a NOPASSWD sudoers entry scoped to exactly `pgbackrest reconcile`, and changes /opt/supabase-admin-agent ownership to root:root so that sudo grant can't be used to elevate a binary the unprivileged agent user could otherwise replace first. Dependencies: requires supabase/supabase-admin-agent#112 merged (adds the pgbackrest reconcile command this sudoers entry grants access to) and INDATA-1151 resolved via supabase/platform#37380 (the platform repo's provisioning scripts currently revert this root ownership on every new project and wake). --- ansible/files/adminapi.sudoers.conf | 4 ++++ .../supabase-admin-agent.sudoers.conf | 2 ++ ansible/tasks/internal/supabase-admin-agent.yml | 11 ++++++++--- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/ansible/files/adminapi.sudoers.conf b/ansible/files/adminapi.sudoers.conf index ab10665a3c..90f360f75e 100644 --- a/ansible/files/adminapi.sudoers.conf +++ b/ansible/files/adminapi.sudoers.conf @@ -21,6 +21,10 @@ Cmnd_Alias PGBOUNCER = /bin/systemctl start pgbouncer.service, /bin/systemctl st # create/remove signal files owned by that user. %adminapi ALL= NOPASSWD: /usr/local/lib/supabase-admin-agent/pgdata-chown %adminapi ALL=(postgres) NOPASSWD: /usr/local/lib/supabase-admin-agent/pgdata-signal +# Backfills /data/pgbackrest_spool (pgbackrest:postgres 0770) on instances whose +# data volume predates postgres#2312's AMI directory-creation task -- called by +# enable, setup-replica, and stanza-upgrade before archiving is activated/resumed. +%adminapi ALL= NOPASSWD: /opt/supabase-admin-agent/supabase-admin-agent pgbackrest ensure-spool-dir # pgBackRest binary entries support two sudo chains used by supabase-admin-agent: # NewRunner() — adminapi → /usr/bin/pgbackrest wrapper → sudo -u pgbackrest real_binary # NewRunnerAs() — adminapi → sudo -u pgbackrest /usr/bin/pgbackrest → sudo -u pgbackrest real_binary diff --git a/ansible/files/supabase_admin_agent_config/supabase-admin-agent.sudoers.conf b/ansible/files/supabase_admin_agent_config/supabase-admin-agent.sudoers.conf index df65f11370..183e246b95 100644 --- a/ansible/files/supabase_admin_agent_config/supabase-admin-agent.sudoers.conf +++ b/ansible/files/supabase_admin_agent_config/supabase-admin-agent.sudoers.conf @@ -1,2 +1,4 @@ %supabase-admin-agent ALL= NOPASSWD: /usr/bin/salt-call %supabase-admin-agent ALL= NOPASSWD: /usr/bin/gpg --homedir /etc/salt/gpgkeys --import, /usr/bin/gpg --homedir /etc/salt/gpgkeys --list-secret-keys * +# pgbackrest reconcile writes /etc/pgbackrest/conf.d (pgbackrest:postgres 02770) and the agent is in neither group, so this subcommand needs root; the salt subcommand above stays unprivileged +%supabase-admin-agent ALL= NOPASSWD: /opt/supabase-admin-agent/supabase-admin-agent pgbackrest reconcile diff --git a/ansible/tasks/internal/supabase-admin-agent.yml b/ansible/tasks/internal/supabase-admin-agent.yml index bb351979ea..af37e53a8f 100644 --- a/ansible/tasks/internal/supabase-admin-agent.yml +++ b/ansible/tasks/internal/supabase-admin-agent.yml @@ -15,7 +15,10 @@ - name: supabase-admin-agent - config dir file: path: /opt/supabase-admin-agent - owner: supabase-admin-agent + # /opt/supabase-admin-agent (dir, unpacked binary, symlink) is all root-owned so the pgbackrest-reconcile sudo grant below can't be used to swap the binary first; the agent group already has effectively unrestricted root via the salt-call entry, this just closes one more tampering path + owner: root + group: root + mode: "0755" state: directory - name: supabase-admin-agent - gpg dir @@ -30,6 +33,7 @@ src: files/supabase_admin_agent_config/supabase-admin-agent.sudoers.conf dest: /etc/sudoers.d/supabase-admin-agent mode: "0440" + validate: "visudo -csf %s" - name: supabase-admin-agent - pgbackrest helper scripts dir file: @@ -74,7 +78,9 @@ remote_src: yes src: /tmp/supabase-admin-agent.tar.gz dest: /opt/supabase-admin-agent/ - owner: supabase-admin-agent + owner: root + group: root + mode: "0755" extra_opts: - --strip-components=1 @@ -83,7 +89,6 @@ path: /opt/supabase-admin-agent/supabase-admin-agent src: "/opt/supabase-admin-agent/supabase-admin-agent-linux-{{ platform }}" state: link - owner: supabase-admin-agent mode: "0755" force: yes