Skip to content

Commit fb3075a

Browse files
committed
feat: add the supabase admin agent to the ami build
1 parent 0271bfe commit fb3075a

File tree

10 files changed

+242
-21
lines changed

10 files changed

+242
-21
lines changed
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
name: "Download Supabase project artifacts"
2+
description: "Authenticate with AWS shared account and download artifacts from S3"
3+
4+
inputs:
5+
region:
6+
description: "AWS region"
7+
required: true
8+
auth-role:
9+
description: "Initial role to assume using GitHub OIDC"
10+
required: true
11+
download-role:
12+
description: "Role to assume for S3 access"
13+
required: true
14+
bucket:
15+
description: "S3 bucket name"
16+
required: true
17+
artifacts:
18+
description: "Newline-separated list of artefact filenames to download"
19+
required: true
20+
21+
runs:
22+
using: "composite"
23+
steps:
24+
- name: GitHub OIDC Auth
25+
uses: aws-actions/[email protected]
26+
with:
27+
aws-region: ${{ inputs.region }}
28+
role-to-assume: ${{ inputs.auth-role }}
29+
role-session-name: github-oidc-session
30+
31+
- name: Assume Destination Role
32+
uses: aws-actions/[email protected]
33+
with:
34+
aws-region: ${{ inputs.region }}
35+
role-to-assume: ${{ inputs.download-role }}
36+
role-session-name: s3-access
37+
role-skip-session-tagging: true
38+
role-chaining: true
39+
40+
- name: Download artifacts from S3
41+
shell: bash
42+
run: |
43+
set -euo pipefail
44+
mkdir -p /tmp/supabase-dist
45+
46+
bucket="${{ inputs.bucket }}"
47+
mapfile -t entries <<< "${{ inputs.artifacts }}"
48+
49+
for entry in "${entries[@]}"; do
50+
# Trim whitespace
51+
entry="${entry#"${entry%%[![:space:]]*}"}"
52+
entry="${entry%"${entry##*[![:space:]]}"}"
53+
54+
# Skip empty lines
55+
if [[ -z "$entry" ]]; then
56+
continue
57+
fi
58+
59+
IFS=',' read -r tool version platform <<< "$entry"
60+
61+
# Validate expected parts
62+
if [[ -z "$tool" || -z "$version" || -z "$platform" ]]; then
63+
echo "Warning: skipping malformed artifact entry: '$entry'" >&2
64+
continue
65+
fi
66+
67+
filename="${tool}-${version}-${platform}.tar.xz"
68+
s3_path="s3://${bucket}/${tool}/v${version}/${filename}"
69+
70+
echo "Downloading $s3_path"
71+
aws s3 cp --no-progress "$s3_path" "/tmp/supabase-dist/$filename"
72+
done

.github/workflows/nix-build.yml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -79,19 +79,19 @@ jobs:
7979
trusted-public-keys = nix-postgres-artifacts:dGZlQOvKcNEjvT7QEAJbcV6b6uk7VF/hWMjhYleiaLI=% cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=
8080
- name: Build psql bundle
8181
run: >
82-
nix run "github:Mic92/nix-fast-build?rev=b1dae483ab7d4139a6297e02b6de9e5d30e43d48"
83-
-- --skip-cached --no-nom
84-
--flake ".#checks.$(nix eval --raw --impure --expr 'builtins.currentSystem')"
82+
nix run "github:Mic92/nix-fast-build?rev=b1dae483ab7d4139a6297e02b6de9e5d30e43d48"
83+
-- --skip-cached --no-nom
84+
--flake ".#checks.$(nix eval --raw --impure --expr 'builtins.currentSystem')"
8585
env:
8686
AWS_ACCESS_KEY_ID: ${{ env.AWS_ACCESS_KEY_ID }}
8787
AWS_SECRET_ACCESS_KEY: ${{ env.AWS_SECRET_ACCESS_KEY }}
8888
AWS_SESSION_TOKEN: ${{ env.AWS_SESSION_TOKEN }}
89-
89+
9090
run-testinfra:
9191
needs: build-run-image
9292
if: ${{ success() }}
9393
uses: ./.github/workflows/testinfra-ami-build.yml
94-
94+
9595
run-tests:
9696
needs: build-run-image
9797
if: ${{ success() }}

.github/workflows/testinfra-ami-build.yml

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ jobs:
3737
ubuntu_release: focal
3838
ubuntu_version: 20.04
3939
mcpu: neoverse-n1
40-
runs-on: ${{ matrix.runner }}
40+
runs-on: ${{ matrix.runner }}
4141
timeout-minutes: 150
4242
permissions:
4343
contents: write
@@ -53,6 +53,18 @@ jobs:
5353
with:
5454
cmd: yq 'to_entries | map(select(.value|type == "!!str")) | map(.key + "=" + .value) | join("\n")' 'ansible/vars.yml'
5555

56+
- name: Download Supabase Artifacts
57+
id: download-artifacts
58+
uses: ./.github/actions/download-supabase-artifacts
59+
with:
60+
region: ap-southeast-1
61+
auth-role: ${{ secrets.SUPABASE_GITHUB_OIDC_ROLE }}
62+
download-role: ${{ secrets.SHARED_AWS_ARTIFACTS_S3_ROLE }}
63+
bucket: ${{ secrets.SHARED_AWS_ARTIFACTS_BUCKET }}
64+
artifacts: |
65+
supabase-admin-agent,1.4.35,linux-arm64
66+
supabase-admin-agent,1.4.35,linux-amd64
67+
5668
- run: docker context create builders
5769

5870
- uses: docker/setup-buildx-action@v3
@@ -73,7 +85,7 @@ jobs:
7385
echo 'postgres-version = "'$PG_VERSION'"' > common-nix.vars.pkr.hcl
7486
# Ensure there's a newline at the end of the file
7587
echo "" >> common-nix.vars.pkr.hcl
76-
88+
7789
- name: Build AMI stage 1
7890
run: |
7991
packer init amazon-arm64-nix.pkr.hcl
@@ -84,7 +96,7 @@ jobs:
8496
run: |
8597
packer init stage2-nix-psql.pkr.hcl
8698
GIT_SHA=${{github.sha}}
87-
packer build -var "git-head-version=${GIT_SHA}" -var "packer-execution-id=${GITHUB_RUN_ID}" -var "postgres_major_version=${POSTGRES_MAJOR_VERSION}" -var-file="development-arm.vars.pkr.hcl" -var-file="common-nix.vars.pkr.hcl" -var "postgres-version=${{ steps.random.outputs.random_string }}" -var "region=ap-southeast-1" -var 'ami_regions=["ap-southeast-1"]' -var "force-deregister=true" -var "git_sha=${GITHUB_SHA}" stage2-nix-psql.pkr.hcl
99+
packer build -var "git-head-version=${GIT_SHA}" -var "packer-execution-id=${GITHUB_RUN_ID}" -var "postgres_major_version=${POSTGRES_MAJOR_VERSION}" -var-file="development-arm.vars.pkr.hcl" -var-file="common-nix.vars.pkr.hcl" -var "postgres-version=${{ steps.random.outputs.random_string }}" -var "region=ap-southeast-1" -var 'ami_regions=["ap-southeast-1"]' -var "force-deregister=true" -var "git_sha=${GITHUB_SHA}" stage2-nix-psql.pkr.hcl
88100
89101
- name: Run tests
90102
timeout-minutes: 10
@@ -93,8 +105,8 @@ jobs:
93105
run: |
94106
# TODO: use poetry for pkg mgmt
95107
pip3 install boto3 boto3-stubs[essential] docker ec2instanceconnectcli pytest pytest-testinfra[paramiko,docker] requests
96-
pytest -vv -s testinfra/test_ami_nix.py
97-
108+
pytest -vv -s testinfra/test_ami_nix.py
109+
98110
- name: Cleanup resources on build cancellation
99111
if: ${{ cancelled() }}
100112
run: |
@@ -111,7 +123,7 @@ jobs:
111123
# Define AMI name patterns
112124
STAGE1_AMI_NAME="supabase-postgres-ci-ami-test-stage-1"
113125
STAGE2_AMI_NAME="${{ steps.random.outputs.random_string }}"
114-
126+
115127
# Function to deregister AMIs by name pattern
116128
deregister_ami_by_name() {
117129
local ami_name_pattern=$1
@@ -121,7 +133,7 @@ jobs:
121133
aws ec2 deregister-image --region ap-southeast-1 --image-id $ami_id
122134
done
123135
}
124-
136+
125137
# Deregister AMIs
126138
deregister_ami_by_name "$STAGE1_AMI_NAME"
127139
deregister_ami_by_name "$STAGE2_AMI_NAME"

ansible/files/permission_check.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,11 +94,17 @@
9494
"systemd-coredump": [
9595
{"groupname": "systemd-coredump", "username": "systemd-coredump"}
9696
],
97+
"supabase-admin-agent": [
98+
{"groupname": "supabase-admin-agent", "username": "supabase-admin-agent"},
99+
{"groupname": "admin", "username": "supabase-admin-agent"},
100+
{"groupname": "salt", "username": "supabase-admin-agent"},
101+
],
97102
}
98103

99104
# postgresql.service is expected to mount /etc as read-only
100105
expected_mount = "/etc ro"
101106

107+
102108
# This program depends on osquery being installed on the system
103109
# Function to run osquery
104110
def run_osquery(query):
@@ -154,6 +160,7 @@ def check_nixbld_users():
154160

155161
print("All nixbld users are in the 'nixbld' group.")
156162

163+
157164
def check_postgresql_mount():
158165
# processes table has the nix .postgres-wrapped path as the
159166
# binary path, rather than /usr/lib/postgresql/bin/postgres which
@@ -182,6 +189,7 @@ def check_postgresql_mount():
182189

183190
print("postgresql.service mounts /etc as read-only.")
184191

192+
185193
def main():
186194
parser = argparse.ArgumentParser(
187195
prog="Supabase Postgres Artifact Permissions Checker",
@@ -234,6 +242,7 @@ def main():
234242
"postgrest",
235243
"tcpdump",
236244
"systemd-coredump",
245+
"supabase-admin-agent",
237246
]
238247
if not qemu_artifact:
239248
usernames.append("ec2-instance-connect")
@@ -251,5 +260,6 @@ def main():
251260
# Check if postgresql.service is using a read-only mount for /etc
252261
check_postgresql_mount()
253262

263+
254264
if __name__ == "__main__":
255265
main()
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
%supabase-admin-agent ALL= NOPASSWD: /usr/bin/salt-call
2+
%supabase-admin-agent ALL= NOPASSWD: /usr/bin/gpg --homedir {{ gpgdir }} --import, /usr/bin/gpg --homedir {{ gpgdir }} --list-secret-keys *
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
[Unit]
2+
Description=Configuration management via supabase-admin-agent salt
3+
After=network.target
4+
5+
[Service]
6+
Type=oneshot
7+
ExecStart=/opt/supabase-admin-agent/supabase-admin-agent --config /opt/supabase-admin-agent/config.yaml salt --apply --store-result
8+
User=supabase-admin-agent
9+
Group=supabase-admin-agent
10+
StandardOutput=journal
11+
StandardError=journal
12+
StateDirectory=supabase-admin-agent
13+
CacheDirectory=supabase-admin-agent
14+
15+
# Security hardening
16+
PrivateTmp=true
17+
18+
[Install]
19+
WantedBy=multi-user.target
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
[Unit]
2+
Description=Run Supabase supabase-admin-agent salt on a schedule
3+
Requires=supabase-admin-agent_salt.service
4+
5+
[Timer]
6+
OnCalendar=*:0/10
7+
# Random delay up to {{ splay }} seconds splay
8+
RandomizedDelaySec={{ splay }}
9+
AccuracySec=1s
10+
Persistent=true
11+
12+
[Install]
13+
WantedBy=timers.target
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
- name: supabase-admin-agent - system group
2+
group:
3+
name: supabase-admin-agent
4+
system: yes
5+
6+
- name: supabase-admin-agent - system user
7+
user:
8+
name: supabase-admin-agent
9+
group: supabase-admin-agent
10+
groups: admin,salt
11+
append: yes
12+
system: yes
13+
shell: /bin/sh
14+
15+
- name: supabase-admin-agent - config dir
16+
file:
17+
path: /opt/supabase-admin-agent
18+
owner: supabase-admin-agent
19+
state: directory
20+
21+
- name: supabase-admin-agent - gpg dir
22+
file:
23+
path: /etc/salt/gpgkeys
24+
owner: root
25+
group: salt
26+
state: directory
27+
28+
- name: give supabase-admin-agent user permissions
29+
copy:
30+
src: files/supabase_admin_agent_config/supabase-admin-agent.sudoers.conf
31+
dest: /etc/sudoers.d/supabase-admin-agent
32+
mode: "0644"
33+
34+
- name: Setting arch (x86)
35+
set_fact:
36+
arch: "x86"
37+
when: platform == "amd64"
38+
39+
- name: Setting arch (arm)
40+
set_fact:
41+
arch: "arm64"
42+
when: platform == "arm64"
43+
44+
- name: Copy supabase-admin-agent archive
45+
copy:
46+
src: "/tmp/supabase-dist/supabase-admin-agent-{{ supabase_admin_agent_release }}-linux-{{ arch }}.tar.xz"
47+
dest: "/tmp/supabase-admin-agent.tar.xz"
48+
mode: "0755"
49+
50+
- name: supabase-admin-agent - unpack archive in /opt
51+
unarchive:
52+
remote_src: yes
53+
src: /tmp/supabase-admin-agent.tar.xz
54+
dest: /opt/supabase-admin-agent/
55+
owner: supabase-admin-agent
56+
extra_opts:
57+
- --strip-components=1
58+
59+
- name: supabase-admin-agent - create symlink
60+
ansible.builtin.file:
61+
path: /opt/supabase-admin-agent/supabase-admin-agent
62+
src: "/opt/supabase-admin-agent/supabase-admin-agent-linux-{{ arch }}"
63+
state: link
64+
owner: supabase-admin-agent
65+
mode: "0755"
66+
force: yes
67+
68+
- name: supabase-admin-agent - create salt systemd timer file
69+
copy:
70+
src: files/supabase_admin_agent_config/supabase-admin-agent_salt.timer
71+
dest: /etc/systemd/system/supabase-admin-agent_salt.timer
72+
73+
- name: supabase-admin-agent - create salt service file
74+
copy:
75+
src: files/supabase_admin_agent_config/supabase-admin-agent_salt.service
76+
dest: /etc/systemd/system/supabase-admin-agent_salt.service
77+
78+
- name: supabase-admin-agent - reload systemd
79+
systemd:
80+
daemon_reload: yes
81+
82+
# Initially ensure supabase-admin-agent is installed but not started
83+
- name: supabase-admin-agent - DISABLE service
84+
systemd:
85+
name: supabase-admin-agent_salt
86+
enabled: no
87+
state: stopped

0 commit comments

Comments
 (0)