mirror of
				https://github.com/minio/minio.git
				synced 2025-11-03 17:51:11 +01:00 
			
		
		
		
	This PR adds support - Request query params - Request headers - Response headers AuditLogEntry is exported and versioned as well starting with this PR.
		
			
				
	
	
		
			357 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			357 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
/*
 | 
						|
 * Minio Cloud Storage, (C) 2017, 2018 Minio, Inc.
 | 
						|
 *
 | 
						|
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
						|
 * you may not use this file except in compliance with the License.
 | 
						|
 * You may obtain a copy of the License at
 | 
						|
 *
 | 
						|
 *     http://www.apache.org/licenses/LICENSE-2.0
 | 
						|
 *
 | 
						|
 * Unless required by applicable law or agreed to in writing, software
 | 
						|
 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
						|
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
						|
 * See the License for the specific language governing permissions and
 | 
						|
 * limitations under the License.
 | 
						|
 */
 | 
						|
 | 
						|
package cmd
 | 
						|
 | 
						|
import (
 | 
						|
	"crypto/tls"
 | 
						|
	"errors"
 | 
						|
	"net"
 | 
						|
	"os"
 | 
						|
	"path/filepath"
 | 
						|
	"strconv"
 | 
						|
	"strings"
 | 
						|
	"time"
 | 
						|
 | 
						|
	etcd "github.com/coreos/etcd/clientv3"
 | 
						|
	dns2 "github.com/miekg/dns"
 | 
						|
	"github.com/minio/cli"
 | 
						|
	"github.com/minio/minio-go/pkg/set"
 | 
						|
	"github.com/minio/minio/cmd/crypto"
 | 
						|
	"github.com/minio/minio/cmd/logger"
 | 
						|
	"github.com/minio/minio/pkg/auth"
 | 
						|
	"github.com/minio/minio/pkg/dns"
 | 
						|
	xnet "github.com/minio/minio/pkg/net"
 | 
						|
)
 | 
						|
 | 
						|
// Check for updates and print a notification message
 | 
						|
