forked from
evan.jarrett.net/at-container-registry
A container registry that uses the AT Protocol for manifest storage and S3 for blob storage.
1# GitHub Actions workflow for verifying ATProto signatures
2
3name: Verify and Deploy
4
5on:
6 push:
7 branches: [main]
8 pull_request:
9 branches: [main]
10
11env:
12 REGISTRY: atcr.io
13 IMAGE_NAME: ${{ github.repository }}
14
15jobs:
16 verify-signature:
17 name: Verify Image Signature
18 runs-on: ubuntu-latest
19 steps:
20 - name: Checkout code
21 uses: actions/checkout@v4
22
23 - name: Set up image tag
24 id: vars
25 run: |
26 echo "IMAGE_TAG=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}" >> $GITHUB_OUTPUT
27
28 - name: Install verification tools
29 run: |
30 # Install ORAS
31 curl -LO https://github.com/oras-project/oras/releases/download/v1.0.0/oras_1.0.0_linux_amd64.tar.gz
32 tar -xzf oras_1.0.0_linux_amd64.tar.gz
33 sudo mv oras /usr/local/bin/
34
35 # Install crane
36 curl -sL "https://github.com/google/go-containerregistry/releases/download/v0.15.2/go-containerregistry_Linux_x86_64.tar.gz" > crane.tar.gz
37 tar -xzf crane.tar.gz
38 sudo mv crane /usr/local/bin/
39
40 # Install atcr-verify (when available)
41 # curl -LO https://github.com/atcr-io/atcr/releases/latest/download/atcr-verify
42 # chmod +x atcr-verify
43 # sudo mv atcr-verify /usr/local/bin/
44
45 - name: Check for signature
46 id: check_signature
47 run: |
48 IMAGE="${{ steps.vars.outputs.IMAGE_TAG }}"
49 echo "Checking signature for $IMAGE"
50
51 # Get image digest
52 DIGEST=$(crane digest "$IMAGE")
53 echo "Image digest: $DIGEST"
54
55 # Check for ATProto signature using ORAS
56 REPO=$(echo "$IMAGE" | cut -d: -f1)
57 REFERRERS=$(curl -s "https://${{ env.REGISTRY }}/v2/${REPO#${{ env.REGISTRY }}/}/referrers/${DIGEST}?artifactType=application/vnd.atproto.signature.v1+json")
58
59 SIG_COUNT=$(echo "$REFERRERS" | jq '.manifests | length')
60
61 if [ "$SIG_COUNT" -eq 0 ]; then
62 echo "❌ No ATProto signature found"
63 echo "has_signature=false" >> $GITHUB_OUTPUT
64 exit 1
65 fi
66
67 echo "✓ Found $SIG_COUNT signature(s)"
68 echo "has_signature=true" >> $GITHUB_OUTPUT
69
70 - name: Verify signature (full verification)
71 if: steps.check_signature.outputs.has_signature == 'true'
72 run: |
73 IMAGE="${{ steps.vars.outputs.IMAGE_TAG }}"
74
75 # Option 1: Use atcr-verify CLI (when available)
76 # atcr-verify "$IMAGE" --policy .atcr/trust-policy.yaml
77
78 # Option 2: Use shell script
79 chmod +x examples/verification/atcr-verify.sh
80 ./examples/verification/atcr-verify.sh "$IMAGE"
81
82 echo "✓ Signature verified successfully"
83
84 - name: Verify signer DID
85 if: steps.check_signature.outputs.has_signature == 'true'
86 run: |
87 IMAGE="${{ steps.vars.outputs.IMAGE_TAG }}"
88
89 # Get signature metadata
90 DIGEST=$(crane digest "$IMAGE")
91 REPO=$(echo "$IMAGE" | cut -d: -f1)
92
93 REFERRERS=$(curl -s "https://${{ env.REGISTRY }}/v2/${REPO#${{ env.REGISTRY }}/}/referrers/${DIGEST}?artifactType=application/vnd.atproto.signature.v1+json")
94 SIG_DIGEST=$(echo "$REFERRERS" | jq -r '.manifests[0].digest')
95
96 # Pull signature artifact
97 oras pull "${REPO}@${SIG_DIGEST}" -o /tmp/sig
98
99 # Extract DID
100 DID=$(jq -r '.atproto.did' /tmp/sig/atproto-signature.json)
101 echo "Signed by DID: $DID"
102
103 # Check against trusted DIDs
104 TRUSTED_DIDS="${{ secrets.TRUSTED_DIDS }}" # e.g., "did:plc:alice123,did:plc:bob456"
105
106 if [[ ",$TRUSTED_DIDS," == *",$DID,"* ]]; then
107 echo "✓ DID is trusted"
108 else
109 echo "❌ DID $DID is not in trusted list"
110 exit 1
111 fi
112
113 deploy:
114 name: Deploy to Kubernetes
115 needs: verify-signature
116 runs-on: ubuntu-latest
117 if: github.ref == 'refs/heads/main'
118 steps:
119 - name: Checkout code
120 uses: actions/checkout@v4
121
122 - name: Set up image tag
123 id: vars
124 run: |
125 echo "IMAGE_TAG=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}" >> $GITHUB_OUTPUT
126
127 - name: Set up kubectl
128 uses: azure/setup-kubectl@v3
129
130 - name: Configure kubectl
131 run: |
132 echo "${{ secrets.KUBE_CONFIG }}" | base64 -d > /tmp/kubeconfig
133 export KUBECONFIG=/tmp/kubeconfig
134
135 - name: Deploy to production
136 run: |
137 kubectl set image deployment/myapp \
138 myapp=${{ steps.vars.outputs.IMAGE_TAG }} \
139 -n production
140
141 kubectl rollout status deployment/myapp -n production
142
143 - name: Verify deployment
144 run: |
145 kubectl get pods -n production -l app=myapp
146
147 # Wait for rollout to complete
148 kubectl wait --for=condition=available --timeout=300s \
149 deployment/myapp -n production
150
151 # Alternative: Use atcr-verify action (when available)
152 verify-with-action:
153 name: Verify with ATCR Action
154 runs-on: ubuntu-latest
155 steps:
156 - name: Checkout code
157 uses: actions/checkout@v4
158
159 - name: Verify image signature
160 # uses: atcr-io/atcr-verify-action@v1
161 # with:
162 # image: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}
163 # policy: .atcr/trust-policy.yaml
164 # fail-on-error: true
165 run: |
166 echo "TODO: Use official atcr-verify GitHub Action"