2017-06-09 22:50:51 -04:00
/ *
2019-10-08 01:47:56 -04:00
* MinIO Cloud Storage , ( C ) 2017 - 2019 MinIO , Inc .
2017-06-09 22:50:51 -04: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 01:47:56 -04:00
"crypto/x509"
2020-02-07 03:18:07 -05:00
"encoding/gob"
2017-06-09 22:50:51 -04:00
"errors"
2018-02-02 21:18:52 -05:00
"net"
2020-03-17 13:36:13 -04:00
"os"
2017-06-09 22:50:51 -04:00
"path/filepath"
"strings"
"time"
2018-09-20 17:56:32 -04:00
dns2 "github.com/miekg/dns"
2017-06-09 22:50:51 -04:00
"github.com/minio/cli"
2019-05-29 19:35:12 -04:00
"github.com/minio/minio-go/v6/pkg/set"
2019-10-04 13:35:33 -04:00
"github.com/minio/minio/cmd/config"
2018-04-05 18:04:40 -04:00
"github.com/minio/minio/cmd/logger"
2019-11-01 18:53:16 -04:00
"github.com/minio/minio/pkg/auth"
2019-10-08 01:47:56 -04:00
"github.com/minio/minio/pkg/certs"
2019-10-04 13:35:33 -04:00
"github.com/minio/minio/pkg/env"
2017-06-09 22:50:51 -04:00
)
2020-02-07 03:18:07 -05:00
func init ( ) {
logger . Init ( GOPATH , GOROOT )
logger . RegisterError ( config . FmtError )
// Initialize globalConsoleSys system
2020-04-09 12:30:02 -04:00
globalConsoleSys = NewConsoleLogger ( GlobalContext )
2020-02-07 03:18:07 -05:00
logger . AddTarget ( globalConsoleSys )
gob . Register ( StorageErr ( "" ) )
}
2019-07-26 05:41:16 -04: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 13:35:33 -04:00
uiErr := config . ErrInvalidGWSSEEnvValue ( nil ) . Msg ( "MINIO_GATEWAY_SSE set but KMS is not configured" )
2019-07-26 05:41:16 -04:00
logger . Fatal ( uiErr , "Unable to start gateway with SSE" )
}
}
2019-10-23 01:59:13 -04:00
if globalCompressConfig . Enabled && ! objAPI . IsCompressionSupported ( ) {
2019-07-26 05:41:16 -04:00
logger . Fatal ( errInvalidArgument ,
"Compression support is requested but '%s' does not support compression" , name )
}
}
2017-06-09 22:50:51 -04:00
// Check for updates and print a notification message
func checkUpdate ( mode string ) {
2017-12-15 15:33:42 -05: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 00:25:34 -04:00
if updateMsg == "" {
return
}
2017-12-15 15:33:42 -05:00
if globalInplaceUpdateDisabled {
2019-09-22 04:24:32 -04:00
logStartupMessage ( updateMsg )
2017-12-15 15:33:42 -05:00
} else {
2019-09-22 04:24:32 -04:00
logStartupMessage ( prepareUpdateMessage ( "Run `mc admin update`" , latestReleaseTime . Sub ( currentReleaseTime ) ) )
2017-06-09 22:50:51 -04:00
}
}
}
2019-01-16 15:04:32 -05:00
func newConfigDirFromCtx ( ctx * cli . Context , option string , getDefaultDir func ( ) string ) ( * ConfigDir , bool ) {
2019-01-02 13:05:16 -05:00
var dir string
2019-01-16 15:04:32 -05:00
var dirSet bool
2019-01-02 13:05:16 -05:00
switch {
case ctx . IsSet ( option ) :
dir = ctx . String ( option )
2019-01-16 15:04:32 -05:00
dirSet = true
2019-01-02 13:05:16 -05:00
case ctx . GlobalIsSet ( option ) :
dir = ctx . GlobalString ( option )
2019-01-16 15:04:32 -05:00
dirSet = true
2019-01-02 13:05:16 -05:00
// cli package does not expose parent's option option. Below code is workaround.
if dir == "" || dir == getDefaultDir ( ) {
2019-01-16 15:04:32 -05:00
dirSet = false // Unset to false since GlobalIsSet() true is a false positive.
2019-01-02 13:05:16 -05:00
if ctx . Parent ( ) . GlobalIsSet ( option ) {
dir = ctx . Parent ( ) . GlobalString ( option )
2019-01-16 15:04:32 -05:00
dirSet = true
2019-01-02 13:05:16 -05: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 15:04:32 -05:00
return & ConfigDir { path : dirAbs } , dirSet
2019-01-02 13:05:16 -05:00
}
2017-06-09 22:50:51 -04:00
func handleCommonCmdArgs ( ctx * cli . Context ) {
2018-02-28 23:13:33 -05:00
2018-12-18 19:08:11 -05:00
// Get "json" flag from command line argument and
2019-02-10 08:49:00 -05:00
// enable json and quite modes if json flag is turned on.
2018-12-18 19:08:11 -05: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" )
if globalCLIContext . Addr == "" || globalCLIContext . Addr == ":" + globalMinioDefaultPort {
globalCLIContext . Addr = ctx . String ( "address" )
}
2020-04-12 21:08:27 -04: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 13:05:16 -05:00
// Set all config, certs and CAs directories.
2019-01-16 15:04:32 -05: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 13:05:16 -05:00
globalCertsCADir = & ConfigDir { path : filepath . Join ( globalCertsDir . Get ( ) , certsCADir ) }
2018-02-28 23:13:33 -05:00
2019-01-02 13:05:16 -05:00
logger . FatalIf ( mkdirAllIgnorePerm ( globalCertsCADir . Get ( ) ) , "Unable to create certs CA directory at %s" , globalCertsCADir . Get ( ) )
2017-06-09 22:50:51 -04:00
}
func handleCommonEnvVars ( ) {
2020-04-24 19:37:05 -04: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-04 18:32:37 -05:00
globalBrowserEnabled , err = config . ParseBool ( env . Get ( config . EnvBrowser , config . EnableOn ) )
2019-10-23 01:59:13 -04:00
if err != nil {
logger . Fatal ( config . ErrInvalidBrowserValue ( err ) , "Invalid MINIO_BROWSER value in environment variable" )
2017-06-09 22:50:51 -04:00
}
2017-10-24 22:04:51 -04:00
2020-05-12 22:24:59 -04: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 02:39:09 -04:00
domains := env . Get ( config . EnvDomain , "" )
if len ( domains ) != 0 {
for _ , domainName := range strings . Split ( domains , config . ValueSeparator ) {
2019-10-08 01:47:56 -04:00
if _ , ok := dns2 . IsDomainName ( domainName ) ; ! ok {
2019-10-04 13:35:33 -04:00
logger . Fatal ( config . ErrInvalidDomainValue ( nil ) . Msg ( "Unknown value `%s`" , domainName ) ,
2019-02-22 22:18:01 -05:00
"Invalid MINIO_DOMAIN value in environment variable" )
}
globalDomainNames = append ( globalDomainNames , domainName )
2018-09-20 17:56:32 -04:00
}
}
2018-05-15 21:20:22 -04:00
2019-10-31 02:39:09 -04:00
publicIPs := env . Get ( config . EnvPublicIPs , "" )
if len ( publicIPs ) != 0 {
minioEndpoints := strings . Split ( publicIPs , config . ValueSeparator )
2018-12-23 06:08:21 -05: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 14:39:42 -04:00
logger . FatalIf ( err , "Unable to initialize MinIO server with [%s] invalid entry found in MINIO_PUBLIC_IPS" , endpoint )
2018-12-23 06:08:21 -05:00
}
for _ , addr := range addrs {
domainIPs . Add ( addr )
}
2018-05-11 15:02:30 -04:00
}
2018-12-23 06:08:21 -05:00
domainIPs . Add ( endpoint )
2018-05-11 15:02:30 -04:00
}
2018-12-23 06:08:21 -05:00
updateDomainIPs ( domainIPs )
2018-12-04 20:35:22 -05:00
} else {
// Add found interfaces IP address to global domain IPS,
// loopback addresses will be naturally dropped.
2019-12-19 16:45:56 -05:00
updateDomainIPs ( mustGetLocalIP4 ( ) )
2018-05-11 15:02:30 -04:00
}
2018-12-04 20:35:22 -05:00
2017-12-15 15:33:42 -05: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-04 18:32:37 -05:00
globalInplaceUpdateDisabled = strings . EqualFold ( env . Get ( config . EnvUpdate , config . EnableOn ) , config . EnableOff )
2019-11-01 18:53:16 -04:00
2019-11-13 20:38:05 -05: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 18:53:16 -04:00
if err != nil {
logger . Fatal ( config . ErrInvalidCredentials ( err ) ,
"Unable to validate credentials inherited from the shell environment" )
}
globalActiveCred = cred
globalConfigEncrypted = true
}
2019-12-14 20:27:57 -05:00
2020-03-17 13:36:13 -04: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-09 22:50:51 -04:00
}
2019-09-22 04:24:32 -04:00
2019-10-15 21:35:41 -04:00
func logStartupMessage ( msg string ) {
2019-09-22 13:45:33 -04:00
if globalConsoleSys != nil {
2019-10-11 21:50:54 -04:00
globalConsoleSys . Send ( msg , string ( logger . All ) )
2019-09-22 13:45:33 -04:00
}
2019-10-15 21:35:41 -04:00
logger . StartupMessage ( msg )
2019-09-22 04:24:32 -04:00
}
2019-10-08 01:47:56 -04: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
}