-
Notifications
You must be signed in to change notification settings - Fork 17.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
crypto/x509: add SetFallbackRoots and golang.org/x/crypto/x509roots/fallback package #43958
Comments
At least from my personal experience, I've used This is all to say - if tzdata was included, I think rootcerts should too, given how I think it's at least just as important. |
examples of other third party packges that provide this: I'm not particularly convinced that having it in std is a good idea, since out of date tzdata is usually just a minor bug but out of date ca certs is a security issue. Maybe somewhere under |
I agree with what the proposal says, though. The current solutions people are using are also potentially vulnerable to not getting new CA roots for a long time. This includes cases where the binary runs on a system that's simply not being updated regularly. At the end of the day, it's up to the developer to keep up to date with Go versions and rebuild/redeploy their binaries as needed. If that maintenance is not happening, the result is the same whether or not this proposal is accepted and used by them. |
One small difference between https://github.com/breml/rootcerts and the other 3rd party packages mentioned above is, that it can be used in a "none intrusive" way, because no application code needs to be changed. A simple blank import ( This is implemented the same way it is done in |
I think there are two interesting parts to this proposal: implementing an API to set the default root pool, and adding a standard library package that contains some set of roots. While I am generally in favor of the first part, I am somewhat opposed to the second. I think adding an API to Speaking as a member of the security team, who would likely end up being responsible for maintaining it, I'm hesitant to implement a root store in the standard library. Even if we are just copying some existing root program, there are a number of somewhat complicated policy enforcement decisions we'd become embroiled in. I'm also not sure what the update process for the store would be, for instance if a root was to become distrusted in the NSS program, would we need to issue a security release of Go in order to provide an updated root store to users? I think if we were to do something like this it'd most likely need to live in a |
@rolandshoemaker Thanks for your thoughtful feedback. The main intend of my proposal is a standard library package that contains some set of roots. What I care about is, a simple, save and trusted way to embed a trusted set of root certificates into a Go binary. Go positions it self as the language of choice for the cloud and cloud native applications. To cite @spf13 (Steve Francia in https://www.infoq.com/articles/go-language-13-years/):
Lots of these cloud native applications will run in container environments and lots of them will need to talk to external APIs, which then will need root certificates to establish the secure connection. There I see a clear benefit in the Go standard library providing a simple way of including the CA root certificates into the Go binary. It is maybe a little bit of a stretch, but if a Go application is run in a container I clearly understand your concern for you as a member of the security team and I completely agree, that is likely your team, who will end up being responsible for the maintenance. But if we look at this from users point of view, then we are definitively in a better place, when the Go security team takes care of this, than if a random open source contributor publishes a package with this functionality. If this proposal gets accepted, I will happily remove/deprecate my package. For the update process as well as the location of such a package, it is my belief, that the package should life in the standard library (and not in
In regards to the NSS trust, I did not go into full details here, but it is my observation, that this problem has relaxed over the last couple years. On one hand, all the mentioned Linux distributions have solved this problem one way or the other, so this is a pretty well understood problem with battle proved solutions available. On the other hand does Mozilla provide the PEM of Root Certificates in Mozilla's Root Store with the Websites (TLS/SSL) Trust Bit Enabled (see: CA/Included Certificates). In my understanding, this is already the correct list of certificates with the correct trust bits enabled. The other part you have identified in my proposal (API to set the default root pool) is less relevant for the use cases I am outlining in the proposal, for the following reasons:
|
How often do root sets change? |
Chrome's new initiative to maintain its own root store may be of interest https://www.chromium.org/Home/chromium-security/root-ca-policy/ |
curl maintains a bundle of the Mozilla root certs: https://curl.se/docs/caextract.html It may not be comprehensive but there were 5 updates in 2021 and 5 so far in 2022.
The big ones are Mozilla, Google, Microsoft and Apple. There may be additional ones.
The Mozilla one is the best candidate here. Their policy is documented here and they have an open process for inclusion. Most open source software and Linux distributions use the Mozilla store. |
My "proof of concept" (https://github.com/breml/rootcerts), mentioned above, does use the root certificates from Mozilla, similar to the way it is done for curl. This root set does contain 146 certificates as of now. I quickly analyzed the list from Included CA Certificates (CSV) from https://wiki.mozilla.org/CA/Included_Certificates (with the email only certificates filtered out). The average validity of these certificates is 23.5 years (with a minimum of 9 years and a maximum of 35 years). So the number of certificates that are needed to be updated will average in the range given by @joeshaw (~6 year). In contrast to tzdata updates, this seams to be in a similar range (see: http://mm.icann.org/pipermail/tz-announce/): 2022 ytd: 1 |
This proposal has been added to the active column of the proposals project |
/cc @golang/security |
One reason this would be interesting to resolve is that currently "single-Go-binary containers" cannot make HTTPS connections. You have to side-load the root set or derive the container from a full Linux distribution. If we could optionally put the root set into the binary, that would make single-binary containers useful for more programs. |
I am conflicted about this. I agree it makes single-binary deployments more convenient, and it feels like a natural feature to provide. On the other hand
This might make more sense as an auto-updated x/crypto package, which then would encourage adding |
From my narrowed github user point of view, having it as a module dependency together with dependabot is nice, so I'm alerted when it gets outdated. If we want it as part of x/crypto it would make tooling such as dependabot not alert us for outdated CAs. |
I agree with all of the points @FiloSottile makes (unsurprisingly.) This is really a platform problem, and in an ideal world platforms would solve it for us. That said this has been a problem for a while, and as far as I can tell, there has been no real movement towards a real platform fix for containers and such. It is probably better that we provide at least some centralized solution for now, in order to dissuade people from doing the less ideal runtime hacks that are described further up this thread. I think if we are to go down this path it should absolutely be a separate Where the list comes from is a complicated question, Mozilla is almost definitely the easiest solution, and has a pretty good history of not being full of terrible stuff. There are some not-chrome-but-adjacent Google folks working on related stuff who it might be worth talking to (I'll need to trawl my inbox to try to remember who exactly to ping.) I agree that the prospect of adding a |
If I understand this correctly, this is related to the fact, that there are no tagged releases for
|
It sounds like we may want to do something to nudge people's behavior here toward things we'd prefer. It also sounds like we could add a very tiny hook in crypto/tls (Roland said SetDefaultRoots) which can be called just once (twice is a panic), and then the actual cert sets can be provided out of standard library by x/crypto, which is easier to update. Do I have that general sketch right? Separately, Filippo said that verifiers are starting to add different kinds of new logic. So is the "pluggable root set" the right approach or is it "pluggable ____" where we still need to fill in the blank? Maybe a verifier function or interface makes more sense than a raw CertPool? |
/cc @rolandshoemaker |
Yeah, that's a good call, maybe this should be Only
I guess if this replaces the default verifier, then it becomes the behavior attached to the SystemCertPool, and SystemCertPool + additional roots becomes "run this and then run a separate verification with the additional roots. I'm ok with it even if SystemCertPool becomes a bit of a misnomer. |
I'll pushback a little on this idea though. Most of the additional logic that Mozilla adds on top of their list is restrictions on various borderline roots, where the CA has done something bad but because of wide legacy deployment, they feel the need to maintain trust for it in very specific circumstances. Is this something we need to support? Possibly, but we could take on significantly less complexity by simply trimming these roots out and providing binary trust decisions. I'm not super strongly opinionated, I tend to lean towards less complexity, but I agree that providing a way of modifying the default system verifier would definitely be extremely powerful, allowing some interesting behaviors down the road. |
My understanding so far is, that in my initial proposal, one may enable the default roots by simply adding package main
import (
_ "crypto/x509/rootcerts"
"log"
"net/http"
)
func main() {
res, err := http.Get("https://www.google.com/robots.txt")
if err != nil {
log.Fatal(err)
}
log.Infof("%d", res.StatusCode)
} With package main
import (
"crypto/x509"
"log"
"net/http"
)
func init() {
x509.SetDefaultRoots()
}
func main() {
res, err := http.Get("https://www.google.com/robots.txt")
if err != nil {
log.Fatal(err)
}
log.Infof("%d", res.StatusCode)
} I am a little bit lost on the Additionally, I see the following issue: If we have a very thin hook in |
I don't want to get in the way of this getting accepted before the freeze, and this is a minor note, but after looking at the implementation I would rather not have the force fallback GODEBUG. On macOS and Windows, the fallback would normally not be reachable, because the OS verifier is always there. What does x509usefallbackroots do on those platforms? Does it make the fallback available anyway? What happens if x509usefallbackroots is set but SetFallbackRoots was not called? An error? A panic? Is it ignored?
|
In my opinion, the
I am fine with the
Again, I don't think the fallback certificates should be limited to *nix OS. |
No change in consensus, so accepted. 🎉 |
I've been mostly sitting on the fence about the GODEBUG behavior, but I think we need to make a final decision and I'm going to propose the following (which is, essentially, what is implemented in my CL):
This gives us (mostly) uniform behavior across platforms, and is the most obvious behavior. Given this is a GODEBUG flag, even though I'm sure people will end up relying on the semantics of this behavior in strange unexpected ways, we'll have a little bit more leeway to change it in the future in case we realize down the road this is wrong. |
I was gonna ask if OS certs would be used if present. That answers it- yes. |
I'm not 100% sure I follow all the discussions, but one thing in particular we (at CircleCI) would like out of this (besides the FROM scratch Docker scenario) is to force the Go based certificate store, even when os-provided certificates are present. E.g. someone runs our software in an old Docker image with an out of date set of rootcerts in. |
Adds a method which allows users to set a fallback certificate pool for usage during verification if the system certificate pool is empty. Updates #43958 Change-Id: I279dd2f753743bce19790f2ae29f063c89c9359d Reviewed-on: https://go-review.googlesource.com/c/go/+/449235 Run-TryBot: Roland Shoemaker <roland@golang.org> TryBot-Result: Gopher Robot <gobot@golang.org> Auto-Submit: Roland Shoemaker <roland@golang.org> Reviewed-by: Damien Neil <dneil@google.com> Reviewed-by: Filippo Valsorda <filippo@golang.org>
Hi @pete-woods
#43958 (comment) summarizes how this will work. So I guess, your use case is (only) partly solved. Whenever a user needs to force the Go embedded certificates (e.g. because the os-provided roots are out of date), he will need to pass the |
@pete-woods Thinking of it a little bit more, there might be a way of setting the environment variable from code in an import, that is loaded before |
The crypto/x509 API has landed and should be in Go 1.20. For the purposes of release notes we have to say what the supporting package will be. Roland and I talked and agreed on |
I'm struggelling using the fallback package (go1.20):
|
I don't think this package has actually been released yet (despite being referred to in the 1.20 release notes) |
@rolandshoemaker With |
Yes, thanks for the reminder! 🎉 |
Proposal
Go programs currently rely on the operating system to provide CA root certificate information in some sort of certificate system store. There are situations, where no such up-to-date CA root certificates are available like (Docker) containers built
FROM scratch
or out of date environments like poorly maintained or no longer updatable systems (e.g. older hardware appliances).For some environments it is possible for the user of a Go program to add additional CA root certificates via
SSL_CERT_FILE
orSSL_CERT_DIR
, but this is not the case for all supported environments (e.g. Windows) and it is definitively not user friendly.Therefore, it is desirable for Go programs to have some mechanism to directly embed CA root certificate information into the program itself, so that they don't have to rely on system store to provide CA root certificates that may be absent (or out of date).
I make the following assumptions, which I think are reasonable:
GO_ROOTCERTS_ENABLE
)Given those assumptions, I propose adding a new package
crypto/x509/rootcerts
. Importing this package (asimport _ "crypto/x509/rootcerts"
) will cause CA root certificates to be embedded in the program. Also, building with the build tagrootcerts
will force the package to be imported, and therefore will cause CA root certificates to be embedded in the program. The embedded CA root certificates will be used if and when no certificate system store is available (or the user forces the usage of the embedded data).Source for the CA Root Certificates
I propose to use 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 as the source for the CA root certificates.
The Mozilla Included CA Certificate List is the source for the CA root certificates embeded in the well known products of the Mozilla Foundation like for example Firefox (web browser) or Thunderbird (email client).
In contrast to most of the other software vendors, Mozilla maintains its Included CA Certificate List publicly and distributes it under an open source license (Mozilla Public License Version 2). This is also the reason why most of the Linux distributions, as well as
other free unix derivates and wide spread tools, use this list of CA root certificates as part of their distribution.
Some examples:
In summary in my opinion it is safe to say that the Mozilla Included CA Certificate List is well established and widely used.
In fact, if a Go program is run on Linux or an other free Unix derivate, chances are high that the root certificates used by the program are already provided by the Mozilla Included CA Certificate List.
Why include into the Go Standard Library
As the sample implementation (link in Annex below) clearly demostrates, that it is possible to write a 3rd party Go package, which achieves the same goal as the proposed package
crypto/x509/rootcerts
would. The main difference between a package in the standard library and a 3rd party package is: TRUST.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). Therefore for a package containing and replacing the root certificates, trust is essential.
The same way, most users of Linux trust the CA root certificates provided by their distribution, it is very likely, that user would trust the CA root certificates provided by a package included in the Go standard library.
Additionally, the possibility to include the CA root certificates during build time, without altering the source code, is not possible with a 3rd party package but only if this package is included into the Go standard library and the build tag is implemented in to Go tool chain.
Update of the CA Root Certificates
The CA Root Certificates included in the standard library are updated with every release of Go (with the current schedule every 6 months). This would work the same way as it currently does for the package
time/tzdata
. The update frequency of the Included CA Certificate List is roughly every few months (2020: 5 times, 2019: 4 times, according to curl ca extract), which seems to be similar to the update frequency of the time zone data information.In regards to updating the CA root certificates compiled into a Go binary, the same limitations apply as for the
time/tzdata
package. The information compiled into a binary is not updated. That being said, for the situations, this package is intended for, it is still an improvement because containers builtFROM scratch
are also not updated by default and out of date / not updatable systems obviously also do not get updates for the CA root certificates.Annex
There is a sample implementation of this approach at github.com/breml/rootcerts with some additional reasoning about when to use such a package and what to keep in mind.
This proposal as well as the sample implementation are highly influenced by the proposal #38017 - time/tzdata and the implementation of the package
time/tzdata
by @ianlancetaylorcc: @FiloSottile, @katiehockman, @mvdan
The text was updated successfully, but these errors were encountered: