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.
Prerequisites
Consult the documentation for your favorite package manager to install:
The Go Programming Language (v1.16 or higher: confirm by running go version )
SoftHSM for implementing a PKCS#11 storage interface
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:
-- CODE language-bash --
go install github.com/google/go-containerregistry/cmd/registry@latest
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:
-- CODE language-bash --
$HOME/go/bin/registry
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:
-- CODE language-bash --
go install github.com/google/ko@latest
The output for a successful verification will look like this:
-- CODE language-bash --
Verification for localhost:1338/demo/rekor-cli-e3df3bc7cfcbe584a2639931193267e9:latest --
The following checks were performed on each of these signatures:
- The cosign claims were validated
- The signatures were verified against the specified public key
- Any certificates were verified against the Fulcio roots.
[{"critical":{"identity":{"docker-reference":"localhost:1338/demo/rekor-cli-e3df3bc7cfcbe584a2639931193267e9"},"image":{"docker-manifest-digest":"sha256:3a46c2e44bfe8ea0231af6ab2f7adebd0bab4a892929b307c0b48d6958863a4d"},"type":"cosign container image signature"},"optional":null}]
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:
OpenBSD: doas mysql_install_db && doas rcctl start mysqld && doas mysql_secure_installation
Follow the prompts from mysql_install_db, answering N to change the root password, and Y to everything else. Afterward, run the database creation script:
-- CODE language-bash --
cd $HOME/sigstore-local/src/rekor/scripts
sudo sh -x createdb.sh
2.2: Installing Trillian
Trillian provides a tamper-proof append-only log based on Merkle Trees using a gRPC API. Trillian stores its records in the MariaDB database we previously created. Install Trillian:
-- CODE language-bash --
go install github.com/google/trillian/cmd/trillian_log_server@latest
go install github.com/google/trillian/cmd/trillian_log_signer@latest
go install github.com/google/trillian/cmd/createtree@latest
Start the log_server, which provides the Trillian "personality" API, used by Rekor, and the Certificate Transparency frontend.
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:
-- CODE language-bash --
Verification for localhost:1338/demo/rekor-cli-e3df3bc7cfcbe584a2639931193267e9:latest --
The following checks were performed on each of these signatures:
- The cosign claims were validated
- The claims were present in the transparency log
- The signatures were integrated into the transparency log when the certificate was valid
- The signatures were verified against the specified public key
- Any certificates were verified against the Fulcio roots.
[{"critical":{"identity":{"docker-reference":"localhost:1338/demo/rekor-cli-e3df3bc7cfcbe584a2639931193267e9"},"image":{"docker-manifest-digest":"sha256:35b25714b56211d548b97a858a1485b254228fe9889607246e96ed03ed77017d"},"type":"cosign container image signature"},"optional":{"Bundle":{"SignedEntryTimestamp":"MEUCIG...yoIY=","Payload":{"body":"...","integratedTime":1643917737,"logIndex":1,"logID":"4d2e4...97291"}}}}]
🍦 Take an ice cream break if you got this far. You earned it!
Level III: Keyless signing with Fulcio
Fulcio is a sigstore component that issues code-signing certificates based on an authenticated OpenID Connect identity. These certificates are short-lived, lasting only 20 minutes!
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:
-- CODE language-bash --
cd $HOME/sigstore-local/src
git clone https://github.com/sigstore/fulcio.git
cd fulcio
go install .
3.2: Configure SoftHSM
SoftHSM implements a cryptographic store accessible through a PKCS #11 interface. You can use it to explore PKCS #11 without having a Hardware Security Module. For this demo, we will configure sigstore to reference tokens in $HOME/sigstore-local/tokens :
Create your first HSM token, setting both PINs to 2324.
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:
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.
-- CODE language-bash --
go install github.com/google/certificate-transparency-go/trillian/ctfe/ct_server@latest
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:
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):
-- CODE language-bash --
cd $HOME/sigstore-local/src
git clone https://github.com/dexidp/dex.git
cd dex
make build
cp bin/dex $HOME/go/bin
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:
-- CODE language-bash --
Verification for localhost:1338/demo/rekor-cli-e3df3bc7cfcbe584a2639931193267e9:latest --
The following checks were performed on each of these signatures:
- The cosign claims were validated
- The claims were present in the transparency log
- The signatures were integrated into the transparency log when the certificate was valid
- Any certificates were verified against the Fulcio roots.
[{"critical":{"identity":{"docker-reference":"localhost:1338/demo/rekor-cli-e3df3bc7cfcbe584a2639931193267e9"},"image":{"docker-manifest-digest":"sha256:dae1e7cdb03fc6f16e3a48111634f0c5fc28752229da2f96a6a4d03f60d6
d609"},"type":"cosign container image signature"},
"optional":{"Bundle":{"SignedEntryTimestamp":"MEUCI...Og4=","Payload":{"body":"...","integratedTime":1644977812,"logIndex":3,"logID":"9d0dd...a86d"}},
"Issuer":"http://localhost:5556","Subject":"blog@chainguard.dev"}}]
😊
Pat yourself on the back! You made it through the tutorial!
Where to next?
If you encounter any problems or would like to learn more about sigstore, see:
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse varius enim in eros elementum tristique. Duis cursus, mi quis viverra ornare, eros dolor interdum nulla, ut commodo diam libero vitae erat. Aenean faucibus nibh et justo cursus id rutrum lorem imperdiet. Nunc ut sem vitae risus tristique posuere.
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.
Prerequisites
Consult the documentation for your favorite package manager to install:
The Go Programming Language (v1.16 or higher: confirm by running go version )
SoftHSM for implementing a PKCS#11 storage interface
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:
-- CODE language-bash --
go install github.com/google/go-containerregistry/cmd/registry@latest
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:
-- CODE language-bash --
$HOME/go/bin/registry
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:
-- CODE language-bash --
go install github.com/google/ko@latest
The output for a successful verification will look like this:
-- CODE language-bash --
Verification for localhost:1338/demo/rekor-cli-e3df3bc7cfcbe584a2639931193267e9:latest --
The following checks were performed on each of these signatures:
- The cosign claims were validated
- The signatures were verified against the specified public key
- Any certificates were verified against the Fulcio roots.
[{"critical":{"identity":{"docker-reference":"localhost:1338/demo/rekor-cli-e3df3bc7cfcbe584a2639931193267e9"},"image":{"docker-manifest-digest":"sha256:3a46c2e44bfe8ea0231af6ab2f7adebd0bab4a892929b307c0b48d6958863a4d"},"type":"cosign container image signature"},"optional":null}]
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:
OpenBSD: doas mysql_install_db && doas rcctl start mysqld && doas mysql_secure_installation
Follow the prompts from mysql_install_db, answering N to change the root password, and Y to everything else. Afterward, run the database creation script:
-- CODE language-bash --
cd $HOME/sigstore-local/src/rekor/scripts
sudo sh -x createdb.sh
2.2: Installing Trillian
Trillian provides a tamper-proof append-only log based on Merkle Trees using a gRPC API. Trillian stores its records in the MariaDB database we previously created. Install Trillian:
-- CODE language-bash --
go install github.com/google/trillian/cmd/trillian_log_server@latest
go install github.com/google/trillian/cmd/trillian_log_signer@latest
go install github.com/google/trillian/cmd/createtree@latest
Start the log_server, which provides the Trillian "personality" API, used by Rekor, and the Certificate Transparency frontend.
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:
-- CODE language-bash --
Verification for localhost:1338/demo/rekor-cli-e3df3bc7cfcbe584a2639931193267e9:latest --
The following checks were performed on each of these signatures:
- The cosign claims were validated
- The claims were present in the transparency log
- The signatures were integrated into the transparency log when the certificate was valid
- The signatures were verified against the specified public key
- Any certificates were verified against the Fulcio roots.
[{"critical":{"identity":{"docker-reference":"localhost:1338/demo/rekor-cli-e3df3bc7cfcbe584a2639931193267e9"},"image":{"docker-manifest-digest":"sha256:35b25714b56211d548b97a858a1485b254228fe9889607246e96ed03ed77017d"},"type":"cosign container image signature"},"optional":{"Bundle":{"SignedEntryTimestamp":"MEUCIG...yoIY=","Payload":{"body":"...","integratedTime":1643917737,"logIndex":1,"logID":"4d2e4...97291"}}}}]
🍦 Take an ice cream break if you got this far. You earned it!
Level III: Keyless signing with Fulcio
Fulcio is a sigstore component that issues code-signing certificates based on an authenticated OpenID Connect identity. These certificates are short-lived, lasting only 20 minutes!
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:
-- CODE language-bash --
cd $HOME/sigstore-local/src
git clone https://github.com/sigstore/fulcio.git
cd fulcio
go install .
3.2: Configure SoftHSM
SoftHSM implements a cryptographic store accessible through a PKCS #11 interface. You can use it to explore PKCS #11 without having a Hardware Security Module. For this demo, we will configure sigstore to reference tokens in $HOME/sigstore-local/tokens :
Create your first HSM token, setting both PINs to 2324.
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:
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.
-- CODE language-bash --
go install github.com/google/certificate-transparency-go/trillian/ctfe/ct_server@latest
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:
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):
-- CODE language-bash --
cd $HOME/sigstore-local/src
git clone https://github.com/dexidp/dex.git
cd dex
make build
cp bin/dex $HOME/go/bin
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:
-- CODE language-bash --
Verification for localhost:1338/demo/rekor-cli-e3df3bc7cfcbe584a2639931193267e9:latest --
The following checks were performed on each of these signatures:
- The cosign claims were validated
- The claims were present in the transparency log
- The signatures were integrated into the transparency log when the certificate was valid
- Any certificates were verified against the Fulcio roots.
[{"critical":{"identity":{"docker-reference":"localhost:1338/demo/rekor-cli-e3df3bc7cfcbe584a2639931193267e9"},"image":{"docker-manifest-digest":"sha256:dae1e7cdb03fc6f16e3a48111634f0c5fc28752229da2f96a6a4d03f60d6
d609"},"type":"cosign container image signature"},
"optional":{"Bundle":{"SignedEntryTimestamp":"MEUCI...Og4=","Payload":{"body":"...","integratedTime":1644977812,"logIndex":3,"logID":"9d0dd...a86d"}},
"Issuer":"http://localhost:5556","Subject":"blog@chainguard.dev"}}]
😊
Pat yourself on the back! You made it through the tutorial!
Where to next?
If you encounter any problems or would like to learn more about sigstore, see: