A container registry that uses the AT Protocol for manifest storage and S3 for blob storage. atcr.io
docker container atproto go
README.md

ATProto Signature Verification Examples#

This directory contains practical examples for verifying ATProto signatures on ATCR container images.

Files#

Scripts#

  • atcr-verify.sh - Standalone signature verification script

    • Verifies ATProto signatures using shell commands
    • Requires: curl, jq, crane, oras
    • Does everything except full cryptographic verification
    • Use this until the atcr-verify CLI tool is built
  • verify-and-pull.sh - Secure image pull wrapper

    • Verifies signatures before pulling images
    • Can be used as a docker pull replacement
    • Configurable via environment variables

Configuration#

  • trust-policy.yaml - Example trust policy configuration

    • Defines which DIDs to trust
    • Specifies policies for different image scopes
    • Includes audit logging and reporting settings
  • kubernetes-webhook.yaml - Kubernetes admission controller

    • Validates signatures before pod creation
    • Includes webhook deployment, service, and configuration
    • Uses trust policy ConfigMap

Quick Start#

1. Verify an Image#

# Make script executable
chmod +x atcr-verify.sh

# Verify an image
./atcr-verify.sh atcr.io/alice/myapp:latest

Output:

═══════════════════════════════════════════════════
 ATProto Signature Verification
═══════════════════════════════════════════════════
 Image: atcr.io/alice/myapp:latest
═══════════════════════════════════════════════════

[1/7] Resolving image digest...
  → sha256:abc123...
[2/7] Discovering ATProto signature artifacts...
  → Found 1 signature(s)
  → Signature digest: sha256:sig789...
  → Signed by DID: did:plc:alice123
[3/7] Fetching signature metadata...
  → DID: did:plc:alice123
  → Handle: alice.bsky.social
  → PDS: https://bsky.social
  → Record: at://did:plc:alice123/io.atcr.manifest/abc123
  → Signed at: 2025-10-31T12:34:56.789Z
[4/7] Resolving DID to public key...
  → Public key: zQ3shokFTS3brHcD...
[5/7] Querying PDS for signed record...
  → Record CID: bafyreig7...
[6/7] Verifying record integrity...
  → Record digest matches image digest
[7/7] Cryptographic signature verification...
  ⚠ Full cryptographic verification requires ATProto crypto library

═══════════════════════════════════════════════════
 ✓ Verification Completed
═══════════════════════════════════════════════════

 Signed by:    alice.bsky.social (did:plc:alice123)
 Signed at:    2025-10-31T12:34:56.789Z
 PDS:          https://bsky.social
 Record:       at://did:plc:alice123/io.atcr.manifest/abc123
 Signature:    sha256:sig789...

═══════════════════════════════════════════════════

2. Secure Pull#

# Make script executable
chmod +x verify-and-pull.sh

# Pull image with verification
./verify-and-pull.sh atcr.io/alice/myapp:latest

# With Docker options
./verify-and-pull.sh atcr.io/alice/myapp:latest --platform linux/amd64

Create an alias for convenience:

# Add to ~/.bashrc or ~/.zshrc
alias docker-pull-secure='/path/to/verify-and-pull.sh'

# Use it
docker-pull-secure atcr.io/alice/myapp:latest

3. Deploy Kubernetes Webhook#

# 1. Generate TLS certificates for webhook
openssl req -x509 -newkey rsa:4096 -keyout tls.key -out tls.crt \
  -days 365 -nodes -subj "/CN=atcr-verify-webhook.atcr-system.svc"

# 2. Create namespace and secret
kubectl create namespace atcr-system
kubectl create secret tls atcr-verify-webhook-certs \
  --cert=tls.crt --key=tls.key -n atcr-system

# 3. Update CA bundle in kubernetes-webhook.yaml
cat tls.crt | base64 -w 0
# Copy output and replace caBundle in kubernetes-webhook.yaml

# 4. Deploy webhook
kubectl apply -f kubernetes-webhook.yaml

# 5. Enable verification for a namespace
kubectl label namespace production atcr-verify=enabled

# 6. Test with a pod
kubectl run test-pod --image=atcr.io/alice/myapp:latest -n production

Prerequisites#

For Scripts#

Install required tools:

macOS (Homebrew):

brew install curl jq crane oras

Linux (apt):

# curl and jq
sudo apt-get install curl jq

# crane
curl -sL "https://github.com/google/go-containerregistry/releases/download/v0.15.2/go-containerregistry_Linux_x86_64.tar.gz" | tar -xz crane
sudo mv crane /usr/local/bin/

# oras
curl -LO "https://github.com/oras-project/oras/releases/download/v1.0.0/oras_1.0.0_linux_amd64.tar.gz"
tar -xzf oras_1.0.0_linux_amd64.tar.gz
sudo mv oras /usr/local/bin/

For Kubernetes Webhook#

