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 }