storj.io/minio@v0.0.0-20230509071714-0cbc90f649b1/cmd/config-current.go (about)

     1  /*
     2   * MinIO Cloud Storage, (C) 2016-2019 MinIO, Inc.
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *     http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  package cmd
    18  
    19  import (
    20  	"context"
    21  	"errors"
    22  	"fmt"
    23  	"strings"
    24  	"sync"
    25  
    26  	"storj.io/minio/cmd/config"
    27  	"storj.io/minio/cmd/config/api"
    28  	"storj.io/minio/cmd/config/cache"
    29  	"storj.io/minio/cmd/config/compress"
    30  	"storj.io/minio/cmd/config/heal"
    31  	xldap "storj.io/minio/cmd/config/identity/ldap"
    32  	"storj.io/minio/cmd/config/identity/openid"
    33  	"storj.io/minio/cmd/config/notify"
    34  	"storj.io/minio/cmd/config/policy/opa"
    35  	"storj.io/minio/cmd/config/scanner"
    36  	"storj.io/minio/cmd/config/storageclass"
    37  	"storj.io/minio/cmd/crypto"
    38  	xhttp "storj.io/minio/cmd/http"
    39  	"storj.io/minio/cmd/logger"
    40  	"storj.io/minio/cmd/logger/target/http"
    41  	"storj.io/minio/pkg/env"
    42  	"storj.io/minio/pkg/kms"
    43  	"storj.io/minio/pkg/madmin"
    44  )
    45  
    46  func initHelp() {
    47  	var kvs = map[string]config.KVS{
    48  		config.CacheSubSys:          cache.DefaultKVS,
    49  		config.CompressionSubSys:    compress.DefaultKVS,
    50  		config.IdentityLDAPSubSys:   xldap.DefaultKVS,
    51  		config.IdentityOpenIDSubSys: openid.DefaultKVS,
    52  		config.PolicyOPASubSys:      opa.DefaultKVS,
    53  		config.RegionSubSys:         config.DefaultRegionKVS,
    54  		config.APISubSys:            api.DefaultKVS,
    55  		config.CredentialsSubSys:    config.DefaultCredentialKVS,
    56  		config.LoggerWebhookSubSys:  logger.DefaultKVS,
    57  		config.AuditWebhookSubSys:   logger.DefaultAuditKVS,
    58  		config.HealSubSys:           heal.DefaultKVS,
    59  		config.ScannerSubSys:        scanner.DefaultKVS,
    60  	}
    61  	for k, v := range notify.DefaultNotificationKVS {
    62  		kvs[k] = v
    63  	}
    64  	if globalIsErasure {
    65  		kvs[config.StorageClassSubSys] = storageclass.DefaultKVS
    66  	}
    67  	config.RegisterDefaultKVS(kvs)
    68  
    69  	// Captures help for each sub-system
    70  	var helpSubSys = config.HelpKVS{
    71  		config.HelpKV{
    72  			Key:         config.RegionSubSys,
    73  			Description: "label the location of the server",
    74  		},
    75  		config.HelpKV{
    76  			Key:         config.CacheSubSys,
    77  			Description: "add caching storage tier",
    78  		},
    79  		config.HelpKV{
    80  			Key:         config.CompressionSubSys,
    81  			Description: "enable server side compression of objects",
    82  		},
    83  		config.HelpKV{
    84  			Key:         config.IdentityOpenIDSubSys,
    85  			Description: "enable OpenID SSO support",
    86  		},
    87  		config.HelpKV{
    88  			Key:         config.IdentityLDAPSubSys,
    89  			Description: "enable LDAP SSO support",
    90  		},
    91  		config.HelpKV{
    92  			Key:         config.PolicyOPASubSys,
    93  			Description: "[DEPRECATED] enable external OPA for policy enforcement",
    94  		},
    95  		config.HelpKV{
    96  			Key:         config.KmsVaultSubSys,
    97  			Description: "enable external HashiCorp Vault key management service",
    98  		},
    99  		config.HelpKV{
   100  			Key:         config.KmsKesSubSys,
   101  			Description: "enable external MinIO key encryption service",
   102  		},
   103  		config.HelpKV{
   104  			Key:         config.APISubSys,
   105  			Description: "manage global HTTP API call specific features, such as throttling, authentication types, etc.",
   106  		},
   107  		config.HelpKV{
   108  			Key:         config.HealSubSys,
   109  			Description: "manage object healing frequency and bitrot verification checks",
   110  		},
   111  		config.HelpKV{
   112  			Key:         config.ScannerSubSys,
   113  			Description: "manage namespace scanning for usage calculation, lifecycle, healing and more",
   114  		},
   115  		config.HelpKV{
   116  			Key:             config.LoggerWebhookSubSys,
   117  			Description:     "send server logs to webhook endpoints",
   118  			MultipleTargets: true,
   119  		},
   120  		config.HelpKV{
   121  			Key:             config.AuditWebhookSubSys,
   122  			Description:     "send audit logs to webhook endpoints",
   123  			MultipleTargets: true,
   124  		},
   125  		config.HelpKV{
   126  			Key:             config.NotifyWebhookSubSys,
   127  			Description:     "publish bucket notifications to webhook endpoints",
   128  			MultipleTargets: true,
   129  		},
   130  		config.HelpKV{
   131  			Key:             config.NotifyAMQPSubSys,
   132  			Description:     "publish bucket notifications to AMQP endpoints",
   133  			MultipleTargets: true,
   134  		},
   135  		config.HelpKV{
   136  			Key:             config.NotifyKafkaSubSys,
   137  			Description:     "publish bucket notifications to Kafka endpoints",
   138  			MultipleTargets: true,
   139  		},
   140  		config.HelpKV{
   141  			Key:             config.NotifyMQTTSubSys,
   142  			Description:     "publish bucket notifications to MQTT endpoints",
   143  			MultipleTargets: true,
   144  		},
   145  		config.HelpKV{
   146  			Key:             config.NotifyNATSSubSys,
   147  			Description:     "publish bucket notifications to NATS endpoints",
   148  			MultipleTargets: true,
   149  		},
   150  		config.HelpKV{
   151  			Key:             config.NotifyNSQSubSys,
   152  			Description:     "publish bucket notifications to NSQ endpoints",
   153  			MultipleTargets: true,
   154  		},
   155  		config.HelpKV{
   156  			Key:             config.NotifyMySQLSubSys,
   157  			Description:     "publish bucket notifications to MySQL databases",
   158  			MultipleTargets: true,
   159  		},
   160  		config.HelpKV{
   161  			Key:             config.NotifyPostgresSubSys,
   162  			Description:     "publish bucket notifications to Postgres databases",
   163  			MultipleTargets: true,
   164  		},
   165  		config.HelpKV{
   166  			Key:             config.NotifyESSubSys,
   167  			Description:     "publish bucket notifications to Elasticsearch endpoints",
   168  			MultipleTargets: true,
   169  		},
   170  		config.HelpKV{
   171  			Key:             config.NotifyRedisSubSys,
   172  			Description:     "publish bucket notifications to Redis datastores",
   173  			MultipleTargets: true,
   174  		},
   175  	}
   176  
   177  	if globalIsErasure {
   178  		helpSubSys = append(helpSubSys, config.HelpKV{})
   179  		copy(helpSubSys[2:], helpSubSys[1:])
   180  		helpSubSys[1] = config.HelpKV{
   181  			Key:         config.StorageClassSubSys,
   182  			Description: "define object level redundancy",
   183  		}
   184  	}
   185  
   186  	var helpMap = map[string]config.HelpKVS{
   187  		"":                          helpSubSys, // Help for all sub-systems.
   188  		config.RegionSubSys:         config.RegionHelp,
   189  		config.APISubSys:            api.Help,
   190  		config.StorageClassSubSys:   storageclass.Help,
   191  		config.CacheSubSys:          cache.Help,
   192  		config.CompressionSubSys:    compress.Help,
   193  		config.HealSubSys:           heal.Help,
   194  		config.ScannerSubSys:        scanner.Help,
   195  		config.IdentityOpenIDSubSys: openid.Help,
   196  		config.IdentityLDAPSubSys:   xldap.Help,
   197  		config.PolicyOPASubSys:      opa.Help,
   198  		config.LoggerWebhookSubSys:  logger.Help,
   199  		config.AuditWebhookSubSys:   logger.HelpAudit,
   200  		config.NotifyAMQPSubSys:     notify.HelpAMQP,
   201  		config.NotifyKafkaSubSys:    notify.HelpKafka,
   202  		config.NotifyMQTTSubSys:     notify.HelpMQTT,
   203  		config.NotifyNATSSubSys:     notify.HelpNATS,
   204  		config.NotifyNSQSubSys:      notify.HelpNSQ,
   205  		config.NotifyMySQLSubSys:    notify.HelpMySQL,
   206  		config.NotifyPostgresSubSys: notify.HelpPostgres,
   207  		config.NotifyRedisSubSys:    notify.HelpRedis,
   208  		config.NotifyWebhookSubSys:  notify.HelpWebhook,
   209  		config.NotifyESSubSys:       notify.HelpES,
   210  	}
   211  
   212  	config.RegisterHelpSubSys(helpMap)
   213  }
   214  
   215  var (
   216  	// globalServerConfig server config.
   217  	globalServerConfig   config.Config
   218  	globalServerConfigMu sync.RWMutex
   219  )
   220  
   221  func validateConfig(s config.Config, setDriveCounts []int) error {
   222  	// We must have a global lock for this so nobody else modifies env while we do.
   223  	defer env.LockSetEnv()()
   224  
   225  	// Disable merging env values with config for validation.
   226  	env.SetEnvOff()
   227  
   228  	// Enable env values to validate KMS.
   229  	defer env.SetEnvOn()
   230  
   231  	if _, err := config.LookupCreds(s[config.CredentialsSubSys][config.Default]); err != nil {
   232  		return err
   233  	}
   234  
   235  	if _, err := config.LookupRegion(s[config.RegionSubSys][config.Default]); err != nil {
   236  		return err
   237  	}
   238  
   239  	if _, err := api.LookupConfig(s[config.APISubSys][config.Default]); err != nil {
   240  		return err
   241  	}
   242  
   243  	if globalIsErasure {
   244  		for _, setDriveCount := range setDriveCounts {
   245  			if _, err := storageclass.LookupConfig(s[config.StorageClassSubSys][config.Default], setDriveCount); err != nil {
   246  				return err
   247  			}
   248  		}
   249  	}
   250  
   251  	if _, err := cache.LookupConfig(s[config.CacheSubSys][config.Default]); err != nil {
   252  		return err
   253  	}
   254  
   255  	compCfg, err := compress.LookupConfig(s[config.CompressionSubSys][config.Default])
   256  	if err != nil {
   257  		return err
   258  	}
   259  	objAPI := newObjectLayerFn()
   260  	if objAPI != nil {
   261  		if compCfg.Enabled && !objAPI.IsCompressionSupported() {
   262  			return fmt.Errorf("Backend does not support compression")
   263  		}
   264  	}
   265  
   266  	if _, err = heal.LookupConfig(s[config.HealSubSys][config.Default]); err != nil {
   267  		return err
   268  	}
   269  
   270  	if _, err = scanner.LookupConfig(s[config.ScannerSubSys][config.Default]); err != nil {
   271  		return err
   272  	}
   273  
   274  	if _, err := openid.LookupConfig(s[config.IdentityOpenIDSubSys][config.Default],
   275  		NewGatewayHTTPTransport(), xhttp.DrainBody); err != nil {
   276  		return err
   277  	}
   278  
   279  	{
   280  		cfg, err := xldap.Lookup(s[config.IdentityLDAPSubSys][config.Default],
   281  			globalRootCAs)
   282  		if err != nil {
   283  			return err
   284  		}
   285  		if cfg.Enabled {
   286  			conn, cerr := cfg.Connect()
   287  			if cerr != nil {
   288  				return cerr
   289  			}
   290  			conn.Close()
   291  		}
   292  	}
   293  
   294  	if _, err := opa.LookupConfig(s[config.PolicyOPASubSys][config.Default],
   295  		NewGatewayHTTPTransport(), xhttp.DrainBody); err != nil {
   296  		return err
   297  	}
   298  
   299  	if _, err := logger.LookupConfig(s); err != nil {
   300  		return err
   301  	}
   302  
   303  	return notify.TestNotificationTargets(GlobalContext, s, NewGatewayHTTPTransport(), GlobalNotificationSys.ConfiguredTargetIDs())
   304  }
   305  
   306  func lookupConfigs(s config.Config, setDriveCounts []int) {
   307  	ctx := GlobalContext
   308  
   309  	var err error
   310  	if !globalActiveCred.IsValid() {
   311  		// Env doesn't seem to be set, we fallback to lookup creds from the config.
   312  		globalActiveCred, err = config.LookupCreds(s[config.CredentialsSubSys][config.Default])
   313  		if err != nil {
   314  			logger.LogIf(ctx, fmt.Errorf("Invalid credentials configuration: %w", err))
   315  		}
   316  	}
   317  
   318  	globalServerRegion, err = config.LookupRegion(s[config.RegionSubSys][config.Default])
   319  	if err != nil {
   320  		logger.LogIf(ctx, fmt.Errorf("Invalid region configuration: %w", err))
   321  	}
   322  
   323  	apiConfig, err := api.LookupConfig(s[config.APISubSys][config.Default])
   324  	if err != nil {
   325  		logger.LogIf(ctx, fmt.Errorf("Invalid api configuration: %w", err))
   326  	}
   327  
   328  	globalAPIConfig.init(apiConfig, setDriveCounts)
   329  
   330  	// Initialize remote instance transport once.
   331  	getRemoteInstanceTransportOnce.Do(func() {
   332  		getRemoteInstanceTransport = newGatewayHTTPTransport(apiConfig.RemoteTransportDeadline)
   333  	})
   334  
   335  	if globalIsErasure {
   336  		for i, setDriveCount := range setDriveCounts {
   337  			sc, err := storageclass.LookupConfig(s[config.StorageClassSubSys][config.Default], setDriveCount)
   338  			if err != nil {
   339  				logger.LogIf(ctx, fmt.Errorf("Unable to initialize storage class config: %w", err))
   340  				break
   341  			}
   342  			// if we validated all setDriveCounts and it was successful
   343  			// proceed to store the correct storage class globally.
   344  			if i == len(setDriveCounts)-1 {
   345  				globalStorageClass.Update(sc)
   346  			}
   347  		}
   348  	}
   349  
   350  	globalCacheConfig, err = cache.LookupConfig(s[config.CacheSubSys][config.Default])
   351  	if err != nil {
   352  		if GlobalIsGateway {
   353  			logger.FatalIf(err, "Unable to setup cache")
   354  		} else {
   355  			logger.LogIf(ctx, fmt.Errorf("Unable to setup cache: %w", err))
   356  		}
   357  	}
   358  
   359  	if globalCacheConfig.Enabled {
   360  		if cacheEncKey := env.Get(cache.EnvCacheEncryptionKey, ""); cacheEncKey != "" {
   361  			globalCacheKMS, err = kms.Parse(cacheEncKey)
   362  			if err != nil {
   363  				logger.LogIf(ctx, fmt.Errorf("Unable to setup encryption cache: %w", err))
   364  			}
   365  		}
   366  	}
   367  
   368  	globalAutoEncryption = crypto.LookupAutoEncryption() // Enable auto-encryption if enabled
   369  	if globalAutoEncryption && GlobalKMS == nil {
   370  		logger.Fatal(errors.New("no KMS configured"), "MINIO_KMS_AUTO_ENCRYPTION requires a valid KMS configuration")
   371  	}
   372  
   373  	globalOpenIDConfig, err = openid.LookupConfig(s[config.IdentityOpenIDSubSys][config.Default],
   374  		NewGatewayHTTPTransport(), xhttp.DrainBody)
   375  	if err != nil {
   376  		logger.LogIf(ctx, fmt.Errorf("Unable to initialize OpenID: %w", err))
   377  	}
   378  
   379  	opaCfg, err := opa.LookupConfig(s[config.PolicyOPASubSys][config.Default],
   380  		NewGatewayHTTPTransport(), xhttp.DrainBody)
   381  	if err != nil {
   382  		logger.LogIf(ctx, fmt.Errorf("Unable to initialize OPA: %w", err))
   383  	}
   384  
   385  	globalOpenIDValidators = getOpenIDValidators(globalOpenIDConfig)
   386  	GlobalPolicyOPA = opa.New(opaCfg)
   387  
   388  	globalLDAPConfig, err = xldap.Lookup(s[config.IdentityLDAPSubSys][config.Default],
   389  		globalRootCAs)
   390  	if err != nil {
   391  		logger.LogIf(ctx, fmt.Errorf("Unable to parse LDAP configuration: %w", err))
   392  	}
   393  
   394  	// Load logger targets based on user's configuration
   395  	loggerUserAgent := getUserAgent(getMinioMode())
   396  
   397  	loggerCfg, err := logger.LookupConfig(s)
   398  	if err != nil {
   399  		logger.LogIf(ctx, fmt.Errorf("Unable to initialize logger: %w", err))
   400  	}
   401  
   402  	for k, l := range loggerCfg.HTTP {
   403  		if l.Enabled {
   404  			// Enable http logging
   405  			if err = logger.AddTarget(
   406  				http.New(
   407  					http.WithTargetName(k),
   408  					http.WithEndpoint(l.Endpoint),
   409  					http.WithAuthToken(l.AuthToken),
   410  					http.WithUserAgent(loggerUserAgent),
   411  					http.WithLogKind(string(logger.All)),
   412  					http.WithTransport(NewGatewayHTTPTransport()),
   413  				),
   414  			); err != nil {
   415  				logger.LogIf(ctx, fmt.Errorf("Unable to initialize console HTTP target: %w", err))
   416  			}
   417  		}
   418  	}
   419  
   420  	for k, l := range loggerCfg.Audit {
   421  		if l.Enabled {
   422  			// Enable http audit logging
   423  			if err = logger.AddAuditTarget(
   424  				http.New(
   425  					http.WithTargetName(k),
   426  					http.WithEndpoint(l.Endpoint),
   427  					http.WithAuthToken(l.AuthToken),
   428  					http.WithUserAgent(loggerUserAgent),
   429  					http.WithLogKind(string(logger.All)),
   430  					http.WithTransport(NewGatewayHTTPTransportWithClientCerts(l.ClientCert, l.ClientKey)),
   431  				),
   432  			); err != nil {
   433  				logger.LogIf(ctx, fmt.Errorf("Unable to initialize audit HTTP target: %w", err))
   434  			}
   435  		}
   436  	}
   437  
   438  	globalConfigTargetList, err = notify.GetNotificationTargets(GlobalContext, s, NewGatewayHTTPTransport(), false)
   439  	if err != nil {
   440  		logger.LogIf(ctx, fmt.Errorf("Unable to initialize notification target(s): %w", err))
   441  	}
   442  
   443  	globalEnvTargetList, err = notify.GetNotificationTargets(GlobalContext, newServerConfig(), NewGatewayHTTPTransport(), true)
   444  	if err != nil {
   445  		logger.LogIf(ctx, fmt.Errorf("Unable to initialize notification target(s): %w", err))
   446  	}
   447  
   448  	// Apply dynamic config values
   449  	logger.LogIf(ctx, applyDynamicConfig(ctx, newObjectLayerFn(), s))
   450  }
   451  
   452  // applyDynamicConfig will apply dynamic config values.
   453  // Dynamic systems should be in config.SubSystemsDynamic as well.
   454  func applyDynamicConfig(ctx context.Context, objAPI ObjectLayer, s config.Config) error {
   455  	if objAPI == nil {
   456  		return nil
   457  	}
   458  
   459  	// Read all dynamic configs.
   460  	// API
   461  	apiConfig, err := api.LookupConfig(s[config.APISubSys][config.Default])
   462  	if err != nil {
   463  		logger.LogIf(ctx, fmt.Errorf("Invalid api configuration: %w", err))
   464  	}
   465  
   466  	// Compression
   467  	cmpCfg, err := compress.LookupConfig(s[config.CompressionSubSys][config.Default])
   468  	if err != nil {
   469  		return fmt.Errorf("Unable to setup Compression: %w", err)
   470  	}
   471  
   472  	// Validate if the object layer supports compression.
   473  	if cmpCfg.Enabled && !objAPI.IsCompressionSupported() {
   474  		return fmt.Errorf("Backend does not support compression")
   475  	}
   476  
   477  	// Heal
   478  	healCfg, err := heal.LookupConfig(s[config.HealSubSys][config.Default])
   479  	if err != nil {
   480  		return fmt.Errorf("Unable to apply heal config: %w", err)
   481  	}
   482  
   483  	// Scanner
   484  	scannerCfg, err := scanner.LookupConfig(s[config.ScannerSubSys][config.Default])
   485  	if err != nil {
   486  		return fmt.Errorf("Unable to apply scanner config: %w", err)
   487  	}
   488  
   489  	// Apply configurations.
   490  	// We should not fail after this.
   491  	globalAPIConfig.init(apiConfig, objAPI.SetDriveCounts())
   492  
   493  	globalCompressConfigMu.Lock()
   494  	globalCompressConfig = cmpCfg
   495  	globalCompressConfigMu.Unlock()
   496  
   497  	globalHealConfigMu.Lock()
   498  	globalHealConfig = healCfg
   499  	globalHealConfigMu.Unlock()
   500  
   501  	// update dynamic scanner values.
   502  	scannerCycle.Update(scannerCfg.Cycle)
   503  	logger.LogIf(ctx, scannerSleeper.Update(scannerCfg.Delay, scannerCfg.MaxWait))
   504  
   505  	// Update all dynamic config values in memory.
   506  	globalServerConfigMu.Lock()
   507  	defer globalServerConfigMu.Unlock()
   508  	if globalServerConfig != nil {
   509  		for k := range config.SubSystemsDynamic {
   510  			globalServerConfig[k] = s[k]
   511  		}
   512  	}
   513  	return nil
   514  }
   515  
   516  // Help - return sub-system level help
   517  type Help struct {
   518  	SubSys          string         `json:"subSys"`
   519  	Description     string         `json:"description"`
   520  	MultipleTargets bool           `json:"multipleTargets"`
   521  	KeysHelp        config.HelpKVS `json:"keysHelp"`
   522  }
   523  
   524  // GetHelp - returns help for sub-sys, a key for a sub-system or all the help.
   525  func GetHelp(subSys, key string, envOnly bool) (Help, error) {
   526  	if len(subSys) == 0 {
   527  		return Help{KeysHelp: config.HelpSubSysMap[subSys]}, nil
   528  	}
   529  	subSystemValue := strings.SplitN(subSys, config.SubSystemSeparator, 2)
   530  	if len(subSystemValue) == 0 {
   531  		return Help{}, config.Errorf("invalid number of arguments %s", subSys)
   532  	}
   533  
   534  	subSys = subSystemValue[0]
   535  
   536  	subSysHelp, ok := config.HelpSubSysMap[""].Lookup(subSys)
   537  	if !ok {
   538  		return Help{}, config.Errorf("unknown sub-system %s", subSys)
   539  	}
   540  
   541  	h, ok := config.HelpSubSysMap[subSys]
   542  	if !ok {
   543  		return Help{}, config.Errorf("unknown sub-system %s", subSys)
   544  	}
   545  	if key != "" {
   546  		value, ok := h.Lookup(key)
   547  		if !ok {
   548  			return Help{}, config.Errorf("unknown key %s for sub-system %s",
   549  				key, subSys)
   550  		}
   551  		h = config.HelpKVS{value}
   552  	}
   553  
   554  	envHelp := config.HelpKVS{}
   555  	if envOnly {
   556  		// Only for multiple targets, make sure
   557  		// to list the ENV, for regular k/v EnableKey is
   558  		// implicit, for ENVs we cannot make it implicit.
   559  		if subSysHelp.MultipleTargets {
   560  			envK := config.EnvPrefix + strings.Join([]string{
   561  				strings.ToTitle(subSys), strings.ToTitle(madmin.EnableKey),
   562  			}, config.EnvWordDelimiter)
   563  			envHelp = append(envHelp, config.HelpKV{
   564  				Key:         envK,
   565  				Description: fmt.Sprintf("enable %s target, default is 'off'", subSys),
   566  				Optional:    false,
   567  				Type:        "on|off",
   568  			})
   569  		}
   570  		for _, hkv := range h {
   571  			envK := config.EnvPrefix + strings.Join([]string{
   572  				strings.ToTitle(subSys), strings.ToTitle(hkv.Key),
   573  			}, config.EnvWordDelimiter)
   574  			envHelp = append(envHelp, config.HelpKV{
   575  				Key:         envK,
   576  				Description: hkv.Description,
   577  				Optional:    hkv.Optional,
   578  				Type:        hkv.Type,
   579  			})
   580  		}
   581  		h = envHelp
   582  	}
   583  
   584  	return Help{
   585  		SubSys:          subSys,
   586  		Description:     subSysHelp.Description,
   587  		MultipleTargets: subSysHelp.MultipleTargets,
   588  		KeysHelp:        h,
   589  	}, nil
   590  }
   591  
   592  func newServerConfig() config.Config {
   593  	return config.New()
   594  }
   595  
   596  // newSrvConfig - initialize a new server config, saves env parameters if
   597  // found, otherwise use default parameters
   598  func newSrvConfig(objAPI ObjectLayer) error {
   599  	// Initialize server config.
   600  	srvCfg := newServerConfig()
   601  
   602  	// hold the mutex lock before a new config is assigned.
   603  	globalServerConfigMu.Lock()
   604  	globalServerConfig = srvCfg
   605  	globalServerConfigMu.Unlock()
   606  
   607  	// Save config into file.
   608  	return saveServerConfig(GlobalContext, objAPI, globalServerConfig)
   609  }
   610  
   611  func getValidConfig(objAPI ObjectLayer) (config.Config, error) {
   612  	return readServerConfig(GlobalContext, objAPI)
   613  }
   614  
   615  // loadConfig - loads a new config from disk, overrides params
   616  // from env if found and valid
   617  func loadConfig(objAPI ObjectLayer) error {
   618  	srvCfg, err := getValidConfig(objAPI)
   619  	if err != nil {
   620  		return err
   621  	}
   622  
   623  	// Override any values from ENVs.
   624  	lookupConfigs(srvCfg, objAPI.SetDriveCounts())
   625  
   626  	// hold the mutex lock before a new config is assigned.
   627  	globalServerConfigMu.Lock()
   628  	globalServerConfig = srvCfg
   629  	globalServerConfigMu.Unlock()
   630  
   631  	return nil
   632  }
   633  
   634  // getOpenIDValidators - returns ValidatorList which contains
   635  // enabled providers in server config.
   636  // A new authentication provider is added like below
   637  // * Add a new provider in pkg/iam/openid package.
   638  func getOpenIDValidators(cfg openid.Config) *openid.Validators {
   639  	validators := openid.NewValidators()
   640  
   641  	if cfg.JWKS.URL != nil {
   642  		validators.Add(openid.NewJWT(cfg))
   643  	}
   644  
   645  	return validators
   646  }