Requirements:

  • Kubernetes cluster (1.16+)
  • kubectl configured
  • Permission to create namespaces and webhooks
  • Webhook container image (build from source or use pre-built)

Configuration#

Environment Variables (verify-and-pull.sh)#

  • VERIFY_SCRIPT - Path to atcr-verify.sh (default: ./atcr-verify.sh)
  • TRUST_POLICY - Path to trust policy (default: ./trust-policy.yaml)
  • REQUIRE_VERIFICATION - Require verification (default: true)
  • SKIP_ATCR_IMAGES - Skip verification for non-ATCR images (default: false)

Example:

# Skip verification for non-ATCR images
SKIP_ATCR_IMAGES=true ./verify-and-pull.sh docker.io/library/nginx:latest

# Allow pulling even if verification fails (NOT RECOMMENDED)
REQUIRE_VERIFICATION=false ./verify-and-pull.sh atcr.io/alice/myapp:latest

Trust Policy#

Edit trust-policy.yaml to customize:

  1. Add your DIDs:

    trustedDIDs:
      did:plc:your-did:
        name: "Your Name"
        validFrom: "2024-01-01T00:00:00Z"
    
  2. Define policies:

    policies:
      - name: my-policy
        scope: "atcr.io/myorg/*"
        require:
          signature: true
          trustedDIDs:
            - did:plc:your-did
        action: enforce
    
  3. Use with verification:

    # When atcr-verify CLI is available:
    atcr-verify IMAGE --policy trust-policy.yaml
    

Integration Patterns#

CI/CD (GitHub Actions)#

- name: Verify image signature
  run: |
    chmod +x examples/verification/atcr-verify.sh
    ./examples/verification/atcr-verify.sh ${{ env.IMAGE }}

- name: Deploy if verified
  if: success()
  run: kubectl set image deployment/app app=${{ env.IMAGE }}

CI/CD (GitLab CI)#

verify:
  script:
    - chmod +x examples/verification/atcr-verify.sh
    - ./examples/verification/atcr-verify.sh $IMAGE

deploy:
  dependencies: [verify]
  script:
    - kubectl set image deployment/app app=$IMAGE

Docker Alias#

# ~/.bashrc or ~/.zshrc
function docker() {
  if [ "$1" = "pull" ] && [[ "$2" =~ ^atcr\.io/ ]]; then
    echo "Using secure pull with signature verification..."
    /path/to/verify-and-pull.sh "${@:2}"
  else
    command docker "$@"
  fi
}

Systemd Service#

# /etc/systemd/system/myapp.service
[Unit]
Description=My Application
After=docker.service

[Service]
Type=oneshot
ExecStartPre=/path/to/verify-and-pull.sh atcr.io/myorg/myapp:latest
ExecStart=/usr/bin/docker run atcr.io/myorg/myapp:latest
Restart=on-failure

[Install]
WantedBy=multi-user.target

Troubleshooting#

"No ATProto signature found"#

Cause: Image doesn't have a signature artifact

Solutions:

  1. Check if image exists: crane digest IMAGE
  2. Re-push image to generate signature
  3. Verify referrers API is working:
    curl "https://atcr.io/v2/REPO/referrers/DIGEST"
    

"Failed to resolve DID"#

Cause: DID resolution failed

Solutions:

  1. Check internet connectivity
  2. Verify DID is valid: curl https://plc.directory/DID
  3. Check if DID document has verificationMethod

"Failed to fetch record from PDS"#

Cause: PDS is unreachable or record doesn't exist

Solutions:

  1. Check PDS endpoint: curl PDS_URL/xrpc/com.atproto.server.describeServer
  2. Verify record URI is correct
  3. Check if record exists in PDS

Webhook Pods Don't Start#

Cause: Webhook is rejecting all pods

Solutions:

  1. Check webhook logs: kubectl logs -n atcr-system -l app=atcr-verify-webhook
  2. Disable webhook temporarily: kubectl delete validatingwebhookconfiguration atcr-verify
  3. Fix issue and re-deploy
  4. Test with labeled namespace first

Security Best Practices#

  1. Always verify in production

    • Enable webhook for production namespaces
    • Set failurePolicy: Fail to block on errors
  2. Use trust policies

    • Define specific trusted DIDs
    • Don't trust all signatures blindly
    • Set expiration dates for temporary access
  3. Monitor verification

    • Enable audit logging
    • Review verification failures
    • Track signature coverage
  4. Rotate keys regularly

    • Update DID documents when keys change
    • Revoke compromised keys immediately
    • Monitor for unexpected key changes
  5. Secure webhook deployment

    • Use TLS for webhook communication
    • Restrict webhook RBAC permissions
    • Keep webhook image updated

Next Steps#

  1. Test verification with your images
  2. Customize trust policy for your organization
  3. Deploy webhook to test clusters first
  4. Monitor verification in CI/CD pipelines
  5. Gradually roll out to production

See Also#

Support#

For issues or questions: