// Copyright (c) 2015-2021 MinIO, Inc. // // This file is part of MinIO Object Storage stack // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . package cmd import ( "context" "encoding/json" "errors" "path" "strings" "github.com/minio/minio/internal/config" "github.com/minio/minio/internal/config/compress" xldap "github.com/minio/minio/internal/config/identity/ldap" "github.com/minio/minio/internal/config/identity/openid" "github.com/minio/minio/internal/config/notify" "github.com/minio/minio/internal/config/policy/opa" "github.com/minio/minio/internal/config/storageclass" "github.com/minio/minio/internal/event/target" "github.com/minio/minio/internal/logger" xnet "github.com/minio/pkg/v2/net" "github.com/minio/pkg/v2/quick" ) // Save config file to corresponding backend func Save(configFile string, data interface{}) error { return quick.SaveConfig(data, configFile, globalEtcdClient) } // Load config from backend func Load(configFile string, data interface{}) (quick.Config, error) { return quick.LoadConfig(configFile, globalEtcdClient, data) } func readConfigWithoutMigrate(ctx context.Context, objAPI ObjectLayer) (config.Config, error) { // Construct path to config.json for the given bucket. configFile := path.Join(minioConfigPrefix, minioConfigFile) configFiles := []string{ getConfigFile(), getConfigFile() + ".deprecated", configFile, } newServerCfg := func() (config.Config, error) { // Initialize server config. srvCfg := newServerConfig() return srvCfg, saveServerConfig(ctx, objAPI, srvCfg) } var data []byte var err error cfg := &serverConfigV33{} for _, cfgFile := range configFiles { if _, err = Load(cfgFile, cfg); err != nil { if !osIsNotExist(err) && !osIsPermission(err) { return nil, err } continue } data, _ = json.Marshal(cfg) break } if osIsPermission(err) { logger.Info("Older config found but is not readable %s, proceeding to read config from other places", err) } if osIsNotExist(err) || osIsPermission(err) || len(data) == 0 { data, err = readConfig(GlobalContext, objAPI, configFile) if err != nil { // when config.json is not found, then we freshly initialize. if errors.Is(err, errConfigNotFound) { return newServerCfg() } return nil, err } data, err = decryptData(data, configFile) if err != nil { return nil, err } newCfg, err := readServerConfig(GlobalContext, objAPI, data) if err == nil { return newCfg, nil } // Read older `.minio.sys/config/config.json`, if not // possible just fail. if err = json.Unmarshal(data, cfg); err != nil { // Unable to parse old JSON simply re-initialize a new one. return newServerCfg() } } if !globalCredViaEnv && cfg.Credential.IsValid() { // Preserve older credential if we do not have // root credentials set via environment variable. globalActiveCred = cfg.Credential } // Init compression config. For future migration, Compression config needs to be copied over from previous version. switch cfg.Version { case "29": // V29 -> V30 cfg.Compression.Enabled = false cfg.Compression.Extensions = strings.Split(compress.DefaultExtensions, config.ValueSeparator) cfg.Compression.MimeTypes = strings.Split(compress.DefaultMimeTypes, config.ValueSeparator) case "30": // V30 -> V31 cfg.OpenID = openid.Config{} cfg.Policy.OPA = opa.Args{ URL: &xnet.URL{}, AuthToken: "", } case "31": // V31 -> V32 cfg.Notify.NSQ = make(map[string]target.NSQArgs) cfg.Notify.NSQ["1"] = target.NSQArgs{} } // Move to latest. cfg.Version = "33" newCfg := newServerConfig() config.SetRegion(newCfg, cfg.Region) storageclass.SetStorageClass(newCfg, cfg.StorageClass) for k, loggerArgs := range cfg.Logger.HTTP { logger.SetLoggerHTTP(newCfg, k, loggerArgs) } for k, auditArgs := range cfg.Logger.AuditWebhook { logger.SetLoggerHTTPAudit(newCfg, k, auditArgs) } xldap.SetIdentityLDAP(newCfg, cfg.LDAPServerConfig) opa.SetPolicyOPAConfig(newCfg, cfg.Policy.OPA) compress.SetCompressionConfig(newCfg, cfg.Compression) for k, args := range cfg.Notify.AMQP { notify.SetNotifyAMQP(newCfg, k, args) } for k, args := range cfg.Notify.Elasticsearch { notify.SetNotifyES(newCfg, k, args) } for k, args := range cfg.Notify.Kafka { notify.SetNotifyKafka(newCfg, k, args) } for k, args := range cfg.Notify.MQTT { notify.SetNotifyMQTT(newCfg, k, args) } for k, args := range cfg.Notify.MySQL { notify.SetNotifyMySQL(newCfg, k, args) } for k, args := range cfg.Notify.NATS { notify.SetNotifyNATS(newCfg, k, args) } for k, args := range cfg.Notify.NSQ { notify.SetNotifyNSQ(newCfg, k, args) } for k, args := range cfg.Notify.PostgreSQL { notify.SetNotifyPostgres(newCfg, k, args) } for k, args := range cfg.Notify.Redis { notify.SetNotifyRedis(newCfg, k, args) } for k, args := range cfg.Notify.Webhook { notify.SetNotifyWebhook(newCfg, k, args) } return newCfg, nil }