From 31dff879032862f47d01778b21dffc32e8c6d549 Mon Sep 17 00:00:00 2001 From: Harshavardhana Date: Tue, 7 Feb 2017 12:51:43 -0800 Subject: [PATCH] Honor envs properly for access and secret key. (#3703) Also changes the behavior of `secretKeyHash` which is not necessary to be sent over the network, each node has its own secretKeyHash to validate. Fixes #3696 Partial(fix) #3700 (More changes needed with some code cleanup) --- .travis.yml | 2 +- appveyor.yml | 4 +- cmd/admin-handlers.go | 21 +++--- cmd/admin-handlers_test.go | 1 + cmd/api-errors.go | 5 +- cmd/auth-handler_test.go | 6 +- cmd/browser-peer-rpc.go | 5 +- cmd/config-migrate_test.go | 9 +-- cmd/config-v13.go | 124 ++++++++++++++++--------------- cmd/config-v13_test.go | 2 +- cmd/credential.go | 63 ++++++++++++---- cmd/jwt.go | 26 +++++-- cmd/main.go | 47 ++++++------ cmd/server-main.go | 23 ++---- cmd/server-main_test.go | 11 ++- cmd/signature-v2_test.go | 22 ++---- cmd/test-utils_test.go | 12 ++- cmd/web-handlers.go | 24 ++++-- cmd/web-handlers_test.go | 146 +------------------------------------ 19 files changed, 238 insertions(+), 315 deletions(-) diff --git a/.travis.yml b/.travis.yml index d6976a5b9..979a61bed 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,7 +14,7 @@ env: script: - make -- make test GOFLAGS="-timeout 15m -race -v" +- make test GOFLAGS="-timeout 20m -race -v" - make coverage after_success: diff --git a/appveyor.yml b/appveyor.yml index 7488c103a..68a87aa07 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -35,9 +35,9 @@ test_script: # Unit tests - ps: Add-AppveyorTest "Unit Tests" -Outcome Running - mkdir build\coverage - - go test -timeout 15m -v -race github.com/minio/minio/cmd... + - go test -timeout 20m -v -race github.com/minio/minio/cmd... - go test -v -race github.com/minio/minio/pkg... - - go test -coverprofile=build\coverage\coverage.txt -covermode=atomic github.com/minio/minio/cmd + - go test -timeout 15m -coverprofile=build\coverage\coverage.txt -covermode=atomic github.com/minio/minio/cmd - ps: Update-AppveyorTest "Unit Tests" -Outcome Passed after_test: diff --git a/cmd/admin-handlers.go b/cmd/admin-handlers.go index d2998c580..2773040e1 100644 --- a/cmd/admin-handlers.go +++ b/cmd/admin-handlers.go @@ -154,24 +154,25 @@ func (adminAPI adminAPIHandlers) ServiceCredentialsHandler(w http.ResponseWriter } // Check passed credentials - cred, err := getCredential(req.Username, req.Password) - switch err { - case errInvalidAccessKeyLength: - writeErrorResponse(w, ErrAdminInvalidAccessKey, r.URL) - return - case errInvalidSecretKeyLength: - writeErrorResponse(w, ErrAdminInvalidSecretKey, r.URL) + err = validateAuthKeys(req.Username, req.Password) + if err != nil { + writeErrorResponse(w, toAPIErrorCode(err), r.URL) return } + creds := credential{ + AccessKey: req.Username, + SecretKey: req.Password, + } + // Notify all other Minio peers to update credentials - updateErrs := updateCredsOnPeers(cred) + updateErrs := updateCredsOnPeers(creds) for peer, err := range updateErrs { errorIf(err, "Unable to update credentials on peer %s.", peer) } - // Update local credentials - serverConfig.SetCredential(cred) + // Update local credentials in memory. + serverConfig.SetCredential(creds) if err = serverConfig.Save(); err != nil { writeErrorResponse(w, ErrInternalError, r.URL) return diff --git a/cmd/admin-handlers_test.go b/cmd/admin-handlers_test.go index 0a8a6c96f..c6b25055b 100644 --- a/cmd/admin-handlers_test.go +++ b/cmd/admin-handlers_test.go @@ -43,6 +43,7 @@ type adminXLTestBed struct { func prepareAdminXLTestBed() (*adminXLTestBed, error) { // reset global variables to start afresh. resetTestGlobals() + // Initialize minio server config. rootPath, err := newTestConfig(globalMinioDefaultRegion) if err != nil { diff --git a/cmd/api-errors.go b/cmd/api-errors.go index 6ac5e7f06..656c6a39f 100644 --- a/cmd/api-errors.go +++ b/cmd/api-errors.go @@ -610,7 +610,10 @@ func toAPIErrorCode(err error) (apiErr APIErrorCode) { apiErr = ErrEntityTooLarge case errDataTooSmall: apiErr = ErrEntityTooSmall - + case errInvalidAccessKeyLength: + apiErr = ErrAdminInvalidAccessKey + case errInvalidSecretKeyLength: + apiErr = ErrAdminInvalidSecretKey } if apiErr != ErrNone { diff --git a/cmd/auth-handler_test.go b/cmd/auth-handler_test.go index fc48afc6c..c0e85bae2 100644 --- a/cmd/auth-handler_test.go +++ b/cmd/auth-handler_test.go @@ -315,11 +315,7 @@ func TestIsReqAuthenticated(t *testing.T) { } defer removeAll(path) - creds, err := getCredential("myuser", "mypassword") - if err != nil { - t.Fatal(err) - } - + creds := newCredentialWithKeys("myuser", "mypassword") serverConfig.SetCredential(creds) // List of test cases for validating http request authentication. diff --git a/cmd/browser-peer-rpc.go b/cmd/browser-peer-rpc.go index 335e0b770..398b907bf 100644 --- a/cmd/browser-peer-rpc.go +++ b/cmd/browser-peer-rpc.go @@ -63,13 +63,12 @@ func (br *browserPeerAPIHandlers) SetAuthPeer(args SetAuthPeerArgs, reply *AuthR return err } - creds, err := getCredential(args.Creds.AccessKey, args.Creds.SecretKey) - if err != nil { + if err := validateAuthKeys(args.Creds.AccessKey, args.Creds.SecretKey); err != nil { return err } // Update credentials in memory - serverConfig.SetCredential(creds) + serverConfig.SetCredential(args.Creds) // Save credentials to config file if err := serverConfig.Save(); err != nil { diff --git a/cmd/config-migrate_test.go b/cmd/config-migrate_test.go index c5b8a0827..b1fb6e24e 100644 --- a/cmd/config-migrate_test.go +++ b/cmd/config-migrate_test.go @@ -50,7 +50,7 @@ func TestServerConfigMigrateV1(t *testing.T) { } // Initialize server config and check again if everything is fine - if _, err := initConfig(); err != nil { + if err := loadConfig(credential{}); err != nil { t.Fatalf("Unable to initialize from updated config file %s", err) } } @@ -143,7 +143,7 @@ func TestServerConfigMigrateV2toV12(t *testing.T) { } // Initialize server config and check again if everything is fine - if _, err := initConfig(); err != nil { + if err := loadConfig(credential{}); err != nil { t.Fatalf("Unable to initialize from updated config file %s", err) } @@ -160,11 +160,6 @@ func TestServerConfigMigrateV2toV12(t *testing.T) { if serverConfig.Credential.SecretKey != secretKey { t.Fatalf("Secret key lost during migration, expected: %v, found: %v", secretKey, serverConfig.Credential.SecretKey) } - - // Initialize server config and check again if everything is fine - if _, err := initConfig(); err != nil { - t.Fatalf("Unable to initialize from updated config file %s", err) - } } // Test if all migrate code returns error with corrupted config files diff --git a/cmd/config-v13.go b/cmd/config-v13.go index 7b014fb6c..8e66c5f70 100644 --- a/cmd/config-v13.go +++ b/cmd/config-v13.go @@ -42,77 +42,88 @@ type serverConfigV13 struct { Notify notifier `json:"notify"` } -// initConfig - initialize server config and indicate if we are -// creating a new file or we are just loading -func initConfig() (bool, error) { - if !isConfigFileExists() { - // Initialize server config. - srvCfg := &serverConfigV13{} - srvCfg.Version = globalMinioConfigVersion - srvCfg.Region = globalMinioDefaultRegion - srvCfg.Credential = newCredential() - - // Enable console logger by default on a fresh run. - srvCfg.Logger.Console = consoleLogger{ - Enable: true, - Level: "error", - } - - // Make sure to initialize notification configs. - srvCfg.Notify.AMQP = make(map[string]amqpNotify) - srvCfg.Notify.AMQP["1"] = amqpNotify{} - srvCfg.Notify.ElasticSearch = make(map[string]elasticSearchNotify) - srvCfg.Notify.ElasticSearch["1"] = elasticSearchNotify{} - srvCfg.Notify.Redis = make(map[string]redisNotify) - srvCfg.Notify.Redis["1"] = redisNotify{} - srvCfg.Notify.NATS = make(map[string]natsNotify) - srvCfg.Notify.NATS["1"] = natsNotify{} - srvCfg.Notify.PostgreSQL = make(map[string]postgreSQLNotify) - srvCfg.Notify.PostgreSQL["1"] = postgreSQLNotify{} - srvCfg.Notify.Kafka = make(map[string]kafkaNotify) - srvCfg.Notify.Kafka["1"] = kafkaNotify{} - srvCfg.Notify.Webhook = make(map[string]webhookNotify) - srvCfg.Notify.Webhook["1"] = webhookNotify{} - - // Create config path. - err := createConfigPath() - if err != nil { - return false, err - } - - // hold the mutex lock before a new config is assigned. - // Save the new config globally. - // unlock the mutex. - serverConfigMu.Lock() - serverConfig = srvCfg - serverConfigMu.Unlock() - - // Save config into file. - return true, serverConfig.Save() +// newConfig - initialize a new server config, saves creds from env +// if globalIsEnvCreds is set otherwise generates a new set of keys +// and those are saved. +func newConfig(envCreds credential) error { + // Initialize server config. + srvCfg := &serverConfigV13{} + srvCfg.Version = globalMinioConfigVersion + srvCfg.Region = globalMinioDefaultRegion + + // If env is set for a fresh start, save them to config file. + if globalIsEnvCreds { + srvCfg.SetCredential(envCreds) + } else { + srvCfg.SetCredential(newCredential()) } + // Enable console logger by default on a fresh run. + srvCfg.Logger.Console = consoleLogger{ + Enable: true, + Level: "error", + } + + // Make sure to initialize notification configs. + srvCfg.Notify.AMQP = make(map[string]amqpNotify) + srvCfg.Notify.AMQP["1"] = amqpNotify{} + srvCfg.Notify.ElasticSearch = make(map[string]elasticSearchNotify) + srvCfg.Notify.ElasticSearch["1"] = elasticSearchNotify{} + srvCfg.Notify.Redis = make(map[string]redisNotify) + srvCfg.Notify.Redis["1"] = redisNotify{} + srvCfg.Notify.NATS = make(map[string]natsNotify) + srvCfg.Notify.NATS["1"] = natsNotify{} + srvCfg.Notify.PostgreSQL = make(map[string]postgreSQLNotify) + srvCfg.Notify.PostgreSQL["1"] = postgreSQLNotify{} + srvCfg.Notify.Kafka = make(map[string]kafkaNotify) + srvCfg.Notify.Kafka["1"] = kafkaNotify{} + srvCfg.Notify.Webhook = make(map[string]webhookNotify) + srvCfg.Notify.Webhook["1"] = webhookNotify{} + + // Create config path. + if err := createConfigPath(); err != nil { + return err + } + + // hold the mutex lock before a new config is assigned. + // Save the new config globally. + // unlock the mutex. + serverConfigMu.Lock() + serverConfig = srvCfg + serverConfigMu.Unlock() + + // Save config into file. + return serverConfig.Save() +} + +// loadConfig - loads a new config from disk, overrides creds from env +// if globalIsEnvCreds is set otherwise serves the creds from loaded +// from the disk. +func loadConfig(envCreds credential) error { configFile, err := getConfigFile() if err != nil { - return false, err + return err } if _, err = os.Stat(configFile); err != nil { - return false, err + return err } srvCfg := &serverConfigV13{} srvCfg.Version = globalMinioConfigVersion qc, err := quick.New(srvCfg) if err != nil { - return false, err + return err } if err = qc.Load(configFile); err != nil { - return false, err + return err } - srvCfg.Credential, err = getCredential(srvCfg.Credential.AccessKey, srvCfg.Credential.SecretKey) - if err != nil { - return false, err + // If env is set override the credentials from config file. + if globalIsEnvCreds { + srvCfg.SetCredential(envCreds) + } else { + srvCfg.SetCredential(srvCfg.Credential) } // hold the mutex lock before a new config is assigned. @@ -123,8 +134,7 @@ func initConfig() (bool, error) { // Set the version properly after the unmarshalled json is loaded. serverConfig.Version = globalMinioConfigVersion - - return false, nil + return nil } // serverConfig server config. @@ -347,7 +357,7 @@ func (s *serverConfigV13) SetCredential(creds credential) { defer serverConfigMu.Unlock() // Set updated credential. - s.Credential = creds + s.Credential = newCredentialWithKeys(creds.AccessKey, creds.SecretKey) } // GetCredentials get current credentials. diff --git a/cmd/config-v13_test.go b/cmd/config-v13_test.go index 54312714e..b620b00ff 100644 --- a/cmd/config-v13_test.go +++ b/cmd/config-v13_test.go @@ -106,7 +106,7 @@ func TestServerConfig(t *testing.T) { setGlobalConfigPath(rootPath) // Initialize server config. - if _, err := initConfig(); err != nil { + if err := loadConfig(credential{}); err != nil { t.Fatalf("Unable to initialize from updated config file %s", err) } } diff --git a/cmd/credential.go b/cmd/credential.go index aad1749b1..c75049050 100644 --- a/cmd/credential.go +++ b/cmd/credential.go @@ -19,6 +19,9 @@ package cmd import ( "crypto/rand" "encoding/base64" + "os" + + "github.com/minio/mc/pkg/console" "golang.org/x/crypto/bcrypt" ) @@ -36,7 +39,7 @@ const ( func mustGetAccessKey() string { keyBytes := make([]byte, accessKeyMaxLen) if _, err := rand.Read(keyBytes); err != nil { - panic(err) + console.Fatalf("Unable to generate access key. Err: %s.\n", err) } for i := 0; i < accessKeyMaxLen; i++ { @@ -49,7 +52,7 @@ func mustGetAccessKey() string { func mustGetSecretKey() string { keyBytes := make([]byte, secretKeyMaxLen) if _, err := rand.Read(keyBytes); err != nil { - panic(err) + console.Fatalf("Unable to generate secret key. Err: %s.\n", err) } return string([]byte(base64.StdEncoding.EncodeToString(keyBytes))[:secretKeyMaxLen]) @@ -69,38 +72,68 @@ func isSecretKeyValid(secretKey string) bool { type credential struct { AccessKey string `json:"accessKey,omitempty"` SecretKey string `json:"secretKey,omitempty"` - SecretKeyHash []byte `json:"secretKeyHash,omitempty"` + secretKeyHash []byte } // Generate a bcrypt hashed key for input secret key. func mustGetHashedSecretKey(secretKey string) []byte { hashedSecretKey, err := bcrypt.GenerateFromPassword([]byte(secretKey), bcrypt.DefaultCost) if err != nil { - panic(err) + console.Fatalf("Unable to generate secret hash for secret key. Err: %s.\n", err) } return hashedSecretKey } -// Initialize a new credential object. +// Initialize a new credential object func newCredential() credential { - secretKey := mustGetSecretKey() - accessKey := mustGetAccessKey() + return newCredentialWithKeys(mustGetAccessKey(), mustGetSecretKey()) +} +func newCredentialWithKeys(accessKey, secretKey string) credential { secretHash := mustGetHashedSecretKey(secretKey) return credential{accessKey, secretKey, secretHash} } -// Converts accessKey and secretKeys into credential object which -// contains bcrypt secret key hash for future validation. -func getCredential(accessKey, secretKey string) (credential, error) { +// Validate incoming auth keys. +func validateAuthKeys(accessKey, secretKey string) error { + // Validate the env values before proceeding. if !isAccessKeyValid(accessKey) { - return credential{}, errInvalidAccessKeyLength + return errInvalidAccessKeyLength } - if !isSecretKeyValid(secretKey) { - return credential{}, errInvalidSecretKeyLength + return errInvalidSecretKeyLength } + return nil +} - secretHash := mustGetHashedSecretKey(secretKey) - return credential{accessKey, secretKey, secretHash}, nil +// Variant of getCredentialFromEnv but upon error fails right here. +func mustGetCredentialFromEnv() credential { + creds, err := getCredentialFromEnv() + if err != nil { + console.Fatalf("Unable to load credentials from environment. Err: %s.\n", err) + } + return creds +} + +// Converts accessKey and secretKeys into credential object which +// contains bcrypt secret key hash for future validation. +func getCredentialFromEnv() (credential, error) { + // Fetch access keys from environment variables and update the config. + accessKey := os.Getenv("MINIO_ACCESS_KEY") + secretKey := os.Getenv("MINIO_SECRET_KEY") + + // Envs are set globally. + globalIsEnvCreds = accessKey != "" && secretKey != "" + + if globalIsEnvCreds { + // Validate the env values before proceeding. + if err := validateAuthKeys(accessKey, secretKey); err != nil { + return credential{}, err + } + + // Return credential object. + return newCredentialWithKeys(accessKey, secretKey), nil + } + + return credential{}, nil } diff --git a/cmd/jwt.go b/cmd/jwt.go index f1f0b2373..54f4668f7 100644 --- a/cmd/jwt.go +++ b/cmd/jwt.go @@ -38,12 +38,15 @@ const ( defaultInterNodeJWTExpiry = 100 * 365 * 24 * time.Hour ) -var errInvalidAccessKeyLength = errors.New("Invalid access key, access key should be 5 to 20 characters in length") -var errInvalidSecretKeyLength = errors.New("Invalid secret key, secret key should be 8 to 40 characters in length") - -var errInvalidAccessKeyID = errors.New("The access key ID you provided does not exist in our records") -var errAuthentication = errors.New("Authentication failed, check your access credentials") -var errNoAuthToken = errors.New("JWT token missing") +var ( + errInvalidAccessKeyLength = errors.New("Invalid access key, access key should be 5 to 20 characters in length") + errInvalidSecretKeyLength = errors.New("Invalid secret key, secret key should be 8 to 40 characters in length") + + errInvalidAccessKeyID = errors.New("The access key ID you provided does not exist in our records") + errChangeCredNotAllowed = errors.New("Changing access key and secret key not allowed") + errAuthentication = errors.New("Authentication failed, check your access credentials") + errNoAuthToken = errors.New("JWT token missing") +) func authenticateJWT(accessKey, secretKey string, expiry time.Duration) (string, error) { // Trim spaces. @@ -65,8 +68,15 @@ func authenticateJWT(accessKey, secretKey string, expiry time.Duration) (string, // Validate secret key. // Using bcrypt to avoid timing attacks. - if bcrypt.CompareHashAndPassword(serverCred.SecretKeyHash, []byte(secretKey)) != nil { - return "", errAuthentication + if serverCred.secretKeyHash != nil { + if bcrypt.CompareHashAndPassword(serverCred.secretKeyHash, []byte(secretKey)) != nil { + return "", errAuthentication + } + } else { + // Secret key hash not set then generate and validate. + if bcrypt.CompareHashAndPassword(mustGetHashedSecretKey(serverCred.SecretKey), []byte(secretKey)) != nil { + return "", errAuthentication + } } utcNow := time.Now().UTC() diff --git a/cmd/main.go b/cmd/main.go index 57c42772a..e6db20ad6 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -163,6 +163,29 @@ func checkUpdate() { } } +// Initializes a new config if it doesn't exist, else migrates any old config +// to newer config and finally loads the config to memory. +func initConfig() { + envCreds := mustGetCredentialFromEnv() + + // Config file does not exist, we create it fresh and return upon success. + if !isConfigFileExists() { + if err := newConfig(envCreds); err != nil { + console.Fatalf("Unable to initialize minio config for the first time. Err: %s.\n", err) + } + console.Println("Created minio configuration file successfully at " + mustGetConfigPath()) + return + } + + // Migrate any old version of config / state files to newer format. + migrate() + + // Once we have migrated all the old config, now load them. + if err := loadConfig(envCreds); err != nil { + console.Fatalf("Unable to initialize minio config. Err: %s.\n", err) + } +} + // Generic Minio initialization to create/load config, prepare loggers, etc.. func minioInit(ctx *cli.Context) { // Set global variables after parsing passed arguments @@ -174,32 +197,12 @@ func minioInit(ctx *cli.Context) { // Is TLS configured?. globalIsSSL = isSSL() - // Migrate any old version of config / state files to newer format. - migrate() - - // Initialize config. - configCreated, err := initConfig() - if err != nil { - console.Fatalf("Unable to initialize minio config. Err: %s.\n", err) - } - if configCreated { - console.Println("Created minio configuration file at " + mustGetConfigPath()) - } + // Initialize minio server config. + initConfig() // Enable all loggers by now so we can use errorIf() and fatalIf() enableLoggers() - // Fetch access keys from environment variables and update the config. - accessKey := os.Getenv("MINIO_ACCESS_KEY") - secretKey := os.Getenv("MINIO_SECRET_KEY") - if accessKey != "" && secretKey != "" { - creds, err := getCredential(accessKey, secretKey) - fatalIf(err, "Credentials are invalid, please set proper credentials `minio server --help`") - - // Set new credentials. - serverConfig.SetCredential(creds) - } - // Init the error tracing module. initError() diff --git a/cmd/server-main.go b/cmd/server-main.go index e54143d73..3164c5783 100644 --- a/cmd/server-main.go +++ b/cmd/server-main.go @@ -21,7 +21,6 @@ import ( "fmt" "net" "net/url" - "os" "path" "sort" "strconv" @@ -128,22 +127,17 @@ func parseStorageEndpoints(eps []string) (endpoints []*url.URL, err error) { return endpoints, nil } -// initServerConfig initialize server config. +// initServer initialize server config. func initServerConfig(c *cli.Context) { + // Initialization such as config generating/loading config, enable logging, .. + minioInit(c) + // Create certs path. - err := createCertsPath() - fatalIf(err, "Unable to create \"certs\" directory.") + fatalIf(createCertsPath(), "Unable to create \"certs\" directory.") // Load user supplied root CAs loadRootCAs() - // When credentials inherited from the env, server cmd has to save them in the disk - if os.Getenv("MINIO_ACCESS_KEY") != "" && os.Getenv("MINIO_SECRET_KEY") != "" { - // Env credentials are already loaded in serverConfig, just save in the disk - err = serverConfig.Save() - fatalIf(err, "Unable to save credentials in the disk.") - } - // Set maxOpenFiles, This is necessary since default operating // system limits of 1024, 2048 are not enough for Minio server. setMaxOpenFiles() @@ -372,8 +366,8 @@ func serverMain(c *cli.Context) { cli.ShowCommandHelpAndExit(c, "server", 1) } - // Initialization routine, such as config loading, enable logging, .. - minioInit(c) + // Initializes server config, certs, logging and system settings. + initServerConfig(c) // Check for new updates from dl.minio.io. checkUpdate() @@ -390,9 +384,6 @@ func serverMain(c *cli.Context) { // as parseStorageEndpoints() depends on it. checkServerSyntax(c) - // Initialize server config. - initServerConfig(c) - // Disks to be used in server init. endpoints, err := parseStorageEndpoints(c.Args()) fatalIf(err, "Unable to parse storage endpoints %s", c.Args()) diff --git a/cmd/server-main_test.go b/cmd/server-main_test.go index 5fbc17029..cf49c9086 100644 --- a/cmd/server-main_test.go +++ b/cmd/server-main_test.go @@ -452,8 +452,14 @@ func TestIsDistributedSetup(t *testing.T) { globalMinioHost = "" } -func TestInitServerConfig(t *testing.T) { - ctx := &cli.Context{} +// Tests init server. +func TestInitServer(t *testing.T) { + app := cli.NewApp() + app.Commands = []cli.Command{serverCmd} + serverFlagSet := flag.NewFlagSet("server", 0) + serverFlagSet.String("address", ":9000", "") + ctx := cli.NewContext(app, serverFlagSet, serverFlagSet) + root, err := newTestConfig(globalMinioDefaultRegion) if err != nil { t.Fatal("Failed to set up test config") @@ -473,6 +479,7 @@ func TestInitServerConfig(t *testing.T) { t.Fatalf("Test %d failed with %v", i+1, tErr) } initServerConfig(ctx) + os.Unsetenv(test.envVar) } } diff --git a/cmd/signature-v2_test.go b/cmd/signature-v2_test.go index 56cc38957..fc5b2dee9 100644 --- a/cmd/signature-v2_test.go +++ b/cmd/signature-v2_test.go @@ -119,17 +119,13 @@ func TestDoesPresignedV2SignatureMatch(t *testing.T) { // TestValidateV2AuthHeader - Tests validate the logic of V2 Authorization header validator. func TestValidateV2AuthHeader(t *testing.T) { - // Initialize server config. - if _, err := initConfig(); err != nil { - t.Fatal(err) + root, err := newTestConfig(globalMinioDefaultRegion) + if err != nil { + t.Fatal("Unable to initialize test config.") } + defer removeAll(root) - // Save config. - if err := serverConfig.Save(); err != nil { - t.Fatal(err) - } accessID := serverConfig.GetCredential().AccessKey - testCases := []struct { authString string expectedError APIErrorCode @@ -194,13 +190,11 @@ func TestValidateV2AuthHeader(t *testing.T) { } func TestDoesPolicySignatureV2Match(t *testing.T) { - if _, err := initConfig(); err != nil { - t.Fatal(err) - } - - if err := serverConfig.Save(); err != nil { - t.Fatal(err) + root, err := newTestConfig(globalMinioDefaultRegion) + if err != nil { + t.Fatal("Unable to initialize test config.") } + defer removeAll(root) creds := serverConfig.GetCredential() policy := "policy" testCases := []struct { diff --git a/cmd/test-utils_test.go b/cmd/test-utils_test.go index 67219df0c..7ef2f25b2 100644 --- a/cmd/test-utils_test.go +++ b/cmd/test-utils_test.go @@ -496,6 +496,10 @@ func resetGlobalIsXL() { globalIsXL = false } +func resetGlobalIsEnvs() { + globalIsEnvCreds = false +} + // Resets all the globals used modified in tests. // Resetting ensures that the changes made to globals by one test doesn't affect others. func resetTestGlobals() { @@ -513,6 +517,8 @@ func resetTestGlobals() { resetGlobalEndpoints() // Reset global isXL flag. resetGlobalIsXL() + // Reset global isEnvCreds flag. + resetGlobalIsEnvs() } // Configure the server for the test run. @@ -527,7 +533,7 @@ func newTestConfig(bucketLocation string) (rootPath string, err error) { setGlobalConfigPath(rootPath) // Initialize server config. - if _, err = initConfig(); err != nil { + if err = newConfig(credential{}); err != nil { return "", err } @@ -1790,6 +1796,7 @@ func ExecObjectLayerAPIAnonTest(t *testing.T, testName, bucketName, objectName, failTestStr := func(testType, failMsg string) string { return fmt.Sprintf("Minio %s: %s fail for \"%s\": \n %s", instanceType, testType, testName, failMsg) } + // httptest Recorder to capture all the response by the http handler. rec := httptest.NewRecorder() // reading the body to preserve it so that it can be used again for second attempt of sending unsigned HTTP request. @@ -1941,8 +1948,10 @@ func ExecObjectLayerAPITest(t *testing.T, objAPITest objAPITestType, endpoints [ // reset globals. // this is to make sure that the tests are not affected by modified value. resetTestGlobals() + // initialize NSLock. initNSLock(false) + // initialize the server and obtain the credentials and root. // credentials are necessary to sign the HTTP request. rootPath, err := newTestConfig(globalMinioDefaultRegion) @@ -2025,6 +2034,7 @@ func ExecObjectLayerDiskAlteredTest(t *testing.T, objTest objTestDiskNotFoundTyp if err != nil { t.Fatalf("Initialization of object layer failed for XL setup: %s", err) } + // Executing the object layer tests for XL. objTest(objLayer, XLTestStr, fsDirs, t) defer removeRoots(fsDirs) diff --git a/cmd/web-handlers.go b/cmd/web-handlers.go index d3f65a0e8..53daca3cc 100644 --- a/cmd/web-handlers.go +++ b/cmd/web-handlers.go @@ -362,12 +362,21 @@ func (web *webAPIHandlers) SetAuth(r *http.Request, args *SetAuthArgs, reply *Se return toJSONError(errAuthentication) } + // If creds are set through ENV disallow changing credentials. + if globalIsEnvCreds { + return toJSONError(errChangeCredNotAllowed) + } + // As we already validated the authentication, we save given access/secret keys. - creds, err := getCredential(args.AccessKey, args.SecretKey) - if err != nil { + if err := validateAuthKeys(args.AccessKey, args.SecretKey); err != nil { return toJSONError(err) } + creds := credential{ + AccessKey: args.AccessKey, + SecretKey: args.SecretKey, + } + // Notify all other Minio peers to update credentials errsMap := updateCredsOnPeers(creds) @@ -375,7 +384,7 @@ func (web *webAPIHandlers) SetAuth(r *http.Request, args *SetAuthArgs, reply *Se serverConfig.SetCredential(creds) // Persist updated credentials. - if err = serverConfig.Save(); err != nil { + if err := serverConfig.Save(); err != nil { errsMap[globalMinioAddr] = err } @@ -397,7 +406,7 @@ func (web *webAPIHandlers) SetAuth(r *http.Request, args *SetAuthArgs, reply *Se } // As we have updated access/secret key, generate new auth token. - token, err := authenticateWeb(args.AccessKey, args.SecretKey) + token, err := authenticateWeb(creds.AccessKey, creds.SecretKey) if err != nil { // Did we have peer errors? if len(errsMap) > 0 { @@ -829,8 +838,13 @@ func toWebAPIError(err error) APIError { HTTPStatusCode: http.StatusBadRequest, Description: err.Error(), } + } else if err == errChangeCredNotAllowed { + return APIError{ + Code: "MethodNotAllowed", + HTTPStatusCode: http.StatusMethodNotAllowed, + Description: err.Error(), + } } - // Convert error type to api error code. var apiErrCode APIErrorCode switch err.(type) { diff --git a/cmd/web-handlers_test.go b/cmd/web-handlers_test.go index 1f8c9e627..ae0c2576a 100644 --- a/cmd/web-handlers_test.go +++ b/cmd/web-handlers_test.go @@ -128,15 +128,6 @@ func TestWebHandlerLogin(t *testing.T) { func testLoginWebHandler(obj ObjectLayer, instanceType string, t TestErrHandler) { // Register the API end points with XL/FS object layer. apiRouter := initTestWebRPCEndPoint(obj) - // initialize the server and obtain the credentials and root. - // credentials are necessary to sign the HTTP request. - rootPath, err := newTestConfig(globalMinioDefaultRegion) - if err != nil { - t.Fatalf("Init Test config failed") - } - // remove the root directory after the test ends. - defer removeAll(rootPath) - credentials := serverConfig.GetCredential() // test cases with sample input and expected output. @@ -177,15 +168,6 @@ func testStorageInfoWebHandler(obj ObjectLayer, instanceType string, t TestErrHa // get random bucket name. // Register the API end points with XL/FS object layer. apiRouter := initTestWebRPCEndPoint(obj) - // initialize the server and obtain the credentials and root. - // credentials are necessary to sign the HTTP request. - rootPath, err := newTestConfig(globalMinioDefaultRegion) - if err != nil { - t.Fatalf("Init Test config failed") - } - // remove the root directory after the test ends. - defer removeAll(rootPath) - credentials := serverConfig.GetCredential() authorization, err := getWebRPCToken(apiRouter, credentials.AccessKey, credentials.SecretKey) @@ -223,15 +205,6 @@ func TestWebHandlerServerInfo(t *testing.T) { func testServerInfoWebHandler(obj ObjectLayer, instanceType string, t TestErrHandler) { // Register the API end points with XL/FS object layer. apiRouter := initTestWebRPCEndPoint(obj) - // initialize the server and obtain the credentials and root. - // credentials are necessary to sign the HTTP request. - rootPath, err := newTestConfig(globalMinioDefaultRegion) - if err != nil { - t.Fatalf("Init Test config failed") - } - // remove the root directory after the test ends. - defer removeAll(rootPath) - credentials := serverConfig.GetCredential() authorization, err := getWebRPCToken(apiRouter, credentials.AccessKey, credentials.SecretKey) @@ -269,15 +242,6 @@ func TestWebHandlerMakeBucket(t *testing.T) { func testMakeBucketWebHandler(obj ObjectLayer, instanceType string, t TestErrHandler) { // Register the API end points with XL/FS object layer. apiRouter := initTestWebRPCEndPoint(obj) - // initialize the server and obtain the credentials and root. - // credentials are necessary to sign the HTTP request. - rootPath, err := newTestConfig(globalMinioDefaultRegion) - if err != nil { - t.Fatalf("Init Test config failed") - } - // remove the root directory after the test ends. - defer removeAll(rootPath) - credentials := serverConfig.GetCredential() authorization, err := getWebRPCToken(apiRouter, credentials.AccessKey, credentials.SecretKey) @@ -329,15 +293,6 @@ func TestWebHandlerListBuckets(t *testing.T) { func testListBucketsWebHandler(obj ObjectLayer, instanceType string, t TestErrHandler) { // Register the API end points with XL/FS object layer. apiRouter := initTestWebRPCEndPoint(obj) - // initialize the server and obtain the credentials and root. - // credentials are necessary to sign the HTTP request. - rootPath, err := newTestConfig(globalMinioDefaultRegion) - if err != nil { - t.Fatalf("Init Test config failed") - } - // remove the root directory after the test ends. - defer removeAll(rootPath) - credentials := serverConfig.GetCredential() authorization, err := getWebRPCToken(apiRouter, credentials.AccessKey, credentials.SecretKey) @@ -386,15 +341,6 @@ func TestWebHandlerListObjects(t *testing.T) { func testListObjectsWebHandler(obj ObjectLayer, instanceType string, t TestErrHandler) { // Register the API end points with XL/FS object layer. apiRouter := initTestWebRPCEndPoint(obj) - // initialize the server and obtain the credentials and root. - // credentials are necessary to sign the HTTP request. - rootPath, err := newTestConfig(globalMinioDefaultRegion) - if err != nil { - t.Fatalf("Init Test config failed") - } - // remove the root directory after the test ends. - defer removeAll(rootPath) - credentials := serverConfig.GetCredential() rec := httptest.NewRecorder() @@ -490,15 +436,6 @@ func TestWebHandlerRemoveObject(t *testing.T) { func testRemoveObjectWebHandler(obj ObjectLayer, instanceType string, t TestErrHandler) { // Register the API end points with XL/FS object layer. apiRouter := initTestWebRPCEndPoint(obj) - // initialize the server and obtain the credentials and root. - // credentials are necessary to sign the HTTP request. - rootPath, err := newTestConfig(globalMinioDefaultRegion) - if err != nil { - t.Fatalf("Init Test config failed") - } - // remove the root directory after the test ends. - defer removeAll(rootPath) - credentials := serverConfig.GetCredential() rec := httptest.NewRecorder() @@ -566,15 +503,6 @@ func TestWebHandlerGenerateAuth(t *testing.T) { func testGenerateAuthWebHandler(obj ObjectLayer, instanceType string, t TestErrHandler) { // Register the API end points with XL/FS object layer. apiRouter := initTestWebRPCEndPoint(obj) - // initialize the server and obtain the credentials and root. - // credentials are necessary to sign the HTTP request. - rootPath, err := newTestConfig(globalMinioDefaultRegion) - if err != nil { - t.Fatalf("Init Test config failed") - } - // remove the root directory after the test ends. - defer removeAll(rootPath) - credentials := serverConfig.GetCredential() rec := httptest.NewRecorder() @@ -612,15 +540,6 @@ func TestWebHandlerSetAuth(t *testing.T) { func testSetAuthWebHandler(obj ObjectLayer, instanceType string, t TestErrHandler) { // Register the API end points with XL/FS object layer. apiRouter := initTestWebRPCEndPoint(obj) - // initialize the server and obtain the credentials and root. - // credentials are necessary to sign the HTTP request. - rootPath, err := newTestConfig(globalMinioDefaultRegion) - if err != nil { - t.Fatalf("Init Test config failed") - } - // remove the root directory after the test ends. - defer removeAll(rootPath) - credentials := serverConfig.GetCredential() rec := httptest.NewRecorder() @@ -673,15 +592,6 @@ func TestWebHandlerGetAuth(t *testing.T) { func testGetAuthWebHandler(obj ObjectLayer, instanceType string, t TestErrHandler) { // Register the API end points with XL/FS object layer. apiRouter := initTestWebRPCEndPoint(obj) - // initialize the server and obtain the credentials and root. - // credentials are necessary to sign the HTTP request. - rootPath, err := newTestConfig(globalMinioDefaultRegion) - if err != nil { - t.Fatalf("Init Test config failed") - } - // remove the root directory after the test ends. - defer removeAll(rootPath) - credentials := serverConfig.GetCredential() rec := httptest.NewRecorder() @@ -718,18 +628,9 @@ func TestWebHandlerUpload(t *testing.T) { func testUploadWebHandler(obj ObjectLayer, instanceType string, t TestErrHandler) { // Register the API end points with XL/FS object layer. apiRouter := initTestWebRPCEndPoint(obj) - // initialize the server and obtain the credentials and root. - // credentials are necessary to sign the HTTP request. - rootPath, err := newTestConfig(globalMinioDefaultRegion) - if err != nil { - t.Fatalf("Init Test config failed") - } - // remove the root directory after the test ends. - defer removeAll(rootPath) - credentials := serverConfig.GetCredential() - content := []byte("temporary file's content") + content := []byte("temporary file's content") authorization, err := getWebRPCToken(apiRouter, credentials.AccessKey, credentials.SecretKey) if err != nil { t.Fatal("Cannot authenticate") @@ -820,15 +721,6 @@ func TestWebHandlerDownload(t *testing.T) { func testDownloadWebHandler(obj ObjectLayer, instanceType string, t TestErrHandler) { // Register the API end points with XL/FS object layer. apiRouter := initTestWebRPCEndPoint(obj) - // initialize the server and obtain the credentials and root. - // credentials are necessary to sign the HTTP request. - rootPath, err := newTestConfig(globalMinioDefaultRegion) - if err != nil { - t.Fatalf("Init Test config failed") - } - // remove the root directory after the test ends. - defer removeAll(rootPath) - credentials := serverConfig.GetCredential() authorization, err := getWebRPCToken(apiRouter, credentials.AccessKey, credentials.SecretKey) @@ -912,15 +804,6 @@ func TestWebHandlerPresignedGetHandler(t *testing.T) { func testWebPresignedGetHandler(obj ObjectLayer, instanceType string, t TestErrHandler) { // Register the API end points with XL/FS object layer. apiRouter := initTestWebRPCEndPoint(obj) - // initialize the server and obtain the credentials and root. - // credentials are necessary to sign the HTTP request. - rootPath, err := newTestConfig(globalMinioDefaultRegion) - if err != nil { - t.Fatalf("Init Test config failed") - } - // remove the root directory after the test ends. - defer removeAll(rootPath) - credentials := serverConfig.GetCredential() authorization, err := getWebRPCToken(apiRouter, credentials.AccessKey, credentials.SecretKey) @@ -1025,15 +908,6 @@ func TestWebHandlerGetBucketPolicyHandler(t *testing.T) { func testWebGetBucketPolicyHandler(obj ObjectLayer, instanceType string, t TestErrHandler) { // Register the API end points with XL/FS object layer. apiRouter := initTestWebRPCEndPoint(obj) - // initialize the server and obtain the credentials and root. - // credentials are necessary to sign the HTTP request. - rootPath, err := newTestConfig(globalMinioDefaultRegion) - if err != nil { - t.Fatalf("Init Test config failed") - } - // remove the root directory after the test ends. - defer removeAll(rootPath) - credentials := serverConfig.GetCredential() authorization, err := getWebRPCToken(apiRouter, credentials.AccessKey, credentials.SecretKey) @@ -1108,15 +982,6 @@ func TestWebHandlerListAllBucketPoliciesHandler(t *testing.T) { func testWebListAllBucketPoliciesHandler(obj ObjectLayer, instanceType string, t TestErrHandler) { // Register the API end points with XL/FS object layer. apiRouter := initTestWebRPCEndPoint(obj) - // initialize the server and obtain the credentials and root. - // credentials are necessary to sign the HTTP request. - rootPath, err := newTestConfig(globalMinioDefaultRegion) - if err != nil { - t.Fatalf("Init Test config failed") - } - // remove the root directory after the test ends. - defer removeAll(rootPath) - credentials := serverConfig.GetCredential() authorization, err := getWebRPCToken(apiRouter, credentials.AccessKey, credentials.SecretKey) @@ -1214,15 +1079,6 @@ func TestWebHandlerSetBucketPolicyHandler(t *testing.T) { func testWebSetBucketPolicyHandler(obj ObjectLayer, instanceType string, t TestErrHandler) { // Register the API end points with XL/FS object layer. apiRouter := initTestWebRPCEndPoint(obj) - // initialize the server and obtain the credentials and root. - // credentials are necessary to sign the HTTP request. - rootPath, err := newTestConfig(globalMinioDefaultRegion) - if err != nil { - t.Fatalf("Init Test config failed") - } - // remove the root directory after the test ends. - defer removeAll(rootPath) - credentials := serverConfig.GetCredential() authorization, err := getWebRPCToken(apiRouter, credentials.AccessKey, credentials.SecretKey)