If you've been following the Chainguard blog, you might ask yourself: how do I run the open-source sigstore stack on my machine?
While sigstore is often deployed using Kubernetes, it is flexible enough to run nearly anywhere: from a Raspberry Pi to an IBM mainframe. This article will demonstrate how to build the sigstore stack (cosign, rekor, fulcio) on your machine and use it to sign and verify container signatures without ever leaving localhost.
Consult the documentation for your favorite package manager to install:
These dependencies may also be downloaded and installed from their respective websites.
Level I: Keyed signing with a local registry
First, we will use cosign, sigstore's container-signing tool, to sign a locally published container using a locally maintained key pair. Here are the commands we will execute, along with where they will access data from:
1.1: Running a local registry
sigstore can sign containers stored within any container registry. To keep our demonstration local, we'll install a simple registry using Go. Open a terminal and run:
As we begin launching several services into the foreground, now is a great time to begin a shell multiplexer such as tmux or screen, or at least a terminal that supports tabs. Start the registry service:
The terminal will now quietly hang until a request arrives. Start a new terminal session, and let's move on!
1.2: Pushing an unsigned image to the local registry
So that we have a target container to sign, we will now build a sample container and upload it to the local registry. First, install the ko container builder:
Download rekor, sigstore's tamper-resistant ledger software:
Build and push an image containing the rekor CLI to our local registry:
1.3: Keyed-signing with cosign
Now we get to use cosign, sigstore's container-signing tool, to sign the container we just published. Install the latest cosign release:
Create a local key pair using any password. For simplicity, I suggest an empty one:
Sgn the published container using the local private key:
Use cosign to verify that the published container matches the local public key:
The output for a successful verification will look like this:
Congratulations! You have just signed your first container! To sign other artifacts such as binaries, see Working with other artifacts.
Level II: Certificate Transparency with Rekor
So far, verification has relied on a single mutable source of truth: the container registry. With Rekor, we will introduce a second immutable source of truth to the system:
2.1: Creating a database backend with MariaDB
While Sigstore can use multiple database backends, this tutorial uses MariaDB. Once you've installed the prerequisites, run the following to start the database up locally in a locked-down manner:
2.2: Installing Trillian
Start the log_server, which provides the Trillian "personality" API, used by Rekor, and the Certificate Transparency frontend.
Start the log signer, which periodically checks the database and sequences data into a Merkle tree:
The Trillian system is multi-tenant and can support multiple independent Merkle trees. Run this command to send a gRPC request to create a tree and save the log_id for future use:
2.3: Installing Rekor
The Rekor project provides a restful API-based server for validation and a transparency log for storage. Install it from source:
Upload a test artifact to verify that Rekor is functioning correctly:
2.4: Verifiable signing with Cosign & Rekor
Upload a signature for our image, using the local key pair we created in step 1.3.
Verify the container against the mutable OCI attestation and the immutable Rekor record:
Success looks like this:
🍦 Take an ice cream break if you got this far. You earned it!
Level III: Keyless signing with Fulcio
Cosign has experimental support for using Fulcio to generate signing certificates, saving users the headache of managing certificates. As our goal is to run everything locally, we have to stand up substantially more architecture to provide secure keyless signing:
3.1: Install Fulcio
As we're going to use some experimental features in this tutorial, you will need to install Fulcio from HEAD:
3.2: Configure SoftHSM
Create your first HSM token, setting both PINs to 2324.
3.3: Create a CA certificate with OpenSC
Many tools need to be informed about the SoftHSM installation location, which is different in each operating system. Run this command to set and reveal the library location:
Create a configuration file for the pkcs11 crypto library using the user PIN code you specified in the previous step:
Generate a new key pair that is stored directly in the HSM. When prompted for a PIN, use 2324:
Create a local CA root certificate:
3.4: Install the Certificate Transparency Frontend
The ct_server is an RFC6962-compliant certificate transparency log that stores the code-signing certificates issued by Fulcio.
Next, create a private key for the front end to use for signing certificates. For the password, I suggest using 2324 again, but you may use anything 4-characters or longer:
Store the password as a shell variable:
Look up the Trillian log ID we previously created and set the LOG_ID variable to the resulting value:
Populate the Certificate Transparency configuration file:
Start the certificate transparency server:
3.5: Installing Dex for OpenID authentication
Dex is a federated OpenID Connect Provider, connecting OpenID identities from multiple providers. We are going to use Dex to provide GitHub authentication. To build it from source (Note: BSD users should use gmake instead of make):
For this demonstration, we'll use GitHub as an OpenID provider. Visit GitHub: Register a new OAuth Application, and fill in the form accordingly:
When you click Register Application, it will output a client ID. Save it to your environment:
Click the Generate a new client secret button, and copy the long alphanumeric string it emits into your environment:
Populate the Dex configuration:
3.6: Setting up Fulcio for keyless signatures
Populate the Fulcio configuration:
3.7: Local Keyless Signing
Now it is time for the finale: keyless signing! In an internet-connected environment, the command-line to sign a container is relatively simple:
However, since we will be using our local environment, we need to override the public-key certificate used to verify identities and the endpoint locations. Run this command to sign the image we pushed locally:
When you run that command, your browser will open to the local Dex instance, which will prompt you to authenticate using GitHub. Afterward, verify the certificate:
Among other things, the output will include the OIDC issuer (our local Dex URL) and the e-mail address you authenticated to Github with. Here is an example:
Where to next?
If you encounter any problems or would like to learn more about sigstore, see:
Happy artifact signing, everyone!