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

View File

@@ -88,7 +88,7 @@ func getOperatorSubnetLoginResponse(session *models.Principal, params operator_a
if username == "" || password == "" {
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)
if err != nil {
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) {
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
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)
if err != nil {
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) {
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
defer cancel()
subnetHTTPClient := &xhttp.Client{Client: restapi.GetConsoleHTTPClient()}
subnetHTTPClient := &xhttp.Client{Client: restapi.GetConsoleHTTPClient("")}
token := params.HTTPRequest.URL.Query().Get("token")
apiKey, err := subnet.GetAPIKey(subnetHTTPClient, token)
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 {
httpClnt := GetConsoleHTTPClient()
httpClnt := GetConsoleHTTPClient(endpoint)
resp, err := httpClnt.Get(endpoint)
if err != nil {
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))
return false
}
response, err := GetConsoleHTTPClient().Do(req)
response, err := GetConsoleHTTPClient(url).Do(req)
if err != nil {
ErrorWithContext(ctx, fmt.Errorf("default Prometheus URL not reachable, trying root testing: (%v)", err))
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))
return false
}
rootResponse, err := GetConsoleHTTPClient().Do(req2)
rootResponse, err := GetConsoleHTTPClient(newTestURL).Do(req2)
if err != nil {
// URL & Root tests didn't work. Prometheus not reachable
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 {
return false, err
}
registerResult, err := subnet.Register(GetConsoleHTTPClient(), serverInfo, apiKey, "", "")
registerResult, err := subnet.Register(GetConsoleHTTPClient(""), serverInfo, apiKey, "", "")
if err != nil {
return false, err
}
@@ -213,7 +213,7 @@ func GetSubnetHTTPClient(ctx context.Context, minioClient MinioAdmin) (*xhttp.Cl
}
subnetHTTPClient.Transport.(*http.Transport).Proxy = http.ProxyURL(subnetProxyURL)
} else {
subnetHTTPClient = GetConsoleHTTPClient()
subnetHTTPClient = GetConsoleHTTPClient("")
}
clientI := &xhttp.Client{
Client: subnetHTTPClient,
@@ -309,7 +309,7 @@ func GetSubnetInfoResponse(session *models.Principal, params subnetApi.SubnetInf
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
defer cancel()
client := &xhttp.Client{
Client: GetConsoleHTTPClient(),
Client: GetConsoleHTTPClient(""),
}
licenseInfo, err := subnet.ParseLicense(client, os.Getenv("CONSOLE_SUBNET_LICENSE"))

View File

@@ -21,7 +21,10 @@ import (
"context"
"encoding/json"
"io"
"net"
"net/http"
"net/url"
"sync"
"time"
"github.com/minio/console/models"
@@ -479,7 +482,7 @@ func newAdminFromClaims(claims *models.Principal) (*madmin.AdminClient, error) {
if err != nil {
return nil, err
}
adminClient.SetCustomTransport(GetConsoleHTTPClient().Transport)
adminClient.SetCustomTransport(GetConsoleHTTPClient(getMinIOServer()).Transport)
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
// 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
// Transport that with loads certs at
// - ${HOME}/.console/certs/CAs
// - ${HOME}/.minio/certs/CAs
func GetConsoleHTTPClient() *http.Client {
if httpClient == nil {
httpClient = PrepareConsoleHTTPClient(false)
// isLocalAddress returns true if the url contains an IPv4/IPv6 hostname
// that points to the local machine - FQDN are not supported
func isLocalIPEndpoint(addr string) bool {
u, err := url.Parse(addr)
if err != nil {
return 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) {

View File

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

View File

@@ -91,7 +91,7 @@ func getLogSearchResponse(session *models.Principal, params logApi.LogSearchPara
}
func logSearch(endpoint string) (*models.LogSearchResponse, error) {
httpClnt := GetConsoleHTTPClient()
httpClnt := GetConsoleHTTPClient(endpoint)
resp, err := httpClnt.Get(endpoint)
if err != nil {
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
for name, provider := range openIDProviders {
// initialize new oauth2 client
oauth2Client, err := openIDProviders.NewOauth2ProviderClient(name, nil, r, GetConsoleHTTPClient())
oauth2Client, err := openIDProviders.NewOauth2ProviderClient(name, nil, r, GetConsoleHTTPClient(""))
if err != nil {
return nil, ErrorWithContext(ctx, err, ErrOauth2Provider)
}
@@ -199,7 +199,7 @@ func getLoginOauth2AuthResponse(params authApi.LoginOauth2AuthParams, openIDProv
lr := params.Body
if openIDProviders != nil {
// initialize new oauth2 client
oauth2Client, err := openIDProviders.NewOauth2ProviderClient(idpName, nil, r, GetConsoleHTTPClient())
oauth2Client, err := openIDProviders.NewOauth2ProviderClient(idpName, nil, r, GetConsoleHTTPClient(""))
if err != nil {
return nil, ErrorWithContext(ctx, err)
}