github.com/minio/minio@v0.0.0-20240328213742-3f72439b8a27/cmd/config-current.go (about)

     1  // Copyright (c) 2015-2021 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  	"bytes"
    22  	"context"
    23  	"errors"
    24  	"fmt"
    25  	"strings"
    26  	"sync"
    27  
    28  	"github.com/minio/kms-go/kes"
    29  	"github.com/minio/minio/internal/auth"
    30  	"github.com/minio/minio/internal/config/browser"
    31  	"github.com/minio/minio/internal/kms"
    32  
    33  	"github.com/minio/madmin-go/v3"
    34  	"github.com/minio/minio/internal/config"
    35  	"github.com/minio/minio/internal/config/api"
    36  	"github.com/minio/minio/internal/config/batch"
    37  	"github.com/minio/minio/internal/config/cache"
    38  	"github.com/minio/minio/internal/config/callhome"
    39  	"github.com/minio/minio/internal/config/compress"
    40  	"github.com/minio/minio/internal/config/dns"
    41  	"github.com/minio/minio/internal/config/drive"
    42  	"github.com/minio/minio/internal/config/etcd"
    43  	"github.com/minio/minio/internal/config/heal"
    44  	xldap "github.com/minio/minio/internal/config/identity/ldap"
    45  	"github.com/minio/minio/internal/config/identity/openid"
    46  	idplugin "github.com/minio/minio/internal/config/identity/plugin"
    47  	xtls "github.com/minio/minio/internal/config/identity/tls"
    48  	"github.com/minio/minio/internal/config/ilm"
    49  	"github.com/minio/minio/internal/config/lambda"
    50  	"github.com/minio/minio/internal/config/notify"
    51  	"github.com/minio/minio/internal/config/policy/opa"
    52  	polplugin "github.com/minio/minio/internal/config/policy/plugin"
    53  	"github.com/minio/minio/internal/config/scanner"
    54  	"github.com/minio/minio/internal/config/storageclass"
    55  	"github.com/minio/minio/internal/config/subnet"
    56  	"github.com/minio/minio/internal/crypto"
    57  	xhttp "github.com/minio/minio/internal/http"
    58  	"github.com/minio/minio/internal/logger"
    59  	"github.com/minio/pkg/v2/env"
    60  )
    61  
    62  func initHelp() {
    63  	kvs := map[string]config.KVS{
    64  		config.EtcdSubSys:           etcd.DefaultKVS,
    65  		config.CompressionSubSys:    compress.DefaultKVS,
    66  		config.IdentityLDAPSubSys:   xldap.DefaultKVS,
    67  		config.IdentityOpenIDSubSys: openid.DefaultKVS,
    68  		config.IdentityTLSSubSys:    xtls.DefaultKVS,
    69  		config.IdentityPluginSubSys: idplugin.DefaultKVS,
    70  		config.PolicyOPASubSys:      opa.DefaultKVS,
    71  		config.PolicyPluginSubSys:   polplugin.DefaultKVS,
    72  		config.SiteSubSys:           config.DefaultSiteKVS,
    73  		config.RegionSubSys:         config.DefaultRegionKVS,
    74  		config.APISubSys:            api.DefaultKVS,
    75  		config.LoggerWebhookSubSys:  logger.DefaultLoggerWebhookKVS,
    76  		config.AuditWebhookSubSys:   logger.DefaultAuditWebhookKVS,
    77  		config.AuditKafkaSubSys:     logger.DefaultAuditKafkaKVS,
    78  		config.ScannerSubSys:        scanner.DefaultKVS,
    79  		config.SubnetSubSys:         subnet.DefaultKVS,
    80  		config.CallhomeSubSys:       callhome.DefaultKVS,
    81  		config.DriveSubSys:          drive.DefaultKVS,
    82  		config.ILMSubSys:            ilm.DefaultKVS,
    83  		config.CacheSubSys:          cache.DefaultKVS,
    84  		config.BatchSubSys:          batch.DefaultKVS,
    85  		config.BrowserSubSys:        browser.DefaultKVS,
    86  	}
    87  	for k, v := range notify.DefaultNotificationKVS {
    88  		kvs[k] = v
    89  	}
    90  	for k, v := range lambda.DefaultLambdaKVS {
    91  		kvs[k] = v
    92  	}
    93  	if globalIsErasure {
    94  		kvs[config.StorageClassSubSys] = storageclass.DefaultKVS
    95  		kvs[config.HealSubSys] = heal.DefaultKVS
    96  	}
    97  	config.RegisterDefaultKVS(kvs)
    98  
    99  	// Captures help for each sub-system
   100  	helpSubSys := config.HelpKVS{
   101  		config.HelpKV{
   102  			Key:         config.SubnetSubSys,
   103  			Type:        "string",
   104  			Description: "register the cluster to MinIO SUBNET",
   105  			Optional:    true,
   106  		},
   107  		config.HelpKV{
   108  			Key:         config.CallhomeSubSys,
   109  			Type:        "string",
   110  			Description: "enable callhome to MinIO SUBNET",
   111  			Optional:    true,
   112  		},
   113  		config.HelpKV{
   114  			Key:         config.DriveSubSys,
   115  			Description: "enable drive specific settings",
   116  		},
   117  		config.HelpKV{
   118  			Key:         config.SiteSubSys,
   119  			Description: "label the server and its location",
   120  		},
   121  		config.HelpKV{
   122  			Key:         config.APISubSys,
   123  			Description: "manage global HTTP API call specific features, such as throttling, authentication types, etc.",
   124  		},
   125  		config.HelpKV{
   126  			Key:         config.ScannerSubSys,
   127  			Description: "manage namespace scanning for usage calculation, lifecycle, healing and more",
   128  		},
   129  		config.HelpKV{
   130  			Key:         config.BatchSubSys,
   131  			Description: "manage batch job workers and wait times",
   132  		},
   133  		config.HelpKV{
   134  			Key:         config.CompressionSubSys,
   135  			Description: "enable server side compression of objects",
   136  		},
   137  		config.HelpKV{
   138  			Key:             config.IdentityOpenIDSubSys,
   139  			Description:     "enable OpenID SSO support",
   140  			MultipleTargets: true,
   141  		},
   142  		config.HelpKV{
   143  			Key:         config.IdentityLDAPSubSys,
   144  			Description: "enable LDAP SSO support",
   145  		},
   146  		config.HelpKV{
   147  			Key:         config.IdentityTLSSubSys,
   148  			Description: "enable X.509 TLS certificate SSO support",
   149  		},
   150  		config.HelpKV{
   151  			Key:         config.IdentityPluginSubSys,
   152  			Description: "enable Identity Plugin via external hook",
   153  		},
   154  		config.HelpKV{
   155  			Key:         config.PolicyPluginSubSys,
   156  			Description: "enable Access Management Plugin for policy enforcement",
   157  		},
   158  		config.HelpKV{
   159  			Key:             config.LoggerWebhookSubSys,
   160  			Description:     "send server logs to webhook endpoints",
   161  			MultipleTargets: true,
   162  		},
   163  		config.HelpKV{
   164  			Key:             config.AuditWebhookSubSys,
   165  			Description:     "send audit logs to webhook endpoints",
   166  			MultipleTargets: true,
   167  		},
   168  		config.HelpKV{
   169  			Key:             config.AuditKafkaSubSys,
   170  			Description:     "send audit logs to kafka endpoints",
   171  			MultipleTargets: true,
   172  		},
   173  		config.HelpKV{
   174  			Key:             config.NotifyWebhookSubSys,
   175  			Description:     "publish bucket notifications to webhook endpoints",
   176  			MultipleTargets: true,
   177  		},
   178  		config.HelpKV{
   179  			Key:             config.NotifyAMQPSubSys,
   180  			Description:     "publish bucket notifications to AMQP endpoints",
   181  			MultipleTargets: true,
   182  		},
   183  		config.HelpKV{
   184  			Key:             config.NotifyKafkaSubSys,
   185  			Description:     "publish bucket notifications to Kafka endpoints",
   186  			MultipleTargets: true,
   187  		},
   188  		config.HelpKV{
   189  			Key:             config.NotifyMQTTSubSys,
   190  			Description:     "publish bucket notifications to MQTT endpoints",
   191  			MultipleTargets: true,
   192  		},
   193  		config.HelpKV{
   194  			Key:             config.NotifyNATSSubSys,
   195  			Description:     "publish bucket notifications to NATS endpoints",
   196  			MultipleTargets: true,
   197  		},
   198  		config.HelpKV{
   199  			Key:             config.NotifyNSQSubSys,
   200  			Description:     "publish bucket notifications to NSQ endpoints",
   201  			MultipleTargets: true,
   202  		},
   203  		config.HelpKV{
   204  			Key:             config.NotifyMySQLSubSys,
   205  			Description:     "publish bucket notifications to MySQL databases",
   206  			MultipleTargets: true,
   207  		},
   208  		config.HelpKV{
   209  			Key:             config.NotifyPostgresSubSys,
   210  			Description:     "publish bucket notifications to Postgres databases",
   211  			MultipleTargets: true,
   212  		},
   213  		config.HelpKV{
   214  			Key:             config.NotifyESSubSys,
   215  			Description:     "publish bucket notifications to Elasticsearch endpoints",
   216  			MultipleTargets: true,
   217  		},
   218  		config.HelpKV{
   219  			Key:             config.NotifyRedisSubSys,
   220  			Description:     "publish bucket notifications to Redis datastores",
   221  			MultipleTargets: true,
   222  		},
   223  		config.HelpKV{
   224  			Key:             config.LambdaWebhookSubSys,
   225  			Description:     "manage remote lambda functions",
   226  			MultipleTargets: true,
   227  		},
   228  		config.HelpKV{
   229  			Key:         config.EtcdSubSys,
   230  			Description: "persist IAM assets externally to etcd",
   231  		},
   232  		config.HelpKV{
   233  			Key:         config.CacheSubSys,
   234  			Type:        "string",
   235  			Description: "enable cache plugin on MinIO for GET/HEAD requests",
   236  			Optional:    true,
   237  		},
   238  		config.HelpKV{
   239  			Key:         config.BrowserSubSys,
   240  			Description: "manage Browser HTTP specific features, such as Security headers, etc.",
   241  			Optional:    true,
   242  		},
   243  	}
   244  
   245  	if globalIsErasure {
   246  		helpSubSys = append(helpSubSys, config.HelpKV{
   247  			Key:         config.StorageClassSubSys,
   248  			Description: "define object level redundancy",
   249  		}, config.HelpKV{
   250  			Key:         config.HealSubSys,
   251  			Description: "manage object healing frequency and bitrot verification checks",
   252  		})
   253  	}
   254  
   255  	helpMap := map[string]config.HelpKVS{
   256  		"":                          helpSubSys, // Help for all sub-systems.
   257  		config.SiteSubSys:           config.SiteHelp,
   258  		config.RegionSubSys:         config.RegionHelp,
   259  		config.APISubSys:            api.Help,
   260  		config.StorageClassSubSys:   storageclass.Help,
   261  		config.EtcdSubSys:           etcd.Help,
   262  		config.CompressionSubSys:    compress.Help,
   263  		config.HealSubSys:           heal.Help,
   264  		config.BatchSubSys:          batch.Help,
   265  		config.ScannerSubSys:        scanner.Help,
   266  		config.IdentityOpenIDSubSys: openid.Help,
   267  		config.IdentityLDAPSubSys:   xldap.Help,
   268  		config.IdentityTLSSubSys:    xtls.Help,
   269  		config.IdentityPluginSubSys: idplugin.Help,
   270  		config.PolicyOPASubSys:      opa.Help,
   271  		config.PolicyPluginSubSys:   polplugin.Help,
   272  		config.LoggerWebhookSubSys:  logger.Help,
   273  		config.AuditWebhookSubSys:   logger.HelpWebhook,
   274  		config.AuditKafkaSubSys:     logger.HelpKafka,
   275  		config.NotifyAMQPSubSys:     notify.HelpAMQP,
   276  		config.NotifyKafkaSubSys:    notify.HelpKafka,
   277  		config.NotifyMQTTSubSys:     notify.HelpMQTT,
   278  		config.NotifyNATSSubSys:     notify.HelpNATS,
   279  		config.NotifyNSQSubSys:      notify.HelpNSQ,
   280  		config.NotifyMySQLSubSys:    notify.HelpMySQL,
   281  		config.NotifyPostgresSubSys: notify.HelpPostgres,
   282  		config.NotifyRedisSubSys:    notify.HelpRedis,
   283  		config.NotifyWebhookSubSys:  notify.HelpWebhook,
   284  		config.NotifyESSubSys:       notify.HelpES,
   285  		config.LambdaWebhookSubSys:  lambda.HelpWebhook,
   286  		config.SubnetSubSys:         subnet.HelpSubnet,
   287  		config.CallhomeSubSys:       callhome.HelpCallhome,
   288  		config.DriveSubSys:          drive.HelpDrive,
   289  		config.CacheSubSys:          cache.Help,
   290  		config.BrowserSubSys:        browser.Help,
   291  	}
   292  
   293  	config.RegisterHelpSubSys(helpMap)
   294  
   295  	// save top-level help for deprecated sub-systems in a separate map.
   296  	deprecatedHelpKVMap := map[string]config.HelpKV{
   297  		config.RegionSubSys: {
   298  			Key:         config.RegionSubSys,
   299  			Description: "[DEPRECATED - use `site` instead] label the location of the server",
   300  		},
   301  		config.PolicyOPASubSys: {
   302  			Key:         config.PolicyOPASubSys,
   303  			Description: "[DEPRECATED - use `policy_plugin` instead] enable external OPA for policy enforcement",
   304  		},
   305  	}
   306  
   307  	config.RegisterHelpDeprecatedSubSys(deprecatedHelpKVMap)
   308  }
   309  
   310  var (
   311  	// globalServerConfig server config.
   312  	globalServerConfig   config.Config
   313  	globalServerConfigMu sync.RWMutex
   314  )
   315  
   316  func validateSubSysConfig(ctx context.Context, s config.Config, subSys string, objAPI ObjectLayer) error {
   317  	switch subSys {
   318  	case config.SiteSubSys:
   319  		if _, err := config.LookupSite(s[config.SiteSubSys][config.Default], s[config.RegionSubSys][config.Default]); err != nil {
   320  			return err
   321  		}
   322  	case config.APISubSys:
   323  		if _, err := api.LookupConfig(s[config.APISubSys][config.Default]); err != nil {
   324  			return err
   325  		}
   326  	case config.BatchSubSys:
   327  		if _, err := batch.LookupConfig(s[config.BatchSubSys][config.Default]); err != nil {
   328  			return err
   329  		}
   330  	case config.StorageClassSubSys:
   331  		if objAPI == nil {
   332  			return errServerNotInitialized
   333  		}
   334  		for _, setDriveCount := range objAPI.SetDriveCounts() {
   335  			if _, err := storageclass.LookupConfig(s[config.StorageClassSubSys][config.Default], setDriveCount); err != nil {
   336  				return err
   337  			}
   338  		}
   339  	case config.CompressionSubSys:
   340  		if _, err := compress.LookupConfig(s[config.CompressionSubSys][config.Default]); err != nil {
   341  			return err
   342  		}
   343  	case config.HealSubSys:
   344  		if _, err := heal.LookupConfig(s[config.HealSubSys][config.Default]); err != nil {
   345  			return err
   346  		}
   347  	case config.ScannerSubSys:
   348  		if _, err := scanner.LookupConfig(s[config.ScannerSubSys][config.Default]); err != nil {
   349  			return err
   350  		}
   351  	case config.EtcdSubSys:
   352  		etcdCfg, err := etcd.LookupConfig(s[config.EtcdSubSys][config.Default], globalRootCAs)
   353  		if err != nil {
   354  			return err
   355  		}
   356  		if etcdCfg.Enabled {
   357  			etcdClnt, err := etcd.New(etcdCfg)
   358  			if err != nil {
   359  				return err
   360  			}
   361  			etcdClnt.Close()
   362  		}
   363  	case config.IdentityOpenIDSubSys:
   364  		if _, err := openid.LookupConfig(s,
   365  			NewHTTPTransport(), xhttp.DrainBody, globalSite.Region); err != nil {
   366  			return err
   367  		}
   368  	case config.IdentityLDAPSubSys:
   369  		cfg, err := xldap.Lookup(s, globalRootCAs)
   370  		if err != nil {
   371  			return err
   372  		}
   373  		if cfg.Enabled() {
   374  			conn, cerr := cfg.LDAP.Connect()
   375  			if cerr != nil {
   376  				return cerr
   377  			}
   378  			conn.Close()
   379  		}
   380  	case config.IdentityTLSSubSys:
   381  		if _, err := xtls.Lookup(s[config.IdentityTLSSubSys][config.Default]); err != nil {
   382  			return err
   383  		}
   384  	case config.IdentityPluginSubSys:
   385  		if _, err := idplugin.LookupConfig(s[config.IdentityPluginSubSys][config.Default],
   386  			NewHTTPTransport(), xhttp.DrainBody, globalSite.Region); err != nil {
   387  			return err
   388  		}
   389  	case config.SubnetSubSys:
   390  		if _, err := subnet.LookupConfig(s[config.SubnetSubSys][config.Default], nil); err != nil {
   391  			return err
   392  		}
   393  	case config.CallhomeSubSys:
   394  		cfg, err := callhome.LookupConfig(s[config.CallhomeSubSys][config.Default])
   395  		if err != nil {
   396  			return err
   397  		}
   398  		// callhome cannot be enabled if license is not registered yet, throw an error.
   399  		if cfg.Enabled() && !globalSubnetConfig.Registered() {
   400  			return errors.New("Deployment is not registered with SUBNET. Please register the deployment via 'mc license register ALIAS'")
   401  		}
   402  	case config.DriveSubSys:
   403  		if _, err := drive.LookupConfig(s[config.DriveSubSys][config.Default]); err != nil {
   404  			return err
   405  		}
   406  	case config.CacheSubSys:
   407  		if _, err := cache.LookupConfig(s[config.CacheSubSys][config.Default], globalRemoteTargetTransport); err != nil {
   408  			return err
   409  		}
   410  	case config.PolicyOPASubSys:
   411  		// In case legacy OPA config is being set, we treat it as if the
   412  		// AuthZPlugin is being set.
   413  		subSys = config.PolicyPluginSubSys
   414  		fallthrough
   415  	case config.PolicyPluginSubSys:
   416  		if ppargs, err := polplugin.LookupConfig(s, GetDefaultConnSettings(), xhttp.DrainBody); err != nil {
   417  			return err
   418  		} else if ppargs.URL == nil {
   419  			// Check if legacy opa is configured.
   420  			if _, err := opa.LookupConfig(s[config.PolicyOPASubSys][config.Default],
   421  				NewHTTPTransport(), xhttp.DrainBody); err != nil {
   422  				return err
   423  			}
   424  		}
   425  	case config.BrowserSubSys:
   426  		if _, err := browser.LookupConfig(s[config.BrowserSubSys][config.Default]); err != nil {
   427  			return err
   428  		}
   429  	default:
   430  		if config.LoggerSubSystems.Contains(subSys) {
   431  			if err := logger.ValidateSubSysConfig(ctx, s, subSys); err != nil {
   432  				return err
   433  			}
   434  		}
   435  	}
   436  
   437  	if config.NotifySubSystems.Contains(subSys) {
   438  		if err := notify.TestSubSysNotificationTargets(ctx, s, subSys, NewHTTPTransport()); err != nil {
   439  			return err
   440  		}
   441  	}
   442  
   443  	if config.LambdaSubSystems.Contains(subSys) {
   444  		if err := lambda.TestSubSysLambdaTargets(GlobalContext, s, subSys, NewHTTPTransport()); err != nil {
   445  			return err
   446  		}
   447  	}
   448  
   449  	return nil
   450  }
   451  
   452  func validateConfig(ctx context.Context, s config.Config, subSys string) error {
   453  	objAPI := newObjectLayerFn()
   454  
   455  	// We must have a global lock for this so nobody else modifies env while we do.
   456  	defer env.LockSetEnv()()
   457  
   458  	// Disable merging env values with config for validation.
   459  	env.SetEnvOff()
   460  
   461  	// Enable env values to validate KMS.
   462  	defer env.SetEnvOn()
   463  	if subSys != "" {
   464  		return validateSubSysConfig(ctx, s, subSys, objAPI)
   465  	}
   466  
   467  	// No sub-system passed. Validate all of them.
   468  	for _, ss := range config.SubSystems.ToSlice() {
   469  		if err := validateSubSysConfig(ctx, s, ss, objAPI); err != nil {
   470  			return err
   471  		}
   472  	}
   473  
   474  	return nil
   475  }
   476  
   477  func lookupConfigs(s config.Config, objAPI ObjectLayer) {
   478  	ctx := GlobalContext
   479  
   480  	dnsURL, dnsUser, dnsPass, err := env.LookupEnv(config.EnvDNSWebhook)
   481  	if err != nil {
   482  		logger.LogIf(ctx, fmt.Errorf("Unable to initialize remote webhook DNS config %w", err))
   483  	}
   484  	if err == nil && dnsURL != "" {
   485  		bootstrapTraceMsg("initialize remote bucket DNS store")
   486  		globalDNSConfig, err = dns.NewOperatorDNS(dnsURL,
   487  			dns.Authentication(dnsUser, dnsPass),
   488  			dns.RootCAs(globalRootCAs))
   489  		if err != nil {
   490  			logger.LogIf(ctx, fmt.Errorf("Unable to initialize remote webhook DNS config %w", err))
   491  		}
   492  	}
   493  
   494  	etcdCfg, err := etcd.LookupConfig(s[config.EtcdSubSys][config.Default], globalRootCAs)
   495  	if err != nil {
   496  		logger.LogIf(ctx, fmt.Errorf("Unable to initialize etcd config: %w", err))
   497  	}
   498  
   499  	if etcdCfg.Enabled {
   500  		bootstrapTraceMsg("initialize etcd store")
   501  		globalEtcdClient, err = etcd.New(etcdCfg)
   502  		if err != nil {
   503  			logger.LogIf(ctx, fmt.Errorf("Unable to initialize etcd config: %w", err))
   504  		}
   505  
   506  		if len(globalDomainNames) != 0 && !globalDomainIPs.IsEmpty() && globalEtcdClient != nil {
   507  			if globalDNSConfig != nil {
   508  				// if global DNS is already configured, indicate with a warning, in case
   509  				// users are confused.
   510  				logger.LogIf(ctx, fmt.Errorf("DNS store is already configured with %s, etcd is not used for DNS store", globalDNSConfig))
   511  			} else {
   512  				globalDNSConfig, err = dns.NewCoreDNS(etcdCfg.Config,
   513  					dns.DomainNames(globalDomainNames),
   514  					dns.DomainIPs(globalDomainIPs),
   515  					dns.DomainPort(globalMinioPort),
   516  					dns.CoreDNSPath(etcdCfg.CoreDNSPath),
   517  				)
   518  				if err != nil {
   519  					logger.LogIf(ctx, fmt.Errorf("Unable to initialize DNS config for %s: %w",
   520  						globalDomainNames, err))
   521  				}
   522  			}
   523  		}
   524  	}
   525  
   526  	// Bucket federation is 'true' only when IAM assets are not namespaced
   527  	// per tenant and all tenants interested in globally available users
   528  	// if namespace was requested such as specifying etcdPathPrefix then
   529  	// we assume that users are interested in global bucket support
   530  	// but not federation.
   531  	globalBucketFederation = etcdCfg.PathPrefix == "" && etcdCfg.Enabled
   532  
   533  	globalSite, err = config.LookupSite(s[config.SiteSubSys][config.Default], s[config.RegionSubSys][config.Default])
   534  	if err != nil {
   535  		logger.LogIf(ctx, fmt.Errorf("Invalid site configuration: %w", err))
   536  	}
   537  
   538  	globalAutoEncryption = crypto.LookupAutoEncryption() // Enable auto-encryption if enabled
   539  	if globalAutoEncryption && GlobalKMS == nil {
   540  		logger.Fatal(errors.New("no KMS configured"), "MINIO_KMS_AUTO_ENCRYPTION requires a valid KMS configuration")
   541  	}
   542  
   543  	transport := NewHTTPTransport()
   544  
   545  	bootstrapTraceMsg("initialize the event notification targets")
   546  	globalNotifyTargetList, err = notify.FetchEnabledTargets(GlobalContext, s, transport)
   547  	if err != nil {
   548  		logger.LogIf(ctx, fmt.Errorf("Unable to initialize notification target(s): %w", err))
   549  	}
   550  
   551  	bootstrapTraceMsg("initialize the lambda targets")
   552  	globalLambdaTargetList, err = lambda.FetchEnabledTargets(GlobalContext, s, transport)
   553  	if err != nil {
   554  		logger.LogIf(ctx, fmt.Errorf("Unable to initialize lambda target(s): %w", err))
   555  	}
   556  
   557  	bootstrapTraceMsg("applying the dynamic configuration")
   558  	// Apply dynamic config values
   559  	if err := applyDynamicConfig(ctx, objAPI, s); err != nil {
   560  		logger.LogIf(ctx, err)
   561  	}
   562  }
   563  
   564  func applyDynamicConfigForSubSys(ctx context.Context, objAPI ObjectLayer, s config.Config, subSys string) error {
   565  	if objAPI == nil {
   566  		return errServerNotInitialized
   567  	}
   568  
   569  	setDriveCounts := objAPI.SetDriveCounts()
   570  	switch subSys {
   571  	case config.APISubSys:
   572  		apiConfig, err := api.LookupConfig(s[config.APISubSys][config.Default])
   573  		if err != nil {
   574  			logger.LogIf(ctx, fmt.Errorf("Invalid api configuration: %w", err))
   575  		}
   576  
   577  		globalAPIConfig.init(apiConfig, setDriveCounts)
   578  		autoGenerateRootCredentials() // Generate the KMS root credentials here since we don't know whether API root access is disabled until now.
   579  
   580  		// Initialize remote instance transport once.
   581  		getRemoteInstanceTransportOnce.Do(func() {
   582  			getRemoteInstanceTransport = NewHTTPTransportWithTimeout(apiConfig.RemoteTransportDeadline)
   583  		})
   584  	case config.CompressionSubSys:
   585  		cmpCfg, err := compress.LookupConfig(s[config.CompressionSubSys][config.Default])
   586  		if err != nil {
   587  			return fmt.Errorf("Unable to setup Compression: %w", err)
   588  		}
   589  		globalCompressConfigMu.Lock()
   590  		globalCompressConfig = cmpCfg
   591  		globalCompressConfigMu.Unlock()
   592  	case config.HealSubSys:
   593  		healCfg, err := heal.LookupConfig(s[config.HealSubSys][config.Default])
   594  		if err != nil {
   595  			return fmt.Errorf("Unable to apply heal config: %w", err)
   596  		}
   597  		globalHealConfig.Update(healCfg)
   598  	case config.BatchSubSys:
   599  		batchCfg, err := batch.LookupConfig(s[config.BatchSubSys][config.Default])
   600  		if err != nil {
   601  			return fmt.Errorf("Unable to apply batch config: %w", err)
   602  		}
   603  		globalBatchConfig.Update(batchCfg)
   604  	case config.ScannerSubSys:
   605  		scannerCfg, err := scanner.LookupConfig(s[config.ScannerSubSys][config.Default])
   606  		if err != nil {
   607  			return fmt.Errorf("Unable to apply scanner config: %w", err)
   608  		}
   609  		// update dynamic scanner values.
   610  		scannerIdleMode.Store(scannerCfg.IdleMode)
   611  		scannerCycle.Store(scannerCfg.Cycle)
   612  		scannerExcessObjectVersions.Store(scannerCfg.ExcessVersions)
   613  		scannerExcessFolders.Store(scannerCfg.ExcessFolders)
   614  		logger.LogIf(ctx, scannerSleeper.Update(scannerCfg.Delay, scannerCfg.MaxWait))
   615  	case config.LoggerWebhookSubSys:
   616  		loggerCfg, err := logger.LookupConfigForSubSys(ctx, s, config.LoggerWebhookSubSys)
   617  		if err != nil {
   618  			logger.LogIf(ctx, fmt.Errorf("Unable to load logger webhook config: %w", err))
   619  		}
   620  		userAgent := getUserAgent(getMinioMode())
   621  		for n, l := range loggerCfg.HTTP {
   622  			if l.Enabled {
   623  				l.LogOnceIf = logger.LogOnceConsoleIf
   624  				l.UserAgent = userAgent
   625  				l.Transport = NewHTTPTransportWithClientCerts(l.ClientCert, l.ClientKey)
   626  			}
   627  			loggerCfg.HTTP[n] = l
   628  		}
   629  		if errs := logger.UpdateHTTPWebhooks(ctx, loggerCfg.HTTP); len(errs) > 0 {
   630  			logger.LogIf(ctx, fmt.Errorf("Unable to update logger webhook config: %v", errs))
   631  		}
   632  	case config.AuditWebhookSubSys:
   633  		loggerCfg, err := logger.LookupConfigForSubSys(ctx, s, config.AuditWebhookSubSys)
   634  		if err != nil {
   635  			logger.LogIf(ctx, fmt.Errorf("Unable to load audit webhook config: %w", err))
   636  		}
   637  		userAgent := getUserAgent(getMinioMode())
   638  		for n, l := range loggerCfg.AuditWebhook {
   639  			if l.Enabled {
   640  				l.LogOnceIf = logger.LogOnceConsoleIf
   641  				l.UserAgent = userAgent
   642  				l.Transport = NewHTTPTransportWithClientCerts(l.ClientCert, l.ClientKey)
   643  			}
   644  			loggerCfg.AuditWebhook[n] = l
   645  		}
   646  
   647  		if errs := logger.UpdateAuditWebhooks(ctx, loggerCfg.AuditWebhook); len(errs) > 0 {
   648  			logger.LogIf(ctx, fmt.Errorf("Unable to update audit webhook targets: %v", errs))
   649  		}
   650  	case config.AuditKafkaSubSys:
   651  		loggerCfg, err := logger.LookupConfigForSubSys(ctx, s, config.AuditKafkaSubSys)
   652  		if err != nil {
   653  			logger.LogIf(ctx, fmt.Errorf("Unable to load audit kafka config: %w", err))
   654  		}
   655  		for n, l := range loggerCfg.AuditKafka {
   656  			if l.Enabled {
   657  				if l.TLS.Enable {
   658  					l.TLS.RootCAs = globalRootCAs
   659  				}
   660  				l.LogOnce = logger.LogOnceIf
   661  				loggerCfg.AuditKafka[n] = l
   662  			}
   663  		}
   664  		if errs := logger.UpdateAuditKafkaTargets(ctx, loggerCfg); len(errs) > 0 {
   665  			logger.LogIf(ctx, fmt.Errorf("Unable to update audit kafka targets: %v", errs))
   666  		}
   667  	case config.StorageClassSubSys:
   668  		for i, setDriveCount := range setDriveCounts {
   669  			sc, err := storageclass.LookupConfig(s[config.StorageClassSubSys][config.Default], setDriveCount)
   670  			if err != nil {
   671  				logger.LogIf(ctx, fmt.Errorf("Unable to initialize storage class config: %w", err))
   672  				break
   673  			}
   674  			// if we validated all setDriveCounts and it was successful
   675  			// proceed to store the correct storage class globally.
   676  			if i == len(setDriveCounts)-1 {
   677  				globalStorageClass.Update(sc)
   678  			}
   679  		}
   680  	case config.SubnetSubSys:
   681  		subnetConfig, err := subnet.LookupConfig(s[config.SubnetSubSys][config.Default], globalProxyTransport)
   682  		if err != nil {
   683  			logger.LogIf(ctx, fmt.Errorf("Unable to parse subnet configuration: %w", err))
   684  		} else {
   685  			globalSubnetConfig.Update(subnetConfig, globalIsCICD)
   686  			globalSubnetConfig.ApplyEnv() // update environment settings for Console UI
   687  		}
   688  	case config.CallhomeSubSys:
   689  		callhomeCfg, err := callhome.LookupConfig(s[config.CallhomeSubSys][config.Default])
   690  		if err != nil {
   691  			logger.LogIf(ctx, fmt.Errorf("Unable to load callhome config: %w", err))
   692  		} else {
   693  			enable := callhomeCfg.Enable && !globalCallhomeConfig.Enabled()
   694  			globalCallhomeConfig.Update(callhomeCfg)
   695  			if enable {
   696  				initCallhome(ctx, objAPI)
   697  			}
   698  		}
   699  	case config.DriveSubSys:
   700  		if driveConfig, err := drive.LookupConfig(s[config.DriveSubSys][config.Default]); err != nil {
   701  			logger.LogIf(ctx, fmt.Errorf("Unable to load drive config: %w", err))
   702  		} else {
   703  			err := globalDriveConfig.Update(driveConfig)
   704  			if err != nil {
   705  				logger.LogIf(ctx, fmt.Errorf("Unable to update drive config: %v", err))
   706  			}
   707  		}
   708  	case config.CacheSubSys:
   709  		cacheCfg, err := cache.LookupConfig(s[config.CacheSubSys][config.Default], globalRemoteTargetTransport)
   710  		if err != nil {
   711  			logger.LogIf(ctx, fmt.Errorf("Unable to load cache config: %w", err))
   712  		} else {
   713  			globalCacheConfig.Update(cacheCfg)
   714  		}
   715  	case config.BrowserSubSys:
   716  		browserCfg, err := browser.LookupConfig(s[config.BrowserSubSys][config.Default])
   717  		if err != nil {
   718  			return fmt.Errorf("Unable to apply browser config: %w", err)
   719  		}
   720  		globalBrowserConfig.Update(browserCfg)
   721  	case config.ILMSubSys:
   722  		ilmCfg, err := ilm.LookupConfig(s[config.ILMSubSys][config.Default])
   723  		if err != nil {
   724  			return fmt.Errorf("Unable to apply ilm config: %w", err)
   725  		}
   726  		if globalTransitionState != nil {
   727  			globalTransitionState.UpdateWorkers(ilmCfg.TransitionWorkers)
   728  		}
   729  		if globalExpiryState != nil {
   730  			globalExpiryState.ResizeWorkers(ilmCfg.ExpirationWorkers)
   731  		}
   732  		globalILMConfig.update(ilmCfg)
   733  	}
   734  	globalServerConfigMu.Lock()
   735  	defer globalServerConfigMu.Unlock()
   736  	if globalServerConfig != nil {
   737  		globalServerConfig[subSys] = s[subSys]
   738  	}
   739  	return nil
   740  }
   741  
   742  // autoGenerateRootCredentials generates root credentials deterministically if
   743  // a KMS is configured, no manual credentials have been specified and if root
   744  // access is disabled.
   745  func autoGenerateRootCredentials() {
   746  	if GlobalKMS == nil {
   747  		return
   748  	}
   749  	if globalAPIConfig.permitRootAccess() || !globalActiveCred.Equal(auth.DefaultCredentials) {
   750  		return
   751  	}
   752  
   753  	if manager, ok := GlobalKMS.(kms.KeyManager); ok {
   754  		stat, err := GlobalKMS.Stat(GlobalContext)
   755  		if err != nil {
   756  			logger.LogIf(GlobalContext, err, "Unable to generate root credentials using KMS")
   757  			return
   758  		}
   759  
   760  		aKey, err := manager.HMAC(GlobalContext, stat.DefaultKey, []byte("root access key"))
   761  		if errors.Is(err, kes.ErrNotAllowed) {
   762  			return // If we don't have permission to compute the HMAC, don't change the cred.
   763  		}
   764  		if err != nil {
   765  			logger.Fatal(err, "Unable to generate root access key using KMS")
   766  		}
   767  
   768  		sKey, err := manager.HMAC(GlobalContext, stat.DefaultKey, []byte("root secret key"))
   769  		if err != nil {
   770  			// Here, we must have permission. Otherwise, we would have failed earlier.
   771  			logger.Fatal(err, "Unable to generate root secret key using KMS")
   772  		}
   773  
   774  		accessKey, err := auth.GenerateAccessKey(20, bytes.NewReader(aKey))
   775  		if err != nil {
   776  			logger.Fatal(err, "Unable to generate root access key")
   777  		}
   778  		secretKey, err := auth.GenerateSecretKey(32, bytes.NewReader(sKey))
   779  		if err != nil {
   780  			logger.Fatal(err, "Unable to generate root secret key")
   781  		}
   782  
   783  		logger.Info("Automatically generated root access key and secret key with the KMS")
   784  		globalActiveCred = auth.Credentials{
   785  			AccessKey: accessKey,
   786  			SecretKey: secretKey,
   787  		}
   788  	}
   789  }
   790  
   791  // applyDynamicConfig will apply dynamic config values.
   792  // Dynamic systems should be in config.SubSystemsDynamic as well.
   793  func applyDynamicConfig(ctx context.Context, objAPI ObjectLayer, s config.Config) error {
   794  	for subSys := range config.SubSystemsDynamic {
   795  		err := applyDynamicConfigForSubSys(ctx, objAPI, s, subSys)
   796  		if err != nil {
   797  			return err
   798  		}
   799  	}
   800  	return nil
   801  }
   802  
   803  // Help - return sub-system level help
   804  type Help struct {
   805  	SubSys          string         `json:"subSys"`
   806  	Description     string         `json:"description"`
   807  	MultipleTargets bool           `json:"multipleTargets"`
   808  	KeysHelp        config.HelpKVS `json:"keysHelp"`
   809  }
   810  
   811  // GetHelp - returns help for sub-sys, a key for a sub-system or all the help.
   812  func GetHelp(subSys, key string, envOnly bool) (Help, error) {
   813  	if len(subSys) == 0 {
   814  		return Help{KeysHelp: config.HelpSubSysMap[subSys]}, nil
   815  	}
   816  	subSystemValue := strings.SplitN(subSys, config.SubSystemSeparator, 2)
   817  	if len(subSystemValue) == 0 {
   818  		return Help{}, config.Errorf("invalid number of arguments %s", subSys)
   819  	}
   820  
   821  	subSys = subSystemValue[0]
   822  
   823  	subSysHelp, ok := config.HelpSubSysMap[""].Lookup(subSys)
   824  	if !ok {
   825  		subSysHelp, ok = config.HelpDeprecatedSubSysMap[subSys]
   826  		if !ok {
   827  			return Help{}, config.Errorf("unknown sub-system %s", subSys)
   828  		}
   829  	}
   830  
   831  	h, ok := config.HelpSubSysMap[subSys]
   832  	if !ok {
   833  		return Help{}, config.Errorf("unknown sub-system %s", subSys)
   834  	}
   835  	if key != "" {
   836  		value, ok := h.Lookup(key)
   837  		if !ok {
   838  			return Help{}, config.Errorf("unknown key %s for sub-system %s",
   839  				key, subSys)
   840  		}
   841  		h = config.HelpKVS{value}
   842  	}
   843  
   844  	help := config.HelpKVS{}
   845  
   846  	// Only for multiple targets, make sure
   847  	// to list the ENV, for regular k/v EnableKey is
   848  	// implicit, for ENVs we cannot make it implicit.
   849  	if subSysHelp.MultipleTargets {
   850  		key := madmin.EnableKey
   851  		if envOnly {
   852  			key = config.EnvPrefix + strings.ToTitle(subSys) + config.EnvWordDelimiter + strings.ToTitle(madmin.EnableKey)
   853  		}
   854  		help = append(help, config.HelpKV{
   855  			Key:         key,
   856  			Description: fmt.Sprintf("enable %s target, default is 'off'", subSys),
   857  			Optional:    false,
   858  			Type:        "on|off",
   859  		})
   860  	}
   861  
   862  	for _, hkv := range h {
   863  		key := hkv.Key
   864  		if envOnly {
   865  			key = config.EnvPrefix + strings.ToTitle(subSys) + config.EnvWordDelimiter + strings.ToTitle(hkv.Key)
   866  		}
   867  		help = append(help, config.HelpKV{
   868  			Key:         key,
   869  			Description: hkv.Description,
   870  			Optional:    hkv.Optional,
   871  			Type:        hkv.Type,
   872  		})
   873  	}
   874  
   875  	return Help{
   876  		SubSys:          subSys,
   877  		Description:     subSysHelp.Description,
   878  		MultipleTargets: subSysHelp.MultipleTargets,
   879  		KeysHelp:        help,
   880  	}, nil
   881  }
   882  
   883  func newServerConfig() config.Config {
   884  	return config.New()
   885  }
   886  
   887  // newSrvConfig - initialize a new server config, saves env parameters if
   888  // found, otherwise use default parameters
   889  func newSrvConfig(objAPI ObjectLayer) error {
   890  	// Initialize server config.
   891  	srvCfg := newServerConfig()
   892  
   893  	// hold the mutex lock before a new config is assigned.
   894  	globalServerConfigMu.Lock()
   895  	globalServerConfig = srvCfg
   896  	globalServerConfigMu.Unlock()
   897  
   898  	// Save config into file.
   899  	return saveServerConfig(GlobalContext, objAPI, srvCfg)
   900  }
   901  
   902  func getValidConfig(objAPI ObjectLayer) (config.Config, error) {
   903  	return readServerConfig(GlobalContext, objAPI, nil)
   904  }
   905  
   906  // loadConfig - loads a new config from disk, overrides params
   907  // from env if found and valid
   908  // data is optional. If nil it will be loaded from backend.
   909  func loadConfig(objAPI ObjectLayer, data []byte) error {
   910  	bootstrapTraceMsg("load the configuration")
   911  	srvCfg, err := readServerConfig(GlobalContext, objAPI, data)
   912  	if err != nil {
   913  		return err
   914  	}
   915  
   916  	bootstrapTraceMsg("lookup the configuration")
   917  	// Override any values from ENVs.
   918  	lookupConfigs(srvCfg, objAPI)
   919  
   920  	// hold the mutex lock before a new config is assigned.
   921  	globalServerConfigMu.Lock()
   922  	globalServerConfig = srvCfg
   923  	globalServerConfigMu.Unlock()
   924  
   925  	return nil
   926  }