Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
35 changes: 35 additions & 0 deletions .github/workflows/ami-release-nix.yml
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,41 @@ jobs:
postgres_version: ${{ matrix.postgres_version }}
region: ${{ env.AWS_REGION }}

- name: Snapshot AMI manifest
if: matrix.target.arch == 'amd64'
env:
AMI_ID: ${{ steps.build-ami.outputs.stage2_ami_id }}
RUN_ID: ${{ github.run_id }}
run: |
ssh-keygen -t ed25519 -N '' -f /tmp/manifest-key -q

VPC_ID=$(aws ec2 describe-vpcs --region "$AWS_REGION" --filters Name=is-default,Values=true --query 'Vpcs[0].VpcId' --output text)
SUBNET_ID=$(aws ec2 describe-subnets --region "$AWS_REGION" --filters Name=vpc-id,Values="$VPC_ID" --query 'Subnets[0].SubnetId' --output text)
SG_ID=$(aws ec2 create-security-group --region "$AWS_REGION" --group-name "manifest-snapshot-$RUN_ID" --description "temp sg for ami manifest snapshot" --vpc-id "$VPC_ID" --query GroupId --output text)
RUNNER_IP=$(curl -s https://checkip.amazonaws.com)
aws ec2 authorize-security-group-ingress --region "$AWS_REGION" --group-id "$SG_ID" --protocol tcp --port 22 --cidr "$RUNNER_IP/32"

cleanup() {
[ -n "${INSTANCE_ID:-}" ] && aws ec2 terminate-instances --region "$AWS_REGION" --instance-ids "$INSTANCE_ID" && aws ec2 wait instance-terminated --region "$AWS_REGION" --instance-ids "$INSTANCE_ID"
aws ec2 delete-security-group --region "$AWS_REGION" --group-id "$SG_ID"
}
trap cleanup EXIT

INSTANCE_ID=$(aws ec2 run-instances --region "$AWS_REGION" --image-id "$AMI_ID" --instance-type t3.small --subnet-id "$SUBNET_ID" --security-group-ids "$SG_ID" --associate-public-ip-address --count 1 --query 'Instances[0].InstanceId' --output text)
aws ec2 wait instance-running --region "$AWS_REGION" --instance-ids "$INSTANCE_ID"
IP=$(aws ec2 describe-instances --region "$AWS_REGION" --instance-ids "$INSTANCE_ID" --query 'Reservations[0].Instances[0].PublicIpAddress' --output text)

for i in $(seq 1 30); do
aws ec2-instance-connect send-ssh-public-key --region "$AWS_REGION" --instance-id "$INSTANCE_ID" --instance-os-user ubuntu --ssh-public-key file:///tmp/manifest-key.pub
ssh -o StrictHostKeyChecking=no -o ConnectTimeout=5 -i /tmp/manifest-key "ubuntu@$IP" true && break
sleep 5
done

scp -o StrictHostKeyChecking=no -i /tmp/manifest-key testinfra/manifest-snapshot.sh "ubuntu@$IP:/tmp/manifest-snapshot.sh"
ssh -o StrictHostKeyChecking=no -i /tmp/manifest-key "ubuntu@$IP" "chmod +x /tmp/manifest-snapshot.sh && sudo /tmp/manifest-snapshot.sh" > ami-manifest.txt

aws s3 cp ami-manifest.txt "s3://${{ secrets.ARTIFACTS_BUCKET }}/ami-manifests/${{ matrix.postgres_version }}/latest.txt"

- name: Setup post build env vars
run: |
POSTGRES_SUPABASE_VERSION=${{ steps.build-ami.outputs.postgres_release_version }}
Expand Down
9 changes: 9 additions & 0 deletions .github/workflows/testinfra-ami-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -126,11 +126,20 @@ jobs:
env:
AMI_ID: ${{ steps.build-ami.outputs.stage2_ami_id }}
EXECUTION_ID: ${{ steps.build-ami.outputs.execution_id }}
MANIFEST_OUTPUT: ami-manifest-${{ matrix.postgres_version }}-${{ matrix.target.arch }}.txt
run: |
# TODO: use uv for pkg mgmt
pip3 install boto3 'boto3-stubs[essential]' ec2instanceconnectcli pytest 'pytest-testinfra[paramiko]' requests
pytest -vv -s testinfra/test_ami_nix.py

- name: Upload AMI manifest
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: ami-manifest-${{ matrix.postgres_version }}-${{ matrix.target.arch }}
path: ami-manifest-*.txt
overwrite: true
retention-days: 1

- name: Cleanup resources on build cancellation
if: ${{ cancelled() }}
run: |
Expand Down
23 changes: 23 additions & 0 deletions testinfra/manifest-snapshot.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#!/bin/sh
set -eu

EXCLUDE='^/(proc|run|tmp|var/log|data)(/|$)'

find / -xdev \( -type f -o -type l \) 2>/dev/null | grep -Ev "$EXCLUDE" | sort | while read -r f; do
if [ -L "$f" ]; then
printf '%s\tlink\t%s\n' "$f" "$(readlink "$f")"
else
printf '%s\t%s\t%s\n' "$f" "$(stat -c '%a:%U:%G' "$f" 2>/dev/null || echo '?')" "$(sha256sum "$f" 2>/dev/null | cut -d' ' -f1)"
fi
done

echo '--- units ---'
systemctl list-unit-files --no-pager 2>/dev/null | sort
echo '--- users ---'
getent passwd | sort
echo '--- groups ---'
getent group | sort
echo '--- nft ---'
nft list ruleset 2>/dev/null || true
echo '--- sysctl ---'
sysctl -a 2>/dev/null | sort
11 changes: 11 additions & 0 deletions testinfra/test_ami_nix.py
Original file line number Diff line number Diff line change
Expand Up @@ -1407,3 +1407,14 @@ def test_apparmor_denies_access_to_sensitive_paths(host):
f"to have succeeded.\nstdout: {result['stdout']}\nstderr: {result['stderr']}"
)
print(f"Confirmed: access to {test_file} denied by AppArmor")


def test_manifest_snapshot(host):
ssh = host["ssh"]
script = os.path.join(os.path.dirname(__file__), "manifest-snapshot.sh")
upload_file_via_sftp(ssh, script, "/tmp/manifest-snapshot.sh")
run_ssh_command(ssh, "chmod +x /tmp/manifest-snapshot.sh")
result = run_ssh_command(ssh, "sudo /tmp/manifest-snapshot.sh")
assert result["succeeded"], result["stderr"]
with open(os.environ.get("MANIFEST_OUTPUT", "ami-manifest.txt"), "w") as f:
f.write(result["stdout"])
Loading