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# GitLab CI pipeline for verifying ATProto signatures
2
3variables:
4 REGISTRY: atcr.io
5 IMAGE_NAME: $CI_PROJECT_PATH
6 IMAGE_TAG: $REGISTRY/$IMAGE_NAME:$CI_COMMIT_SHA
7
8stages:
9 - build
10 - verify
11 - deploy
12
13build_image:
14 stage: build
15 image: docker:latest
16 services:
17 - docker:dind
18 script:
19 - docker build -t $IMAGE_TAG .
20 - docker push $IMAGE_TAG
21
22verify_signature:
23 stage: verify
24 image: alpine:latest
25 before_script:
26 - apk add --no-cache curl jq
27 script:
28 - |
29 echo "Verifying signature for $IMAGE_TAG"
30
31 # Install crane
32 wget https://github.com/google/go-containerregistry/releases/download/v0.15.2/go-containerregistry_Linux_x86_64.tar.gz
33 tar -xzf go-containerregistry_Linux_x86_64.tar.gz crane
34 mv crane /usr/local/bin/
35
36 # Get image digest
37 DIGEST=$(crane digest "$IMAGE_TAG")
38 echo "Image digest: $DIGEST"
39
40 # Extract repository path
41 REPO=$(echo "$IMAGE_TAG" | cut -d: -f1)
42 REPO_PATH=${REPO#$REGISTRY/}
43
44 # Check for ATProto signature
45 REFERRERS=$(curl -s "https://$REGISTRY/v2/$REPO_PATH/referrers/${DIGEST}?artifactType=application/vnd.atproto.signature.v1+json")
46
47 SIG_COUNT=$(echo "$REFERRERS" | jq '.manifests | length')
48
49 if [ "$SIG_COUNT" -eq 0 ]; then
50 echo "❌ No ATProto signature found"
51 exit 1
52 fi
53
54 echo "✓ Found $SIG_COUNT signature(s)"
55
56verify_full:
57 stage: verify
58 image: alpine:latest
59 before_script:
60 - apk add --no-cache curl jq bash
61 script:
62 - |
63 # Option 1: Use atcr-verify CLI (when available)
64 # wget https://github.com/atcr-io/atcr/releases/latest/download/atcr-verify
65 # chmod +x atcr-verify
66 # ./atcr-verify "$IMAGE_TAG" --policy .atcr/trust-policy.yaml
67
68 # Option 2: Use shell script
69 chmod +x examples/verification/atcr-verify.sh
70 ./examples/verification/atcr-verify.sh "$IMAGE_TAG"
71
72 echo "✓ Signature verified successfully"
73
74verify_trust:
75 stage: verify
76 image: alpine:latest
77 before_script:
78 - apk add --no-cache curl jq
79 script:
80 - |
81 # Install crane and ORAS
82 wget https://github.com/google/go-containerregistry/releases/download/v0.15.2/go-containerregistry_Linux_x86_64.tar.gz
83 tar -xzf go-containerregistry_Linux_x86_64.tar.gz crane
84 mv crane /usr/local/bin/
85
86 wget https://github.com/oras-project/oras/releases/download/v1.0.0/oras_1.0.0_linux_amd64.tar.gz
87 tar -xzf oras_1.0.0_linux_amd64.tar.gz
88 mv oras /usr/local/bin/
89
90 # Get signature metadata
91 DIGEST=$(crane digest "$IMAGE_TAG")
92 REPO=$(echo "$IMAGE_TAG" | cut -d: -f1)
93 REPO_PATH=${REPO#$REGISTRY/}
94
95 REFERRERS=$(curl -s "https://$REGISTRY/v2/$REPO_PATH/referrers/${DIGEST}?artifactType=application/vnd.atproto.signature.v1+json")
96 SIG_DIGEST=$(echo "$REFERRERS" | jq -r '.manifests[0].digest')
97
98 # Pull signature artifact
99 oras pull "${REPO}@${SIG_DIGEST}" -o /tmp/sig
100
101 # Extract DID
102 DID=$(jq -r '.atproto.did' /tmp/sig/atproto-signature.json)
103 echo "Signed by DID: $DID"
104
105 # Check against trusted DIDs (from CI/CD variables)
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
113deploy_production:
114 stage: deploy
115 image: bitnami/kubectl:latest
116 dependencies:
117 - verify_signature
118 - verify_full
119 - verify_trust
120 only:
121 - main
122 script:
123 - |
124 # Configure kubectl
125 echo "$KUBE_CONFIG" | base64 -d > /tmp/kubeconfig
126 export KUBECONFIG=/tmp/kubeconfig
127
128 # Deploy to production
129 kubectl set image deployment/myapp \
130 myapp=$IMAGE_TAG \
131 -n production
132
133 kubectl rollout status deployment/myapp -n production
134
135 # Verify deployment
136 kubectl get pods -n production -l app=myapp
137
138# Alternative: Manual approval before deploy
139deploy_production_manual:
140 stage: deploy
141 image: bitnami/kubectl:latest
142 dependencies:
143 - verify_signature
144 when: manual
145 only:
146 - main
147 script:
148 - |
149 echo "Deploying $IMAGE_TAG to production"
150
151 echo "$KUBE_CONFIG" | base64 -d > /tmp/kubeconfig
152 export KUBECONFIG=/tmp/kubeconfig
153
154 kubectl set image deployment/myapp \
155 myapp=$IMAGE_TAG \
156 -n production