Suppose I'm using a third party network library that I don't want to modify. It uses the standard http.Request
interface to make some HTTPS requests.
I'm running it on an embedded Linux instance that doesn't have a certificate roots installed, and the only directory I can access is /data
. This means you get the error:
Get https://example.com/: x509: failed to load system roots and no roots provided
Is there any way to actually provide roots? As far as I can tell Go looks in these directories for X509 certificate roots (see also):
var certFiles = []string{
"/etc/ssl/certs/ca-certificates.crt", // Debian/Ubuntu/Gentoo etc.
"/etc/pki/tls/certs/ca-bundle.crt", // Fedora/RHEL
"/etc/ssl/ca-bundle.pem", // OpenSUSE
"/etc/pki/tls/cacert.pem", // OpenELEC
}
var certDirectories = []string{
"/system/etc/security/cacerts", // Android
}
As I said, I don't have access to those, and the root pool seems to be private so you can't append to it:
var (
once sync.Once
systemRoots *CertPool
)
func systemRootsPool() *CertPool {
once.Do(initSystemRoots)
return systemRoots
}
Is this just impossible with Go?
Something like this seems to work (I actually only need one certificate chain, which you can get from Firefox easily by clicking on the SSL lock icon, More Information, View Certificate, Details, Export, Change type to "X509 certificate with chain (PEM)".
func initCerts() error {
certs := x509.NewCertPool()
pemData, err := ioutil.ReadFile("api.example.crt")
if err != nil {
return err
}
certs.AppendCertsFromPEM(pemData)
newTlsConfig := &tls.Config{}
newTlsConfig.RootCAs = certs
defaultTransport := http.DefaultTransport.(*http.Transport)
defaultTransport.TLSClientConfig = newTlsConfig
return nil
}
I'm not certain, but the docs suggest that you must do that before using any TLS functions, so I put it at the start of my main()
.