From 6b5824129516a4898ff342dac338bb203a8e0d4b Mon Sep 17 00:00:00 2001 From: Ludovic Fernandez Date: Tue, 18 Jan 2022 23:22:04 +0100 Subject: [PATCH] feat: extended support of cert pool (#1565) - `LEGO_CA_CERTIFICATES` now accepts multiple file paths to be added by using `os.PathListSeparator` (`:` on POSIX, `;` on Windows) as a separator. - `LEGO_CA_SYSTEM_CERT_POOL` initiates the cert pool with a copy of the system cert pool. --- lego/client_config.go | 52 +++++++++++++++++++++++++++++++++---------- 1 file changed, 40 insertions(+), 12 deletions(-) diff --git a/lego/client_config.go b/lego/client_config.go index ed8c4a57..e75739c1 100644 --- a/lego/client_config.go +++ b/lego/client_config.go @@ -7,6 +7,8 @@ import ( "net" "net/http" "os" + "strconv" + "strings" "time" "github.com/go-acme/lego/v4/certcrypto" @@ -16,10 +18,15 @@ import ( const ( // caCertificatesEnvVar is the environment variable name that can be used to // specify the path to PEM encoded CA Certificates that can be used to - // authenticate an ACME server with a HTTPS certificate not issued by a CA in + // authenticate an ACME server with an HTTPS certificate not issued by a CA in // the system-wide trusted root list. + // Multiple file paths can be added by using os.PathListSeparator as a separator. caCertificatesEnvVar = "LEGO_CA_CERTIFICATES" + // caSystemCertPool is the environment variable name that can be used to define + // if the certificates pool must use a copy of the system cert pool. + caSystemCertPool = "LEGO_CA_SYSTEM_CERT_POOL" + // caServerNameEnvVar is the environment variable name that can be used to // specify the CA server name that can be used to // authenticate an ACME server with a HTTPS certificate not issued by a CA in @@ -81,23 +88,44 @@ func createDefaultHTTPClient() *http.Client { } // initCertPool creates a *x509.CertPool populated with the PEM certificates -// found in the filepath specified in the caCertificatesEnvVar OS environment -// variable. If the caCertificatesEnvVar is not set then initCertPool will -// return nil. If there is an error creating a *x509.CertPool from the provided -// caCertificatesEnvVar value then initCertPool will panic. +// found in the filepath specified in the caCertificatesEnvVar OS environment variable. +// If the caCertificatesEnvVar is not set then initCertPool will return nil. +// If there is an error creating a *x509.CertPool from the provided caCertificatesEnvVar value then initCertPool will panic. +// If the caSystemCertPool is set to a "truthy value" (`1`, `t`, `T`, `TRUE`, `true`, `True`) then a copy of system cert pool will be used. +// caSystemCertPool requires caCertificatesEnvVar to be set. func initCertPool() *x509.CertPool { - if customCACertsPath := os.Getenv(caCertificatesEnvVar); customCACertsPath != "" { - customCAs, err := os.ReadFile(customCACertsPath) + customCACertsPath := os.Getenv(caCertificatesEnvVar) + if customCACertsPath == "" { + return nil + } + + certPool := getCertPool() + + for _, customPath := range strings.Split(customCACertsPath, string(os.PathListSeparator)) { + customCAs, err := os.ReadFile(customPath) if err != nil { panic(fmt.Sprintf("error reading %s=%q: %v", - caCertificatesEnvVar, customCACertsPath, err)) + caCertificatesEnvVar, customPath, err)) } - certPool := x509.NewCertPool() + if ok := certPool.AppendCertsFromPEM(customCAs); !ok { panic(fmt.Sprintf("error creating x509 cert pool from %s=%q: %v", - caCertificatesEnvVar, customCACertsPath, err)) + caCertificatesEnvVar, customPath, err)) } - return certPool } - return nil + + return certPool +} + +func getCertPool() *x509.CertPool { + useSystemCertPool, _ := strconv.ParseBool(os.Getenv(caSystemCertPool)) + if !useSystemCertPool { + return x509.NewCertPool() + } + + pool, err := x509.SystemCertPool() + if err == nil { + return pool + } + return x509.NewCertPool() }