/* * MinIO Cloud Storage, (C) 2017-2019 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/x509" "errors" "net" "path/filepath" "strings" "time" dns2 "github.com/miekg/dns" "github.com/minio/cli" "github.com/minio/minio-go/v6/pkg/set" "github.com/minio/minio/cmd/config" "github.com/minio/minio/cmd/config/etcd" "github.com/minio/minio/cmd/logger" "github.com/minio/minio/pkg/auth" "github.com/minio/minio/pkg/certs" "github.com/minio/minio/pkg/dns" "github.com/minio/minio/pkg/env" ) func verifyObjectLayerFeatures(name string, objAPI ObjectLayer) { if (globalAutoEncryption || GlobalKMS != nil) && !objAPI.IsEncryptionSupported() { logger.Fatal(errInvalidArgument, "Encryption support is requested but '%s' does not support encryption", name) } if strings.HasPrefix(name, "gateway") { if GlobalGatewaySSE.IsSet() && GlobalKMS == nil { uiErr := config.ErrInvalidGWSSEEnvValue(nil).Msg("MINIO_GATEWAY_SSE set but KMS is not configured") logger.Fatal(uiErr, "Unable to start gateway with SSE") } } if globalIsCompressionEnabled && !objAPI.IsCompressionSupported() { logger.Fatal(errInvalidArgument, "Compression support is requested but '%s' does not support compression", name) } } // 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 updateMsg == "" { return } if globalInplaceUpdateDisabled { logStartupMessage(updateMsg) } else { logStartupMessage(prepareUpdateMessage("Run `mc admin update`", latestReleaseTime.Sub(currentReleaseTime))) } } } func newConfigDirFromCtx(ctx *cli.Context, option string, getDefaultDir func() string) (*ConfigDir, bool) { var dir string var dirSet bool switch { case ctx.IsSet(option): dir = ctx.String(option) dirSet = true case ctx.GlobalIsSet(option): dir = ctx.GlobalString(option) dirSet = true // cli package does not expose parent's option option. Below code is workaround. if dir == "" || dir == getDefaultDir() { dirSet = false // Unset to false since GlobalIsSet() true is a false positive. if ctx.Parent().GlobalIsSet(option) { dir = ctx.Parent().GlobalString(option) dirSet = true } } default: // Neither local nor global option is provided. In this case, try to use // default directory. dir = getDefaultDir() if dir == "" { logger.FatalIf(errInvalidArgument, "%s option must be provided", option) } } if dir == "" { logger.FatalIf(errors.New("empty directory"), "%s directory cannot be empty", option) } // Disallow relative paths, figure out absolute paths. dirAbs, err := filepath.Abs(dir) logger.FatalIf(err, "Unable to fetch absolute path for %s=%s", option, dir) logger.FatalIf(mkdirAllIgnorePerm(dirAbs), "Unable to create directory specified %s=%s", option, dir) return &ConfigDir{path: dirAbs}, dirSet } func handleCommonCmdArgs(ctx *cli.Context) { // Get "json" flag from command line argument and // enable json and quite modes if json flag is turned on. globalCLIContext.JSON = ctx.IsSet("json") || ctx.GlobalIsSet("json") if globalCLIContext.JSON { logger.EnableJSON() } // Get quiet flag from command line argument. globalCLIContext.Quiet = ctx.IsSet("quiet") || ctx.GlobalIsSet("quiet") if globalCLIContext.Quiet { logger.EnableQuiet() } // Get anonymous flag from command line argument. globalCLIContext.Anonymous = ctx.IsSet("anonymous") || ctx.GlobalIsSet("anonymous") if globalCLIContext.Anonymous { logger.EnableAnonymous() } // Fetch address option globalCLIContext.Addr = ctx.GlobalString("address") if globalCLIContext.Addr == "" || globalCLIContext.Addr == ":"+globalMinioDefaultPort { globalCLIContext.Addr = ctx.String("address") } // Set all config, certs and CAs directories. var configSet, certsSet bool globalConfigDir, configSet = newConfigDirFromCtx(ctx, "config-dir", defaultConfigDir.Get) globalCertsDir, certsSet = newConfigDirFromCtx(ctx, "certs-dir", defaultCertsDir.Get) // Remove this code when we deprecate and remove config-dir. // This code is to make sure we inherit from the config-dir // option if certs-dir is not provided. if !certsSet && configSet { globalCertsDir = &ConfigDir{path: filepath.Join(globalConfigDir.Get(), certsDir)} } globalCertsCADir = &ConfigDir{path: filepath.Join(globalCertsDir.Get(), certsCADir)} logger.FatalIf(mkdirAllIgnorePerm(globalCertsCADir.Get()), "Unable to create certs CA directory at %s", globalCertsCADir.Get()) // Check "compat" flag from command line argument. globalCLIContext.StrictS3Compat = ctx.IsSet("compat") || ctx.GlobalIsSet("compat") } func handleCommonEnvVars() { accessKey := env.Get(config.EnvAccessKey, "") secretKey := env.Get(config.EnvSecretKey, "") if accessKey != "" && secretKey != "" { cred, err := auth.CreateCredentials(accessKey, secretKey) if err != nil { logger.Fatal(config.ErrInvalidCredentials(err), "Unable to validate credentials inherited from the shell environment") } cred.Expiration = timeSentinel // credential Envs are set globally. globalIsEnvCreds = true globalActiveCred = cred } if browser := env.Get(config.EnvBrowser, "on"); browser != "" { browserFlag, err := config.ParseBoolFlag(browser) if err != nil { logger.Fatal(config.ErrInvalidBrowserValue(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) } var err error globalEtcdClient, err = etcd.New(globalRootCAs) if err != nil { logger.FatalIf(err, "Unable to initialize etcd config") } for _, domainName := range strings.Split(env.Get(config.EnvDomain, ""), ",") { if domainName != "" { if _, ok := dns2.IsDomainName(domainName); !ok { logger.Fatal(config.ErrInvalidDomainValue(nil).Msg("Unknown value `%s`", domainName), "Invalid MINIO_DOMAIN value in environment variable") } globalDomainNames = append(globalDomainNames, domainName) } } minioEndpointsEnv, ok := env.Lookup(config.EnvPublicIPs) if ok { minioEndpoints := strings.Split(minioEndpointsEnv, ",") var domainIPs = set.NewStringSet() for _, endpoint := range minioEndpoints { if net.ParseIP(endpoint) == nil { // Checking if the IP is a DNS entry. addrs, err := net.LookupHost(endpoint) if err != nil { logger.FatalIf(err, "Unable to initialize MinIO server with [%s] invalid entry found in MINIO_PUBLIC_IPS", endpoint) } for _, addr := range addrs { domainIPs.Add(addr) } continue } domainIPs.Add(endpoint) } updateDomainIPs(domainIPs) } else { // Add found interfaces IP address to global domain IPS, // loopback addresses will be naturally dropped. updateDomainIPs(localIP4) } if len(globalDomainNames) != 0 && !globalDomainIPs.IsEmpty() && globalEtcdClient != nil { var err error globalDNSConfig, err = dns.NewCoreDNS(globalDomainNames, globalDomainIPs, globalMinioPort, globalEtcdClient) logger.FatalIf(err, "Unable to initialize DNS config for %s.", globalDomainNames) } // 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(env.Get(config.EnvUpdate, "off"), "off") // Get WORM environment variable. if worm := env.Get(config.EnvWorm, "off"); worm != "" { wormFlag, err := config.ParseBoolFlag(worm) if err != nil { logger.Fatal(config.ErrInvalidWormValue(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) } } func logStartupMessage(msg string, data ...interface{}) { if globalConsoleSys != nil { globalConsoleSys.Send(msg, string(logger.All)) } logger.StartupMessage(msg, data...) } func getTLSConfig() (x509Certs []*x509.Certificate, c *certs.Certs, secureConn bool, err error) { if !(isFile(getPublicCertFile()) && isFile(getPrivateKeyFile())) { return nil, nil, false, nil } if x509Certs, err = config.ParsePublicCertFile(getPublicCertFile()); err != nil { return nil, nil, false, err } c, err = certs.New(getPublicCertFile(), getPrivateKeyFile(), config.LoadX509KeyPair) if err != nil { return nil, nil, false, err } secureConn = true return x509Certs, c, secureConn, nil }