Repository: go
Issue: proposal: x/crypto/x509roots/fallback: export certificate bundle
Labels: Proposal,Proposal-Crypto

### Proposal Details

The `x/crypto/x509roots` package was added in https://github.com/golang/go/issues/43958 and https://github.com/golang/go/issues/57792 (cc @rolandshoemaker @rsc @FiloSottile who were involved in prior discussion).

One feature was discussed in issue https://github.com/golang/go/issues/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](https://gokrazy.org/) 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](https://github.com/gokrazy/tools/blob/a889f31ae4650cca1db08476a6c879e95beef949/cmd/gokr-packer/cacerts.go#L16) 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`](https://pkg.go.dev/golang.org/x/crypto/x509roots/nss) parser not sufficient?

One might wonder: Why is the [`x/crypto/x509roots/nss`](https://pkg.go.dev/golang.org/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.

## Proposal

Add the following code to x509roots/fallback/fallback.go:

```go
// Bundle returns the fallback X.509 trusted roots as a certificate bundle.
//
// This function is primarily useful for programs that build environments in
// which Go programs should have access to the fallback roots, such as Docker
// containers.
func Bundle() []*x509.Certificate {
	return bundle
}
```

https://go.dev/cl/506840 is an implementation of this proposal.

## Open Questions

One open question that came up during the review of https://go.dev/cl/506840:

> > I'm not sure "Bundle() []*x509.Certificate" is the right API for this, for example because of https://go.dev/issue/57178: in the future the bundle in fallback might include constrained roots.
> 
> We could rename to UnconstrainedRoots(). Are constrained roots going to be vital to have a functioning certificate store?
> 
> (The whole subject of constrained roots is something I’ll need to consider when working with the generator, too, which makes it less appealing to integrate at the generator level.)