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()