# Image Signing with ATProto ATCR provides cryptographic verification of container images through ATProto's native signature system. Every manifest stored in a PDS is cryptographically signed, providing tamper-proof image verification. ## Overview **Key Fact:** Every image pushed to ATCR is automatically signed via ATProto's repository commit signing. No additional signing tools or steps are required. When you push an image: 1. Manifest stored in your PDS as an `io.atcr.manifest` record 2. PDS signs the repository commit containing the manifest (ECDSA K-256) 3. Signature is part of the ATProto repository chain 4. Verification proves the manifest came from your DID and hasn't been tampered with **This document explains:** - How ATProto signatures work for ATCR images - How to verify signatures using standard and custom tools - Integration options for different use cases - When to use optional X.509 certificates (Hold-as-CA) ## ATProto Signature Model ### How It Works ATProto uses a **repository commit signing** model similar to Git: ``` 1. docker push atcr.io/alice/myapp:latest ↓ 2. AppView stores manifest in alice's PDS as io.atcr.manifest record ↓ 3. PDS creates repository commit containing the new record ↓ 4. PDS signs commit with alice's private key (ECDSA K-256) ↓ 5. Commit becomes part of alice's cryptographically signed repo chain ``` **What this proves:** - ✅ Manifest came from alice's PDS (DID-based identity) - ✅ Manifest content hasn't been tampered with - ✅ Manifest was created at a specific time (commit timestamp) - ✅ Manifest is part of alice's verifiable repository history **Trust model:** - Public keys distributed via DID documents (PLC directory, did:web) - Signatures use ECDSA K-256 (secp256k1) - Verification is decentralized (no central CA required) - Users control their own DIDs and can rotate keys ### Signature Metadata In addition to ATProto's native commit signatures, ATCR creates **ORAS signature artifacts** that bridge ATProto signatures to the OCI ecosystem: ```json { "$type": "io.atcr.atproto.signature", "version": "1.0", "subject": { "digest": "sha256:abc123...", "mediaType": "application/vnd.oci.image.manifest.v1+json" }, "atproto": { "did": "did:plc:alice123", "handle": "alice.bsky.social", "pdsEndpoint": "https://bsky.social", "recordUri": "at://did:plc:alice123/io.atcr.manifest/abc123", "commitCid": "bafyreih8...", "signedAt": "2025-10-31T12:34:56.789Z" }, "signature": { "algorithm": "ECDSA-K256-SHA256", "keyId": "did:plc:alice123#atproto", "publicKeyMultibase": "zQ3shokFTS3brHcDQrn82RUDfCZESWL1ZdCEJwekUDdo1Ko4Z" } } ``` **Stored as:** - OCI artifact with `artifactType: application/vnd.atproto.signature.v1+json` - Linked to image manifest via OCI Referrers API - Discoverable by standard OCI tools (ORAS, Cosign, Crane) ## Verification ### Quick Verification (Shell Script) For manual verification, use the provided shell scripts: ```bash # Verify an image ./examples/verification/atcr-verify.sh atcr.io/alice/myapp:latest # Output shows: # - DID and handle of signer # - PDS endpoint # - ATProto record URI # - Signature verification status ``` **See:** [examples/verification/README.md](../examples/verification/README.md) for complete examples including: - Standalone verification script - Secure pull wrapper (verify before pull) - Kubernetes webhook deployment - CI/CD integration examples ### Standard Tools (Discovery Only) Standard OCI tools can **discover** ATProto signature artifacts but cannot **verify** them (different signature format): ```bash # Discover signatures with ORAS oras discover atcr.io/alice/myapp:latest \ --artifact-type application/vnd.atproto.signature.v1+json # Fetch signature metadata oras pull atcr.io/alice/myapp@sha256:sig789... # View with Cosign (discovery only) cosign tree atcr.io/alice/myapp:latest ``` **Note:** Cosign/Notary cannot verify ATProto signatures directly because they use a different signature format and trust model. Use integration plugins or the `atcr-verify` CLI tool instead. ## Integration Options ATCR supports multiple integration approaches depending on your use case: ### 1. **Plugins (Recommended for Kubernetes)** ⭐ Build plugins for existing policy/verification engines: **Ratify Verifier Plugin:** - Integrates with OPA Gatekeeper - Verifies ATProto signatures using Ratify's plugin interface - Policy-based enforcement for Kubernetes **OPA Gatekeeper External Provider:** - HTTP service that verifies ATProto signatures - Rego policies call external provider - Flexible and easy to deploy **Containerd 2.0 Bindir Plugin:** - Verifies signatures at containerd level - Works with any CRI-compatible runtime - No Kubernetes required **See:** [docs/SIGNATURE_INTEGRATION.md](./SIGNATURE_INTEGRATION.md) for complete plugin implementation examples ### 2. **CLI Tool (atcr-verify)** Standalone CLI tool for signature verification: ```bash # Install go install github.com/atcr-io/atcr/cmd/atcr-verify@latest # Verify image atcr-verify atcr.io/alice/myapp:latest --policy trust-policy.yaml # Use in CI/CD atcr-verify $IMAGE --quiet && kubectl apply -f deployment.yaml ``` **Features:** - Trust policy management (which DIDs to trust) - Multiple output formats (text, JSON, SARIF) - Offline verification with cached DID documents - Library usage for custom integrations **See:** [docs/ATCR_VERIFY_CLI.md](./ATCR_VERIFY_CLI.md) for complete CLI specification ### 3. **External Services** Deploy verification as a service: **GitHub Actions:** ```yaml - name: Verify image signature uses: atcr-io/atcr-verify-action@v1 with: image: atcr.io/alice/myapp:${{ github.sha }} policy: .atcr/trust-policy.yaml ``` **GitLab CI, Jenkins, CircleCI:** - Use `atcr-verify` CLI in pipeline - Fail build if verification fails - Enforce signature requirements before deployment ### 4. **X.509 Certificates (Hold-as-CA)** ⚠️ Optional approach where hold services issue X.509 certificates based on ATProto signatures: **Use cases:** - Enterprise environments requiring PKI compliance - Tools that only support X.509 (legacy systems) - Notation integration (P-256 certificates) **Trade-offs:** - ❌ Introduces centralization (hold acts as CA) - ❌ Trust shifts from DIDs to hold operator - ❌ Requires hold service infrastructure **See:** [docs/HOLD_AS_CA.md](./HOLD_AS_CA.md) for complete architecture and security considerations ## Integration Strategy Decision Matrix Choose the right integration approach: | Use Case | Recommended Approach | Priority | |----------|---------------------|----------| | **Kubernetes admission control** | Ratify plugin or Gatekeeper provider | HIGH | | **CI/CD verification** | atcr-verify CLI or GitHub Actions | HIGH | | **Docker/containerd** | Containerd bindir plugin | MEDIUM | | **Policy enforcement** | OPA Gatekeeper + external provider | HIGH | | **Manual verification** | Shell scripts or atcr-verify CLI | LOW | | **Enterprise PKI compliance** | Hold-as-CA (X.509 certificates) | OPTIONAL | | **Legacy tool support** | Hold-as-CA or external bridge service | OPTIONAL | **See:** [docs/INTEGRATION_STRATEGY.md](./INTEGRATION_STRATEGY.md) for complete integration planning guide including: - Architecture layers and data flow - Tool compatibility matrix (16+ tools) - Implementation roadmap (4 phases) - When to use each approach ## Trust Policies Define which signatures you trust: ```yaml # trust-policy.yaml version: 1.0 trustedDIDs: did:plc:alice123: name: "Alice (DevOps Lead)" validFrom: "2024-01-01T00:00:00Z" expiresAt: null did:plc:bob456: name: "Bob (Security Team)" validFrom: "2024-06-01T00:00:00Z" expiresAt: "2025-12-31T23:59:59Z" policies: - name: production-images scope: "atcr.io/*/prod-*" require: signature: true trustedDIDs: - did:plc:alice123 - did:plc:bob456 minSignatures: 1 action: enforce # reject if policy fails - name: dev-images scope: "atcr.io/*/dev-*" require: signature: false action: audit # log but don't reject ``` **Use with:** - `atcr-verify` CLI: `atcr-verify IMAGE --policy trust-policy.yaml` - Kubernetes webhooks: ConfigMap with policy - CI/CD pipelines: Fail build if policy not met ## Security Considerations ### What ATProto Signatures Prove ✅ **Identity:** Manifest signed by specific DID (e.g., `did:plc:alice123`) ✅ **Integrity:** Manifest content hasn't been tampered with ✅ **Timestamp:** When the manifest was signed ✅ **Authenticity:** Signature created with private key for that DID ### What They Don't Prove ❌ **Vulnerability-free:** Signature doesn't mean image is safe ❌ **Authorization:** DID ownership doesn't imply permission to deploy ❌ **Key security:** Private key could be compromised ❌ **PDS trustworthiness:** Malicious PDS could create fake records ### Trust Dependencies When verifying signatures, you're trusting: 1. **DID resolution** (PLC directory, did:web) - public key is correct for DID 2. **PDS integrity** - PDS serves correct records and doesn't forge signatures 3. **Cryptographic primitives** - ECDSA K-256 remains secure 4. **Your trust policy** - DIDs you've chosen to trust are legitimate ### Best Practices **1. Use Trust Policies** Don't blindly trust all signatures - define which DIDs you trust: ```yaml trustedDIDs: - did:plc:your-org-team - did:plc:your-ci-system ``` **2. Monitor Signature Coverage** Track which images have signatures: ```bash atcr-verify --check-coverage namespace/production ``` **3. Enforce in Production** Use Kubernetes admission control to block unsigned images: ```yaml # Ratify + Gatekeeper or custom webhook enforceSignatures: true failurePolicy: Fail ``` **4. Verify in CI/CD** Never deploy unsigned images: ```yaml # GitHub Actions - name: Verify signature run: atcr-verify $IMAGE || exit 1 ``` **5. Plan for Compromised Keys** - Rotate DID keys periodically - Monitor DID documents for unexpected key changes - Have incident response plan for key compromise ## Implementation Status ### ✅ Available Now - **ATProto signatures**: All manifests automatically signed by PDS - **ORAS artifacts**: Signature metadata stored as OCI artifacts - **OCI Referrers API**: Discovery via standard OCI endpoints - **Shell scripts**: Manual verification examples - **Documentation**: Complete integration guides ### 🔄 In Development - **atcr-verify CLI**: Standalone verification tool - **Ratify plugin**: Kubernetes integration - **Gatekeeper provider**: OPA policy enforcement - **GitHub Actions**: CI/CD integration ### 📋 Planned - **Containerd plugin**: Runtime-level verification - **Hold-as-CA**: X.509 certificate generation (optional) - **Web UI**: Signature viewer in AppView - **Offline bundles**: Air-gapped verification ## Comparison with Other Signing Solutions | Feature | ATCR (ATProto) | Cosign (Sigstore) | Notation (Notary v2) | |---------|---------------|-------------------|---------------------| | **Signing** | Automatic (PDS) | Manual or keyless | Manual | | **Keys** | K-256 (secp256k1) | P-256 or RSA | P-256, P-384, P-521 | | **Trust** | DID-based | OIDC + Fulcio CA | X.509 PKI | | **Storage** | ATProto PDS | OCI registry | OCI registry | | **Centralization** | Decentralized | Centralized (Fulcio) | Configurable | | **Transparency Log** | ATProto firehose | Rekor | Configurable | | **Verification** | Custom tools/plugins | Cosign CLI | Notation CLI | | **Kubernetes** | Plugins (Ratify) | Policy Controller | Policy Controller | **ATCR advantages:** - ✅ Decentralized trust (no CA required) - ✅ Automatic signing (no extra tools) - ✅ DID-based identity (portable, self-sovereign) - ✅ Transparent via ATProto firehose **ATCR trade-offs:** - ⚠️ Requires custom verification tools/plugins - ⚠️ K-256 not supported by Notation (needs Hold-as-CA) - ⚠️ Smaller ecosystem than Cosign/Notation ## Why Not Use Cosign Directly? **Question:** Why not just integrate with Cosign's keyless signing (OIDC + Fulcio)? **Answer:** ATProto and Cosign use incompatible authentication models: | Requirement | Cosign Keyless | ATProto | |-------------|---------------|---------| | **Identity protocol** | OIDC | ATProto OAuth + DPoP | | **Token format** | JWT from OIDC provider | DPoP-bound access token | | **CA** | Fulcio (Sigstore CA) | None (DID-based PKI) | | **Infrastructure** | Fulcio + Rekor + TUF | PDS + DID resolver | **To make Cosign work, we'd need to:** 1. Deploy Fulcio (certificate authority) 2. Deploy Rekor (transparency log) 3. Deploy TUF (metadata distribution) 4. Build OIDC provider bridge for ATProto OAuth 5. Maintain all this infrastructure **Instead:** We leverage ATProto's existing signatures and build lightweight plugins/tools for verification. This is simpler, more decentralized, and aligns with ATCR's design philosophy. **For tools that need X.509 certificates:** See [Hold-as-CA](./HOLD_AS_CA.md) for an optional centralized approach. ## Getting Started ### Verify Your First Image ```bash # 1. Check if image has ATProto signature oras discover atcr.io/alice/myapp:latest \ --artifact-type application/vnd.atproto.signature.v1+json # 2. Pull signature metadata oras pull atcr.io/alice/myapp@sha256:sig789... # 3. Verify with shell script ./examples/verification/atcr-verify.sh atcr.io/alice/myapp:latest # 4. Use atcr-verify CLI (when available) atcr-verify atcr.io/alice/myapp:latest --policy trust-policy.yaml ``` ### Deploy Kubernetes Verification ```bash # 1. Choose an approach # Option A: Ratify plugin (recommended) # Option B: Gatekeeper external provider # Option C: Custom admission webhook # 2. Follow integration guide # See docs/SIGNATURE_INTEGRATION.md for step-by-step # 3. Enable for namespace kubectl label namespace production atcr-verify=enabled # 4. Test with sample pod kubectl run test --image=atcr.io/alice/myapp:latest -n production ``` ### Integrate with CI/CD ```bash # GitHub Actions - name: Verify signature run: | curl -LO https://github.com/atcr-io/atcr/releases/latest/download/atcr-verify chmod +x atcr-verify ./atcr-verify ${{ env.IMAGE }} --policy .atcr/trust-policy.yaml # GitLab CI verify_image: script: - wget https://github.com/atcr-io/atcr/releases/latest/download/atcr-verify - chmod +x atcr-verify - ./atcr-verify $IMAGE --policy .atcr/trust-policy.yaml ``` ## Documentation ### Core Documentation - **[ATProto Signatures](./ATPROTO_SIGNATURES.md)** - Technical deep-dive into signature format and verification - **[Signature Integration](./SIGNATURE_INTEGRATION.md)** - Tool-specific integration guides (Ratify, Gatekeeper, Containerd) - **[Integration Strategy](./INTEGRATION_STRATEGY.md)** - High-level overview and decision matrix - **[atcr-verify CLI](./ATCR_VERIFY_CLI.md)** - CLI tool specification and usage - **[Hold-as-CA](./HOLD_AS_CA.md)** - Optional X.509 certificate approach ### Examples - **[examples/verification/](../examples/verification/)** - Shell scripts, Kubernetes configs, trust policies - **[examples/plugins/](../examples/plugins/)** - Plugin skeletons for Ratify, Gatekeeper, Containerd ### External References - **ATProto:** https://atproto.com/specs/repository (repository commit signing) - **ORAS:** https://oras.land/ (artifact registry) - **OCI Referrers API:** https://github.com/opencontainers/distribution-spec/blob/main/spec.md#listing-referrers - **Ratify:** https://ratify.dev/ (verification framework) - **OPA Gatekeeper:** https://open-policy-agent.github.io/gatekeeper/ ## Support For questions or issues: - GitHub Issues: https://github.com/atcr-io/atcr/issues - Documentation: https://docs.atcr.io - Security: security@atcr.io ## Summary **Key Points:** 1. **Automatic signing**: Every ATCR image is automatically signed via ATProto's native signature system 2. **No additional tools**: Signing happens transparently when you push images 3. **Decentralized trust**: DID-based signatures, no central CA required 4. **Standard discovery**: ORAS artifacts and OCI Referrers API for signature metadata 5. **Custom verification**: Use plugins, CLI tools, or shell scripts (not Cosign directly) 6. **Multiple integrations**: Kubernetes (Ratify, Gatekeeper), CI/CD (atcr-verify), containerd 7. **Optional X.509**: Hold-as-CA for enterprise PKI compliance (centralized) **Next Steps:** 1. Read [examples/verification/README.md](../examples/verification/README.md) for practical examples 2. Choose integration approach from [INTEGRATION_STRATEGY.md](./INTEGRATION_STRATEGY.md) 3. Implement plugin or deploy CLI tool from [SIGNATURE_INTEGRATION.md](./SIGNATURE_INTEGRATION.md) 4. Define trust policy for your organization 5. Deploy to test environment first, then production