internal/app/http_client.go (82 lines of code) (raw):
package app
import (
"crypto/tls"
"crypto/x509"
"errors"
"fmt"
"net/http"
"os"
"path/filepath"
"runtime"
"strings"
securejoin "github.com/cyphar/filepath-securejoin"
"github.com/sirupsen/logrus"
"github.com/urfave/cli/v2"
"gitlab.com/gitlab-org/release-cli/internal/flags"
"gitlab.com/gitlab-org/release-cli/internal/gitlab"
)
func newHTTPClient(ctx *cli.Context, log logrus.FieldLogger) (gitlab.HTTPClient, error) {
extraCA := ctx.String(flags.AdditionalCACertBundle)
rootCAs, err := x509.SystemCertPool()
if err != nil {
// SystemCertPool is not supported on Windows, safe to call x509.NewCertPool() below
if runtime.GOOS != "windows" {
return nil, fmt.Errorf("getting system cert pool: %w", err)
}
rootCAs = x509.NewCertPool()
}
if extraCA != "" {
if err := loadExtraCACert(rootCAs, extraCA, log); err != nil {
return nil, err
}
}
transport := http.DefaultTransport.(*http.Transport).Clone()
transport.TLSClientConfig = &tls.Config{
// nolint: gosec
// G402: TLS InsecureSkipVerify may be true
// set to true at your own risk
InsecureSkipVerify: ctx.Bool(flags.InsecureHTTPS),
RootCAs: rootCAs,
}
return &http.Client{
Timeout: ctx.Duration(flags.Timeout),
Transport: transport,
}, nil
}
func loadExtraCACert(rootCAs *x509.CertPool, ca string, log logrus.FieldLogger) error {
l := log.WithFields(logrus.Fields{
flags.AdditionalCACertBundle: ca,
})
caBytes, err := getCA(ca)
if err != nil {
return err
}
// Append our cert to the system pool
if ok := rootCAs.AppendCertsFromPEM(caBytes); !ok {
l.Warn("No certs appended, using system certs only")
}
return nil
}
func getCA(ca string) ([]byte, error) {
// If a new line is found it means that we were given a CA block rather than a path.
if strings.ContainsRune(ca, '\n') {
return []byte(ca), nil
}
baseDir, err := os.Getwd()
if err != nil {
return nil, err
}
filePath, err := securejoin.SecureJoin(baseDir, ca)
if err != nil {
return nil, err
}
if _, statErr := os.Stat(filePath); errors.Is(statErr, os.ErrNotExist) {
buildsDir := os.Getenv("CI_BUILDS_DIR")
if buildsDir == "" {
return nil, statErr
}
rel, err := filepath.Rel(buildsDir, ca)
if err != nil {
return nil, err
}
filePath, err = securejoin.SecureJoin(buildsDir, rel)
if err != nil {
return nil, err
}
}
return os.ReadFile(filepath.Clean(filePath))
}