Code signing with HashiCorp Vault and GitHub Actions
Leverage HashiCorp Vault as a trusted certificate authority (CA) to issue short-lived code signing certificates to a GitHub Actions workflow.
Code signing is an essential element of software supply chain security, enabling users of your code to verify that the code they are running is actually the code you released. This helps defend against supply chain attacks such as dependency confusion attacks by supporting end-users’ ability to verify code authorship and enforce code-integrity controls.
At a high level, code signing workflows depend on four components:
- An artifact that must be signed before being distributed. Artifacts include things like container images, Windows executables and libraries, and Java .jar files.
- A code signing scheme that establishes how the artifact is signed and how signatures are verified. Examples of code signing schemes include:
- Microsoft Authenticode for Windows executables and libraries
- Docker Content Trust for container images
- Cosign for generic OCI artifacts (including container images and more).
- A code signing key and certificate that establish the identity of the person or system signing the artifact.
- A trusted certificate authority (CA) that verifies the identity of a person or system and issues a corresponding code-signing certificate attesting that identity. The end user is ultimately responsible for deciding which CAs should be trusted. Software vendors (Microsoft, Apple, Google, Mozilla) provide default lists of trusted CAs, but enterprises typically tweak those lists to add and remove CAs as needed.
This blog post focuses on leveraging HashiCorp Vault as a trusted CA to issue short-lived code signing certificates to a GitHub Actions workflow that signs a PowerShell script using Microsoft Authenticode.
» Workflow and PKI architecture
The diagram below illustrates the workflow of this solution:
This workflow uses a two-tier public key infrastructure (PKI). We’ll use OpenSSL to operate the root CA and Vault to operate the code signing issuing CA.
You can see an entire sample code repository of this post’s solution in the code signing with Vault GitHub repository.
» Generating the root CA
Once you have a copy of the sample code repository linked above, navigate to the root-ca
directory, then review and execute the generate
script (./generate
).
This script will generate an Elliptic Curve P-521 key pair and issue a self-signed root certificate. You’ll find the corresponding certificate in root.crt
, and the EC parameters and private key will be in root.key
.
» Configuring Vault
The sample code in the repository also includes a HashiCorp Terraform module that provisions the required resources in Vault. Once you have a Vault cluster up and running, take the following three steps (Note: the HashiCorp Cloud Platform, also called HCP, makes this step much easier):
- Review the Terraform code in the
terraform
directory. - Create a Terraform variables file and populate it with values appropriate to your environment. Important: Set
pki_codesign_cert
tonull
for the time being (more on that later). - Run
terraform apply
, review the plan, and deploy the changes to your Vault cluster.
At this point, you’ll have these items in your Vault cluster:
- PKI secrets engine with
- The code signing CA’s EC key pair and corresponding certificate signing request (CSR)
- The PKI role for controlling the issuance of code signing certificates
- JWT authentication backend for authenticating GitHub Actions pipelines into Vault
- Sample ACL policy to authorize access to the code signing certificate PKI role
» Issuing Vault’s CA certificate
Since the root CA is offline under OpenSSL, the next step is to have the root CA issue a certificate for Vault’s code signing CA.
Retrieve the Vault CA’s certificate signing request from the Terraform outputs and paste it into a file under the root-ca
directory. Name that file codesigning-ca.csr
. You can inspect the contents of the CSR with OpenSSL:
openssl req -in codesign-ca.csr -noout -text
If you’re satisfied with the parameters of the CSR, run the sign
script (./sign
) to have the root CA issue the certificate for Vault's CA. The certificate will be stored in codesign-ca.crt
.
Next, import Vault’s CA certificate by modifying your Terraform variables file to include both the Vault CA certificate and the root CA certificate:
pki_codesign_cert = <<EOF
-----BEGIN CERTIFICATE-----
Vault CA cert here
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
Root CA cert here
-----END CERTIFICATE-----
EOF
» Running a GitHub pipeline
The repository for this post includes a sample GitHub workflow you can use to test this code signing pipeline concept.
Note that this is not a production-ready workflow. At the very least, you don’t want to download the Vault CLI every time without further security checks.
» Verifying a signed script
Once the hello-world.ps1
script is signed by your pipeline, you can check its Authenticode signature by running:
Get-AuthenticodeSignature -FilePath .\hello-world.ps1 | Format-List
Be sure that your computer trusts the root CA used for this exercise or signature verification will fail. If you’re not using a discardable environment, remember to remove the root CA certificate from the trust store when you’re done with signature verification.
» Why not use a publicly trusted CA?
Code signing traditionally relies on certificates issued by CAs, such as DigiCert and GlobalSign, that are trusted by the major software vendors. These CAs must verify the identity of the individual or organization requesting the certificate, a process that is often manual, lengthy, and expensive.
If you are distributing software broadly, it makes sense to go through that process: you’ll be issued a certificate that is automatically trusted by all your users by virtue of the CA being part of the major vendors’ trusted CA programs.
But what if you’re looking to secure internal distribution of software, e.g. ensuring that a line-of-business application was built by an approved system and not tampered with since? In that case, ask yourself the following questions:
- Do you buy a certificate for your organization and allow all your developers to use it? What’s your process for authenticating and authorizing individual signing requests?
- Do you buy individual developer certificates and incur the expense and overhead of managing the certificates’ lifecycles?
- If your CA requires you to keep private keys in a hardware token, how do you integrate the signature process into automated build pipelines?
The solution outlined in this post gives your business important benefits when internally distributing software:
- Minimize the risk of compromised signing keys by adopting short-lived certificates — the key is useless an hour after it’s generated.
- Reduce toil by automating the issuance of certificates in accordance with rules established in advance by your security team.
- Future-proof your code signing workflow by leveraging Vault’s many sources of identity. If tomorrow you want users to be able to manually sign code, they can authenticate to Vault with OIDC, SAML, or Kerberos and obtain certificates in the same way.
» Next steps
Learn more about how Vault helps organizations run secure and efficient PKI from this HashiConf talk on automating PKI with Vault, and find out how to build your own certificate authority with Vault on HashiCorp Developer.
Sign up for the latest HashiCorp news
More blog posts like this one
HCP Vault Dedicated adds secrets sync, cross-region DR, EST PKI, and more
The newest HCP Vault Dedicated 1.18 upgrade includes a range of new features that include expanding DR region coverage, syncing secrets across providers, and adding PKI EST among other key features.
Fix the developers vs. security conflict by shifting further left
Resolve the friction between dev and security teams with platform-led workflows that make cloud security seamless and scalable.
HashiCorp at AWS re:Invent: Your blueprint to cloud success
If you’re attending AWS re:Invent in Las Vegas, Dec. 2 - Dec. 6th, visit us for breakout sessions, expert talks, and product demos to learn how to take a unified approach to Infrastructure and Security Lifecycle Management.