Description
Proposal Details
The x/crypto/x509roots
package was added in #43958 and #57792 (cc @rolandshoemaker @rsc @FiloSottile who were involved in prior discussion).
One feature was discussed in issue #43958 but did not make it into the package as currently released: accessing the x509 fallback root certificate bundle programmatically, i.e. not just using it for verification in the current process, but e.g. exporting it to a file for later usage by a different process on a different machine.
Motivation / use-case
In my case, the gokrazy packer (a Go program) creates a self-contained root file system image to be run (with the Linux kernel) on a Raspberry Pi (or similar), PC or (Cloud or on-prem) VM that only contains other Go programs. A gokrazy root file system contains no C runtime or similar — similar to FROM scratch
Docker containers.
The gokrazy packer can easily be run on Linux, where we just copy the system root file into the resulting image. But on macOS and Windows, we don’t have a system root file that we can copy. That’s where we currently use github.com/breml/rootcerts.
Ideally, we would programmatically create a roots file (at “gokrazy packer time”) that the Go runtime would then load (at “Raspberry Pi run time”).
Background: Why is the x/crypto/x509roots/nss
parser not sufficient?
One might wonder: Why is the x/crypto/x509roots/nss
parser not sufficient for this use-case?
I originally thought that using nss.Parse might actually make implementing breml/rootcerts easier, but it turns out that breml/rootcerts already uses an approach that does not require nss.Parse: https://github.com/breml/rootcerts/blob/7000414306b0b352acb0de167dc22ebe5a584085/generate_data.go#L34
I considered doing the http.Get in the gokrazy packer, but then my program requires internet access (undesirable, especially when running in isolated CI/CD environments) and can fail when the source is slow or unavailable. So, I’ll need a cached copy and then have to deal with keeping it up-to-date.
Instead of dealing with cache management on my user’s disk, it might be better to obtain the root certs at go:generate time and embed them into my application. But then I’m effectively doing myself the work that breml/rootcerts is currently doing for me, and have not gained anything.
I think the key observation is: obtaining the root certs is not the tricky part, but updating/distributing the root certs is an annoying problem to solve. If I could just access the fallback store that the x/crypto module already contains, GitHub’s dependabot would from time to time submit a PR to update the x/crypto dependency, and that would be the easiest solution in terms of how much infrastructure I would need to maintain.
Updated Proposal (based on discussion)
Move the embedded fallback root bundle into the new x509roots/fallback/bundle
package, which exposes those roots with the following API:
type Root struct {
Certificate []byte // DER-encoded certificate
Constraint func([]*x509.Certificate) error // nil if root is unconstrained
}
// Roots returns the parsed bundle of roots from the NSS trust store. Constrained roots will have a non-nil
// Root.Constraint function. All other roots are unconstrained.
func Roots() []Root { … }
The existing x509roots/fallback
package then uses the x509roots/fallback/bundle
package like so (this is the entire updated fallback.go
):
package fallback
import (
"crypto/x509"
"golang.org/x/crypto/x509roots/fallback/bundle"
)
func init() {
x509.SetFallbackRoots(newFallbackCertPool())
}
func newFallbackCertPool() *x509.CertPool {
p := x509.NewCertPool()
for _, c := range bundle.Roots() {
cert, err := x509.ParseCertificate(c.Certificate)
if err != nil {
panic(err)
}
if c.Constraint == nil {
p.AddCert(cert)
} else {
p.AddCertWithConstraint(cert, c.Constraint)
}
}
return p
}
User code (outside x/crypto
) can create a PEM-encoded ca-certificates.crt
file like so:
func fallbackBundle() string {
var certs []byte
for _, c := range bundle.Roots() {
certs = append(certs, pem.EncodeToMemory(&pem.Block{
Type: "CERTIFICATE",
Bytes: c.Certificate,
})...)
}
return string(certs)
}
Metadata
Metadata
Assignees
Type
Projects
Status