diff options
author | Vladimir Shushlin <vshushlin@gitlab.com> | 2020-07-20 11:32:43 +0300 |
---|---|---|
committer | Vladimir Shushlin <vshushlin@gitlab.com> | 2020-07-20 11:32:43 +0300 |
commit | 7e2600ea4ad6a8ef2d19c07eadb8c7aa7b8c65e2 (patch) | |
tree | 7d18810a56ee6e0fd3a68c3748b5adb2a11830f5 | |
parent | dc7bf0c3a847320b9370decc8792aff3712dd3e2 (diff) | |
parent | 8d65281f548d6a25e764e1ca47a9821a3d8152a7 (diff) |
Merge branch '415-424-load-ssl-cert-dir' into 'master'
Load certs from SSL_CERT_DIR
Closes #415
See merge request gitlab-org/gitlab-pages!302
-rw-r--r-- | internal/httptransport/LICENSE | 27 | ||||
-rw-r--r-- | internal/httptransport/transport.go | 30 | ||||
-rw-r--r-- | internal/httptransport/transport_darwin.go | 118 |
3 files changed, 154 insertions, 21 deletions
diff --git a/internal/httptransport/LICENSE b/internal/httptransport/LICENSE new file mode 100644 index 00000000..6a66aea5 --- /dev/null +++ b/internal/httptransport/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2009 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/internal/httptransport/transport.go b/internal/httptransport/transport.go index d6ca6de2..e6094ed7 100644 --- a/internal/httptransport/transport.go +++ b/internal/httptransport/transport.go @@ -3,10 +3,8 @@ package httptransport import ( "crypto/tls" "crypto/x509" - "io/ioutil" "net" "net/http" - "os" "strconv" "sync" "time" @@ -19,6 +17,8 @@ var ( sysPoolOnce = &sync.Once{} sysPool *x509.CertPool + // only overridden by transport_darwin.go + loadExtraCerts = func() {} // InternalTransport can be used with http.Client with TLS and certificates InternalTransport = newInternalTransport() ) @@ -52,10 +52,10 @@ func NewTransportWithMetrics(gaugeVec *prometheus.GaugeVec, counterVec *promethe } } -// This is here because macOS does not support the SSL_CERT_FILE -// environment variable. We have arrange things to read SSL_CERT_FILE as -// late as possible to avoid conflicts with file descriptor passing at -// startup. +// This is here because macOS does not support the SSL_CERT_FILE and +// SSL_CERT_DIR environment variables. We have arranged things to read +// SSL_CERT_FILE and SSL_CERT_DIR as late as possible to avoid conflicts +// with file descriptor passing at startup. func pool() *x509.CertPool { sysPoolOnce.Do(loadPool) return sysPool @@ -71,21 +71,9 @@ func loadPool() { return } - // Try to load from SSL_CERT_FILE - // TODO: Handle SSL_CERT_DIR? - // See https://gitlab.com/gitlab-org/gitlab-pages/-/issues/415 - sslCertFile := os.Getenv("SSL_CERT_FILE") - if sslCertFile == "" { - return - } - - certPem, err := ioutil.ReadFile(sslCertFile) - if err != nil { - log.WithError(err).Error("failed to read SSL_CERT_FILE") - return - } - - sysPool.AppendCertsFromPEM(certPem) + // Go does not load SSL_CERT_FILE and SSL_CERT_DIR on darwin systems so we need to + // load them manually in OSX. See https://golang.org/src/crypto/x509/root_unix.go + loadExtraCerts() } // withRoundTripper takes an original RoundTripper, reports metrics based on the diff --git a/internal/httptransport/transport_darwin.go b/internal/httptransport/transport_darwin.go new file mode 100644 index 00000000..b73009da --- /dev/null +++ b/internal/httptransport/transport_darwin.go @@ -0,0 +1,118 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +// Modified copy of https://golang.org/src/crypto/x509/root_unix.go + +package httptransport + +import ( + "io/ioutil" + "os" + "path/filepath" + "strings" + + log "github.com/sirupsen/logrus" +) + +const ( + // certFileEnv is the environment variable which identifies where to locate + // the SSL certificate file. If set this overrides the system default. + certFileEnv = "SSL_CERT_FILE" + + // certDirEnv is the environment variable which identifies which directory + // to check for SSL certificate files. If set this overrides the system default. + // It is a colon separated list of directories. + // See https://www.openssl.org/docs/man1.0.2/man1/c_rehash.html. + certDirEnv = "SSL_CERT_DIR" +) + +func init() { + // override and load SSL_CERT_FILE and SSL_CERT_DIR in OSX. + loadExtraCerts = func() { + if err := loadCertFile(); err != nil { + log.WithError(err).Error("failed to read SSL_CERT_FILE") + } + + if err := loadCertDir(); err != nil { + log.WithError(err).Error("failed to load SSL_CERT_DIR") + } + } +} + +func loadCertFile() error { + sslCertFile := os.Getenv(certFileEnv) + if sslCertFile == "" { + return nil + } + + data, err := ioutil.ReadFile(sslCertFile) + if err != nil && !os.IsNotExist(err) { + return err + } + + sysPool.AppendCertsFromPEM(data) + + return nil +} + +func loadCertDir() error { + var firstErr error + var dirs []string + if d := os.Getenv(certDirEnv); d != "" { + // OpenSSL and BoringSSL both use ":" as the SSL_CERT_DIR separator. + // See: + // * https://golang.org/issue/35325 + // * https://www.openssl.org/docs/man1.0.2/man1/c_rehash.html + dirs = strings.Split(d, ":") + } + + for _, directory := range dirs { + fis, err := readUniqueDirectoryEntries(directory) + if err != nil { + if firstErr == nil && !os.IsNotExist(err) { + firstErr = err + } + continue + } + + rootsAdded := false + for _, fi := range fis { + data, err := ioutil.ReadFile(directory + "/" + fi.Name()) + if err == nil && sysPool.AppendCertsFromPEM(data) { + rootsAdded = true + } + } + + if rootsAdded { + return nil + } + } + + return firstErr +} + +// readUniqueDirectoryEntries is like ioutil.ReadDir but omits +// symlinks that point within the directory. +func readUniqueDirectoryEntries(dir string) ([]os.FileInfo, error) { + fis, err := ioutil.ReadDir(dir) + if err != nil { + return nil, err + } + uniq := fis[:0] + for _, fi := range fis { + if !isSameDirSymlink(fi, dir) { + uniq = append(uniq, fi) + } + } + return uniq, nil +} + +// isSameDirSymlink reports whether fi in dir is a symlink with a +// target not containing a slash. +func isSameDirSymlink(fi os.FileInfo, dir string) bool { + if fi.Mode()&os.ModeSymlink == 0 { + return false + } + target, err := os.Readlink(filepath.Join(dir, fi.Name())) + return err == nil && !strings.Contains(target, "/") +} |