Embed CA Root Certificates in Go Programs

Abstract

Package rootcerts provides an embedded copy of the Mozilla Included CA Certificate List, more specifically the PEM of Root Certificates in Mozilla’s Root Store with the Websites (TLS/SSL) Trust Bit Enabled. If this package is imported anywhere in the program and the crypto/x509 package cannot find the system certificate pool, it will use the embedded certificates instead.

Background

In my professional work as well as in my private projects I often deploy Go programs as containers. Be it to run them in the cloud, in K8s or on your local machine. It is pretty common knowledge, how Go programs can be shipped in very light containers built from scratch (e.g. Create a small Docker image for a GoLang binary).

Whenever a Go program does something time and/or HTTPS related, a container build from scratch needed to include some additional files to work properly. One file is the IANA time zone database, the other is the CA root certificates, often included in a file called ca-certificates.pem (or similar).

With the release of Go 1.15, the first of these two problems have been resolved by the new time/tzdata package in the Go standard library. If this package is included in a Go program, the IANA time zone database is embedded and there is no longer a needed to supply this database as an additional file.

Context switch!

Since many years, I operate and use small NAS (network attached storage) systems (e.g. by QNAP) to store and backup my data. All these systems offer SSH access and I used this to run my own selection of tools (e.g. rclone , syncthing, tailscale) on them. All of these tools have in common, that they are built with Go and with this allow for easy cross compilation to the required CPU architecture (some of them run on ARM processors). Unfortunately, over time, some of these NAS systems no longer got operating system updates and with this also the CA root certificates aged and eventually expired. This made the usage of the before mentioned tools more cumbersome, because I need to supply the necessary certificates my self and I always need to pass the SSL_CERT_FILE environment variable when starting one of these tools.

Package github.com/breml/rootcerts

When Go 1.15 was released and I learned about the new package time/tzdata, I was very happy, because this package solved a pain for me. Right after, one of my first thoughts has been: “we should have a similar package for the CA root certificates”.

Fast forward some months, I finally took the time to dig into the internal working of the time/tzdata as well as the crypto/x509 package to figure out, how a similar package could be built, which allows to embed the CA root certificates in a Go program.

The result is a new Go package named github.com/breml/rootcerts.

For the above mentioned use case, where we embed the Go program in a Docker container FROM scratch, we can now replace the embedding of the CA root certificates in the container with embedding them in the Go program like this:

import (
    _ "github.com/breml/rootcerts"
)

The same code snipped also makes my life much easier for the tools I use on my NAS systems.

Words of Caution

The root certificates are the top-most certificates in the trust chain and used to ensure the trustworthiness of the certificates signed by them either directly (intermediate certificates) or indirectly (through intermediate certificates). As a user of the github.com/breml/rootcerts package, you have the obligation to double check the source as well as the integrity of the root certificates provided in this package. This is absolutely crucial and should not be taken lightly. All certificates that are validated by programs built upon this package, e.g. by using TLS for communication, rely on the trustworthiness of these root certificates.

I highly encourage you to read the respective sections in the README.md: