This document should allow you to stand up a fully functioning sigstore stack, including:
- Fulcio
- Rekor
- CTLog
- Trillian - backing Rekor and CTLog
- Tuf mirror
There's a reusable action that you can use as is.
You need to install yq
. You can do this like so:
go install github.com/mikefarah/yq/v4@latest
You should be able to install KinD and Knative bits by running (from head, after cloning the repo):
./hack/setup-kind.sh
Or by downloading a release version of the script
curl -fLo /tmp/setup-kind.sh https://github.com/sigstore/scaffolding/releases/download/v0.6.4/setup-kind.sh
chmod u+x /tmp/setup-kind.sh
/tmp/setup-kind.sh
NOTE If you run the script multiple times, you will have to delete the cluster and uninstall the docker registry container between running the setup-kind.sh it spins up a registry container in a daemon mode. To clean a previously running registry, you can do one of these:
YOLO:
docker rm -f `docker ps -a | grep 'registry:2' | awk -F " " '{print $1}'`
Or to check things first:
docker ps -a | grep registry
b1e3f3238f7a registry:2 "/entrypoint.sh /etc…" 15 minutes ago Up 15 minutes 0.0.0.0:5000->5000/tcp, :::5001->5001/tcp registry.local
So that's the running version of the registry, so first kill and then remove it:
docker rm -f b1e3f3238f7a
curl -Lo /tmp/setup-scaffolding-from-release.sh https://github.com/sigstore/scaffolding/releases/download/v0.6.4/setup-scaffolding-from-release.sh
chmod u+x /tmp/setup-scaffolding-from-release.sh
/tmp/setup-scaffolding-from-release.sh
If you're deploying to kind cluster created above, tell ko
where it is, or
change to where you're deploying your images.
export KO_DOCKER_REPO=registry.local:5001/sigstore
./hack/setup-scaffolding.sh
The step above creates 5 namespaces:
- trillian-system
- ctlog-system
- fulcio-system
- rekor-system
- tuf-system
trillian-system
namespace contains all things related to
Trillian. This means there will be two
services log-server
, log-signer
, and a mysql pod.
To access these services from the cluster, you'd use:
log-server.trillian-system.svc
log-signer.trillian-system.svc
mysql-trillian.trillian-system.svc
ctlog-system
namespace contains the
ctlog service and
can be accessed with:
ctlog.ctlog-system.svc
fulcio-system
namespace contains Fulcio
and Fulcio can be accessed in the cluster with:
fulcio.fulcio-system.svc
for HTTP orfulcio-grpc.fulcio-system.svc
for GRPC
rekor-system
namespace contains Rekor
and Rekor can be accessed in the cluster with:
rekor.rekor-system.svc
tuf-system
namespace contains TUF root
mirror that we need to point cosign and other tools to, because the root
of trust is not the public cosign instance. Tuf can be accessed in the cluster
with:
tuf.tuf-system.svc
To make it easier to test keyless signing without going through the browser
based auth, you can install OIDC issuer
on your TEST cluster. It does no
authentication, so do not install this on anything except your local kind test
cluster. Just by doing a curl against it will give you an OIDC token that you
can use as --identity-token on the calls with cosign
.
ko apply -BRf ./testdata/config/gettoken
In order to access the services running in the cluster, we utilize port-forwarding provided by Kubernetes.
Setup port forwarding:
kubectl -n kourier-system port-forward service/kourier-internal 8080:80 &
Add the following entries to your /etc/hosts
file
127.0.0.1 rekor.rekor-system.svc
127.0.0.1 fulcio.fulcio-system.svc
127.0.0.1 ctlog.ctlog-system.svc
127.0.0.1 gettoken.default.svc
127.0.0.1 tuf.tuf-system.svc
Instead of having to specify these in various flags when calling cosign and long URLs, let's create some up front:
export REKOR_URL=http://rekor.rekor-system.svc:8080
export FULCIO_URL=http://fulcio.fulcio-system.svc:8080
export FULCIO_GRPC_URL=http://fulcio-grpc.fulcio-system.svc:8080
export ISSUER_URL=http://gettoken.default.svc:8080
export TUF_MIRROR=http://tuf.tuf-system.svc:8080
For testing keyless signing we need an OIDC token provider, so let's create one that runs on the cluster and issues OIDC tokens.
ko apply -BRf ./testdata/config/gettoken
Let's first run a quick smoke test that does a cosign sign followed by making sure that the rekor entry is created for it.
- Get TUF root from the tuf-system namespace
kubectl -n tuf-system get secrets tuf-root -ojsonpath='{.data.root}' | base64 -d > ./root.json
- Initialize cosign with our root.
cosign initialize --mirror $TUF_MIRROR --root ./root.json
An example invocation of this on my machine looked like this:
vaikas@villes-mbp scaffolding % cosign initialize --mirror $TUF_MIRROR --root ./root.json
Root status:
{
"local": "/Users/vaikas/.sigstore/root",
"remote": "http://tuf.tuf-system.svc:8080",
"metadata": {
"root.json": {
"version": 1,
"len": 2178,
"expiration": "04 Feb 23 23:28 UTC",
"error": ""
},
"snapshot.json": {
"version": 1,
"len": 618,
"expiration": "04 Feb 23 23:28 UTC",
"error": ""
},
"targets.json": {
"version": 1,
"len": 1028,
"expiration": "04 Feb 23 23:28 UTC",
"error": ""
},
"timestamp.json": {
"version": 1,
"len": 619,
"expiration": "04 Feb 23 23:28 UTC",
"error": ""
}
},
"targets": [
"rekor.pub",
"ctfe.pub",
"fulcio_v1.crt.pem"
]
}
If you have an image that you want to play with, great, you can also create one easily like this (that gets then uploaded to our local registry):
KO_DOCKER_REPO=registry.local:5001/sigstore
pushd $(mktemp -d)
go mod init example.com/demo
cat <<EOF > main.go
package main
import "fmt"
func main() {
fmt.Println("hello world")
}
EOF
demoimage=`ko publish -B example.com/demo`
export demoimage=$demoimage
echo Created image $demoimage
popd
Then let's sign it (or change $demoimage to something else).
COSIGN_EXPERIMENTAL=1 cosign sign --rekor-url $REKOR_URL --fulcio-url $FULCIO_URL --force --allow-insecure-registry $demoimage --identity-token `curl -s $ISSUER_URL`
An example invocation from my local instance is like so:
vaikas@villes-mbp scaffolding % COSIGN_EXPERIMENTAL=1 cosign sign --rekor-url $REKOR_URL --fulcio-url $FULCIO_URL --force --allow-insecure-registry $demoimage --identity-token `curl -s $ISSUER_URL`
Generating ephemeral keys...
Retrieving signed certificate...
Note that there may be personally identifiable information associated with this signed artifact.
This may include the email address associated with the account with which you authenticate.
This information will be used for signing this artifact and will be stored in public transparency logs and cannot be removed later.
Successfully verified SCT...
tlog entry created with index: 0
Pushing signature to: registry.local:5001/sigstore/demo
Then let's verify the signature.
COSIGN_EXPERIMENTAL=1 cosign verify --rekor-url $REKOR_URL --allow-insecure-registry $demoimage
An example invocation from my local instance is like so:
vaikas@villes-mbp scaffolding % COSIGN_EXPERIMENTAL=1 cosign verify --rekor-url $REKOR_URL --allow-insecure-registry $demoimage
**Warning** Missing fallback target fulcio.crt.pem, skipping
Verification for registry.local:5001/sigstore/demo@sha256:b6cfc6e87706304be13f607b238d905db1096619c0217c82f4151117e0112025 --
The following checks were performed on each of these signatures:
- The cosign claims were validated
- Existence of the claims in the transparency log was verified offline
- Any certificates were verified against the Fulcio roots.
[{"critical":{"identity":{"docker-reference":"registry.local:5000/sigstore/demo"},"image":{"docker-manifest-digest":"sha256:b6cfc6e87706304be13f607b238d905db1096619c0217c82f4151117e0112025"},"type":"cosign container image signature"},"optional":{"Bundle":{"SignedEntryTimestamp":"MEUCIQD8MdBVswffTOuubuvTHIWw4BMkOmUmgrQEavmAnWZ1MAIgSNO+gf4ldCql0botNgtb23RWPD4iYv0Qq93sheWf5wo=","Payload":{"body":"eyJhcGlWZXJzaW9uIjoiMC4w<SNIPPED_HERE_FOR_READABILITY>b45b4573e2a7e5f876bdff025b06f3243"}},"Issuer":"https://kubernetes.default.svc","Subject":"https://kubernetes.io/namespaces/default/serviceaccounts/default"}}]
And the **Warning**
is just letting us know that there's no custom metadata
on TUF, and we fallback on the hard-coded names, and that's one of the ones we
expect for Fulcio (and the other is the one we use: fulcio_v1.crt.pem)
echo -n 'foobar test attestation' > ./predicate-file
COSIGN_EXPERIMENTAL=1 cosign attest --predicate ./predicate-file --fulcio-url $FULCIO_URL --rekor-url $REKOR_URL --allow-insecure-registry --force $demoimage --identity-token `curl -s $ISSUER_URL`
An example invocation from my local instance:
vaikas@villes-mbp scaffolding % echo -n 'foobar test attestation' > ./predicate-file
COSIGN_EXPERIMENTAL=1 cosign attest --predicate ./predicate-file --fulcio-url $FULCIO_URL --rekor-url $REKOR_URL --allow-insecure-registry --force $demoimage --identity-token `curl -s $ISSUER_URL`
Generating ephemeral keys...
Retrieving signed certificate...
Note that there may be personally identifiable information associated with this signed artifact.
This may include the email address associated with the account with which you authenticate.
This information will be used for signing this artifact and will be stored in public transparency logs and cannot be removed later.
Successfully verified SCT...
Using payload from: ./predicate-file
tlog entry created with index: 1
And then finally let's verify the attestation we just created:
COSIGN_EXPERIMENTAL=1 cosign verify-attestation --rekor-url $REKOR_URL --allow-insecure-registry $demoimage
An example invocation from my local instance:
vaikas@villes-mbp scaffolding % COSIGN_EXPERIMENTAL=1 cosign verify-attestation --rekor-url $REKOR_URL --allow-insecure-registry $demoimage
**Warning** Missing fallback target fulcio.crt.pem, skipping
Verification for registry.local:5001/sigstore/demo@sha256:b6cfc6e87706304be13f607b238d905db1096619c0217c82f4151117e0112025 --
The following checks were performed on each of these signatures:
- The cosign claims were validated
- Existence of the claims in the transparency log was verified offline
- Any certificates were verified against the Fulcio roots.
Certificate subject: https://kubernetes.io/namespaces/default/serviceaccounts/default
Certificate issuer URL: https://kubernetes.default.svc
{"payloadType":"application/vnd.in-toto+json","payload":"eyJfdHlwZSI6Imh0dHBzOi8vaW4tdG90by5pby9TdGF0ZW1lbnQvdjAuMSIsInByZWRpY2F0ZVR5cGUiOiJjb3NpZ24uc2lnc3RvcmUuZGV2L2F0dGVzdGF0aW9uL3YxIiwic3ViamVjdCI6W3sibmFtZSI6InJlZ2lzdHJ5LmxvY2FsOjUwMDAvc2lnc3RvcmUvZGVtbyIsImRpZ2VzdCI6eyJzaGEyNTYiOiJiNmNmYzZlODc3MDYzMDRiZTEzZjYwN2IyMzhkOTA1ZGIxMDk2NjE5YzAyMTdjODJmNDE1MTExN2UwMTEyMDI1In19XSwicHJlZGljYXRlIjp7IkRhdGEiOiJmb29iYXIgdGVzdCBhdHRlc3RhdGlvbiIsIlRpbWVzdGFtcCI6IjIwMjItMDgtMDdUMDM6NTU6NDhaIn19","signatures":[{"keyid":"","sig":"MEUCIHXTVuffNLmCtnYg2AqCZ1YZfN87Ct3jL6Opx6ZA1czAAiEAs4BG3wEHP49Kg2YB+7gcFqg64J77aS/IDKb6sSbmRzU="}]}
And you can inspect the payload
of the attestation by base64 decoding the payload, so for me:
vaikas@villes-mbp scaffolding % echo 'eyJfdHlwZSI6Imh0dHBzOi8vaW4tdG90by5pby9TdGF0ZW1lbnQvdjAuMSIsInByZWRpY2F0ZVR5cGUiOiJjb3NpZ24uc2lnc3RvcmUuZGV2L2F0dGVzdGF0aW9uL3YxIiwic3ViamVjdCI6W3sibmFtZSI6InJlZ2lzdHJ5LmxvY2FsOjUwMDAvc2lnc3RvcmUvZGVtbyIsImRpZ2VzdCI6eyJzaGEyNTYiOiJiNmNmYzZlODc3MDYzMDRiZTEzZjYwN2IyMzhkOTA1ZGIxMDk2NjE5YzAyMTdjODJmNDE1MTExN2UwMTEyMDI1In19XSwicHJlZGljYXRlIjp7IkRhdGEiOiJmb29iYXIgdGVzdCBhdHRlc3RhdGlvbiIsIlRpbWVzdGFtcCI6IjIwMjItMDgtMDdUMDM6NTU6NDhaIn19' | base64 -d
{"_type":"https://in-toto.io/Statement/v0.1","predicateType":"cosign.sigstore.dev/attestation/v1","subject":[{"name":"registry.local:5001/sigstore/demo","digest":{"sha256":"b6cfc6e87706304be13f607b238d905db1096619c0217c82f4151117e0112025"}}],"predicate":{"Data":"foobar test attestation","Timestamp":"2022-08-07T03:55:48Z"}}%
Notice our predicate is foobar test attestation
as was in our predicate file.