From 3b2cba390b237ef0bf8cae11a1c5115ba0e7c918 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rton=20Boros?= Date: Mon, 2 Nov 2026 22:30:22 +0200 Subject: [PATCH 1/6] feat(ansible): bake supa-tripwire into the image Refs: MPG-1420 Co-Authored-By: Claude Sonnet 5.5 --- .../files/supa_tripwire/supa-tripwire-wrapped | 13 ++++ ansible/files/supa_tripwire/supa-tripwire.env | 1 + .../files/supa_tripwire/supa-tripwire.service | 21 ++++++ ansible/tasks/internal/supa-tripwire.yml | 66 +++++++++++++++++++ ansible/tasks/setup-supabase-internal.yml | 4 ++ ansible/vars.yml | 8 +++ 6 files changed, 113 insertions(+) create mode 100644 ansible/files/supa_tripwire/supa-tripwire-wrapped create mode 100644 ansible/files/supa_tripwire/supa-tripwire.env create mode 100644 ansible/files/supa_tripwire/supa-tripwire.service create mode 100644 ansible/tasks/internal/supa-tripwire.yml diff --git a/ansible/files/supa_tripwire/supa-tripwire-wrapped b/ansible/files/supa_tripwire/supa-tripwire-wrapped new file mode 100644 index 0000000000..85c762123d --- /dev/null +++ b/ansible/files/supa_tripwire/supa-tripwire-wrapped @@ -0,0 +1,13 @@ +#!/usr/bin/env bash + +POSTGRES_BIN=$(readlink -f $(which postgres)) +POSTGRES_DIR=$(dirname "$POSTGRES_BIN") +WRAPPED="$POSTGRES_DIR/.postgres-wrapped" + +if [ -f "$WRAPPED" ]; then + TARGET="$WRAPPED" +else + TARGET="$POSTGRES_BIN" +fi + +exec /usr/local/bin/supa-tripwire "$TARGET" diff --git a/ansible/files/supa_tripwire/supa-tripwire.env b/ansible/files/supa_tripwire/supa-tripwire.env new file mode 100644 index 0000000000..183aa69ff9 --- /dev/null +++ b/ansible/files/supa_tripwire/supa-tripwire.env @@ -0,0 +1 @@ +# supa-tripwire environment, written by salt diff --git a/ansible/files/supa_tripwire/supa-tripwire.service b/ansible/files/supa_tripwire/supa-tripwire.service new file mode 100644 index 0000000000..db2423d831 --- /dev/null +++ b/ansible/files/supa_tripwire/supa-tripwire.service @@ -0,0 +1,21 @@ +# /etc/systemd/system/supa-tripwire.service +[Unit] +Description=supa-tripwire - Postgres badness monitor +After=postgresql.service + +[Service] +User=root +Restart=always +RestartSec=5 + +EnvironmentFile=/etc/supa-tripwire.env + +ExecStart=/usr/local/bin/supa-tripwire-wrapped + +NoNewPrivileges=true +SecureBits=no-setuid-fixup-locked +ProtectSystem=strict +ProtectHome=true + +[Install] +WantedBy=multi-user.target diff --git a/ansible/tasks/internal/supa-tripwire.yml b/ansible/tasks/internal/supa-tripwire.yml new file mode 100644 index 0000000000..05a28a6ea8 --- /dev/null +++ b/ansible/tasks/internal/supa-tripwire.yml @@ -0,0 +1,66 @@ +- name: Supa-tripwire - download archive + ansible.builtin.get_url: + url: "{{ supa_tripwire_artifacts[platform]['url'] }}" + checksum: "{{ supa_tripwire_artifacts[platform]['checksum'] }}" + dest: "/tmp/supa-tripwire.tar.xz" + mode: "0600" + timeout: 90 + register: download_result + until: download_result is succeeded + retries: 3 + delay: 2 + +- name: Supa-tripwire - extract binary + ansible.builtin.unarchive: + remote_src: true + src: /tmp/supa-tripwire.tar.xz + dest: /tmp + include: + - "supa-tripwire-v{{ supa_tripwire_release }}-{{ platform }}-linux-musl" + +- name: Supa-tripwire - install binary + ansible.builtin.copy: + remote_src: true + src: "/tmp/supa-tripwire-v{{ supa_tripwire_release }}-{{ platform }}-linux-musl" + dest: /usr/local/bin/supa-tripwire + owner: root + group: root + mode: "0755" + +- name: Supa-tripwire - install wrapper + ansible.builtin.copy: + src: files/supa_tripwire/supa-tripwire-wrapped + dest: /usr/local/bin/supa-tripwire-wrapped + owner: root + group: root + mode: "0755" + +- name: Supa-tripwire - install environment file + ansible.builtin.copy: + src: files/supa_tripwire/supa-tripwire.env + dest: /etc/supa-tripwire.env + owner: root + group: root + mode: "0600" + +- name: Supa-tripwire - install service file + ansible.builtin.copy: + src: files/supa_tripwire/supa-tripwire.service + dest: /etc/systemd/system/supa-tripwire.service + owner: root + group: root + mode: "0644" + +- name: Supa-tripwire - enable service + ansible.builtin.systemd: + name: supa-tripwire + daemon_reload: true + enabled: true + +- name: Supa-tripwire - remove temporary files + ansible.builtin.file: + path: "{{ item }}" + state: absent + loop: + - /tmp/supa-tripwire.tar.xz + - "/tmp/supa-tripwire-v{{ supa_tripwire_release }}-{{ platform }}-linux-musl" diff --git a/ansible/tasks/setup-supabase-internal.yml b/ansible/tasks/setup-supabase-internal.yml index 8c8e9ec7ad..356186a0a3 100644 --- a/ansible/tasks/setup-supabase-internal.yml +++ b/ansible/tasks/setup-supabase-internal.yml @@ -94,6 +94,10 @@ ansible.builtin.import_tasks: file: internal/admin-mgr.yml +- name: Install supa-tripwire + ansible.builtin.import_tasks: + file: internal/supa-tripwire.yml + - name: Install adminapi ansible.builtin.import_tasks: file: internal/admin-api.yml diff --git a/ansible/vars.yml b/ansible/vars.yml index 2ddb6b6058..6cb3c5e0b0 100644 --- a/ansible/vars.yml +++ b/ansible/vars.yml @@ -37,6 +37,14 @@ adminmgr_artifacts: arm64: url: "https://supabase-public-artifacts-bucket.s3.amazonaws.com/admin-mgr/v{{ adminmgr_release }}/admin-mgr_{{ adminmgr_release }}_linux_arm64.tar.gz" checksum: sha256:3477bb0c4dc8e6fea72b1237558cf048de675e211021f9790ef7c134e7452cf3 +supa_tripwire_release: 0.2.0 +supa_tripwire_artifacts: + amd64: + url: "https://supabase-public-artifacts-bucket.s3.amazonaws.com/supa-tripwire/supa-tripwire-v{{ supa_tripwire_release }}-amd64.tar.xz" + checksum: sha256:7d81ce39815d8f1f1732153b753987271f6e9a84838cfdc4e538f44927b746ef + arm64: + url: "https://supabase-public-artifacts-bucket.s3.amazonaws.com/supa-tripwire/supa-tripwire-v{{ supa_tripwire_release }}-arm64.tar.xz" + checksum: sha256:cf2e6486d53626fff1312f45500bcacad418dd49eed83560dd58e28a0ec9432d aws_cli_release: 2.23.11 aws_cli_artifacts: amd64: From 4137677411c9b64e48d04e1a1e48c98bf4dbf859 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rton=20Boros?= Date: Mon, 2 Nov 2026 23:51:21 +0200 Subject: [PATCH 2/6] chore(ansible): reword supa-tripwire env comment Refs: MPG-1420 Co-Authored-By: Claude Sonnet 5.5 --- ansible/files/supa_tripwire/supa-tripwire.env | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ansible/files/supa_tripwire/supa-tripwire.env b/ansible/files/supa_tripwire/supa-tripwire.env index 183aa69ff9..d66431973e 100644 --- a/ansible/files/supa_tripwire/supa-tripwire.env +++ b/ansible/files/supa_tripwire/supa-tripwire.env @@ -1 +1 @@ -# supa-tripwire environment, written by salt +# Intentionally empty. Per-project settings filled during runtime. From bb1af2a343790c1b2d31e636e63010243deb0b9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rton=20Boros?= Date: Tue, 3 Nov 2026 00:59:55 +0200 Subject: [PATCH 3/6] feat(ansible): fetch supa-tripwire from the internal bucket at build Refs: MPG-1420 Co-Authored-By: Claude Sonnet 5.5 --- .github/workflows/ami-release-nix.yml | 1 + .github/workflows/nix-build.yml | 2 ++ .github/workflows/qemu-image-build.yml | 12 ++++++++ .github/workflows/qemu-image-release.yml | 5 ++++ ansible/tasks/internal/supa-tripwire.yml | 24 +++++++++------- ansible/tasks/setup-supabase-internal.yml | 3 ++ ansible/vars.yml | 10 ++----- nix/packages/build-ami/build-ami.sh | 4 +++ .../build-qemu-image/build-qemu-image.sh | 3 ++ nix/packages/build-qemu-image/default.nix | 2 ++ packer/qemu.pkr.hcl | 9 ++++++ packer/scripts/fetch-supa-tripwire.sh | 28 +++++++++++++++++++ packer/stage1-nix.pkr.hcl | 9 ++++++ 13 files changed, 95 insertions(+), 17 deletions(-) create mode 100755 packer/scripts/fetch-supa-tripwire.sh diff --git a/.github/workflows/ami-release-nix.yml b/.github/workflows/ami-release-nix.yml index 74810ef270..bf0bcd753f 100644 --- a/.github/workflows/ami-release-nix.yml +++ b/.github/workflows/ami-release-nix.yml @@ -88,6 +88,7 @@ jobs: AWS_MAX_ATTEMPTS: 10 AWS_RETRY_MODE: adaptive AMI_RELEASE: "true" + SUPA_TRIPWIRE_REQUIRED: "1" arch: ${{ matrix.target.arch }} git_sha: ${{ github.sha }} packages_git_sha: ${{ steps.resolve-git-sha.outputs.sha }} diff --git a/.github/workflows/nix-build.yml b/.github/workflows/nix-build.yml index 61e7d6e314..ec4c7e3b85 100644 --- a/.github/workflows/nix-build.yml +++ b/.github/workflows/nix-build.yml @@ -267,6 +267,8 @@ jobs: qemu: needs: builds-aarch64-linux uses: $/.github/workflows/qemu-image-build.yml + secrets: + DEV_AWS_ROLE: ${{ secrets.DEV_AWS_ROLE }} check: name: CI ✅ diff --git a/.github/workflows/qemu-image-build.yml b/.github/workflows/qemu-image-build.yml index 1391c95146..8bd1bc1c59 100644 --- a/.github/workflows/qemu-image-build.yml +++ b/.github/workflows/qemu-image-build.yml @@ -2,6 +2,10 @@ name: Build QEMU image on: workflow_call: + secrets: + DEV_AWS_ROLE: + description: AWS role used to fetch the supa-tripwire archive + required: true inputs: arch: description: Guest architecture (amd64 is experimental and publishes only to staging) @@ -44,11 +48,18 @@ jobs: artifact: ${{ inputs.publish && format('qemu-image-{0}', inputs.arch || 'arm64') || '' }} permissions: contents: read + id-token: write steps: - name: Checkout Repo uses: actions/checkout@v7.0.1 + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v6.3.0 + with: + role-to-assume: ${{ secrets.DEV_AWS_ROLE }} + aws-region: us-east-1 + - name: Install Nix uses: $/.github/actions/install-nix @@ -84,6 +95,7 @@ jobs: - name: Build QEMU artifact env: GIT_SHA: ${{ steps.resolve-git-sha.outputs.sha }} + SUPA_TRIPWIRE_REQUIRED: ${{ inputs.publish && '1' || '' }} run: BUILD_QEMU_IMAGE_HW_VIRT_ONLY=1 nix run .#build-qemu-image "$POSTGRES_MAJOR_VERSION" "$TARGET_ARCH" - name: Grab release version diff --git a/.github/workflows/qemu-image-release.yml b/.github/workflows/qemu-image-release.yml index 60fc6af7bf..f63d4e72cb 100644 --- a/.github/workflows/qemu-image-release.yml +++ b/.github/workflows/qemu-image-release.yml @@ -23,6 +23,11 @@ permissions: jobs: build: uses: $/.github/workflows/qemu-image-build.yml + permissions: + contents: read + id-token: write + secrets: + DEV_AWS_ROLE: ${{ secrets.DEV_AWS_ROLE }} with: arch: ${{ inputs.arch || 'arm64' }} publish: true diff --git a/ansible/tasks/internal/supa-tripwire.yml b/ansible/tasks/internal/supa-tripwire.yml index 05a28a6ea8..6901cdb418 100644 --- a/ansible/tasks/internal/supa-tripwire.yml +++ b/ansible/tasks/internal/supa-tripwire.yml @@ -1,14 +1,18 @@ -- name: Supa-tripwire - download archive - ansible.builtin.get_url: - url: "{{ supa_tripwire_artifacts[platform]['url'] }}" - checksum: "{{ supa_tripwire_artifacts[platform]['checksum'] }}" - dest: "/tmp/supa-tripwire.tar.xz" +- name: Supa-tripwire - copy archive + ansible.builtin.copy: + src: "files/supa_tripwire/supa-tripwire-v{{ supa_tripwire_release }}-{{ platform }}.tar.xz" + dest: /tmp/supa-tripwire.tar.xz mode: "0600" - timeout: 90 - register: download_result - until: download_result is succeeded - retries: 3 - delay: 2 + +- name: Supa-tripwire - checksum archive + ansible.builtin.stat: + path: /tmp/supa-tripwire.tar.xz + checksum_algorithm: sha256 + register: supa_tripwire_archive + +- name: Supa-tripwire - verify archive + ansible.builtin.assert: + that: supa_tripwire_archive.stat.checksum == supa_tripwire_sha256[platform] - name: Supa-tripwire - extract binary ansible.builtin.unarchive: diff --git a/ansible/tasks/setup-supabase-internal.yml b/ansible/tasks/setup-supabase-internal.yml index 356186a0a3..1d49d9ff75 100644 --- a/ansible/tasks/setup-supabase-internal.yml +++ b/ansible/tasks/setup-supabase-internal.yml @@ -97,6 +97,9 @@ - name: Install supa-tripwire ansible.builtin.import_tasks: file: internal/supa-tripwire.yml + when: >- + lookup('ansible.builtin.fileglob', + playbook_dir ~ '/files/supa_tripwire/supa-tripwire-v' ~ supa_tripwire_release ~ '-' ~ platform ~ '.tar.xz') | length > 0 - name: Install adminapi ansible.builtin.import_tasks: diff --git a/ansible/vars.yml b/ansible/vars.yml index 6cb3c5e0b0..0b62cfc36b 100644 --- a/ansible/vars.yml +++ b/ansible/vars.yml @@ -38,13 +38,9 @@ adminmgr_artifacts: url: "https://supabase-public-artifacts-bucket.s3.amazonaws.com/admin-mgr/v{{ adminmgr_release }}/admin-mgr_{{ adminmgr_release }}_linux_arm64.tar.gz" checksum: sha256:3477bb0c4dc8e6fea72b1237558cf048de675e211021f9790ef7c134e7452cf3 supa_tripwire_release: 0.2.0 -supa_tripwire_artifacts: - amd64: - url: "https://supabase-public-artifacts-bucket.s3.amazonaws.com/supa-tripwire/supa-tripwire-v{{ supa_tripwire_release }}-amd64.tar.xz" - checksum: sha256:7d81ce39815d8f1f1732153b753987271f6e9a84838cfdc4e538f44927b746ef - arm64: - url: "https://supabase-public-artifacts-bucket.s3.amazonaws.com/supa-tripwire/supa-tripwire-v{{ supa_tripwire_release }}-arm64.tar.xz" - checksum: sha256:cf2e6486d53626fff1312f45500bcacad418dd49eed83560dd58e28a0ec9432d +supa_tripwire_sha256: + amd64: 7d81ce39815d8f1f1732153b753987271f6e9a84838cfdc4e538f44927b746ef + arm64: cf2e6486d53626fff1312f45500bcacad418dd49eed83560dd58e28a0ec9432d aws_cli_release: 2.23.11 aws_cli_artifacts: amd64: diff --git a/nix/packages/build-ami/build-ami.sh b/nix/packages/build-ami/build-ami.sh index 001cd495ca..90cf602977 100644 --- a/nix/packages/build-ami/build-ami.sh +++ b/nix/packages/build-ami/build-ami.sh @@ -125,9 +125,13 @@ packer init -var-file "packer/$arch.vars.pkr.hcl" packer/stage1-nix.pkr.hcl echo "::group::AMI Build: Stage 1" +supa_tripwire_dir=$(mktemp -d) +bash packer/scripts/fetch-supa-tripwire.sh "$arch" "$supa_tripwire_dir" + packer build "${common_packer_args[@]}" \ -var "ami_name=$ami_name-stage-1" \ -var "input-hash=$input_hash" \ + -var "supa_tripwire_dir=$supa_tripwire_dir" \ packer/stage1-nix.pkr.hcl stage1_ami_id=$(find_ami "$postgres_release_version-stage1" "Name=tag:inputHash,Values=$input_hash") diff --git a/nix/packages/build-qemu-image/build-qemu-image.sh b/nix/packages/build-qemu-image/build-qemu-image.sh index 957d5bf54e..bc23902ae5 100644 --- a/nix/packages/build-qemu-image/build-qemu-image.sh +++ b/nix/packages/build-qemu-image/build-qemu-image.sh @@ -56,6 +56,8 @@ postgres_version=$(yq -er ".postgres_release.postgres$postgres_major_version" an # Build the cloud-init seed ISO before invoking packer. cloud-localds "$workdir/seeds-cloudimg.iso" user-data-cloudimg meta-data +bash packer/scripts/fetch-supa-tripwire.sh "$arch" "$workdir/supa-tripwire" + nix_store_paths=$(cat "$(nix build --no-link --print-out-paths "github:supabase/postgres/$git_sha#packages.$system.nix-store-paths-$postgres_major_version")") export LC_ALL=C.UTF-8 export TZ=UTC @@ -68,5 +70,6 @@ PACKER_LOG=${PACKER_LOG:-1} packer build \ -var "postgres-version=$postgres_version" \ -var "postgres_major_version=$postgres_major_version" \ -var "qemu_binary=$qemu" \ + -var "supa_tripwire_dir=$workdir/supa-tripwire" \ -var "workdir=$workdir" \ packer/qemu.pkr.hcl diff --git a/nix/packages/build-qemu-image/default.nix b/nix/packages/build-qemu-image/default.nix index 94762f6f47..93838e218d 100644 --- a/nix/packages/build-qemu-image/default.nix +++ b/nix/packages/build-qemu-image/default.nix @@ -1,4 +1,5 @@ { + awscli2, cloud-utils, coreutils, gitMinimal, @@ -11,6 +12,7 @@ writeShellApplication { name = "build-qemu-image"; runtimeInputs = [ + awscli2 cloud-utils coreutils gitMinimal diff --git a/packer/qemu.pkr.hcl b/packer/qemu.pkr.hcl index e307d37e07..0797623067 100644 --- a/packer/qemu.pkr.hcl +++ b/packer/qemu.pkr.hcl @@ -10,6 +10,10 @@ variable "postgres_major_version" { default = "" } +variable "supa_tripwire_dir" { + type = string +} + # Working dir to pick up non-source files and put output files variable "workdir" { type = string @@ -89,6 +93,11 @@ build { destination = "/tmp/ansible-playbook" } + provisioner "file" { + source = "${var.supa_tripwire_dir}/" + destination = "/tmp/ansible-playbook/ansible/files/supa_tripwire/" + } + provisioner "file" { source = "audit-specs" destination = "/tmp/ansible-playbook" diff --git a/packer/scripts/fetch-supa-tripwire.sh b/packer/scripts/fetch-supa-tripwire.sh new file mode 100755 index 0000000000..fa2595849a --- /dev/null +++ b/packer/scripts/fetch-supa-tripwire.sh @@ -0,0 +1,28 @@ +#!/usr/bin/env bash +# Usage: fetch-supa-tripwire.sh +# Fetches the supa-tripwire archive from the internal artifacts bucket. Without access it +# warns and continues unless SUPA_TRIPWIRE_REQUIRED=1. +set -euo pipefail + +arch=$1 +dir=$2 + +case ${SUPA_TRIPWIRE_REQUIRED:-} in +'' | 1) ;; +*) + echo "SUPA_TRIPWIRE_REQUIRED must be 1 or unset" >&2 + exit 1 + ;; +esac + +mkdir -p "$dir" +touch "$dir/.keep" + +archive=supa-tripwire-v$(yq -er .supa_tripwire_release ansible/vars.yml)-$arch.tar.xz +if ! aws s3 cp --only-show-errors --region us-east-1 "s3://supabase-internal-artifacts/supa-tripwire/$archive" "$dir/$archive"; then + if [[ ${SUPA_TRIPWIRE_REQUIRED:-} == 1 ]]; then + echo "::error::could not fetch $archive" >&2 + exit 1 + fi + echo "::warning::could not fetch $archive, building without supa-tripwire" >&2 +fi diff --git a/packer/stage1-nix.pkr.hcl b/packer/stage1-nix.pkr.hcl index 7ee3c0b993..ab7c4b60b5 100644 --- a/packer/stage1-nix.pkr.hcl +++ b/packer/stage1-nix.pkr.hcl @@ -1,3 +1,7 @@ +variable "supa_tripwire_dir" { + type = string +} + variable "ami_name" { type = string } @@ -229,6 +233,11 @@ build { destination = "/tmp/ansible-playbook" } + provisioner "file" { + source = "${var.supa_tripwire_dir}/" + destination = "/tmp/ansible-playbook/ansible/files/supa_tripwire/" + } + provisioner "shell" { environment_vars = [ "POSTGRES_MAJOR_VERSION=${var.postgres_major_version}", From bfaeefcfe72cff933698f278a089737875cfb483 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rton=20Boros?= Date: Tue, 3 Nov 2026 16:29:33 +0200 Subject: [PATCH 4/6] refactor(ansible): install supa-tripwire with one presigned script Refs: MPG-1420 Co-Authored-By: Claude Sonnet 5.5 --- .github/workflows/ami-release-nix.yml | 1 - .github/workflows/qemu-image-build.yml | 1 - ansible/files/supa_tripwire/install.sh | 71 +++++++++++++++++++ .../files/supa_tripwire/supa-tripwire-wrapped | 13 ---- ansible/files/supa_tripwire/supa-tripwire.env | 1 - .../files/supa_tripwire/supa-tripwire.service | 21 ------ ansible/tasks/internal/supa-tripwire.yml | 70 ------------------ ansible/tasks/setup-supabase-internal.yml | 9 ++- nix/packages/build-ami/build-ami.sh | 7 +- .../build-qemu-image/build-qemu-image.sh | 5 +- packer/qemu.pkr.hcl | 12 +--- packer/scripts/fetch-supa-tripwire.sh | 28 -------- packer/stage1-nix.pkr.hcl | 10 +-- 13 files changed, 83 insertions(+), 166 deletions(-) create mode 100755 ansible/files/supa_tripwire/install.sh delete mode 100644 ansible/files/supa_tripwire/supa-tripwire-wrapped delete mode 100644 ansible/files/supa_tripwire/supa-tripwire.env delete mode 100644 ansible/files/supa_tripwire/supa-tripwire.service delete mode 100644 ansible/tasks/internal/supa-tripwire.yml delete mode 100755 packer/scripts/fetch-supa-tripwire.sh diff --git a/.github/workflows/ami-release-nix.yml b/.github/workflows/ami-release-nix.yml index bf0bcd753f..74810ef270 100644 --- a/.github/workflows/ami-release-nix.yml +++ b/.github/workflows/ami-release-nix.yml @@ -88,7 +88,6 @@ jobs: AWS_MAX_ATTEMPTS: 10 AWS_RETRY_MODE: adaptive AMI_RELEASE: "true" - SUPA_TRIPWIRE_REQUIRED: "1" arch: ${{ matrix.target.arch }} git_sha: ${{ github.sha }} packages_git_sha: ${{ steps.resolve-git-sha.outputs.sha }} diff --git a/.github/workflows/qemu-image-build.yml b/.github/workflows/qemu-image-build.yml index 8bd1bc1c59..db0dedacf7 100644 --- a/.github/workflows/qemu-image-build.yml +++ b/.github/workflows/qemu-image-build.yml @@ -95,7 +95,6 @@ jobs: - name: Build QEMU artifact env: GIT_SHA: ${{ steps.resolve-git-sha.outputs.sha }} - SUPA_TRIPWIRE_REQUIRED: ${{ inputs.publish && '1' || '' }} run: BUILD_QEMU_IMAGE_HW_VIRT_ONLY=1 nix run .#build-qemu-image "$POSTGRES_MAJOR_VERSION" "$TARGET_ARCH" - name: Grab release version diff --git a/ansible/files/supa_tripwire/install.sh b/ansible/files/supa_tripwire/install.sh new file mode 100755 index 0000000000..6fdb1676cc --- /dev/null +++ b/ansible/files/supa_tripwire/install.sh @@ -0,0 +1,71 @@ +#!/usr/bin/env bash +# Usage: install.sh [url] +# url is a presigned URL, or an s3:// URL to presign with the caller's AWS credentials. +# Defaults to the internal artifacts bucket. Safe to rerun on a live host. +set -euo pipefail + +version=$1 +arch=$2 +sha256=$3 + +archive=supa-tripwire-v$version-$arch.tar.xz +tmp=$(mktemp -d) +trap 'rm -rf "$tmp"' EXIT + +url=${4:-s3://supabase-internal-artifacts/supa-tripwire/$archive} +[[ $url != s3://* ]] || url=$(aws s3 presign --region ap-southeast-1 "$url") +curl -fsSL --retry 3 -o "$tmp/$archive" "$url" +echo "$sha256 $tmp/$archive" | sha256sum -c --quiet - + +tar -xJf "$tmp/$archive" -O "supa-tripwire-v$version-$arch-linux-musl" >/usr/local/bin/supa-tripwire.new +chmod 0755 /usr/local/bin/supa-tripwire.new +mv -f /usr/local/bin/supa-tripwire.new /usr/local/bin/supa-tripwire + +cat >/usr/local/bin/supa-tripwire-wrapped <<'WRAPPER' +#!/usr/bin/env bash + +POSTGRES_BIN=$(readlink -f $(which postgres)) +POSTGRES_DIR=$(dirname "$POSTGRES_BIN") +WRAPPED="$POSTGRES_DIR/.postgres-wrapped" + +if [ -f "$WRAPPED" ]; then + TARGET="$WRAPPED" +else + TARGET="$POSTGRES_BIN" +fi + +exec /usr/local/bin/supa-tripwire "$TARGET" +WRAPPER +chmod 0755 /usr/local/bin/supa-tripwire-wrapped + +[[ -e /etc/supa-tripwire.env ]] || install -m 0600 /dev/null /etc/supa-tripwire.env + +cat >/etc/systemd/system/supa-tripwire.service <<'UNIT' +# /etc/systemd/system/supa-tripwire.service +[Unit] +Description=supa-tripwire - Postgres badness monitor +After=postgresql.service + +[Service] +User=root +Restart=always +RestartSec=5 + +EnvironmentFile=/etc/supa-tripwire.env + +ExecStart=/usr/local/bin/supa-tripwire-wrapped + +NoNewPrivileges=true +SecureBits=no-setuid-fixup-locked +ProtectSystem=strict +ProtectHome=true + +[Install] +WantedBy=multi-user.target +UNIT + +systemctl daemon-reload || true +systemctl enable supa-tripwire +if systemctl is-active --quiet supa-tripwire; then + systemctl restart supa-tripwire +fi diff --git a/ansible/files/supa_tripwire/supa-tripwire-wrapped b/ansible/files/supa_tripwire/supa-tripwire-wrapped deleted file mode 100644 index 85c762123d..0000000000 --- a/ansible/files/supa_tripwire/supa-tripwire-wrapped +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/env bash - -POSTGRES_BIN=$(readlink -f $(which postgres)) -POSTGRES_DIR=$(dirname "$POSTGRES_BIN") -WRAPPED="$POSTGRES_DIR/.postgres-wrapped" - -if [ -f "$WRAPPED" ]; then - TARGET="$WRAPPED" -else - TARGET="$POSTGRES_BIN" -fi - -exec /usr/local/bin/supa-tripwire "$TARGET" diff --git a/ansible/files/supa_tripwire/supa-tripwire.env b/ansible/files/supa_tripwire/supa-tripwire.env deleted file mode 100644 index d66431973e..0000000000 --- a/ansible/files/supa_tripwire/supa-tripwire.env +++ /dev/null @@ -1 +0,0 @@ -# Intentionally empty. Per-project settings filled during runtime. diff --git a/ansible/files/supa_tripwire/supa-tripwire.service b/ansible/files/supa_tripwire/supa-tripwire.service deleted file mode 100644 index db2423d831..0000000000 --- a/ansible/files/supa_tripwire/supa-tripwire.service +++ /dev/null @@ -1,21 +0,0 @@ -# /etc/systemd/system/supa-tripwire.service -[Unit] -Description=supa-tripwire - Postgres badness monitor -After=postgresql.service - -[Service] -User=root -Restart=always -RestartSec=5 - -EnvironmentFile=/etc/supa-tripwire.env - -ExecStart=/usr/local/bin/supa-tripwire-wrapped - -NoNewPrivileges=true -SecureBits=no-setuid-fixup-locked -ProtectSystem=strict -ProtectHome=true - -[Install] -WantedBy=multi-user.target diff --git a/ansible/tasks/internal/supa-tripwire.yml b/ansible/tasks/internal/supa-tripwire.yml deleted file mode 100644 index 6901cdb418..0000000000 --- a/ansible/tasks/internal/supa-tripwire.yml +++ /dev/null @@ -1,70 +0,0 @@ -- name: Supa-tripwire - copy archive - ansible.builtin.copy: - src: "files/supa_tripwire/supa-tripwire-v{{ supa_tripwire_release }}-{{ platform }}.tar.xz" - dest: /tmp/supa-tripwire.tar.xz - mode: "0600" - -- name: Supa-tripwire - checksum archive - ansible.builtin.stat: - path: /tmp/supa-tripwire.tar.xz - checksum_algorithm: sha256 - register: supa_tripwire_archive - -- name: Supa-tripwire - verify archive - ansible.builtin.assert: - that: supa_tripwire_archive.stat.checksum == supa_tripwire_sha256[platform] - -- name: Supa-tripwire - extract binary - ansible.builtin.unarchive: - remote_src: true - src: /tmp/supa-tripwire.tar.xz - dest: /tmp - include: - - "supa-tripwire-v{{ supa_tripwire_release }}-{{ platform }}-linux-musl" - -- name: Supa-tripwire - install binary - ansible.builtin.copy: - remote_src: true - src: "/tmp/supa-tripwire-v{{ supa_tripwire_release }}-{{ platform }}-linux-musl" - dest: /usr/local/bin/supa-tripwire - owner: root - group: root - mode: "0755" - -- name: Supa-tripwire - install wrapper - ansible.builtin.copy: - src: files/supa_tripwire/supa-tripwire-wrapped - dest: /usr/local/bin/supa-tripwire-wrapped - owner: root - group: root - mode: "0755" - -- name: Supa-tripwire - install environment file - ansible.builtin.copy: - src: files/supa_tripwire/supa-tripwire.env - dest: /etc/supa-tripwire.env - owner: root - group: root - mode: "0600" - -- name: Supa-tripwire - install service file - ansible.builtin.copy: - src: files/supa_tripwire/supa-tripwire.service - dest: /etc/systemd/system/supa-tripwire.service - owner: root - group: root - mode: "0644" - -- name: Supa-tripwire - enable service - ansible.builtin.systemd: - name: supa-tripwire - daemon_reload: true - enabled: true - -- name: Supa-tripwire - remove temporary files - ansible.builtin.file: - path: "{{ item }}" - state: absent - loop: - - /tmp/supa-tripwire.tar.xz - - "/tmp/supa-tripwire-v{{ supa_tripwire_release }}-{{ platform }}-linux-musl" diff --git a/ansible/tasks/setup-supabase-internal.yml b/ansible/tasks/setup-supabase-internal.yml index 1d49d9ff75..656b2e09de 100644 --- a/ansible/tasks/setup-supabase-internal.yml +++ b/ansible/tasks/setup-supabase-internal.yml @@ -95,11 +95,10 @@ file: internal/admin-mgr.yml - name: Install supa-tripwire - ansible.builtin.import_tasks: - file: internal/supa-tripwire.yml - when: >- - lookup('ansible.builtin.fileglob', - playbook_dir ~ '/files/supa_tripwire/supa-tripwire-v' ~ supa_tripwire_release ~ '-' ~ platform ~ '.tar.xz') | length > 0 + ansible.builtin.script: + cmd: "files/supa_tripwire/install.sh {{ supa_tripwire_release }} {{ platform }} {{ supa_tripwire_sha256[platform] }} {{ lookup('ansible.builtin.env', 'SUPA_TRIPWIRE_URL') }}" + when: lookup('ansible.builtin.env', 'SUPA_TRIPWIRE_URL') | length > 0 + no_log: true - name: Install adminapi ansible.builtin.import_tasks: diff --git a/nix/packages/build-ami/build-ami.sh b/nix/packages/build-ami/build-ami.sh index 90cf602977..67634f9e7a 100644 --- a/nix/packages/build-ami/build-ami.sh +++ b/nix/packages/build-ami/build-ami.sh @@ -123,15 +123,14 @@ common_packer_args=( # both templates require the same plugins, so one init covers both stages packer init -var-file "packer/$arch.vars.pkr.hcl" packer/stage1-nix.pkr.hcl -echo "::group::AMI Build: Stage 1" +SUPA_TRIPWIRE_URL=$(aws s3 presign --region ap-southeast-1 --expires-in 3600 "s3://supabase-internal-artifacts/supa-tripwire/supa-tripwire-v$(yq -er .supa_tripwire_release ansible/vars.yml)-$arch.tar.xz" || true) +export SUPA_TRIPWIRE_URL -supa_tripwire_dir=$(mktemp -d) -bash packer/scripts/fetch-supa-tripwire.sh "$arch" "$supa_tripwire_dir" +echo "::group::AMI Build: Stage 1" packer build "${common_packer_args[@]}" \ -var "ami_name=$ami_name-stage-1" \ -var "input-hash=$input_hash" \ - -var "supa_tripwire_dir=$supa_tripwire_dir" \ packer/stage1-nix.pkr.hcl stage1_ami_id=$(find_ami "$postgres_release_version-stage1" "Name=tag:inputHash,Values=$input_hash") diff --git a/nix/packages/build-qemu-image/build-qemu-image.sh b/nix/packages/build-qemu-image/build-qemu-image.sh index bc23902ae5..eb6dd7d8cb 100644 --- a/nix/packages/build-qemu-image/build-qemu-image.sh +++ b/nix/packages/build-qemu-image/build-qemu-image.sh @@ -56,9 +56,9 @@ postgres_version=$(yq -er ".postgres_release.postgres$postgres_major_version" an # Build the cloud-init seed ISO before invoking packer. cloud-localds "$workdir/seeds-cloudimg.iso" user-data-cloudimg meta-data -bash packer/scripts/fetch-supa-tripwire.sh "$arch" "$workdir/supa-tripwire" - nix_store_paths=$(cat "$(nix build --no-link --print-out-paths "github:supabase/postgres/$git_sha#packages.$system.nix-store-paths-$postgres_major_version")") +SUPA_TRIPWIRE_URL=$(aws s3 presign --region ap-southeast-1 --expires-in 3600 "s3://supabase-internal-artifacts/supa-tripwire/supa-tripwire-v$(yq -er .supa_tripwire_release ansible/vars.yml)-$arch.tar.xz" || true) +export SUPA_TRIPWIRE_URL export LC_ALL=C.UTF-8 export TZ=UTC packer init packer/qemu.pkr.hcl @@ -70,6 +70,5 @@ PACKER_LOG=${PACKER_LOG:-1} packer build \ -var "postgres-version=$postgres_version" \ -var "postgres_major_version=$postgres_major_version" \ -var "qemu_binary=$qemu" \ - -var "supa_tripwire_dir=$workdir/supa-tripwire" \ -var "workdir=$workdir" \ packer/qemu.pkr.hcl diff --git a/packer/qemu.pkr.hcl b/packer/qemu.pkr.hcl index 0797623067..e87b5dd836 100644 --- a/packer/qemu.pkr.hcl +++ b/packer/qemu.pkr.hcl @@ -10,10 +10,6 @@ variable "postgres_major_version" { default = "" } -variable "supa_tripwire_dir" { - type = string -} - # Working dir to pick up non-source files and put output files variable "workdir" { type = string @@ -93,11 +89,6 @@ build { destination = "/tmp/ansible-playbook" } - provisioner "file" { - source = "${var.supa_tripwire_dir}/" - destination = "/tmp/ansible-playbook/ansible/files/supa_tripwire/" - } - provisioner "file" { source = "audit-specs" destination = "/tmp/ansible-playbook" @@ -121,7 +112,8 @@ build { provisioner "shell" { environment_vars = [ "NIX_STORE_PATHS=${jsonencode({ nix_store_paths = var.nix_store_paths })}", - "POSTGRES_MAJOR_VERSION=${var.postgres_major_version}" + "POSTGRES_MAJOR_VERSION=${var.postgres_major_version}", + "SUPA_TRIPWIRE_URL=${env("SUPA_TRIPWIRE_URL")}" ] use_env_var_file = true script = "packer/scripts/qemu-bootstrap-nix.sh" diff --git a/packer/scripts/fetch-supa-tripwire.sh b/packer/scripts/fetch-supa-tripwire.sh deleted file mode 100755 index fa2595849a..0000000000 --- a/packer/scripts/fetch-supa-tripwire.sh +++ /dev/null @@ -1,28 +0,0 @@ -#!/usr/bin/env bash -# Usage: fetch-supa-tripwire.sh -# Fetches the supa-tripwire archive from the internal artifacts bucket. Without access it -# warns and continues unless SUPA_TRIPWIRE_REQUIRED=1. -set -euo pipefail - -arch=$1 -dir=$2 - -case ${SUPA_TRIPWIRE_REQUIRED:-} in -'' | 1) ;; -*) - echo "SUPA_TRIPWIRE_REQUIRED must be 1 or unset" >&2 - exit 1 - ;; -esac - -mkdir -p "$dir" -touch "$dir/.keep" - -archive=supa-tripwire-v$(yq -er .supa_tripwire_release ansible/vars.yml)-$arch.tar.xz -if ! aws s3 cp --only-show-errors --region us-east-1 "s3://supabase-internal-artifacts/supa-tripwire/$archive" "$dir/$archive"; then - if [[ ${SUPA_TRIPWIRE_REQUIRED:-} == 1 ]]; then - echo "::error::could not fetch $archive" >&2 - exit 1 - fi - echo "::warning::could not fetch $archive, building without supa-tripwire" >&2 -fi diff --git a/packer/stage1-nix.pkr.hcl b/packer/stage1-nix.pkr.hcl index ab7c4b60b5..8e3731d2aa 100644 --- a/packer/stage1-nix.pkr.hcl +++ b/packer/stage1-nix.pkr.hcl @@ -1,7 +1,3 @@ -variable "supa_tripwire_dir" { - type = string -} - variable "ami_name" { type = string } @@ -233,15 +229,11 @@ build { destination = "/tmp/ansible-playbook" } - provisioner "file" { - source = "${var.supa_tripwire_dir}/" - destination = "/tmp/ansible-playbook/ansible/files/supa_tripwire/" - } - provisioner "shell" { environment_vars = [ "POSTGRES_MAJOR_VERSION=${var.postgres_major_version}", "POSTGRES_SUPABASE_VERSION=${var.postgres-version}", + "SUPA_TRIPWIRE_URL=${env("SUPA_TRIPWIRE_URL")}", ] use_env_var_file = true script = "packer/scripts/surrogate-bootstrap-nix.sh" From 600abe4c553f6e50403040926b53285bc183a771 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rton=20Boros?= Date: Wed, 4 Nov 2026 15:07:30 +0200 Subject: [PATCH 5/6] fix(packer): read supa-tripwire url via a variable default Refs: MPG-1420 Co-Authored-By: Claude Sonnet 5.5 --- packer/qemu.pkr.hcl | 7 ++++++- packer/stage1-nix.pkr.hcl | 7 ++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/packer/qemu.pkr.hcl b/packer/qemu.pkr.hcl index e87b5dd836..2d16b76d97 100644 --- a/packer/qemu.pkr.hcl +++ b/packer/qemu.pkr.hcl @@ -10,6 +10,11 @@ variable "postgres_major_version" { default = "" } +variable "supa_tripwire_url" { + type = string + default = env("SUPA_TRIPWIRE_URL") +} + # Working dir to pick up non-source files and put output files variable "workdir" { type = string @@ -113,7 +118,7 @@ build { environment_vars = [ "NIX_STORE_PATHS=${jsonencode({ nix_store_paths = var.nix_store_paths })}", "POSTGRES_MAJOR_VERSION=${var.postgres_major_version}", - "SUPA_TRIPWIRE_URL=${env("SUPA_TRIPWIRE_URL")}" + "SUPA_TRIPWIRE_URL=${var.supa_tripwire_url}" ] use_env_var_file = true script = "packer/scripts/qemu-bootstrap-nix.sh" diff --git a/packer/stage1-nix.pkr.hcl b/packer/stage1-nix.pkr.hcl index 8e3731d2aa..e9d8269ecf 100644 --- a/packer/stage1-nix.pkr.hcl +++ b/packer/stage1-nix.pkr.hcl @@ -1,3 +1,8 @@ +variable "supa_tripwire_url" { + type = string + default = env("SUPA_TRIPWIRE_URL") +} + variable "ami_name" { type = string } @@ -233,7 +238,7 @@ build { environment_vars = [ "POSTGRES_MAJOR_VERSION=${var.postgres_major_version}", "POSTGRES_SUPABASE_VERSION=${var.postgres-version}", - "SUPA_TRIPWIRE_URL=${env("SUPA_TRIPWIRE_URL")}", + "SUPA_TRIPWIRE_URL=${var.supa_tripwire_url}", ] use_env_var_file = true script = "packer/scripts/surrogate-bootstrap-nix.sh" From e94d80ef0a71010c5dabf7461583262e61ac4057 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rton=20Boros?= Date: Wed, 4 Nov 2026 16:46:30 +0200 Subject: [PATCH 6/6] fix(ansible): show supa-tripwire install errors Refs: MPG-1420 Co-Authored-By: Claude Sonnet 5.5 --- ansible/files/supa_tripwire/install.sh | 6 +++--- ansible/tasks/setup-supabase-internal.yml | 5 +++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/ansible/files/supa_tripwire/install.sh b/ansible/files/supa_tripwire/install.sh index 6fdb1676cc..4ca66aa2ed 100755 --- a/ansible/files/supa_tripwire/install.sh +++ b/ansible/files/supa_tripwire/install.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Usage: install.sh [url] -# url is a presigned URL, or an s3:// URL to presign with the caller's AWS credentials. +# Usage: [SUPA_TRIPWIRE_URL=] install.sh +# SUPA_TRIPWIRE_URL is a presigned URL, or an s3:// URL to presign with the caller's AWS credentials. # Defaults to the internal artifacts bucket. Safe to rerun on a live host. set -euo pipefail @@ -12,7 +12,7 @@ archive=supa-tripwire-v$version-$arch.tar.xz tmp=$(mktemp -d) trap 'rm -rf "$tmp"' EXIT -url=${4:-s3://supabase-internal-artifacts/supa-tripwire/$archive} +url=${SUPA_TRIPWIRE_URL:-s3://supabase-internal-artifacts/supa-tripwire/$archive} [[ $url != s3://* ]] || url=$(aws s3 presign --region ap-southeast-1 "$url") curl -fsSL --retry 3 -o "$tmp/$archive" "$url" echo "$sha256 $tmp/$archive" | sha256sum -c --quiet - diff --git a/ansible/tasks/setup-supabase-internal.yml b/ansible/tasks/setup-supabase-internal.yml index 656b2e09de..bb326d037a 100644 --- a/ansible/tasks/setup-supabase-internal.yml +++ b/ansible/tasks/setup-supabase-internal.yml @@ -96,9 +96,10 @@ - name: Install supa-tripwire ansible.builtin.script: - cmd: "files/supa_tripwire/install.sh {{ supa_tripwire_release }} {{ platform }} {{ supa_tripwire_sha256[platform] }} {{ lookup('ansible.builtin.env', 'SUPA_TRIPWIRE_URL') }}" + cmd: "files/supa_tripwire/install.sh {{ supa_tripwire_release }} {{ platform }} {{ supa_tripwire_sha256[platform] }}" + environment: + SUPA_TRIPWIRE_URL: "{{ lookup('ansible.builtin.env', 'SUPA_TRIPWIRE_URL') }}" when: lookup('ansible.builtin.env', 'SUPA_TRIPWIRE_URL') | length > 0 - no_log: true - name: Install adminapi ansible.builtin.import_tasks: