Docker Security Best Practices
Sign Your Images for Integrity and Authenticity
Image signing is a security process that attaches a digital signature to a container image, allowing developers to verify its authenticity and integrity. By signing an image, you create a cryptographic "seal" that ensures the code has not been tampered with and originates from a trusted source. There are some traditional methods for signing container images, such as using PGP (Pretty Good Privacy) signatures. These methods work and they're easy to understand and apply. However, they often require manual key management and do not integrate seamlessly with modern container registries and deployment workflows. Other alternatives, like Docker Content Trust (DCT) using Notary, provide a more integrated approach but can be complex to set up and manage. Sigstore is a newer solution designed to simplify the process of signing and verifying container images.
Sigstore simplifies the process by providing a suite of tools - primarily Cosign, Rekor, and Fulcio - that automate the generation and verification of these signatures. It works by linking an image's cryptographic hash to a developer's identity, recording the event in a tamper-proof transparency log, and allowing systems to verify the image's "provenance" before execution.
With PGP, for example, you need to use external tools to sign the image and manage keys manually. Sigstore's Cosign tool automates much of this process. It can also store signatures directly in the container registry alongside the image, which is a significant improvement in usability and practicality. Sigstore supports keyless signing and can easily integrate with CI/CD pipelines, OCI registries, and Kubernetes environments. In the following example, we will demonstrate how to sign a Docker image using Sigstore's Cosign tool and verify it in Harbor.
Start by installing Cosign:
curl -O -L "https://github.com/sigstore/cosign/releases/download/v3.0.4/cosign-linux-amd64"
sudo mv cosign-linux-amd64 /usr/local/bin/cosign
sudo chmod +x /usr/local/bin/cosign
cosign version
Create a minimal image to sign:
mkdir -p $HOME/cosign-example && cd $HOME/cosign-example
cat <<'EOF' > Dockerfile
FROM alpine:latest
CMD ["echo", "Hello, Cosign!"]
EOF
Log in to your Harbor registry if it's not already done:
export REGISTRY_URL=$(curl -s http://ifconfig.me)
docker login $REGISTRY_URL # required before push/sign
We already created a private project named private in Harbor; create it if you haven't done so yet.
Build and push the image:
docker build -t $REGISTRY_URL/private/cosign-example:latest .
docker push $REGISTRY_URL/private/cosign-example:latest
While Sigstore is well-known for its Keyless signing (which relies on OIDC providers like GitHub or Google for identity), we will use a Static Key Pair for this example. This approach allows us to maintain full control over our credentials without depending on an external identity provider or needing internet access for identity verification (useful for air-gapped environments and secure CI/CD pipelines).
Generate a key pair:
COSIGN_PASSWORD='p@ssw0rd' cosign generate-key-pair
Sign the immutable digest to avoid tag re-assignment issues:
# Export the image digest
IMAGE_DIGEST=$(docker inspect $REGISTRY_URL/private/cosign-example:latest | jq -r '.[0].RepoDigests[0]')
# Export registry credentials
REGISTRY_USERNAME=admin
REGISTRY_PASSWORD=p@ssw0rd
# Sign the image
COSIGN_PASSWORD='p@ssw0rd' cosign sign --yes --key cosign.key \
--registry-username $REGISTRY_USERNAME \Painless Docker - 2nd Edition
A Comprehensive Guide to Mastering Docker and its EcosystemEnroll now to unlock all content and receive all future updates for free.
Hurry! This limited time offer ends in:
To redeem this offer, copy the coupon code below and apply it at checkout:
