github.com/minio/mc@v0.0.0-20240503112107-b471de8d1882/cmd/client-admin.go (about)

     1  // Copyright (c) 2015-2022 MinIO, Inc.
     2  //
     3  // This file is part of MinIO Object Storage stack
     4  //
     5  // This program is free software: you can redistribute it and/or modify
     6  // it under the terms of the GNU Affero General Public License as published by
     7  // the Free Software Foundation, either version 3 of the License, or
     8  // (at your option) any later version.
     9  //
    10  // This program is distributed in the hope that it will be useful
    11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13  // GNU Affero General Public License for more details.
    14  //
    15  // You should have received a copy of the GNU Affero General Public License
    16  // along with this program.  If not, see <http://www.gnu.org/licenses/>.
    17  
    18  package cmd
    19  
    20  import (
    21  	"crypto/tls"
    22  	"fmt"
    23  	"net"
    24  	"net/http"
    25  	"net/url"
    26  	"sync"
    27  	"time"
    28  
    29  	"github.com/mattn/go-ieproxy"
    30  	"github.com/minio/madmin-go/v3"
    31  	"github.com/minio/mc/pkg/httptracer"
    32  	"github.com/minio/mc/pkg/probe"
    33  	"github.com/minio/minio-go/v7/pkg/credentials"
    34  )
    35  
    36  // NewAdminFactory encloses New function with client cache.
    37  func NewAdminFactory() func(config *Config) (*madmin.AdminClient, *probe.Error) {
    38  	clientCache := make(map[uint32]*madmin.AdminClient)
    39  	mutex := &sync.Mutex{}
    40  
    41  	// Return New function.
    42  	return func(config *Config) (*madmin.AdminClient, *probe.Error) {
    43  		// Creates a parsed URL.
    44  		targetURL, e := url.Parse(config.HostURL)
    45  		if e != nil {
    46  			return nil, probe.NewError(e)
    47  		}
    48  		hostName := targetURL.Host
    49  
    50  		confSum := getConfigHash(config)
    51  
    52  		useTLS := isHostTLS(config)
    53  
    54  		// Lookup previous cache by hash.
    55  		mutex.Lock()
    56  		defer mutex.Unlock()
    57  		var api *madmin.AdminClient
    58  		var found bool
    59  		if api, found = clientCache[confSum]; !found {
    60  
    61  			transport := getTransportForConfig(config, true)
    62  
    63  			credsChain, err := getCredentialsChainForConfig(config, transport)
    64  			if err != nil {
    65  				return nil, err
    66  			}
    67  
    68  			creds := credentials.NewChainCredentials(credsChain)
    69  
    70  			// Not found. Instantiate a new MinIO
    71  			var e error
    72  			api, e = madmin.NewWithOptions(hostName, &madmin.Options{
    73  				Creds:  creds,
    74  				Secure: useTLS,
    75  			})
    76  			if e != nil {
    77  				return nil, probe.NewError(e)
    78  			}
    79  
    80  			// Set custom transport.
    81  			api.SetCustomTransport(transport)
    82  
    83  			// Set app info.
    84  			api.SetAppInfo(config.AppName, config.AppVersion)
    85  
    86  			// Cache the new MinIO Client with hash of config as key.
    87  			clientCache[confSum] = api
    88  		}
    89  
    90  		// Store the new api object.
    91  		return api, nil
    92  	}
    93  }
    94  
    95  // newAdminClient gives a new client interface
    96  func newAdminClient(aliasedURL string) (*madmin.AdminClient, *probe.Error) {
    97  	alias, urlStrFull, aliasCfg, err := expandAlias(aliasedURL)
    98  	if err != nil {
    99  		return nil, err.Trace(aliasedURL)
   100  	}
   101  	// Verify if the aliasedURL is a real URL, fail in those cases
   102  	// indicating the user to add alias.
   103  	if aliasCfg == nil && urlRgx.MatchString(aliasedURL) {
   104  		return nil, errInvalidAliasedURL(aliasedURL).Trace(aliasedURL)
   105  	}
   106  
   107  	if aliasCfg == nil {
   108  		return nil, probe.NewError(fmt.Errorf("No valid configuration found for '%s' host alias", urlStrFull))
   109  	}
   110  
   111  	s3Config := NewS3Config(alias, urlStrFull, aliasCfg)
   112  
   113  	s3Client, err := s3AdminNew(s3Config)
   114  	if err != nil {
   115  		return nil, err.Trace(alias, urlStrFull)
   116  	}
   117  	return s3Client, nil
   118  }
   119  
   120  func newAnonymousClient(aliasedURL string) (*madmin.AnonymousClient, *probe.Error) {
   121  	_, urlStrFull, aliasCfg, err := expandAlias(aliasedURL)
   122  	if err != nil {
   123  		return nil, err.Trace(aliasedURL)
   124  	}
   125  	// Verify if the aliasedURL is a real URL, fail in those cases
   126  	// indicating the user to add alias.
   127  	if aliasCfg == nil && urlRgx.MatchString(aliasedURL) {
   128  		return nil, errInvalidAliasedURL(aliasedURL).Trace(aliasedURL)
   129  	}
   130  	if aliasCfg == nil {
   131  		return nil, probe.NewError(fmt.Errorf("No valid configuration found for '%s' host alias", urlStrFull))
   132  	}
   133  
   134  	// Creates a parsed URL.
   135  	targetURL, e := url.Parse(urlStrFull)
   136  	if e != nil {
   137  		return nil, probe.NewError(e)
   138  	}
   139  
   140  	// By default enable HTTPs.
   141  	useTLS := true
   142  	if targetURL.Scheme == "http" {
   143  		useTLS = false
   144  	}
   145  
   146  	// Construct an anonymous client
   147  	anonClient, e := madmin.NewAnonymousClient(targetURL.Host, useTLS)
   148  	if e != nil {
   149  		return nil, probe.NewError(e)
   150  	}
   151  
   152  	// Keep TLS config.
   153  	tlsConfig := &tls.Config{
   154  		RootCAs: globalRootCAs,
   155  		// Can't use SSLv3 because of POODLE and BEAST
   156  		// Can't use TLSv1.0 because of POODLE and BEAST using CBC cipher
   157  		// Can't use TLSv1.1 because of RC4 cipher usage
   158  		MinVersion: tls.VersionTLS12,
   159  	}
   160  	if globalInsecure {
   161  		tlsConfig.InsecureSkipVerify = true
   162  	}
   163  	// Set custom transport
   164  	var transport http.RoundTripper = &http.Transport{
   165  		Proxy: ieproxy.GetProxyFunc(),
   166  		DialContext: (&net.Dialer{
   167  			Timeout:   10 * time.Second,
   168  			KeepAlive: 15 * time.Second,
   169  		}).DialContext,
   170  		MaxIdleConnsPerHost:   256,
   171  		IdleConnTimeout:       90 * time.Second,
   172  		TLSHandshakeTimeout:   10 * time.Second,
   173  		ExpectContinueTimeout: 10 * time.Second,
   174  		TLSClientConfig:       tlsConfig,
   175  		// Set this value so that the underlying transport round-tripper
   176  		// doesn't try to auto decode the body of objects with
   177  		// content-encoding set to `gzip`.
   178  		//
   179  		// Refer:
   180  		//    https://golang.org/src/net/http/transport.go?h=roundTrip#L1843
   181  		DisableCompression: true,
   182  	}
   183  	if globalDebug {
   184  		transport = httptracer.GetNewTraceTransport(newTraceV4(), transport)
   185  	}
   186  	anonClient.SetCustomTransport(transport)
   187  
   188  	return anonClient, nil
   189  }
   190  
   191  // s3AdminNew returns an initialized minioAdmin structure. If debug is enabled,
   192  // it also enables an internal trace transport.
   193  var s3AdminNew = NewAdminFactory()