github.com/minio/minio@v0.0.0-20240328213742-3f72439b8a27/cmd/server-main.go (about) 1 // Copyright (c) 2015-2024 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 "context" 22 "encoding/hex" 23 "errors" 24 "fmt" 25 "io" 26 "log" 27 "math/rand" 28 "net" 29 "os" 30 "os/signal" 31 "runtime" 32 "strings" 33 "syscall" 34 "time" 35 36 "github.com/coreos/go-systemd/v22/daemon" 37 "github.com/minio/cli" 38 "github.com/minio/madmin-go/v3" 39 "github.com/minio/minio-go/v7" 40 "github.com/minio/minio-go/v7/pkg/credentials" 41 "github.com/minio/minio-go/v7/pkg/set" 42 "github.com/minio/minio/internal/auth" 43 "github.com/minio/minio/internal/bucket/bandwidth" 44 "github.com/minio/minio/internal/color" 45 "github.com/minio/minio/internal/config" 46 "github.com/minio/minio/internal/handlers" 47 "github.com/minio/minio/internal/hash/sha256" 48 xhttp "github.com/minio/minio/internal/http" 49 xioutil "github.com/minio/minio/internal/ioutil" 50 "github.com/minio/minio/internal/logger" 51 "github.com/minio/pkg/v2/certs" 52 "github.com/minio/pkg/v2/env" 53 "golang.org/x/exp/slices" 54 "gopkg.in/yaml.v2" 55 ) 56 57 // ServerFlags - server command specific flags 58 var ServerFlags = []cli.Flag{ 59 cli.StringFlag{ 60 Name: "config", 61 Usage: "specify server configuration via YAML configuration", 62 EnvVar: "MINIO_CONFIG", 63 }, 64 cli.StringFlag{ 65 Name: "address", 66 Value: ":" + GlobalMinioDefaultPort, 67 Usage: "bind to a specific ADDRESS:PORT, ADDRESS can be an IP or hostname", 68 EnvVar: "MINIO_ADDRESS", 69 }, 70 cli.IntFlag{ 71 Name: "listeners", // Deprecated Oct 2022 72 Value: 1, 73 Usage: "bind N number of listeners per ADDRESS:PORT", 74 EnvVar: "MINIO_LISTENERS", 75 Hidden: true, 76 }, 77 cli.StringFlag{ 78 Name: "console-address", 79 Usage: "bind to a specific ADDRESS:PORT for embedded Console UI, ADDRESS can be an IP or hostname", 80 EnvVar: "MINIO_CONSOLE_ADDRESS", 81 }, 82 cli.DurationFlag{ 83 Name: "shutdown-timeout", 84 Value: xhttp.DefaultShutdownTimeout, 85 Usage: "shutdown timeout to gracefully shutdown server", 86 EnvVar: "MINIO_SHUTDOWN_TIMEOUT", 87 Hidden: true, 88 }, 89 cli.DurationFlag{ 90 Name: "idle-timeout", 91 Value: xhttp.DefaultIdleTimeout, 92 Usage: "idle timeout is the maximum amount of time to wait for the next request when keep-alive are enabled", 93 EnvVar: "MINIO_IDLE_TIMEOUT", 94 Hidden: true, 95 }, 96 cli.DurationFlag{ 97 Name: "read-header-timeout", 98 Value: xhttp.DefaultReadHeaderTimeout, 99 Usage: "read header timeout is the amount of time allowed to read request headers", 100 EnvVar: "MINIO_READ_HEADER_TIMEOUT", 101 Hidden: true, 102 }, 103 cli.DurationFlag{ 104 Name: "conn-client-read-deadline", 105 Usage: "custom connection READ deadline for incoming requests", 106 Hidden: true, 107 EnvVar: "MINIO_CONN_CLIENT_READ_DEADLINE", 108 }, 109 cli.DurationFlag{ 110 Name: "conn-client-write-deadline", 111 Usage: "custom connection WRITE deadline for outgoing requests", 112 Hidden: true, 113 EnvVar: "MINIO_CONN_CLIENT_WRITE_DEADLINE", 114 }, 115 cli.DurationFlag{ 116 Name: "conn-read-deadline", 117 Usage: "custom connection READ deadline", 118 Hidden: true, 119 Value: 10 * time.Minute, 120 EnvVar: "MINIO_CONN_READ_DEADLINE", 121 }, 122 cli.DurationFlag{ 123 Name: "conn-write-deadline", 124 Usage: "custom connection WRITE deadline", 125 Hidden: true, 126 Value: 10 * time.Minute, 127 EnvVar: "MINIO_CONN_WRITE_DEADLINE", 128 }, 129 cli.DurationFlag{ 130 Name: "conn-user-timeout", 131 Usage: "custom TCP_USER_TIMEOUT for socket buffers", 132 Hidden: true, 133 Value: 10 * time.Minute, 134 EnvVar: "MINIO_CONN_USER_TIMEOUT", 135 }, 136 cli.StringFlag{ 137 Name: "interface", 138 Usage: "bind to right VRF device for MinIO services", 139 Hidden: true, 140 EnvVar: "MINIO_INTERFACE", 141 }, 142 cli.DurationFlag{ 143 Name: "dns-cache-ttl", 144 Usage: "custom DNS cache TTL", 145 Hidden: true, 146 Value: func() time.Duration { 147 if orchestrated { 148 return 30 * time.Second 149 } 150 return 10 * time.Minute 151 }(), 152 EnvVar: "MINIO_DNS_CACHE_TTL", 153 }, 154 cli.IntFlag{ 155 Name: "max-idle-conns-per-host", 156 Usage: "set a custom max idle connections per host value", 157 Hidden: true, 158 Value: 2048, 159 EnvVar: "MINIO_MAX_IDLE_CONNS_PER_HOST", 160 }, 161 cli.StringSliceFlag{ 162 Name: "ftp", 163 Usage: "enable and configure an FTP(Secure) server", 164 }, 165 cli.StringSliceFlag{ 166 Name: "sftp", 167 Usage: "enable and configure an SFTP server", 168 }, 169 cli.StringFlag{ 170 Name: "crossdomain-xml", 171 Usage: "provide a custom crossdomain-xml configuration to report at http://endpoint/crossdomain.xml", 172 Hidden: true, 173 EnvVar: "MINIO_CROSSDOMAIN_XML", 174 }, 175 } 176 177 var gatewayCmd = cli.Command{ 178 Name: "gateway", 179 Usage: "start object storage gateway", 180 Hidden: true, 181 Flags: append(ServerFlags, GlobalFlags...), 182 HideHelpCommand: true, 183 Action: gatewayMain, 184 } 185 186 func gatewayMain(ctx *cli.Context) error { 187 logger.Fatal(errInvalidArgument, "Gateway is deprecated, To continue to use Gateway please use releases no later than 'RELEASE.2022-10-24T18-35-07Z'. We recommend all our users to migrate from gateway mode to server mode. Please read https://blog.min.io/deprecation-of-the-minio-gateway/") 188 return nil 189 } 190 191 var serverCmd = cli.Command{ 192 Name: "server", 193 Usage: "start object storage server", 194 Flags: append(ServerFlags, GlobalFlags...), 195 Action: serverMain, 196 CustomHelpTemplate: `NAME: 197 {{.HelpName}} - {{.Usage}} 198 199 USAGE: 200 {{.HelpName}} {{if .VisibleFlags}}[FLAGS] {{end}}DIR1 [DIR2..] 201 {{.HelpName}} {{if .VisibleFlags}}[FLAGS] {{end}}DIR{1...64} 202 {{.HelpName}} {{if .VisibleFlags}}[FLAGS] {{end}}DIR{1...64} DIR{65...128} 203 204 DIR: 205 DIR points to a directory on a filesystem. When you want to combine 206 multiple drives into a single large system, pass one directory per 207 filesystem separated by space. You may also use a '...' convention 208 to abbreviate the directory arguments. Remote directories in a 209 distributed setup are encoded as HTTP(s) URIs. 210 {{if .VisibleFlags}} 211 FLAGS: 212 {{range .VisibleFlags}}{{.}} 213 {{end}}{{end}} 214 EXAMPLES: 215 1. Start MinIO server on "/home/shared" directory. 216 {{.Prompt}} {{.HelpName}} /home/shared 217 218 2. Start single node server with 64 local drives "/mnt/data1" to "/mnt/data64". 219 {{.Prompt}} {{.HelpName}} /mnt/data{1...64} 220 221 3. Start distributed MinIO server on an 32 node setup with 32 drives each, run following command on all the nodes 222 {{.Prompt}} {{.HelpName}} http://node{1...32}.example.com/mnt/export{1...32} 223 224 4. Start distributed MinIO server in an expanded setup, run the following command on all the nodes 225 {{.Prompt}} {{.HelpName}} http://node{1...16}.example.com/mnt/export{1...32} \ 226 http://node{17...64}.example.com/mnt/export{1...64} 227 228 5. Start distributed MinIO server, with FTP and SFTP servers on all interfaces via port 8021, 8022 respectively 229 {{.Prompt}} {{.HelpName}} http://node{1...4}.example.com/mnt/export{1...4} \ 230 --ftp="address=:8021" --ftp="passive-port-range=30000-40000" \ 231 --sftp="address=:8022" --sftp="ssh-private-key=${HOME}/.ssh/id_rsa" 232 `, 233 } 234 235 func serverCmdArgs(ctx *cli.Context) []string { 236 v, _, _, err := env.LookupEnv(config.EnvArgs) 237 if err != nil { 238 logger.FatalIf(err, "Unable to validate passed arguments in %s:%s", 239 config.EnvArgs, os.Getenv(config.EnvArgs)) 240 } 241 if v == "" { 242 v, _, _, err = env.LookupEnv(config.EnvVolumes) 243 if err != nil { 244 logger.FatalIf(err, "Unable to validate passed arguments in %s:%s", 245 config.EnvVolumes, os.Getenv(config.EnvVolumes)) 246 } 247 } 248 if v == "" { 249 // Fall back to older environment value MINIO_ENDPOINTS 250 v, _, _, err = env.LookupEnv(config.EnvEndpoints) 251 if err != nil { 252 logger.FatalIf(err, "Unable to validate passed arguments in %s:%s", 253 config.EnvEndpoints, os.Getenv(config.EnvEndpoints)) 254 } 255 } 256 if v == "" { 257 if !ctx.Args().Present() || ctx.Args().First() == "help" { 258 cli.ShowCommandHelpAndExit(ctx, ctx.Command.Name, 1) 259 } 260 return ctx.Args() 261 } 262 return strings.Fields(v) 263 } 264 265 func mergeServerCtxtFromConfigFile(configFile string, ctxt *serverCtxt) error { 266 rd, err := Open(configFile) 267 if err != nil { 268 return err 269 } 270 defer rd.Close() 271 272 cf := &config.ServerConfig{} 273 dec := yaml.NewDecoder(rd) 274 dec.SetStrict(true) 275 if err = dec.Decode(cf); err != nil { 276 return err 277 } 278 if cf.Version != "v1" { 279 return fmt.Errorf("unexpected version: %s", cf.Version) 280 } 281 282 ctxt.RootUser = cf.RootUser 283 ctxt.RootPwd = cf.RootPwd 284 285 if cf.Addr != "" { 286 ctxt.Addr = cf.Addr 287 } 288 if cf.ConsoleAddr != "" { 289 ctxt.ConsoleAddr = cf.ConsoleAddr 290 } 291 if cf.CertsDir != "" { 292 ctxt.CertsDir = cf.CertsDir 293 ctxt.certsDirSet = true 294 } 295 296 if cf.Options.FTP.Address != "" { 297 ctxt.FTP = append(ctxt.FTP, fmt.Sprintf("address=%s", cf.Options.FTP.Address)) 298 } 299 if cf.Options.FTP.PassivePortRange != "" { 300 ctxt.FTP = append(ctxt.FTP, fmt.Sprintf("passive-port-range=%s", cf.Options.FTP.PassivePortRange)) 301 } 302 303 if cf.Options.SFTP.Address != "" { 304 ctxt.SFTP = append(ctxt.SFTP, fmt.Sprintf("address=%s", cf.Options.SFTP.Address)) 305 } 306 if cf.Options.SFTP.SSHPrivateKey != "" { 307 ctxt.SFTP = append(ctxt.SFTP, fmt.Sprintf("ssh-private-key=%s", cf.Options.SFTP.SSHPrivateKey)) 308 } 309 310 ctxt.Layout, err = buildDisksLayoutFromConfFile(cf.Pools) 311 return err 312 } 313 314 func serverHandleCmdArgs(ctxt serverCtxt) { 315 handleCommonArgs(ctxt) 316 317 logger.FatalIf(CheckLocalServerAddr(globalMinioAddr), "Unable to validate passed arguments") 318 319 var err error 320 var setupType SetupType 321 322 // Check and load TLS certificates. 323 globalPublicCerts, globalTLSCerts, globalIsTLS, err = getTLSConfig() 324 logger.FatalIf(err, "Unable to load the TLS configuration") 325 326 // Check and load Root CAs. 327 globalRootCAs, err = certs.GetRootCAs(globalCertsCADir.Get()) 328 logger.FatalIf(err, "Failed to read root CAs (%v)", err) 329 330 // Add the global public crts as part of global root CAs 331 for _, publicCrt := range globalPublicCerts { 332 globalRootCAs.AddCert(publicCrt) 333 } 334 335 // Register root CAs for remote ENVs 336 env.RegisterGlobalCAs(globalRootCAs) 337 338 globalEndpoints, setupType, err = createServerEndpoints(globalMinioAddr, ctxt.Layout.pools, ctxt.Layout.legacy) 339 logger.FatalIf(err, "Invalid command line arguments") 340 globalNodes = globalEndpoints.GetNodes() 341 342 globalIsErasure = (setupType == ErasureSetupType) 343 globalIsDistErasure = (setupType == DistErasureSetupType) 344 if globalIsDistErasure { 345 globalIsErasure = true 346 } 347 globalIsErasureSD = (setupType == ErasureSDSetupType) 348 if globalDynamicAPIPort && globalIsDistErasure { 349 logger.FatalIf(errInvalidArgument, "Invalid --address=\"%s\", port '0' is not allowed in a distributed erasure coded setup", ctxt.Addr) 350 } 351 352 globalLocalNodeName = GetLocalPeer(globalEndpoints, globalMinioHost, globalMinioPort) 353 nodeNameSum := sha256.Sum256([]byte(globalLocalNodeName)) 354 globalLocalNodeNameHex = hex.EncodeToString(nodeNameSum[:]) 355 356 // Initialize, see which NIC the service is running on, and save it as global value 357 setGlobalInternodeInterface(ctxt.Interface) 358 359 // allow transport to be HTTP/1.1 for proxying. 360 globalProxyTransport = NewCustomHTTPProxyTransport()() 361 globalProxyEndpoints = GetProxyEndpoints(globalEndpoints) 362 globalInternodeTransport = NewInternodeHTTPTransport(ctxt.MaxIdleConnsPerHost)() 363 globalRemoteTargetTransport = NewRemoteTargetHTTPTransport(false)() 364 globalHealthChkTransport = NewHTTPTransport() 365 globalForwarder = handlers.NewForwarder(&handlers.Forwarder{ 366 PassHost: true, 367 RoundTripper: NewHTTPTransportWithTimeout(1 * time.Hour), 368 Logger: func(err error) { 369 if err != nil && !errors.Is(err, context.Canceled) { 370 logger.LogIf(GlobalContext, err) 371 } 372 }, 373 }) 374 375 globalTCPOptions = xhttp.TCPOptions{ 376 UserTimeout: int(ctxt.UserTimeout.Milliseconds()), 377 ClientReadTimeout: ctxt.ConnClientReadDeadline, 378 ClientWriteTimeout: ctxt.ConnClientWriteDeadline, 379 Interface: ctxt.Interface, 380 } 381 382 // On macOS, if a process already listens on LOCALIPADDR:PORT, net.Listen() falls back 383 // to IPv6 address ie minio will start listening on IPv6 address whereas another 384 // (non-)minio process is listening on IPv4 of given port. 385 // To avoid this error situation we check for port availability. 386 logger.FatalIf(xhttp.CheckPortAvailability(globalMinioHost, globalMinioPort, globalTCPOptions), "Unable to start the server") 387 388 globalConnReadDeadline = ctxt.ConnReadDeadline 389 globalConnWriteDeadline = ctxt.ConnWriteDeadline 390 } 391 392 func initAllSubsystems(ctx context.Context) { 393 // Initialize notification peer targets 394 globalNotificationSys = NewNotificationSys(globalEndpoints) 395 396 // Create new notification system 397 globalEventNotifier = NewEventNotifier(GlobalContext) 398 399 // Create new bucket metadata system. 400 if globalBucketMetadataSys == nil { 401 globalBucketMetadataSys = NewBucketMetadataSys() 402 } else { 403 // Reinitialize safely when testing. 404 globalBucketMetadataSys.Reset() 405 } 406 407 // Create the bucket bandwidth monitor 408 globalBucketMonitor = bandwidth.NewMonitor(ctx, uint64(totalNodeCount())) 409 410 // Create a new config system. 411 globalConfigSys = NewConfigSys() 412 413 // Create new IAM system. 414 globalIAMSys = NewIAMSys() 415 416 // Create new policy system. 417 globalPolicySys = NewPolicySys() 418 419 // Create new lifecycle system. 420 globalLifecycleSys = NewLifecycleSys() 421 422 // Create new bucket encryption subsystem 423 globalBucketSSEConfigSys = NewBucketSSEConfigSys() 424 425 // Create new bucket object lock subsystem 426 globalBucketObjectLockSys = NewBucketObjectLockSys() 427 428 // Create new bucket quota subsystem 429 globalBucketQuotaSys = NewBucketQuotaSys() 430 431 // Create new bucket versioning subsystem 432 if globalBucketVersioningSys == nil { 433 globalBucketVersioningSys = NewBucketVersioningSys() 434 } 435 436 // Create new bucket replication subsystem 437 globalBucketTargetSys = NewBucketTargetSys(GlobalContext) 438 439 // Create new ILM tier configuration subsystem 440 globalTierConfigMgr = NewTierConfigMgr() 441 442 globalTransitionState = newTransitionState(GlobalContext) 443 globalSiteResyncMetrics = newSiteResyncMetrics(GlobalContext) 444 } 445 446 func configRetriableErrors(err error) bool { 447 if err == nil { 448 return false 449 } 450 451 notInitialized := strings.Contains(err.Error(), "Server not initialized, please try again") || 452 errors.Is(err, errServerNotInitialized) 453 454 // Initializing sub-systems needs a retry mechanism for 455 // the following reasons: 456 // - Read quorum is lost just after the initialization 457 // of the object layer. 458 // - Write quorum not met when upgrading configuration 459 // version is needed, migration is needed etc. 460 rquorum := InsufficientReadQuorum{} 461 wquorum := InsufficientWriteQuorum{} 462 463 // One of these retriable errors shall be retried. 464 return errors.Is(err, errDiskNotFound) || 465 errors.Is(err, errConfigNotFound) || 466 errors.Is(err, context.DeadlineExceeded) || 467 errors.Is(err, errErasureWriteQuorum) || 468 errors.Is(err, errErasureReadQuorum) || 469 errors.Is(err, io.ErrUnexpectedEOF) || 470 errors.As(err, &rquorum) || 471 errors.As(err, &wquorum) || 472 isErrObjectNotFound(err) || 473 isErrBucketNotFound(err) || 474 errors.Is(err, os.ErrDeadlineExceeded) || 475 notInitialized 476 } 477 478 func bootstrapTraceMsg(msg string) { 479 info := madmin.TraceInfo{ 480 TraceType: madmin.TraceBootstrap, 481 Time: UTCNow(), 482 NodeName: globalLocalNodeName, 483 FuncName: "BOOTSTRAP", 484 Message: fmt.Sprintf("%s %s", getSource(2), msg), 485 } 486 globalBootstrapTracer.Record(info) 487 488 if serverDebugLog { 489 fmt.Println(time.Now().Round(time.Millisecond).Format(time.RFC3339), " bootstrap: ", msg) 490 } 491 492 noSubs := globalTrace.NumSubscribers(madmin.TraceBootstrap) == 0 493 if noSubs { 494 return 495 } 496 497 globalTrace.Publish(info) 498 } 499 500 func bootstrapTrace(msg string, worker func()) { 501 if serverDebugLog { 502 fmt.Println(time.Now().Round(time.Millisecond).Format(time.RFC3339), " bootstrap: ", msg) 503 } 504 505 now := time.Now() 506 worker() 507 dur := time.Since(now) 508 509 info := madmin.TraceInfo{ 510 TraceType: madmin.TraceBootstrap, 511 Time: UTCNow(), 512 NodeName: globalLocalNodeName, 513 FuncName: "BOOTSTRAP", 514 Message: fmt.Sprintf("%s %s (duration: %s)", getSource(2), msg, dur), 515 } 516 globalBootstrapTracer.Record(info) 517 518 if globalTrace.NumSubscribers(madmin.TraceBootstrap) == 0 { 519 return 520 } 521 522 globalTrace.Publish(info) 523 } 524 525 func initServerConfig(ctx context.Context, newObject ObjectLayer) error { 526 t1 := time.Now() 527 528 r := rand.New(rand.NewSource(time.Now().UnixNano())) 529 530 for { 531 select { 532 case <-ctx.Done(): 533 // Retry was canceled successfully. 534 return fmt.Errorf("Initializing sub-systems stopped gracefully %w", ctx.Err()) 535 default: 536 } 537 538 // These messages only meant primarily for distributed setup, so only log during distributed setup. 539 if globalIsDistErasure { 540 logger.Info("Waiting for all MinIO sub-systems to be initialize...") 541 } 542 543 // Upon success migrating the config, initialize all sub-systems 544 // if all sub-systems initialized successfully return right away 545 err := initConfigSubsystem(ctx, newObject) 546 if err == nil { 547 // All successful return. 548 if globalIsDistErasure { 549 // These messages only meant primarily for distributed setup, so only log during distributed setup. 550 logger.Info("All MinIO sub-systems initialized successfully in %s", time.Since(t1)) 551 } 552 return nil 553 } 554 555 if configRetriableErrors(err) { 556 logger.Info("Waiting for all MinIO sub-systems to be initialized.. possible cause (%v)", err) 557 time.Sleep(time.Duration(r.Float64() * float64(5*time.Second))) 558 continue 559 } 560 561 // Any other unhandled return right here. 562 return fmt.Errorf("Unable to initialize sub-systems: %w", err) 563 } 564 } 565 566 func initConfigSubsystem(ctx context.Context, newObject ObjectLayer) error { 567 // %w is used by all error returns here to make sure 568 // we wrap the underlying error, make sure when you 569 // are modifying this code that you do so, if and when 570 // you want to add extra context to your error. This 571 // ensures top level retry works accordingly. 572 573 // Initialize config system. 574 if err := globalConfigSys.Init(newObject); err != nil { 575 if configRetriableErrors(err) { 576 return fmt.Errorf("Unable to initialize config system: %w", err) 577 } 578 579 // Any other config errors we simply print a message and proceed forward. 580 logger.LogIf(ctx, fmt.Errorf("Unable to initialize config, some features may be missing: %w", err)) 581 } 582 583 return nil 584 } 585 586 func setGlobalInternodeInterface(interfaceName string) { 587 globalInternodeInterfaceOnce.Do(func() { 588 if interfaceName != "" { 589 globalInternodeInterface = interfaceName 590 return 591 } 592 ip := "127.0.0.1" 593 host, _ := mustSplitHostPort(globalLocalNodeName) 594 if host != "" { 595 if net.ParseIP(host) != nil { 596 ip = host 597 } else { 598 ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) 599 defer cancel() 600 601 haddrs, err := globalDNSCache.LookupHost(ctx, host) 602 if err == nil { 603 ip = haddrs[0] 604 } 605 } 606 } 607 ifs, _ := net.Interfaces() 608 for _, interf := range ifs { 609 addrs, err := interf.Addrs() 610 if err == nil { 611 for _, addr := range addrs { 612 if strings.SplitN(addr.String(), "/", 2)[0] == ip { 613 globalInternodeInterface = interf.Name 614 } 615 } 616 } 617 } 618 }) 619 } 620 621 // Return the list of address that MinIO server needs to listen on: 622 // - Returning 127.0.0.1 is necessary so Console will be able to send 623 // requests to the local S3 API. 624 // - The returned List needs to be deduplicated as well. 625 func getServerListenAddrs() []string { 626 // Use a string set to avoid duplication 627 addrs := set.NewStringSet() 628 // Listen on local interface to receive requests from Console 629 for _, ip := range mustGetLocalIPs() { 630 if ip != nil && ip.IsLoopback() { 631 addrs.Add(net.JoinHostPort(ip.String(), globalMinioPort)) 632 } 633 } 634 host, _ := mustSplitHostPort(globalMinioAddr) 635 if host != "" { 636 ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) 637 defer cancel() 638 639 haddrs, err := globalDNSCache.LookupHost(ctx, host) 640 if err == nil { 641 for _, addr := range haddrs { 642 addrs.Add(net.JoinHostPort(addr, globalMinioPort)) 643 } 644 } else { 645 // Unable to lookup host in 2-secs, let it fail later anyways. 646 addrs.Add(globalMinioAddr) 647 } 648 } else { 649 addrs.Add(globalMinioAddr) 650 } 651 return addrs.ToSlice() 652 } 653 654 // serverMain handler called for 'minio server' command. 655 func serverMain(ctx *cli.Context) { 656 var warnings []string 657 658 signal.Notify(globalOSSignalCh, os.Interrupt, syscall.SIGTERM, syscall.SIGQUIT) 659 660 go handleSignals() 661 662 setDefaultProfilerRates() 663 664 // Initialize globalConsoleSys system 665 bootstrapTrace("newConsoleLogger", func() { 666 globalConsoleSys = NewConsoleLogger(GlobalContext) 667 logger.AddSystemTarget(GlobalContext, globalConsoleSys) 668 669 // Set node name, only set for distributed setup. 670 globalConsoleSys.SetNodeName(globalLocalNodeName) 671 }) 672 673 // Always load ENV variables from files first. 674 loadEnvVarsFromFiles() 675 676 // Handle all server command args and build the disks layout 677 bootstrapTrace("serverHandleCmdArgs", func() { 678 err := buildServerCtxt(ctx, &globalServerCtxt) 679 logger.FatalIf(err, "Unable to prepare the list of endpoints") 680 681 serverHandleCmdArgs(globalServerCtxt) 682 }) 683 684 // DNS cache subsystem to reduce outgoing DNS requests 685 runDNSCache(ctx) 686 687 // Handle all server environment vars. 688 serverHandleEnvVars() 689 690 // Load the root credentials from the shell environment or from 691 // the config file if not defined, set the default one. 692 loadRootCredentials() 693 694 // Perform any self-tests 695 bootstrapTrace("selftests", func() { 696 bitrotSelfTest() 697 erasureSelfTest() 698 compressSelfTest() 699 }) 700 701 // Initialize KMS configuration 702 bootstrapTrace("handleKMSConfig", handleKMSConfig) 703 704 // Initialize all help 705 bootstrapTrace("initHelp", initHelp) 706 707 // Initialize all sub-systems 708 bootstrapTrace("initAllSubsystems", func() { 709 initAllSubsystems(GlobalContext) 710 }) 711 712 // Is distributed setup, error out if no certificates are found for HTTPS endpoints. 713 if globalIsDistErasure { 714 if globalEndpoints.HTTPS() && !globalIsTLS { 715 logger.Fatal(config.ErrNoCertsAndHTTPSEndpoints(nil), "Unable to start the server") 716 } 717 if !globalEndpoints.HTTPS() && globalIsTLS { 718 logger.Fatal(config.ErrCertsAndHTTPEndpoints(nil), "Unable to start the server") 719 } 720 } 721 722 // Check for updates in non-blocking manner. 723 go func() { 724 if !globalServerCtxt.Quiet && !globalInplaceUpdateDisabled { 725 // Check for new updates from dl.min.io. 726 bootstrapTrace("checkUpdate", func() { 727 checkUpdate(getMinioMode()) 728 }) 729 } 730 }() 731 732 // Set system resources to maximum. 733 bootstrapTrace("setMaxResources", func() { 734 _ = setMaxResources() 735 }) 736 737 // Verify kernel release and version. 738 if oldLinux() { 739 warnings = append(warnings, color.YellowBold("- Detected Linux kernel version older than 4.0.0 release, there are some known potential performance problems with this kernel version. MinIO recommends a minimum of 4.x.x linux kernel version for best performance")) 740 } 741 742 maxProcs := runtime.GOMAXPROCS(0) 743 cpuProcs := runtime.NumCPU() 744 if maxProcs < cpuProcs { 745 warnings = append(warnings, color.YellowBold("- Detected GOMAXPROCS(%d) < NumCPU(%d), please make sure to provide all PROCS to MinIO for optimal performance", maxProcs, cpuProcs)) 746 } 747 748 var getCert certs.GetCertificateFunc 749 if globalTLSCerts != nil { 750 getCert = globalTLSCerts.GetCertificate 751 } 752 753 // Initialize gridn 754 bootstrapTrace("initGrid", func() { 755 logger.FatalIf(initGlobalGrid(GlobalContext, globalEndpoints), "Unable to configure server grid RPC services") 756 }) 757 758 // Configure server. 759 bootstrapTrace("configureServer", func() { 760 handler, err := configureServerHandler(globalEndpoints) 761 if err != nil { 762 logger.Fatal(config.ErrUnexpectedError(err), "Unable to configure one of server's RPC services") 763 } 764 // Allow grid to start after registering all services. 765 xioutil.SafeClose(globalGridStart) 766 767 httpServer := xhttp.NewServer(getServerListenAddrs()). 768 UseHandler(setCriticalErrorHandler(corsHandler(handler))). 769 UseTLSConfig(newTLSConfig(getCert)). 770 UseShutdownTimeout(globalServerCtxt.ShutdownTimeout). 771 UseIdleTimeout(globalServerCtxt.IdleTimeout). 772 UseReadHeaderTimeout(globalServerCtxt.ReadHeaderTimeout). 773 UseBaseContext(GlobalContext). 774 UseCustomLogger(log.New(io.Discard, "", 0)). // Turn-off random logging by Go stdlib 775 UseTCPOptions(globalTCPOptions) 776 777 httpServer.TCPOptions.Trace = bootstrapTraceMsg 778 go func() { 779 serveFn, err := httpServer.Init(GlobalContext, func(listenAddr string, err error) { 780 logger.LogIf(GlobalContext, fmt.Errorf("Unable to listen on `%s`: %v", listenAddr, err)) 781 }) 782 if err != nil { 783 globalHTTPServerErrorCh <- err 784 return 785 } 786 globalHTTPServerErrorCh <- serveFn() 787 }() 788 789 setHTTPServer(httpServer) 790 }) 791 792 if globalIsDistErasure { 793 bootstrapTrace("verifying system configuration", func() { 794 // Additionally in distributed setup, validate the setup and configuration. 795 if err := verifyServerSystemConfig(GlobalContext, globalEndpoints, globalGrid.Load()); err != nil { 796 logger.Fatal(err, "Unable to start the server") 797 } 798 }) 799 } 800 801 if !globalDisableFreezeOnBoot { 802 // Freeze the services until the bucket notification subsystem gets initialized. 803 bootstrapTrace("freezeServices", freezeServices) 804 } 805 806 var newObject ObjectLayer 807 bootstrapTrace("newObjectLayer", func() { 808 var err error 809 newObject, err = newObjectLayer(GlobalContext, globalEndpoints) 810 if err != nil { 811 logFatalErrs(err, Endpoint{}, true) 812 } 813 }) 814 815 xhttp.SetDeploymentID(globalDeploymentID()) 816 xhttp.SetMinIOVersion(Version) 817 818 for _, n := range globalNodes { 819 nodeName := n.Host 820 if n.IsLocal { 821 nodeName = globalLocalNodeName 822 } 823 nodeNameSum := sha256.Sum256([]byte(nodeName + globalDeploymentID())) 824 globalNodeNamesHex[hex.EncodeToString(nodeNameSum[:])] = struct{}{} 825 } 826 827 var err error 828 bootstrapTrace("initServerConfig", func() { 829 if err = initServerConfig(GlobalContext, newObject); err != nil { 830 var cerr config.Err 831 // For any config error, we don't need to drop into safe-mode 832 // instead its a user error and should be fixed by user. 833 if errors.As(err, &cerr) { 834 logger.FatalIf(err, "Unable to initialize the server") 835 } 836 837 // If context was canceled 838 if errors.Is(err, context.Canceled) { 839 logger.FatalIf(err, "Server startup canceled upon user request") 840 } 841 842 logger.LogIf(GlobalContext, err) 843 } 844 845 if !globalServerCtxt.StrictS3Compat { 846 warnings = append(warnings, color.YellowBold("- Strict AWS S3 compatible incoming PUT, POST content payload validation is turned off, caution is advised do not use in production")) 847 } 848 }) 849 if globalActiveCred.Equal(auth.DefaultCredentials) { 850 msg := fmt.Sprintf("- Detected default credentials '%s', we recommend that you change these values with 'MINIO_ROOT_USER' and 'MINIO_ROOT_PASSWORD' environment variables", 851 globalActiveCred) 852 warnings = append(warnings, color.YellowBold(msg)) 853 } 854 855 // Initialize users credentials and policies in background right after config has initialized. 856 go func() { 857 bootstrapTrace("globalIAMSys.Init", func() { 858 globalIAMSys.Init(GlobalContext, newObject, globalEtcdClient, globalRefreshIAMInterval) 859 }) 860 861 // Initialize Console UI 862 if globalBrowserEnabled { 863 bootstrapTrace("initConsoleServer", func() { 864 srv, err := initConsoleServer() 865 if err != nil { 866 logger.FatalIf(err, "Unable to initialize console service") 867 } 868 869 setConsoleSrv(srv) 870 871 go func() { 872 logger.FatalIf(newConsoleServerFn().Serve(), "Unable to initialize console server") 873 }() 874 }) 875 } 876 877 // if we see FTP args, start FTP if possible 878 if len(globalServerCtxt.FTP) > 0 { 879 bootstrapTrace("go startFTPServer", func() { 880 go startFTPServer(globalServerCtxt.FTP) 881 }) 882 } 883 884 // If we see SFTP args, start SFTP if possible 885 if len(globalServerCtxt.SFTP) > 0 { 886 bootstrapTrace("go startSFTPServer", func() { 887 go startSFTPServer(globalServerCtxt.SFTP) 888 }) 889 } 890 }() 891 892 go func() { 893 r := rand.New(rand.NewSource(time.Now().UnixNano())) 894 895 if !globalDisableFreezeOnBoot { 896 defer bootstrapTrace("unfreezeServices", unfreezeServices) 897 t := time.AfterFunc(5*time.Minute, func() { 898 warnings = append(warnings, color.YellowBold("- Initializing the config subsystem is taking longer than 5 minutes. Please set '_MINIO_DISABLE_API_FREEZE_ON_BOOT=true' to not freeze the APIs")) 899 }) 900 defer t.Stop() 901 } 902 903 // Initialize data scanner. 904 bootstrapTrace("initDataScanner", func() { 905 if v := env.Get("_MINIO_SCANNER", config.EnableOn); v == config.EnableOn { 906 initDataScanner(GlobalContext, newObject) 907 } 908 }) 909 910 // Initialize background replication 911 bootstrapTrace("initBackgroundReplication", func() { 912 initBackgroundReplication(GlobalContext, newObject) 913 }) 914 915 // Initialize background ILM worker poool 916 bootstrapTrace("initBackgroundExpiry", func() { 917 initBackgroundExpiry(GlobalContext, newObject) 918 }) 919 920 bootstrapTrace("globalTransitionState.Init", func() { 921 globalTransitionState.Init(newObject) 922 }) 923 924 // Initialize batch job pool. 925 bootstrapTrace("newBatchJobPool", func() { 926 globalBatchJobPool = newBatchJobPool(GlobalContext, newObject, 100) 927 }) 928 929 // Initialize the license update job 930 bootstrapTrace("initLicenseUpdateJob", func() { 931 initLicenseUpdateJob(GlobalContext, newObject) 932 }) 933 934 go func() { 935 // Initialize transition tier configuration manager 936 bootstrapTrace("globalTierConfigMgr.Init", func() { 937 if err := globalTierConfigMgr.Init(GlobalContext, newObject); err != nil { 938 logger.LogIf(GlobalContext, err) 939 } 940 }) 941 }() 942 943 // Initialize bucket notification system. 944 bootstrapTrace("initBucketTargets", func() { 945 logger.LogIf(GlobalContext, globalEventNotifier.InitBucketTargets(GlobalContext, newObject)) 946 }) 947 948 var buckets []BucketInfo 949 // List buckets to initialize bucket metadata sub-sys. 950 bootstrapTrace("listBuckets", func() { 951 for { 952 buckets, err = newObject.ListBuckets(GlobalContext, BucketOptions{}) 953 if err != nil { 954 if configRetriableErrors(err) { 955 logger.Info("Waiting for list buckets to succeed to initialize buckets.. possible cause (%v)", err) 956 time.Sleep(time.Duration(r.Float64() * float64(time.Second))) 957 continue 958 } 959 logger.LogIf(GlobalContext, fmt.Errorf("Unable to list buckets to initialize bucket metadata sub-system: %w", err)) 960 } 961 962 break 963 } 964 }) 965 966 // Initialize bucket metadata sub-system. 967 bootstrapTrace("globalBucketMetadataSys.Init", func() { 968 globalBucketMetadataSys.Init(GlobalContext, buckets, newObject) 969 }) 970 971 // initialize replication resync state. 972 bootstrapTrace("initResync", func() { 973 globalReplicationPool.initResync(GlobalContext, buckets, newObject) 974 }) 975 976 // Initialize site replication manager after bucket metadata 977 bootstrapTrace("globalSiteReplicationSys.Init", func() { 978 globalSiteReplicationSys.Init(GlobalContext, newObject) 979 }) 980 981 // Initialize quota manager. 982 bootstrapTrace("globalBucketQuotaSys.Init", func() { 983 globalBucketQuotaSys.Init(newObject) 984 }) 985 986 // Populate existing buckets to the etcd backend 987 if globalDNSConfig != nil { 988 // Background this operation. 989 bootstrapTrace("go initFederatorBackend", func() { 990 go initFederatorBackend(buckets, newObject) 991 }) 992 } 993 994 // Prints the formatted startup message, if err is not nil then it prints additional information as well. 995 printStartupMessage(getAPIEndpoints(), err) 996 997 // Print a warning at the end of the startup banner so it is more noticeable 998 if newObject.BackendInfo().StandardSCParity == 0 { 999 warnings = append(warnings, color.YellowBold("- The standard parity is set to 0. This can lead to data loss.")) 1000 } 1001 objAPI := newObjectLayerFn() 1002 if objAPI != nil { 1003 printStorageInfo(objAPI.StorageInfo(GlobalContext, true)) 1004 } 1005 if len(warnings) > 0 { 1006 logger.Info(color.Yellow("STARTUP WARNINGS:")) 1007 for _, warn := range warnings { 1008 logger.Info(warn) 1009 } 1010 } 1011 }() 1012 1013 region := globalSite.Region 1014 if region == "" { 1015 region = "us-east-1" 1016 } 1017 bootstrapTrace("globalMinioClient", func() { 1018 globalMinioClient, err = minio.New(globalLocalNodeName, &minio.Options{ 1019 Creds: credentials.NewStaticV4(globalActiveCred.AccessKey, globalActiveCred.SecretKey, ""), 1020 Secure: globalIsTLS, 1021 Transport: globalProxyTransport, 1022 Region: region, 1023 }) 1024 logger.FatalIf(err, "Unable to initialize MinIO client") 1025 }) 1026 1027 go bootstrapTrace("startResourceMetricsCollection", func() { 1028 startResourceMetricsCollection() 1029 }) 1030 1031 // Add User-Agent to differentiate the requests. 1032 globalMinioClient.SetAppInfo("minio-perf-test", ReleaseTag) 1033 1034 if serverDebugLog { 1035 fmt.Println("== DEBUG Mode enabled ==") 1036 fmt.Println("Currently set environment settings:") 1037 ks := []string{ 1038 config.EnvAccessKey, 1039 config.EnvSecretKey, 1040 config.EnvRootUser, 1041 config.EnvRootPassword, 1042 } 1043 for _, v := range os.Environ() { 1044 // Do not print sensitive creds in debug. 1045 if slices.Contains(ks, strings.Split(v, "=")[0]) { 1046 continue 1047 } 1048 fmt.Println(v) 1049 } 1050 fmt.Println("======") 1051 } 1052 1053 daemon.SdNotify(false, daemon.SdNotifyReady) 1054 1055 <-globalOSSignalCh 1056 } 1057 1058 // Initialize object layer with the supplied disks, objectLayer is nil upon any error. 1059 func newObjectLayer(ctx context.Context, endpointServerPools EndpointServerPools) (newObject ObjectLayer, err error) { 1060 return newErasureServerPools(ctx, endpointServerPools) 1061 }