func checkUpdate(mode string) {
 | 
						|
	// Its OK to ignore any errors during doUpdate() here.
 | 
						|
	if updateMsg, _, currentReleaseTime, latestReleaseTime, err := getUpdateInfo(2*time.Second, mode); err == nil {
 | 
						|
		if globalInplaceUpdateDisabled {
 | 
						|
			logger.StartupMessage(updateMsg)
 | 
						|
		} else {
 | 
						|
			logger.StartupMessage(prepareUpdateMessage("Run `minio update`", latestReleaseTime.Sub(currentReleaseTime)))
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// Load logger targets based on user's configuration
 | 
						|
func loadLoggers() {
 | 
						|
	auditEndpoint, ok := os.LookupEnv("MINIO_AUDIT_LOGGER_HTTP_ENDPOINT")
 | 
						|
	if ok {
 | 
						|
		// Enable audit HTTP logging through ENV.
 | 
						|
		logger.AddAuditTarget(logger.NewHTTP(auditEndpoint, NewCustomHTTPTransport()))
 | 
						|
	}
 | 
						|
 | 
						|
	loggerEndpoint, ok := os.LookupEnv("MINIO_LOGGER_HTTP_ENDPOINT")
 | 
						|
	if ok {
 | 
						|
		// Enable HTTP logging through ENV.
 | 
						|
		logger.AddTarget(logger.NewHTTP(loggerEndpoint, NewCustomHTTPTransport()))
 | 
						|
	} else {
 | 
						|
		for _, l := range globalServerConfig.Logger.HTTP {
 | 
						|
			if l.Enabled {
 | 
						|
				// Enable http logging
 | 
						|
				logger.AddTarget(logger.NewHTTP(l.Endpoint, NewCustomHTTPTransport()))
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if globalServerConfig.Logger.Console.Enabled {
 | 
						|
		// Enable console logging
 | 
						|
		logger.AddTarget(logger.NewConsole())
 | 
						|
	}
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
func handleCommonCmdArgs(ctx *cli.Context) {
 | 
						|
 | 
						|
	var configDir string
 | 
						|
 | 
						|
	switch {
 | 
						|
	case ctx.IsSet("config-dir"):
 | 
						|
		configDir = ctx.String("config-dir")
 | 
						|
	case ctx.GlobalIsSet("config-dir"):
 | 
						|
		configDir = ctx.GlobalString("config-dir")
 | 
						|
		// cli package does not expose parent's "config-dir" option.  Below code is workaround.
 | 
						|
		if configDir == "" || configDir == getConfigDir() {
 | 
						|
			if ctx.Parent().GlobalIsSet("config-dir") {
 | 
						|
				configDir = ctx.Parent().GlobalString("config-dir")
 | 
						|
			}
 | 
						|
		}
 | 
						|
	default:
 | 
						|
		// Neither local nor global config-dir option is provided.  In this case, try to use
 | 
						|
		// default config directory.
 | 
						|
		configDir = getConfigDir()
 | 
						|
		if configDir == "" {
 | 
						|
			logger.FatalIf(errors.New("missing option"), "config-dir option must be provided")
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if configDir == "" {
 | 
						|
		logger.FatalIf(errors.New("empty directory"), "Configuration directory cannot be empty")
 | 
						|
	}
 | 
						|
 | 
						|
	// Disallow relative paths, figure out absolute paths.
 | 
						|
	configDirAbs, err := filepath.Abs(configDir)
 | 
						|
	logger.FatalIf(err, "Unable to fetch absolute path for config directory %s", configDir)
 | 
						|
	setConfigDir(configDirAbs)
 | 
						|
}
 | 
						|
 | 
						|
// Parses the given compression exclude list `extensions` or `content-types`.
 | 
						|
func parseCompressIncludes(includes []string) ([]string, error) {
 | 
						|
	for _, e := range includes {
 | 
						|
		if len(e) == 0 {
 | 
						|
			return nil, uiErrInvalidCompressionIncludesValue(nil).Msg("extension/mime-type (%s) cannot be empty", e)
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return includes, nil
 | 
						|
}
 | 
						|
 | 
						|
func handleCommonEnvVars() {
 | 
						|
	compressEnvDelimiter := ","
 | 
						|
	// Start profiler if env is set.
 | 
						|
	if profiler := os.Getenv("_MINIO_PROFILER"); profiler != "" {
 | 
						|
		var err error
 | 
						|
		globalProfiler, err = startProfiler(profiler, "")
 | 
						|
		logger.FatalIf(err, "Unable to setup a profiler")
 | 
						|
	}
 | 
						|
 | 
						|
	accessKey := os.Getenv("MINIO_ACCESS_KEY")
 | 
						|
	secretKey := os.Getenv("MINIO_SECRET_KEY")
 | 
						|
	if accessKey != "" && secretKey != "" {
 | 
						|
		cred, err := auth.CreateCredentials(accessKey, secretKey)
 | 
						|
		if err != nil {
 | 
						|
			logger.Fatal(uiErrInvalidCredentials(err), "Unable to validate credentials inherited from the shell environment")
 | 
						|
		}
 | 
						|
		cred.Expiration = timeSentinel
 | 
						|
 | 
						|
		// credential Envs are set globally.
 | 
						|
		globalIsEnvCreds = true
 | 
						|
		globalActiveCred = cred
 | 
						|
	}
 | 
						|
 | 
						|
	if browser := os.Getenv("MINIO_BROWSER"); browser != "" {
 | 
						|
		browserFlag, err := ParseBoolFlag(browser)
 | 
						|
		if err != nil {
 | 
						|
			logger.Fatal(uiErrInvalidBrowserValue(nil).Msg("Unknown value `%s`", browser), "Invalid MINIO_BROWSER value in environment variable")
 | 
						|
		}
 | 
						|
 | 
						|
		// browser Envs are set globally, this does not represent
 | 
						|
		// if browser is turned off or on.
 | 
						|
		globalIsEnvBrowser = true
 | 
						|
		globalIsBrowserEnabled = bool(browserFlag)
 | 
						|
	}
 | 
						|
 | 
						|
	traceFile := os.Getenv("MINIO_HTTP_TRACE")
 | 
						|
	if traceFile != "" {
 | 
						|
		var err error
 | 
						|
		globalHTTPTraceFile, err = os.OpenFile(traceFile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0660)
 | 
						|
		logger.FatalIf(err, "error opening file %s", traceFile)
 | 
						|
	}
 | 
						|
 | 
						|
	etcdEndpointsEnv, ok := os.LookupEnv("MINIO_ETCD_ENDPOINTS")
 | 
						|
	if ok {
 | 
						|
		etcdEndpoints := strings.Split(etcdEndpointsEnv, ",")
 | 
						|
 | 
						|
		var etcdSecure bool
 | 
						|
		for _, endpoint := range etcdEndpoints {
 | 
						|
			u, err := xnet.ParseURL(endpoint)
 | 
						|
			if err != nil {
 | 
						|
				logger.FatalIf(err, "Unable to initialize etcd with %s", etcdEndpoints)
 | 
						|
			}
 | 
						|
			// If one of the endpoint is https, we will use https directly.
 | 
						|
			etcdSecure = etcdSecure || u.Scheme == "https"
 | 
						|
		}
 | 
						|
 | 
						|
		var err error
 | 
						|
		if etcdSecure {
 | 
						|
			// This is only to support client side certificate authentication
 | 
						|
			// https://coreos.com/etcd/docs/latest/op-guide/security.html
 | 
						|
			etcdClientCertFile, ok1 := os.LookupEnv("MINIO_ETCD_CLIENT_CERT")
 | 
						|
			etcdClientCertKey, ok2 := os.LookupEnv("MINIO_ETCD_CLIENT_CERT_KEY")
 | 
						|
			var getClientCertificate func(*tls.CertificateRequestInfo) (*tls.Certificate, error)
 | 
						|
			if ok1 && ok2 {
 | 
						|
				getClientCertificate = func(unused *tls.CertificateRequestInfo) (*tls.Certificate, error) {
 | 
						|
					cert, terr := tls.LoadX509KeyPair(etcdClientCertFile, etcdClientCertKey)
 | 
						|
					return &cert, terr
 | 
						|
				}
 | 
						|
			}
 | 
						|
			globalEtcdClient, err = etcd.New(etcd.Config{
 | 
						|
				Endpoints:         etcdEndpoints,
 | 
						|
				DialTimeout:       defaultDialTimeout,
 | 
						|
				DialKeepAliveTime: defaultDialKeepAlive,
 | 
						|
				TLS: &tls.Config{
 | 
						|
					RootCAs:              globalRootCAs,
 | 
						|
					GetClientCertificate: getClientCertificate,
 | 
						|
				},
 | 
						|
			})
 | 
						|
		} else {
 | 
						|
			globalEtcdClient, err = etcd.New(etcd.Config{
 | 
						|
				Endpoints:         etcdEndpoints,
 | 
						|
				DialTimeout:       defaultDialTimeout,
 | 
						|
				DialKeepAliveTime: defaultDialKeepAlive,
 | 
						|
			})
 | 
						|
		}
 | 
						|
		logger.FatalIf(err, "Unable to initialize etcd with %s", etcdEndpoints)
 | 
						|
	}
 | 
						|
 | 
						|
	globalDomainName, globalIsEnvDomainName = os.LookupEnv("MINIO_DOMAIN")
 | 
						|
	if globalDomainName != "" {
 | 
						|
		if _, ok = dns2.IsDomainName(globalDomainName); !ok {
 | 
						|
			logger.Fatal(uiErrInvalidDomainValue(nil).Msg("Unknown value `%s`", globalDomainName), "Invalid MINIO_DOMAIN value in environment variable")
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	minioEndpointsEnv, ok := os.LookupEnv("MINIO_PUBLIC_IPS")
 | 
						|
	if ok {
 | 
						|
		minioEndpoints := strings.Split(minioEndpointsEnv, ",")
 | 
						|
		globalDomainIPs = set.NewStringSet()
 | 
						|
		for i, ip := range minioEndpoints {
 | 
						|
			if net.ParseIP(ip) == nil {
 | 
						|
				logger.FatalIf(errInvalidArgument, "Unable to initialize Minio server with invalid MINIO_PUBLIC_IPS[%d]: %s", i, ip)
 | 
						|
			}
 | 
						|
			globalDomainIPs.Add(ip)
 | 
						|
		}
 | 
						|
	}
 | 
						|
	if globalDomainName != "" && !globalDomainIPs.IsEmpty() && globalEtcdClient != nil {
 | 
						|
		var err error
 | 
						|
		globalDNSConfig, err = dns.NewCoreDNS(globalDomainName, globalDomainIPs, globalMinioPort, globalEtcdClient)
 | 
						|
		logger.FatalIf(err, "Unable to initialize DNS config for %s.", globalDomainName)
 | 
						|
	}
 | 
						|
 | 
						|
	if drives := os.Getenv("MINIO_CACHE_DRIVES"); drives != "" {
 | 
						|
		driveList, err := parseCacheDrives(strings.Split(drives, cacheEnvDelimiter))
 | 
						|
		if err != nil {
 | 
						|
			logger.Fatal(err, "Unable to parse MINIO_CACHE_DRIVES value (%s)", drives)
 | 
						|
		}
 | 
						|
		globalCacheDrives = driveList
 | 
						|
		globalIsDiskCacheEnabled = true
 | 
						|
	}
 | 
						|
 | 
						|
	if excludes := os.Getenv("MINIO_CACHE_EXCLUDE"); excludes != "" {
 | 
						|
		excludeList, err := parseCacheExcludes(strings.Split(excludes, cacheEnvDelimiter))
 | 
						|
		if err != nil {
 | 
						|
			logger.Fatal(err, "Unable to parse MINIO_CACHE_EXCLUDE value (`%s`)", excludes)
 | 
						|
		}
 | 
						|
		globalCacheExcludes = excludeList
 | 
						|
	}
 | 
						|
 | 
						|
	if expiryStr := os.Getenv("MINIO_CACHE_EXPIRY"); expiryStr != "" {
 | 
						|
		expiry, err := strconv.Atoi(expiryStr)
 | 
						|
		if err != nil {
 | 
						|
			logger.Fatal(uiErrInvalidCacheExpiryValue(err), "Unable to parse MINIO_CACHE_EXPIRY value (`%s`)", expiryStr)
 | 
						|
		}
 | 
						|
		globalCacheExpiry = expiry
 | 
						|
	}
 | 
						|
 | 
						|
	if maxUseStr := os.Getenv("MINIO_CACHE_MAXUSE"); maxUseStr != "" {
 | 
						|
		maxUse, err := strconv.Atoi(maxUseStr)
 | 
						|
		if err != nil {
 | 
						|
			logger.Fatal(uiErrInvalidCacheMaxUse(err), "Unable to parse MINIO_CACHE_MAXUSE value (`%s`)", maxUseStr)
 | 
						|
		}
 | 
						|
		// maxUse should be a valid percentage.
 | 
						|
		if maxUse > 0 && maxUse <= 100 {
 | 
						|
			globalCacheMaxUse = maxUse
 | 
						|
		}
 | 
						|
	}
 | 
						|
	// In place update is true by default if the MINIO_UPDATE is not set
 | 
						|
	// or is not set to 'off', if MINIO_UPDATE is set to 'off' then
 | 
						|
	// in-place update is off.
 | 
						|
	globalInplaceUpdateDisabled = strings.EqualFold(os.Getenv("MINIO_UPDATE"), "off")
 | 
						|
 | 
						|
	// Validate and store the storage class env variables only for XL/Dist XL setups
 | 
						|
	if globalIsXL {
 | 
						|
		var err error
 | 
						|
 | 
						|
		// Check for environment variables and parse into storageClass struct
 | 
						|
		if ssc := os.Getenv(standardStorageClassEnv); ssc != "" {
 | 
						|
			globalStandardStorageClass, err = parseStorageClass(ssc)
 | 
						|
			logger.FatalIf(err, "Invalid value set in environment variable %s", standardStorageClassEnv)
 | 
						|
		}
 | 
						|
 | 
						|
		if rrsc := os.Getenv(reducedRedundancyStorageClassEnv); rrsc != "" {
 | 
						|
			globalRRStorageClass, err = parseStorageClass(rrsc)
 | 
						|
			logger.FatalIf(err, "Invalid value set in environment variable %s", reducedRedundancyStorageClassEnv)
 | 
						|
		}
 | 
						|
 | 
						|
		// Validation is done after parsing both the storage classes. This is needed because we need one
 | 
						|
		// storage class value to deduce the correct value of the other storage class.
 | 
						|
		if globalRRStorageClass.Scheme != "" {
 | 
						|
			err = validateParity(globalStandardStorageClass.Parity, globalRRStorageClass.Parity)
 | 
						|
			logger.FatalIf(err, "Invalid value set in environment variable %s", reducedRedundancyStorageClassEnv)
 | 
						|
			globalIsStorageClass = true
 | 
						|
		}
 | 
						|
 | 
						|
		if globalStandardStorageClass.Scheme != "" {
 | 
						|
			err = validateParity(globalStandardStorageClass.Parity, globalRRStorageClass.Parity)
 | 
						|
			logger.FatalIf(err, "Invalid value set in environment variable %s", standardStorageClassEnv)
 | 
						|
			globalIsStorageClass = true
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	// Get WORM environment variable.
 | 
						|
	if worm := os.Getenv("MINIO_WORM"); worm != "" {
 | 
						|
		wormFlag, err := ParseBoolFlag(worm)
 | 
						|
		if err != nil {
 | 
						|
			logger.Fatal(uiErrInvalidWormValue(nil).Msg("Unknown value `%s`", worm), "Invalid MINIO_WORM value in environment variable")
 | 
						|
		}
 | 
						|
 | 
						|
		// worm Envs are set globally, this does not represent
 | 
						|
		// if worm is turned off or on.
 | 
						|
		globalIsEnvWORM = true
 | 
						|
		globalWORMEnabled = bool(wormFlag)
 | 
						|
	}
 | 
						|
 | 
						|
	kmsConf, err := crypto.NewVaultConfig()
 | 
						|
	if err != nil {
 | 
						|
		logger.Fatal(err, "Unable to initialize hashicorp vault")
 | 
						|
	}
 | 
						|
	if kmsConf.Vault.Endpoint != "" {
 | 
						|
		kms, err := crypto.NewVault(kmsConf)
 | 
						|
		if err != nil {
 | 
						|
			logger.Fatal(err, "Unable to initialize KMS")
 | 
						|
		}
 | 
						|
		globalKMS = kms
 | 
						|
		globalKMSKeyID = kmsConf.Vault.Key.Name
 | 
						|
		globalKMSConfig = kmsConf
 | 
						|
	}
 | 
						|
 | 
						|
	if compress := os.Getenv("MINIO_COMPRESS"); compress != "" {
 | 
						|
		globalIsCompressionEnabled = strings.EqualFold(compress, "true")
 | 
						|
	}
 | 
						|
 | 
						|
	compressExtensions := os.Getenv("MINIO_COMPRESS_EXTENSIONS")
 | 
						|
	compressMimeTypes := os.Getenv("MINIO_COMPRESS_MIMETYPES")
 | 
						|
	if compressExtensions != "" || compressMimeTypes != "" {
 | 
						|
		globalIsEnvCompression = true
 | 
						|
		if compressExtensions != "" {
 | 
						|
			extensions, err := parseCompressIncludes(strings.Split(compressExtensions, compressEnvDelimiter))
 | 
						|
			if err != nil {
 | 
						|
				logger.Fatal(err, "Invalid MINIO_COMPRESS_EXTENSIONS value (`%s`)", extensions)
 | 
						|
			}
 | 
						|
			globalCompressExtensions = extensions
 | 
						|
		}
 | 
						|
		if compressMimeTypes != "" {
 | 
						|
			contenttypes, err := parseCompressIncludes(strings.Split(compressMimeTypes, compressEnvDelimiter))
 | 
						|
			if err != nil {
 | 
						|
				logger.Fatal(err, "Invalid MINIO_COMPRESS_MIMETYPES value (`%s`)", contenttypes)
 | 
						|
			}
 | 
						|
			globalCompressMimeTypes = contenttypes
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 |