Skip TLS verif for local address (#2323)

Since the console is talking locally to MinIO, skip the TLS verification 
if any.

This will allow users to avoid defining the correct MINIO_SERVER_URL
domain address, when TLS is enabled, is useful in a bare-metal setup.
This commit is contained in:
Anis Elleuch
2022-10-17 20:34:05 +01:00
committed by GitHub
parent c31b311b4e
commit 40f64709a6
8 changed files with 80 additions and 33 deletions

View File

@@ -106,7 +106,7 @@ func getLoginDetailsResponse(params authApi.LoginDetailParams) (*models.LoginDet
if oauth2.IsIDPEnabled() { if oauth2.IsIDPEnabled() {
loginStrategy = models.LoginDetailsLoginStrategyRedirectDashServiceDashAccount loginStrategy = models.LoginDetailsLoginStrategyRedirectDashServiceDashAccount
// initialize new oauth2 client // initialize new oauth2 client
oauth2Client, err := oauth2.NewOauth2ProviderClient(nil, r, restapi.GetConsoleHTTPClient()) oauth2Client, err := oauth2.NewOauth2ProviderClient(nil, r, restapi.GetConsoleHTTPClient(""))
if err != nil { if err != nil {
return nil, restapi.ErrorWithContext(ctx, err) return nil, restapi.ErrorWithContext(ctx, err)
} }
@@ -144,7 +144,7 @@ func getLoginOauth2AuthResponse(params authApi.LoginOauth2AuthParams) (*models.L
if oauth2.IsIDPEnabled() { if oauth2.IsIDPEnabled() {
// initialize new oauth2 client // initialize new oauth2 client
oauth2Client, err := oauth2.NewOauth2ProviderClient(nil, r, restapi.GetConsoleHTTPClient()) oauth2Client, err := oauth2.NewOauth2ProviderClient(nil, r, restapi.GetConsoleHTTPClient(""))
if err != nil { if err != nil {
return nil, restapi.ErrorWithContext(ctx, err) return nil, restapi.ErrorWithContext(ctx, err)
} }

View File

@@ -88,7 +88,7 @@ func getOperatorSubnetLoginResponse(session *models.Principal, params operator_a
if username == "" || password == "" { if username == "" || password == "" {
return nil, restapi.ErrorWithContext(ctx, errors.New("empty credentials")) return nil, restapi.ErrorWithContext(ctx, errors.New("empty credentials"))
} }
subnetHTTPClient := &xhttp.Client{Client: restapi.GetConsoleHTTPClient()} subnetHTTPClient := &xhttp.Client{Client: restapi.GetConsoleHTTPClient("")}
token, mfa, err := restapi.SubnetLogin(subnetHTTPClient, username, password) token, mfa, err := restapi.SubnetLogin(subnetHTTPClient, username, password)
if err != nil { if err != nil {
return nil, restapi.ErrorWithContext(ctx, err) return nil, restapi.ErrorWithContext(ctx, err)
@@ -102,7 +102,7 @@ func getOperatorSubnetLoginResponse(session *models.Principal, params operator_a
func getOperatorSubnetLoginMFAResponse(session *models.Principal, params operator_api.OperatorSubnetLoginMFAParams) (*models.OperatorSubnetLoginResponse, *models.Error) { func getOperatorSubnetLoginMFAResponse(session *models.Principal, params operator_api.OperatorSubnetLoginMFAParams) (*models.OperatorSubnetLoginResponse, *models.Error) {
ctx, cancel := context.WithCancel(params.HTTPRequest.Context()) ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
defer cancel() defer cancel()
subnetHTTPClient := &xhttp.Client{Client: restapi.GetConsoleHTTPClient()} subnetHTTPClient := &xhttp.Client{Client: restapi.GetConsoleHTTPClient("")}
res, err := subnet.LoginWithMFA(subnetHTTPClient, *params.Body.Username, *params.Body.MfaToken, *params.Body.Otp) res, err := subnet.LoginWithMFA(subnetHTTPClient, *params.Body.Username, *params.Body.MfaToken, *params.Body.Otp)
if err != nil { if err != nil {
return nil, restapi.ErrorWithContext(ctx, err) return nil, restapi.ErrorWithContext(ctx, err)
@@ -115,7 +115,7 @@ func getOperatorSubnetLoginMFAResponse(session *models.Principal, params operato
func getOperatorSubnetAPIKeyResponse(session *models.Principal, params operator_api.OperatorSubnetAPIKeyParams) (*models.OperatorSubnetAPIKey, *models.Error) { func getOperatorSubnetAPIKeyResponse(session *models.Principal, params operator_api.OperatorSubnetAPIKeyParams) (*models.OperatorSubnetAPIKey, *models.Error) {
ctx, cancel := context.WithCancel(params.HTTPRequest.Context()) ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
defer cancel() defer cancel()
subnetHTTPClient := &xhttp.Client{Client: restapi.GetConsoleHTTPClient()} subnetHTTPClient := &xhttp.Client{Client: restapi.GetConsoleHTTPClient("")}
token := params.HTTPRequest.URL.Query().Get("token") token := params.HTTPRequest.URL.Query().Get("token")
apiKey, err := subnet.GetAPIKey(subnetHTTPClient, token) apiKey, err := subnet.GetAPIKey(subnetHTTPClient, token)
if err != nil { if err != nil {

View File

@@ -948,7 +948,7 @@ func getUsageWidgetsForDeployment(ctx context.Context, prometheusURL string, mAd
} }
func unmarshalPrometheus(ctx context.Context, endpoint string, data interface{}) bool { func unmarshalPrometheus(ctx context.Context, endpoint string, data interface{}) bool {
httpClnt := GetConsoleHTTPClient() httpClnt := GetConsoleHTTPClient(endpoint)
resp, err := httpClnt.Get(endpoint) resp, err := httpClnt.Get(endpoint)
if err != nil { if err != nil {
ErrorWithContext(ctx, fmt.Errorf("Unable to fetch labels from prometheus (%s)", resp.Status)) ErrorWithContext(ctx, fmt.Errorf("Unable to fetch labels from prometheus (%s)", resp.Status))
@@ -975,7 +975,7 @@ func testPrometheusURL(ctx context.Context, url string) bool {
ErrorWithContext(ctx, fmt.Errorf("error Building Request: (%v)", err)) ErrorWithContext(ctx, fmt.Errorf("error Building Request: (%v)", err))
return false return false
} }
response, err := GetConsoleHTTPClient().Do(req) response, err := GetConsoleHTTPClient(url).Do(req)
if err != nil { if err != nil {
ErrorWithContext(ctx, fmt.Errorf("default Prometheus URL not reachable, trying root testing: (%v)", err)) ErrorWithContext(ctx, fmt.Errorf("default Prometheus URL not reachable, trying root testing: (%v)", err))
newTestURL := req.URL.Scheme + "://" + req.URL.Host + "/-/healthy" newTestURL := req.URL.Scheme + "://" + req.URL.Host + "/-/healthy"
@@ -984,7 +984,7 @@ func testPrometheusURL(ctx context.Context, url string) bool {
ErrorWithContext(ctx, fmt.Errorf("error Building Root Request: (%v)", err)) ErrorWithContext(ctx, fmt.Errorf("error Building Root Request: (%v)", err))
return false return false
} }
rootResponse, err := GetConsoleHTTPClient().Do(req2) rootResponse, err := GetConsoleHTTPClient(newTestURL).Do(req2)
if err != nil { if err != nil {
// URL & Root tests didn't work. Prometheus not reachable // URL & Root tests didn't work. Prometheus not reachable
ErrorWithContext(ctx, fmt.Errorf("root Prometheus URL not reachable: (%v)", err)) ErrorWithContext(ctx, fmt.Errorf("root Prometheus URL not reachable: (%v)", err))

View File

@@ -91,7 +91,7 @@ func SubnetRegisterWithAPIKey(ctx context.Context, minioClient MinioAdmin, apiKe
if err != nil { if err != nil {
return false, err return false, err
} }
registerResult, err := subnet.Register(GetConsoleHTTPClient(), serverInfo, apiKey, "", "") registerResult, err := subnet.Register(GetConsoleHTTPClient(""), serverInfo, apiKey, "", "")
if err != nil { if err != nil {
return false, err return false, err
} }
@@ -213,7 +213,7 @@ func GetSubnetHTTPClient(ctx context.Context, minioClient MinioAdmin) (*xhttp.Cl
} }
subnetHTTPClient.Transport.(*http.Transport).Proxy = http.ProxyURL(subnetProxyURL) subnetHTTPClient.Transport.(*http.Transport).Proxy = http.ProxyURL(subnetProxyURL)
} else { } else {
subnetHTTPClient = GetConsoleHTTPClient() subnetHTTPClient = GetConsoleHTTPClient("")
} }
clientI := &xhttp.Client{ clientI := &xhttp.Client{
Client: subnetHTTPClient, Client: subnetHTTPClient,
@@ -309,7 +309,7 @@ func GetSubnetInfoResponse(session *models.Principal, params subnetApi.SubnetInf
ctx, cancel := context.WithCancel(params.HTTPRequest.Context()) ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
defer cancel() defer cancel()
client := &xhttp.Client{ client := &xhttp.Client{
Client: GetConsoleHTTPClient(), Client: GetConsoleHTTPClient(""),
} }
licenseInfo, err := subnet.ParseLicense(client, os.Getenv("CONSOLE_SUBNET_LICENSE")) licenseInfo, err := subnet.ParseLicense(client, os.Getenv("CONSOLE_SUBNET_LICENSE"))

View File

@@ -21,7 +21,10 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"io" "io"
"net"
"net/http" "net/http"
"net/url"
"sync"
"time" "time"
"github.com/minio/console/models" "github.com/minio/console/models"
@@ -479,7 +482,7 @@ func newAdminFromClaims(claims *models.Principal) (*madmin.AdminClient, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
adminClient.SetCustomTransport(GetConsoleHTTPClient().Transport) adminClient.SetCustomTransport(GetConsoleHTTPClient(getMinIOServer()).Transport)
return adminClient, nil return adminClient, nil
} }
@@ -498,17 +501,54 @@ func newAdminFromCreds(accessKey, secretKey, endpoint string, tlsEnabled bool) (
// httpClient is a custom http client, this client should not be called directly and instead be // httpClient is a custom http client, this client should not be called directly and instead be
// called using GetConsoleHTTPClient() to ensure is initialized and the certificates are loaded correctly // called using GetConsoleHTTPClient() to ensure is initialized and the certificates are loaded correctly
var httpClient *http.Client var httpClients = struct {
sync.Mutex
m map[string]*http.Client
}{
m: make(map[string]*http.Client),
}
// GetConsoleHTTPClient will initialize the console HTTP Client with fully populated custom TLS // isLocalAddress returns true if the url contains an IPv4/IPv6 hostname
// Transport that with loads certs at // that points to the local machine - FQDN are not supported
// - ${HOME}/.console/certs/CAs func isLocalIPEndpoint(addr string) bool {
// - ${HOME}/.minio/certs/CAs u, err := url.Parse(addr)
func GetConsoleHTTPClient() *http.Client { if err != nil {
if httpClient == nil { return false
httpClient = PrepareConsoleHTTPClient(false)
} }
return httpClient return isLocalIPAddress(u.Hostname())
}
// isLocalAddress returns true if the url contains an IPv4/IPv6 hostname
// that points to the local machine - FQDN are not supported
func isLocalIPAddress(ipAddr string) bool {
if ipAddr == "" {
return false
}
ip := net.ParseIP(ipAddr)
return ip != nil && ip.IsLoopback()
}
// GetConsoleHTTPClient caches different http clients depending on the target endpoint while taking
// in consideration CA certs stored in ${HOME}/.console/certs/CAs and ${HOME}/.minio/certs/CAs
// If the target endpoint points to a loopback device, skip the TLS verification.
func GetConsoleHTTPClient(address string) *http.Client {
u, err := url.Parse(address)
if err == nil {
address = u.Hostname()
}
httpClients.Lock()
client, ok := httpClients.m[address]
httpClients.Unlock()
if ok {
return client
}
client = PrepareConsoleHTTPClient(isLocalIPAddress(address))
httpClients.Lock()
httpClients.m[address] = client
httpClients.Unlock()
return client
} }
func (ac AdminClient) speedtest(ctx context.Context, opts madmin.SpeedtestOpts) (chan madmin.SpeedTestResult, error) { func (ac AdminClient) speedtest(ctx context.Context, opts madmin.SpeedtestOpts) (chan madmin.SpeedTestResult, error) {

View File

@@ -330,12 +330,14 @@ func (s consoleSTSAssumeRole) IsExpired() bool {
} }
func NewConsoleCredentials(accessKey, secretKey, location string) (*credentials.Credentials, error) { func NewConsoleCredentials(accessKey, secretKey, location string) (*credentials.Credentials, error) {
minioURL := getMinIOServer()
// Future authentication methods can be added under this switch statement // Future authentication methods can be added under this switch statement
switch { switch {
// LDAP authentication for Console // LDAP authentication for Console
case ldap.GetLDAPEnabled(): case ldap.GetLDAPEnabled():
{ {
creds, err := auth.GetCredentialsFromLDAP(GetConsoleHTTPClient(), getMinIOServer(), accessKey, secretKey) creds, err := auth.GetCredentialsFromLDAP(GetConsoleHTTPClient(minioURL), minioURL, accessKey, secretKey)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -354,8 +356,8 @@ func NewConsoleCredentials(accessKey, secretKey, location string) (*credentials.
DurationSeconds: int(xjwt.GetConsoleSTSDuration().Seconds()), DurationSeconds: int(xjwt.GetConsoleSTSDuration().Seconds()),
} }
stsAssumeRole := &credentials.STSAssumeRole{ stsAssumeRole := &credentials.STSAssumeRole{
Client: GetConsoleHTTPClient(), Client: GetConsoleHTTPClient(minioURL),
STSEndpoint: getMinIOServer(), STSEndpoint: minioURL,
Options: opts, Options: opts,
} }
consoleSTSWrapper := consoleSTSAssumeRole{stsAssumeRole: stsAssumeRole} consoleSTSWrapper := consoleSTSAssumeRole{stsAssumeRole: stsAssumeRole}
@@ -374,10 +376,12 @@ func getConsoleCredentialsFromSession(claims *models.Principal) *credentials.Cre
// from the provided session token // from the provided session token
func newMinioClient(claims *models.Principal) (*minio.Client, error) { func newMinioClient(claims *models.Principal) (*minio.Client, error) {
creds := getConsoleCredentialsFromSession(claims) creds := getConsoleCredentialsFromSession(claims)
minioClient, err := minio.New(getMinIOEndpoint(), &minio.Options{ endpoint := getMinIOEndpoint()
secure := getMinIOEndpointIsSecure()
minioClient, err := minio.New(endpoint, &minio.Options{
Creds: creds, Creds: creds,
Secure: getMinIOEndpointIsSecure(), Secure: secure,
Transport: GetConsoleHTTPClient().Transport, Transport: GetConsoleHTTPClient(getMinIOServer()).Transport,
}) })
if err != nil { if err != nil {
return nil, err return nil, err
@@ -414,7 +418,7 @@ func newS3BucketClient(claims *models.Principal, bucketName string, prefix strin
if err != nil { if err != nil {
return nil, fmt.Errorf("the provided endpoint is invalid") return nil, fmt.Errorf("the provided endpoint is invalid")
} }
s3Config := newS3Config(objectURL, claims.STSAccessKeyID, claims.STSSecretAccessKey, claims.STSSessionToken, false) s3Config := newS3Config(objectURL, claims.STSAccessKeyID, claims.STSSecretAccessKey, claims.STSSessionToken)
client, pErr := mc.S3New(s3Config) client, pErr := mc.S3New(s3Config)
if pErr != nil { if pErr != nil {
return nil, pErr.Cause return nil, pErr.Cause
@@ -438,7 +442,7 @@ func pathJoinFinalSlash(elem ...string) string {
// newS3Config simply creates a new Config struct using the passed // newS3Config simply creates a new Config struct using the passed
// parameters. // parameters.
func newS3Config(endpoint, accessKey, secretKey, sessionToken string, insecure bool) *mc.Config { func newS3Config(endpoint, accessKey, secretKey, sessionToken string) *mc.Config {
// We have a valid alias and hostConfig. We populate the/ // We have a valid alias and hostConfig. We populate the/
// consoleCredentials from the match found in the config file. // consoleCredentials from the match found in the config file.
s3Config := new(mc.Config) s3Config := new(mc.Config)
@@ -446,13 +450,16 @@ func newS3Config(endpoint, accessKey, secretKey, sessionToken string, insecure b
s3Config.AppName = globalAppName s3Config.AppName = globalAppName
s3Config.AppVersion = pkg.Version s3Config.AppVersion = pkg.Version
s3Config.Debug = false s3Config.Debug = false
s3Config.Insecure = insecure
s3Config.HostURL = endpoint s3Config.HostURL = endpoint
s3Config.AccessKey = accessKey s3Config.AccessKey = accessKey
s3Config.SecretKey = secretKey s3Config.SecretKey = secretKey
s3Config.SessionToken = sessionToken s3Config.SessionToken = sessionToken
s3Config.Signature = "S3v4" s3Config.Signature = "S3v4"
insecure := isLocalIPEndpoint(endpoint)
s3Config.Insecure = insecure
s3Config.Transport = PrepareSTSClientTransport(insecure) s3Config.Transport = PrepareSTSClientTransport(insecure)
return s3Config return s3Config

View File

@@ -91,7 +91,7 @@ func getLogSearchResponse(session *models.Principal, params logApi.LogSearchPara
} }
func logSearch(endpoint string) (*models.LogSearchResponse, error) { func logSearch(endpoint string) (*models.LogSearchResponse, error) {
httpClnt := GetConsoleHTTPClient() httpClnt := GetConsoleHTTPClient(endpoint)
resp, err := httpClnt.Get(endpoint) resp, err := httpClnt.Get(endpoint)
if err != nil { if err != nil {
return nil, fmt.Errorf("the Log Search API cannot be reached. Please review the URL and try again %v", err) return nil, fmt.Errorf("the Log Search API cannot be reached. Please review the URL and try again %v", err)

View File

@@ -157,7 +157,7 @@ func getLoginDetailsResponse(params authApi.LoginDetailParams, openIDProviders o
loginStrategy = models.LoginDetailsLoginStrategyRedirect loginStrategy = models.LoginDetailsLoginStrategyRedirect
for name, provider := range openIDProviders { for name, provider := range openIDProviders {
// initialize new oauth2 client // initialize new oauth2 client
oauth2Client, err := openIDProviders.NewOauth2ProviderClient(name, nil, r, GetConsoleHTTPClient()) oauth2Client, err := openIDProviders.NewOauth2ProviderClient(name, nil, r, GetConsoleHTTPClient(""))
if err != nil { if err != nil {
return nil, ErrorWithContext(ctx, err, ErrOauth2Provider) return nil, ErrorWithContext(ctx, err, ErrOauth2Provider)
} }
@@ -199,7 +199,7 @@ func getLoginOauth2AuthResponse(params authApi.LoginOauth2AuthParams, openIDProv
lr := params.Body lr := params.Body
if openIDProviders != nil { if openIDProviders != nil {
// initialize new oauth2 client // initialize new oauth2 client
oauth2Client, err := openIDProviders.NewOauth2ProviderClient(idpName, nil, r, GetConsoleHTTPClient()) oauth2Client, err := openIDProviders.NewOauth2ProviderClient(idpName, nil, r, GetConsoleHTTPClient(""))
if err != nil { if err != nil {
return nil, ErrorWithContext(ctx, err) return nil, ErrorWithContext(ctx, err)
} }