diff --git a/pkg/client/clients.go b/pkg/client/clients.go index 366b634d..6db7815e 100644 --- a/pkg/client/clients.go +++ b/pkg/client/clients.go @@ -2,8 +2,6 @@ package client import ( - "crypto/tls" - "net/http" "time" "github.com/platform9/pf9ctl/pkg/cmdexec" @@ -29,10 +27,6 @@ type Client struct { // New creates the clients needed by the CLI // to interact with the external services. func NewClient(fqdn string, executor cmdexec.Executor, allowInsecure bool, noTracking bool) (Client, error) { - // Bring the hammer down to make default http allow insecure - if allowInsecure { - http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true} - } return Client{ Resmgr: resmgr.NewResmgr(fqdn, HTTPMaxRetry, HTTPRetryMinWait, HTTPRetryMaxWait, allowInsecure), Keystone: keystone.NewKeystone(fqdn), diff --git a/pkg/keystone/endpoints.go b/pkg/keystone/endpoints.go index bff4fab0..43c8d15f 100644 --- a/pkg/keystone/endpoints.go +++ b/pkg/keystone/endpoints.go @@ -54,7 +54,7 @@ func GetEndpointForRegion( url := fmt.Sprintf("%s/keystone/v3/endpoints", fqdn) // Generate the http client object - client := &http.Client{} + client := newKeystoneHTTPClient() // Create the context to invoke the service manager API. e_api := EndpointManagerAPI{client, url, auth.Token} diff --git a/pkg/keystone/keystone.go b/pkg/keystone/keystone.go index 683d4de7..9963c53d 100644 --- a/pkg/keystone/keystone.go +++ b/pkg/keystone/keystone.go @@ -3,15 +3,64 @@ package keystone import ( + "crypto/tls" + "crypto/x509" "encoding/json" "fmt" "net/http" + "os" "strings" + "time" "github.com/google/uuid" "go.uber.org/zap" ) +func keystoneRootCAs() *x509.CertPool { + rootCAs := x509.NewCertPool() + + // Prefer the DU cert if present (installed by pcdctl). + for _, p := range []string{ + "/etc/pki/ca-trust/source/anchors/du.crt", + "/usr/local/share/ca-certificates/du.crt", + } { + if b, err := os.ReadFile(p); err == nil { + _ = rootCAs.AppendCertsFromPEM(b) + } + } + + // Load OS CA bundle directly (avoids per-process caching). + for _, p := range []string{ + "/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem", // RHEL-family + "/etc/ssl/certs/ca-certificates.crt", // Debian-family + } { + if b, err := os.ReadFile(p); err == nil && rootCAs.AppendCertsFromPEM(b) { + return rootCAs + } + } + + // Fallback: best-effort system pool (may be cached). + if sysPool, sysErr := x509.SystemCertPool(); sysErr == nil && sysPool != nil { + return sysPool + } + + return rootCAs +} + +func newKeystoneHTTPClient() *http.Client { + transport := &http.Transport{ + Proxy: http.ProxyFromEnvironment, + TLSClientConfig: &tls.Config{ + RootCAs: keystoneRootCAs(), + MinVersion: tls.VersionTLS12, + }, + } + return &http.Client{ + Timeout: 30 * time.Second, + Transport: transport, + } +} + type KeystoneAuth struct { DUFqdn string Token string @@ -155,14 +204,24 @@ func (k KeystoneImpl) GetAuth( }`, username, password, tenant) } } - resp, err := http.Post(url, "application/json", strings.NewReader(body)) + + client := newKeystoneHTTPClient() + + req, err := http.NewRequest("POST", url, strings.NewReader(body)) + if err != nil { + return auth, err + } + req.Header.Set("Content-Type", "application/json") + + resp, err := client.Do(req) if err != nil { zap.S().Debugf("Error calling keystone API:%s\n", err.Error()) return auth, err } + defer resp.Body.Close() if resp.StatusCode != 201 { - zap.S().Debugf("Error in StatusCode:%s\n", resp.StatusCode) + zap.S().Debugf("Error in StatusCode:%d\n", resp.StatusCode) return auth, fmt.Errorf("Unable to get keystone token, status: %d", resp.StatusCode) } diff --git a/pkg/keystone/services.go b/pkg/keystone/services.go index 39bed4fb..5de08dee 100644 --- a/pkg/keystone/services.go +++ b/pkg/keystone/services.go @@ -50,7 +50,7 @@ func GetServiceID( url := fmt.Sprintf("%s/keystone/v3/services", fqdn) // Generate the http client object - client := &http.Client{} + client := newKeystoneHTTPClient() // Create the context to invoke the service manager API. s_api := ServiceManagerAPI{client, url, auth.Token}