2017-06-10 04:50:51 +02:00
/ *
2019-10-08 07:47:56 +02:00
* MinIO Cloud Storage , ( C ) 2017 - 2019 MinIO , Inc .
2017-06-10 04:50:51 +02:00
*
* Licensed under the Apache License , Version 2.0 ( the "License" ) ;
* you may not use this file except in compliance with the License .
* You may obtain a copy of the License at
*
* http : //www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing , software
* distributed under the License is distributed on an "AS IS" BASIS ,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND , either express or implied .
* See the License for the specific language governing permissions and
* limitations under the License .
* /
package cmd
import (
2019-10-08 07:47:56 +02:00
"crypto/x509"
2020-02-07 09:18:07 +01:00
"encoding/gob"
2017-06-10 04:50:51 +02:00
"errors"
2018-02-03 03:18:52 +01:00
"net"
2020-03-17 18:36:13 +01:00
"os"
2017-06-10 04:50:51 +02:00
"path/filepath"
"strings"
"time"
2018-09-20 23:56:32 +02:00
dns2 "github.com/miekg/dns"
2017-06-10 04:50:51 +02:00
"github.com/minio/cli"
2020-07-14 18:38:05 +02:00
"github.com/minio/minio-go/v7/pkg/set"
2019-10-04 19:35:33 +02:00
"github.com/minio/minio/cmd/config"
2018-04-06 00:04:40 +02:00
"github.com/minio/minio/cmd/logger"
2019-11-01 23:53:16 +01:00
"github.com/minio/minio/pkg/auth"
2019-10-08 07:47:56 +02:00
"github.com/minio/minio/pkg/certs"
2019-10-04 19:35:33 +02:00
"github.com/minio/minio/pkg/env"
2017-06-10 04:50:51 +02:00
)
2020-02-07 09:18:07 +01:00
func init ( ) {
logger . Init ( GOPATH , GOROOT )
logger . RegisterError ( config . FmtError )
// Initialize globalConsoleSys system
2020-04-09 18:30:02 +02:00
globalConsoleSys = NewConsoleLogger ( GlobalContext )
2020-02-07 09:18:07 +01:00
logger . AddTarget ( globalConsoleSys )
gob . Register ( StorageErr ( "" ) )
}
2019-07-26 11:41:16 +02:00
func verifyObjectLayerFeatures ( name string , objAPI ObjectLayer ) {
if ( globalAutoEncryption || GlobalKMS != nil ) && ! objAPI . IsEncryptionSupported ( ) {
logger . Fatal ( errInvalidArgument ,
"Encryption support is requested but '%s' does not support encryption" , name )
}
if strings . HasPrefix ( name , "gateway" ) {
if GlobalGatewaySSE . IsSet ( ) && GlobalKMS == nil {
2019-10-04 19:35:33 +02:00
uiErr := config . ErrInvalidGWSSEEnvValue ( nil ) . Msg ( "MINIO_GATEWAY_SSE set but KMS is not configured" )
2019-07-26 11:41:16 +02:00
logger . Fatal ( uiErr , "Unable to start gateway with SSE" )
}
}
2019-10-23 07:59:13 +02:00
if globalCompressConfig . Enabled && ! objAPI . IsCompressionSupported ( ) {
2019-07-26 11:41:16 +02:00
logger . Fatal ( errInvalidArgument ,
"Compression support is requested but '%s' does not support compression" , name )
}
}
2017-06-10 04:50:51 +02:00
// Check for updates and print a notification message
func checkUpdate ( mode string ) {
2017-12-15 21:33:42 +01:00
// Its OK to ignore any errors during doUpdate() here.
if updateMsg , _ , currentReleaseTime , latestReleaseTime , err := getUpdateInfo ( 2 * time . Second , mode ) ; err == nil {
2019-08-13 06:25:34 +02:00
if updateMsg == "" {
return
}
2017-12-15 21:33:42 +01:00
if globalInplaceUpdateDisabled {
2019-09-22 10:24:32 +02:00
logStartupMessage ( updateMsg )
2017-12-15 21:33:42 +01:00
} else {
2019-09-22 10:24:32 +02:00
logStartupMessage ( prepareUpdateMessage ( "Run `mc admin update`" , latestReleaseTime . Sub ( currentReleaseTime ) ) )
2017-06-10 04:50:51 +02:00
}
}
}
2019-01-16 21:04:32 +01:00
func newConfigDirFromCtx ( ctx * cli . Context , option string , getDefaultDir func ( ) string ) ( * ConfigDir , bool ) {
2019-01-02 19:05:16 +01:00
var dir string
2019-01-16 21:04:32 +01:00
var dirSet bool
2019-01-02 19:05:16 +01:00
switch {
case ctx . IsSet ( option ) :
dir = ctx . String ( option )
2019-01-16 21:04:32 +01:00
dirSet = true
2019-01-02 19:05:16 +01:00
case ctx . GlobalIsSet ( option ) :
dir = ctx . GlobalString ( option )
2019-01-16 21:04:32 +01:00
dirSet = true
2019-01-02 19:05:16 +01:00
// cli package does not expose parent's option option. Below code is workaround.
if dir == "" || dir == getDefaultDir ( ) {
2019-01-16 21:04:32 +01:00
dirSet = false // Unset to false since GlobalIsSet() true is a false positive.
2019-01-02 19:05:16 +01:00
if ctx . Parent ( ) . GlobalIsSet ( option ) {
dir = ctx . Parent ( ) . GlobalString ( option )
2019-01-16 21:04:32 +01:00
dirSet = true
2019-01-02 19:05:16 +01:00
}
}
default :
// Neither local nor global option is provided. In this case, try to use
// default directory.
dir = getDefaultDir ( )
if dir == "" {
logger . FatalIf ( errInvalidArgument , "%s option must be provided" , option )
}
}
if dir == "" {
logger . FatalIf ( errors . New ( "empty directory" ) , "%s directory cannot be empty" , option )
}
// Disallow relative paths, figure out absolute paths.
dirAbs , err := filepath . Abs ( dir )
logger . FatalIf ( err , "Unable to fetch absolute path for %s=%s" , option , dir )
logger . FatalIf ( mkdirAllIgnorePerm ( dirAbs ) , "Unable to create directory specified %s=%s" , option , dir )
2019-01-16 21:04:32 +01:00
return & ConfigDir { path : dirAbs } , dirSet
2019-01-02 19:05:16 +01:00
}
2017-06-10 04:50:51 +02:00
func handleCommonCmdArgs ( ctx * cli . Context ) {
2018-03-01 05:13:33 +01:00
2018-12-19 01:08:11 +01:00
// Get "json" flag from command line argument and
2019-02-10 14:49:00 +01:00
// enable json and quite modes if json flag is turned on.
2018-12-19 01:08:11 +01:00
globalCLIContext . JSON = ctx . IsSet ( "json" ) || ctx . GlobalIsSet ( "json" )
if globalCLIContext . JSON {
logger . EnableJSON ( )
}
// Get quiet flag from command line argument.
globalCLIContext . Quiet = ctx . IsSet ( "quiet" ) || ctx . GlobalIsSet ( "quiet" )
if globalCLIContext . Quiet {
logger . EnableQuiet ( )
}
// Get anonymous flag from command line argument.
globalCLIContext . Anonymous = ctx . IsSet ( "anonymous" ) || ctx . GlobalIsSet ( "anonymous" )
if globalCLIContext . Anonymous {
logger . EnableAnonymous ( )
}
// Fetch address option
globalCLIContext . Addr = ctx . GlobalString ( "address" )
2020-05-17 17:46:23 +02:00
if globalCLIContext . Addr == "" || globalCLIContext . Addr == ":" + GlobalMinioDefaultPort {
2018-12-19 01:08:11 +01:00
globalCLIContext . Addr = ctx . String ( "address" )
}
2020-04-13 03:08:27 +02:00
// Check "no-compat" flag from command line argument.
globalCLIContext . StrictS3Compat = true
if ctx . IsSet ( "no-compat" ) || ctx . GlobalIsSet ( "no-compat" ) {
globalCLIContext . StrictS3Compat = false
}
2019-01-02 19:05:16 +01:00
// Set all config, certs and CAs directories.
2019-01-16 21:04:32 +01:00
var configSet , certsSet bool
globalConfigDir , configSet = newConfigDirFromCtx ( ctx , "config-dir" , defaultConfigDir . Get )
globalCertsDir , certsSet = newConfigDirFromCtx ( ctx , "certs-dir" , defaultCertsDir . Get )
// Remove this code when we deprecate and remove config-dir.
// This code is to make sure we inherit from the config-dir
// option if certs-dir is not provided.
if ! certsSet && configSet {
globalCertsDir = & ConfigDir { path : filepath . Join ( globalConfigDir . Get ( ) , certsDir ) }
}
2019-01-02 19:05:16 +01:00
globalCertsCADir = & ConfigDir { path : filepath . Join ( globalCertsDir . Get ( ) , certsCADir ) }
2018-03-01 05:13:33 +01:00
2019-01-02 19:05:16 +01:00
logger . FatalIf ( mkdirAllIgnorePerm ( globalCertsCADir . Get ( ) ) , "Unable to create certs CA directory at %s" , globalCertsCADir . Get ( ) )
2017-06-10 04:50:51 +02:00
}
func handleCommonEnvVars ( ) {
2020-04-25 01:37:05 +02:00
wormEnabled , err := config . LookupWorm ( )
if err != nil {
logger . Fatal ( config . ErrInvalidWormValue ( err ) , "Invalid worm configuration" )
}
if wormEnabled {
logger . Fatal ( errors . New ( "WORM is deprecated" ) , "global MINIO_WORM support is removed, please downgrade your server or migrate to https://github.com/minio/minio/tree/master/docs/retention" )
}
2019-12-05 00:32:37 +01:00
globalBrowserEnabled , err = config . ParseBool ( env . Get ( config . EnvBrowser , config . EnableOn ) )
2019-10-23 07:59:13 +02:00
if err != nil {
logger . Fatal ( config . ErrInvalidBrowserValue ( err ) , "Invalid MINIO_BROWSER value in environment variable" )
2017-06-10 04:50:51 +02:00
}
2017-10-25 04:04:51 +02:00
2020-05-13 04:24:59 +02:00
globalFSOSync , err = config . ParseBool ( env . Get ( config . EnvFSOSync , config . EnableOff ) )
if err != nil {
logger . Fatal ( config . ErrInvalidFSOSyncValue ( err ) , "Invalid MINIO_FS_OSYNC value in environment variable" )
}
2019-10-31 07:39:09 +01:00
domains := env . Get ( config . EnvDomain , "" )
if len ( domains ) != 0 {
for _ , domainName := range strings . Split ( domains , config . ValueSeparator ) {
2019-10-08 07:47:56 +02:00
if _ , ok := dns2 . IsDomainName ( domainName ) ; ! ok {
2019-10-04 19:35:33 +02:00
logger . Fatal ( config . ErrInvalidDomainValue ( nil ) . Msg ( "Unknown value `%s`" , domainName ) ,
2019-02-23 04:18:01 +01:00
"Invalid MINIO_DOMAIN value in environment variable" )
}
globalDomainNames = append ( globalDomainNames , domainName )
2018-09-20 23:56:32 +02:00
}
}
2018-05-16 03:20:22 +02:00
2019-10-31 07:39:09 +01:00
publicIPs := env . Get ( config . EnvPublicIPs , "" )
if len ( publicIPs ) != 0 {
minioEndpoints := strings . Split ( publicIPs , config . ValueSeparator )
2018-12-23 12:08:21 +01:00
var domainIPs = set . NewStringSet ( )
for _ , endpoint := range minioEndpoints {
if net . ParseIP ( endpoint ) == nil {
// Checking if the IP is a DNS entry.
addrs , err := net . LookupHost ( endpoint )
if err != nil {
2019-04-09 20:39:42 +02:00
logger . FatalIf ( err , "Unable to initialize MinIO server with [%s] invalid entry found in MINIO_PUBLIC_IPS" , endpoint )
2018-12-23 12:08:21 +01:00
}
for _ , addr := range addrs {
domainIPs . Add ( addr )
}
2018-05-11 21:02:30 +02:00
}
2018-12-23 12:08:21 +01:00
domainIPs . Add ( endpoint )
2018-05-11 21:02:30 +02:00
}
2018-12-23 12:08:21 +01:00
updateDomainIPs ( domainIPs )
2018-12-05 02:35:22 +01:00
} else {
// Add found interfaces IP address to global domain IPS,
// loopback addresses will be naturally dropped.
2019-12-19 22:45:56 +01:00
updateDomainIPs ( mustGetLocalIP4 ( ) )
2018-05-11 21:02:30 +02:00
}
2018-12-05 02:35:22 +01:00
2017-12-15 21:33:42 +01:00
// In place update is true by default if the MINIO_UPDATE is not set
// or is not set to 'off', if MINIO_UPDATE is set to 'off' then
// in-place update is off.
2019-12-05 00:32:37 +01:00
globalInplaceUpdateDisabled = strings . EqualFold ( env . Get ( config . EnvUpdate , config . EnableOn ) , config . EnableOff )
2019-11-01 23:53:16 +01:00
2019-11-14 02:38:05 +01:00
if env . IsSet ( config . EnvAccessKey ) || env . IsSet ( config . EnvSecretKey ) {
cred , err := auth . CreateCredentials ( env . Get ( config . EnvAccessKey , "" ) , env . Get ( config . EnvSecretKey , "" ) )
2019-11-01 23:53:16 +01:00
if err != nil {
logger . Fatal ( config . ErrInvalidCredentials ( err ) ,
"Unable to validate credentials inherited from the shell environment" )
}
globalActiveCred = cred
globalConfigEncrypted = true
}
2019-12-15 02:27:57 +01:00
2020-03-17 18:36:13 +01:00
if env . IsSet ( config . EnvAccessKeyOld ) && env . IsSet ( config . EnvSecretKeyOld ) {
oldCred , err := auth . CreateCredentials ( env . Get ( config . EnvAccessKeyOld , "" ) , env . Get ( config . EnvSecretKeyOld , "" ) )
if err != nil {
logger . Fatal ( config . ErrInvalidCredentials ( err ) ,
"Unable to validate the old credentials inherited from the shell environment" )
}
globalOldCred = oldCred
os . Unsetenv ( config . EnvAccessKeyOld )
os . Unsetenv ( config . EnvSecretKeyOld )
}
2017-06-10 04:50:51 +02:00
}
2019-09-22 10:24:32 +02:00
2019-10-16 03:35:41 +02:00
func logStartupMessage ( msg string ) {
2019-09-22 19:45:33 +02:00
if globalConsoleSys != nil {
2019-10-12 03:50:54 +02:00
globalConsoleSys . Send ( msg , string ( logger . All ) )
2019-09-22 19:45:33 +02:00
}
2019-10-16 03:35:41 +02:00
logger . StartupMessage ( msg )
2019-09-22 10:24:32 +02:00
}
2019-10-08 07:47:56 +02:00
func getTLSConfig ( ) ( x509Certs [ ] * x509 . Certificate , c * certs . Certs , secureConn bool , err error ) {
if ! ( isFile ( getPublicCertFile ( ) ) && isFile ( getPrivateKeyFile ( ) ) ) {
return nil , nil , false , nil
}
if x509Certs , err = config . ParsePublicCertFile ( getPublicCertFile ( ) ) ; err != nil {
return nil , nil , false , err
}
c , err = certs . New ( getPublicCertFile ( ) , getPrivateKeyFile ( ) , config . LoadX509KeyPair )
if err != nil {
return nil , nil , false , err
}
secureConn = true
return x509Certs , c , secureConn , nil
}