mirror of
https://github.com/minio/minio.git
synced 2024-12-24 22:25:54 -05:00
Bring in safe mode support (#8478)
This PR refactors object layer handling such that upon failure in sub-system initialization server reaches a stage of safe-mode operation wherein only certain API operations are enabled and available. This allows for fixing many scenarios such as - incorrect configuration in vault, etcd, notification targets - missing files, incomplete config migrations unable to read encrypted content etc - any other issues related to notification, policies, lifecycle etc
This commit is contained in:
parent
1c90a6bd49
commit
822eb5ddc7
@ -33,7 +33,7 @@ import (
|
|||||||
|
|
||||||
func validateAdminReqConfigKV(ctx context.Context, w http.ResponseWriter, r *http.Request) ObjectLayer {
|
func validateAdminReqConfigKV(ctx context.Context, w http.ResponseWriter, r *http.Request) ObjectLayer {
|
||||||
// Get current object layer instance.
|
// Get current object layer instance.
|
||||||
objectAPI := globalObjectAPI
|
objectAPI := newObjectLayerWithoutSafeModeFn()
|
||||||
if objectAPI == nil {
|
if objectAPI == nil {
|
||||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL)
|
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL)
|
||||||
return nil
|
return nil
|
||||||
@ -136,9 +136,16 @@ func (a adminAPIHandlers) SetConfigKVHandler(w http.ResponseWriter, r *http.Requ
|
|||||||
}
|
}
|
||||||
cfg, err := readServerConfig(ctx, objectAPI)
|
cfg, err := readServerConfig(ctx, objectAPI)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
// Config not found for some reason, allow things to continue
|
||||||
|
// by initializing a new fresh config in safe mode.
|
||||||
|
if err == errConfigNotFound && globalSafeMode {
|
||||||
|
cfg = newServerConfig()
|
||||||
|
err = nil
|
||||||
|
} else {
|
||||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
defaultKVS := configDefaultKVS()
|
defaultKVS := configDefaultKVS()
|
||||||
oldCfg := cfg.Clone()
|
oldCfg := cfg.Clone()
|
||||||
@ -174,6 +181,9 @@ func (a adminAPIHandlers) SetConfigKVHandler(w http.ResponseWriter, r *http.Requ
|
|||||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Make sure to write backend is encrypted
|
||||||
|
saveConfig(context.Background(), objectAPI, backendEncryptedFile, backendEncryptedMigrationComplete)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetConfigKVHandler - GET /minio/admin/v2/get-config-kv?key={key}
|
// GetConfigKVHandler - GET /minio/admin/v2/get-config-kv?key={key}
|
||||||
@ -280,9 +290,16 @@ func (a adminAPIHandlers) RestoreConfigHistoryKVHandler(w http.ResponseWriter, r
|
|||||||
|
|
||||||
cfg, err := readServerConfig(ctx, objectAPI)
|
cfg, err := readServerConfig(ctx, objectAPI)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
// Config not found for some reason, allow things to continue
|
||||||
|
// by initializing a new fresh config in safe mode.
|
||||||
|
if err == errConfigNotFound && globalSafeMode {
|
||||||
|
cfg = newServerConfig()
|
||||||
|
err = nil
|
||||||
|
} else {
|
||||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
defaultKVS := configDefaultKVS()
|
defaultKVS := configDefaultKVS()
|
||||||
oldCfg := cfg.Clone()
|
oldCfg := cfg.Clone()
|
||||||
@ -425,6 +442,9 @@ func (a adminAPIHandlers) SetConfigHandler(w http.ResponseWriter, r *http.Reques
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Make sure to write backend is encrypted
|
||||||
|
saveConfig(context.Background(), objectAPI, backendEncryptedFile, backendEncryptedMigrationComplete)
|
||||||
|
|
||||||
// Reply to the client before restarting minio server.
|
// Reply to the client before restarting minio server.
|
||||||
writeSuccessResponseHeadersOnly(w)
|
writeSuccessResponseHeadersOnly(w)
|
||||||
}
|
}
|
||||||
|
529
cmd/admin-handlers-users.go
Normal file
529
cmd/admin-handlers-users.go
Normal file
@ -0,0 +1,529 @@
|
|||||||
|
/*
|
||||||
|
* MinIO Cloud Storage, (C) 2019 MinIO, Inc.
|
||||||
|
*
|
||||||
|
* 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 (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
"github.com/minio/minio/cmd/logger"
|
||||||
|
iampolicy "github.com/minio/minio/pkg/iam/policy"
|
||||||
|
"github.com/minio/minio/pkg/madmin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func validateAdminUsersReq(ctx context.Context, w http.ResponseWriter, r *http.Request) ObjectLayer {
|
||||||
|
// Get current object layer instance.
|
||||||
|
objectAPI := newObjectLayerWithoutSafeModeFn()
|
||||||
|
if objectAPI == nil || globalNotificationSys == nil || globalIAMSys == nil {
|
||||||
|
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate request signature.
|
||||||
|
adminAPIErr := checkAdminRequestAuthType(ctx, r, "")
|
||||||
|
if adminAPIErr != ErrNone {
|
||||||
|
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(adminAPIErr), r.URL)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return objectAPI
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveUser - DELETE /minio/admin/v2/remove-user?accessKey=<access_key>
|
||||||
|
func (a adminAPIHandlers) RemoveUser(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := newContext(r, w, "RemoveUser")
|
||||||
|
|
||||||
|
objectAPI := validateAdminUsersReq(ctx, w, r)
|
||||||
|
if objectAPI == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deny if WORM is enabled
|
||||||
|
if globalWORMEnabled {
|
||||||
|
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrMethodNotAllowed), r.URL)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
vars := mux.Vars(r)
|
||||||
|
accessKey := vars["accessKey"]
|
||||||
|
|
||||||
|
if err := globalIAMSys.DeleteUser(accessKey); err != nil {
|
||||||
|
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Notify all other MinIO peers to delete user.
|
||||||
|
for _, nerr := range globalNotificationSys.DeleteUser(accessKey) {
|
||||||
|
if nerr.Err != nil {
|
||||||
|
logger.GetReqInfo(ctx).SetTags("peerAddress", nerr.Host.String())
|
||||||
|
logger.LogIf(ctx, nerr.Err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListUsers - GET /minio/admin/v2/list-users
|
||||||
|
func (a adminAPIHandlers) ListUsers(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := newContext(r, w, "ListUsers")
|
||||||
|
|
||||||
|
objectAPI := validateAdminUsersReq(ctx, w, r)
|
||||||
|
if objectAPI == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
allCredentials, err := globalIAMSys.ListUsers()
|
||||||
|
if err != nil {
|
||||||
|
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := json.Marshal(allCredentials)
|
||||||
|
if err != nil {
|
||||||
|
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
password := globalActiveCred.SecretKey
|
||||||
|
econfigData, err := madmin.EncryptData(password, data)
|
||||||
|
if err != nil {
|
||||||
|
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
writeSuccessResponseJSON(w, econfigData)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUserInfo - GET /minio/admin/v2/user-info
|
||||||
|
func (a adminAPIHandlers) GetUserInfo(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := newContext(r, w, "GetUserInfo")
|
||||||
|
|
||||||
|
objectAPI := validateAdminUsersReq(ctx, w, r)
|
||||||
|
if objectAPI == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
vars := mux.Vars(r)
|
||||||
|
name := vars["accessKey"]
|
||||||
|
|
||||||
|
userInfo, err := globalIAMSys.GetUserInfo(name)
|
||||||
|
if err != nil {
|
||||||
|
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := json.Marshal(userInfo)
|
||||||
|
if err != nil {
|
||||||
|
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
writeSuccessResponseJSON(w, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateGroupMembers - PUT /minio/admin/v2/update-group-members
|
||||||
|
func (a adminAPIHandlers) UpdateGroupMembers(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := newContext(r, w, "UpdateGroupMembers")
|
||||||
|
|
||||||
|
objectAPI := validateAdminUsersReq(ctx, w, r)
|
||||||
|
if objectAPI == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
defer r.Body.Close()
|
||||||
|
data, err := ioutil.ReadAll(r.Body)
|
||||||
|
if err != nil {
|
||||||
|
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrInvalidRequest), r.URL)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var updReq madmin.GroupAddRemove
|
||||||
|
err = json.Unmarshal(data, &updReq)
|
||||||
|
if err != nil {
|
||||||
|
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrInvalidRequest), r.URL)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if updReq.IsRemove {
|
||||||
|
err = globalIAMSys.RemoveUsersFromGroup(updReq.Group, updReq.Members)
|
||||||
|
} else {
|
||||||
|
err = globalIAMSys.AddUsersToGroup(updReq.Group, updReq.Members)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Notify all other MinIO peers to load group.
|
||||||
|
for _, nerr := range globalNotificationSys.LoadGroup(updReq.Group) {
|
||||||
|
if nerr.Err != nil {
|
||||||
|
logger.GetReqInfo(ctx).SetTags("peerAddress", nerr.Host.String())
|
||||||
|
logger.LogIf(ctx, nerr.Err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetGroup - /minio/admin/v2/group?group=mygroup1
|
||||||
|
func (a adminAPIHandlers) GetGroup(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := newContext(r, w, "GetGroup")
|
||||||
|
|
||||||
|
objectAPI := validateAdminUsersReq(ctx, w, r)
|
||||||
|
if objectAPI == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
vars := mux.Vars(r)
|
||||||
|
group := vars["group"]
|
||||||
|
|
||||||
|
gdesc, err := globalIAMSys.GetGroupDescription(group)
|
||||||
|
if err != nil {
|
||||||
|
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
body, err := json.Marshal(gdesc)
|
||||||
|
if err != nil {
|
||||||
|
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
writeSuccessResponseJSON(w, body)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListGroups - GET /minio/admin/v2/groups
|
||||||
|
func (a adminAPIHandlers) ListGroups(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := newContext(r, w, "ListGroups")
|
||||||
|
|
||||||
|
objectAPI := validateAdminUsersReq(ctx, w, r)
|
||||||
|
if objectAPI == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
groups, err := globalIAMSys.ListGroups()
|
||||||
|
if err != nil {
|
||||||
|
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
body, err := json.Marshal(groups)
|
||||||
|
if err != nil {
|
||||||
|
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
writeSuccessResponseJSON(w, body)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetGroupStatus - PUT /minio/admin/v2/set-group-status?group=mygroup1&status=enabled
|
||||||
|
func (a adminAPIHandlers) SetGroupStatus(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := newContext(r, w, "SetGroupStatus")
|
||||||
|
|
||||||
|
objectAPI := validateAdminUsersReq(ctx, w, r)
|
||||||
|
if objectAPI == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
vars := mux.Vars(r)
|
||||||
|
group := vars["group"]
|
||||||
|
status := vars["status"]
|
||||||
|
|
||||||
|
var err error
|
||||||
|
if status == statusEnabled {
|
||||||
|
err = globalIAMSys.SetGroupStatus(group, true)
|
||||||
|
} else if status == statusDisabled {
|
||||||
|
err = globalIAMSys.SetGroupStatus(group, false)
|
||||||
|
} else {
|
||||||
|
err = errInvalidArgument
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Notify all other MinIO peers to reload user.
|
||||||
|
for _, nerr := range globalNotificationSys.LoadGroup(group) {
|
||||||
|
if nerr.Err != nil {
|
||||||
|
logger.GetReqInfo(ctx).SetTags("peerAddress", nerr.Host.String())
|
||||||
|
logger.LogIf(ctx, nerr.Err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetUserStatus - PUT /minio/admin/v2/set-user-status?accessKey=<access_key>&status=[enabled|disabled]
|
||||||
|
func (a adminAPIHandlers) SetUserStatus(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := newContext(r, w, "SetUserStatus")
|
||||||
|
|
||||||
|
objectAPI := validateAdminUsersReq(ctx, w, r)
|
||||||
|
if objectAPI == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deny if WORM is enabled
|
||||||
|
if globalWORMEnabled {
|
||||||
|
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrMethodNotAllowed), r.URL)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
vars := mux.Vars(r)
|
||||||
|
accessKey := vars["accessKey"]
|
||||||
|
status := vars["status"]
|
||||||
|
|
||||||
|
// Custom IAM policies not allowed for admin user.
|
||||||
|
if accessKey == globalActiveCred.AccessKey {
|
||||||
|
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrInvalidRequest), r.URL)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := globalIAMSys.SetUserStatus(accessKey, madmin.AccountStatus(status)); err != nil {
|
||||||
|
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Notify all other MinIO peers to reload user.
|
||||||
|
for _, nerr := range globalNotificationSys.LoadUser(accessKey, false) {
|
||||||
|
if nerr.Err != nil {
|
||||||
|
logger.GetReqInfo(ctx).SetTags("peerAddress", nerr.Host.String())
|
||||||
|
logger.LogIf(ctx, nerr.Err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddUser - PUT /minio/admin/v2/add-user?accessKey=<access_key>
|
||||||
|
func (a adminAPIHandlers) AddUser(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := newContext(r, w, "AddUser")
|
||||||
|
|
||||||
|
objectAPI := validateAdminUsersReq(ctx, w, r)
|
||||||
|
if objectAPI == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deny if WORM is enabled
|
||||||
|
if globalWORMEnabled {
|
||||||
|
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrMethodNotAllowed), r.URL)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
vars := mux.Vars(r)
|
||||||
|
accessKey := vars["accessKey"]
|
||||||
|
|
||||||
|
// Custom IAM policies not allowed for admin user.
|
||||||
|
if accessKey == globalActiveCred.AccessKey {
|
||||||
|
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAddUserInvalidArgument), r.URL)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.ContentLength > maxEConfigJSONSize || r.ContentLength == -1 {
|
||||||
|
// More than maxConfigSize bytes were available
|
||||||
|
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAdminConfigTooLarge), r.URL)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
password := globalActiveCred.SecretKey
|
||||||
|
configBytes, err := madmin.DecryptData(password, io.LimitReader(r.Body, r.ContentLength))
|
||||||
|
if err != nil {
|
||||||
|
logger.LogIf(ctx, err)
|
||||||
|
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAdminConfigBadJSON), r.URL)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var uinfo madmin.UserInfo
|
||||||
|
if err = json.Unmarshal(configBytes, &uinfo); err != nil {
|
||||||
|
logger.LogIf(ctx, err)
|
||||||
|
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAdminConfigBadJSON), r.URL)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = globalIAMSys.SetUser(accessKey, uinfo); err != nil {
|
||||||
|
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Notify all other Minio peers to reload user
|
||||||
|
for _, nerr := range globalNotificationSys.LoadUser(accessKey, false) {
|
||||||
|
if nerr.Err != nil {
|
||||||
|
logger.GetReqInfo(ctx).SetTags("peerAddress", nerr.Host.String())
|
||||||
|
logger.LogIf(ctx, nerr.Err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// InfoCannedPolicy - GET /minio/admin/v2/info-canned-policy?name={policyName}
|
||||||
|
func (a adminAPIHandlers) InfoCannedPolicy(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := newContext(r, w, "InfoCannedPolicy")
|
||||||
|
|
||||||
|
objectAPI := validateAdminUsersReq(ctx, w, r)
|
||||||
|
if objectAPI == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := globalIAMSys.InfoPolicy(mux.Vars(r)["name"])
|
||||||
|
if err != nil {
|
||||||
|
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Write(data)
|
||||||
|
w.(http.Flusher).Flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListCannedPolicies - GET /minio/admin/v2/list-canned-policies
|
||||||
|
func (a adminAPIHandlers) ListCannedPolicies(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := newContext(r, w, "ListCannedPolicies")
|
||||||
|
|
||||||
|
objectAPI := validateAdminUsersReq(ctx, w, r)
|
||||||
|
if objectAPI == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
policies, err := globalIAMSys.ListPolicies()
|
||||||
|
if err != nil {
|
||||||
|
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = json.NewEncoder(w).Encode(policies); err != nil {
|
||||||
|
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w.(http.Flusher).Flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveCannedPolicy - DELETE /minio/admin/v2/remove-canned-policy?name=<policy_name>
|
||||||
|
func (a adminAPIHandlers) RemoveCannedPolicy(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := newContext(r, w, "RemoveCannedPolicy")
|
||||||
|
|
||||||
|
objectAPI := validateAdminUsersReq(ctx, w, r)
|
||||||
|
if objectAPI == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
vars := mux.Vars(r)
|
||||||
|
policyName := vars["name"]
|
||||||
|
|
||||||
|
// Deny if WORM is enabled
|
||||||
|
if globalWORMEnabled {
|
||||||
|
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrMethodNotAllowed), r.URL)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := globalIAMSys.DeletePolicy(policyName); err != nil {
|
||||||
|
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Notify all other MinIO peers to delete policy
|
||||||
|
for _, nerr := range globalNotificationSys.DeletePolicy(policyName) {
|
||||||
|
if nerr.Err != nil {
|
||||||
|
logger.GetReqInfo(ctx).SetTags("peerAddress", nerr.Host.String())
|
||||||
|
logger.LogIf(ctx, nerr.Err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddCannedPolicy - PUT /minio/admin/v2/add-canned-policy?name=<policy_name>
|
||||||
|
func (a adminAPIHandlers) AddCannedPolicy(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := newContext(r, w, "AddCannedPolicy")
|
||||||
|
|
||||||
|
objectAPI := validateAdminUsersReq(ctx, w, r)
|
||||||
|
if objectAPI == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
vars := mux.Vars(r)
|
||||||
|
policyName := vars["name"]
|
||||||
|
|
||||||
|
// Deny if WORM is enabled
|
||||||
|
if globalWORMEnabled {
|
||||||
|
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrMethodNotAllowed), r.URL)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error out if Content-Length is missing.
|
||||||
|
if r.ContentLength <= 0 {
|
||||||
|
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrMissingContentLength), r.URL)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error out if Content-Length is beyond allowed size.
|
||||||
|
if r.ContentLength > maxBucketPolicySize {
|
||||||
|
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrEntityTooLarge), r.URL)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
iamPolicy, err := iampolicy.ParseConfig(io.LimitReader(r.Body, r.ContentLength))
|
||||||
|
if err != nil {
|
||||||
|
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrMalformedPolicy), r.URL)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Version in policy must not be empty
|
||||||
|
if iamPolicy.Version == "" {
|
||||||
|
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrMalformedPolicy), r.URL)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = globalIAMSys.SetPolicy(policyName, *iamPolicy); err != nil {
|
||||||
|
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Notify all other MinIO peers to reload policy
|
||||||
|
for _, nerr := range globalNotificationSys.LoadPolicy(policyName) {
|
||||||
|
if nerr.Err != nil {
|
||||||
|
logger.GetReqInfo(ctx).SetTags("peerAddress", nerr.Host.String())
|
||||||
|
logger.LogIf(ctx, nerr.Err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetPolicyForUserOrGroup - PUT /minio/admin/v2/set-policy?policy=xxx&user-or-group=?[&is-group]
|
||||||
|
func (a adminAPIHandlers) SetPolicyForUserOrGroup(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := newContext(r, w, "SetPolicyForUserOrGroup")
|
||||||
|
|
||||||
|
objectAPI := validateAdminUsersReq(ctx, w, r)
|
||||||
|
if objectAPI == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
vars := mux.Vars(r)
|
||||||
|
policyName := vars["policyName"]
|
||||||
|
entityName := vars["userOrGroup"]
|
||||||
|
isGroup := vars["isGroup"] == "true"
|
||||||
|
|
||||||
|
// Deny if WORM is enabled
|
||||||
|
if globalWORMEnabled {
|
||||||
|
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrMethodNotAllowed), r.URL)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := globalIAMSys.PolicyDBSet(entityName, policyName, isGroup); err != nil {
|
||||||
|
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Notify all other MinIO peers to reload policy
|
||||||
|
for _, nerr := range globalNotificationSys.LoadPolicyMapping(entityName, isGroup) {
|
||||||
|
if nerr.Err != nil {
|
||||||
|
logger.GetReqInfo(ctx).SetTags("peerAddress", nerr.Host.String())
|
||||||
|
logger.LogIf(ctx, nerr.Err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -22,7 +22,6 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
@ -42,7 +41,6 @@ import (
|
|||||||
"github.com/minio/minio/cmd/logger"
|
"github.com/minio/minio/cmd/logger"
|
||||||
"github.com/minio/minio/pkg/cpu"
|
"github.com/minio/minio/pkg/cpu"
|
||||||
"github.com/minio/minio/pkg/handlers"
|
"github.com/minio/minio/pkg/handlers"
|
||||||
iampolicy "github.com/minio/minio/pkg/iam/policy"
|
|
||||||
"github.com/minio/minio/pkg/madmin"
|
"github.com/minio/minio/pkg/madmin"
|
||||||
"github.com/minio/minio/pkg/mem"
|
"github.com/minio/minio/pkg/mem"
|
||||||
xnet "github.com/minio/minio/pkg/net"
|
xnet "github.com/minio/minio/pkg/net"
|
||||||
@ -94,7 +92,7 @@ func updateServer(updateURL, sha256Hex string, latestReleaseTime time.Time) (us
|
|||||||
return us, nil
|
return us, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ServerUpdateHandler - POST /minio/admin/v1/update?updateURL={updateURL}
|
// ServerUpdateHandler - POST /minio/admin/v2/update?updateURL={updateURL}
|
||||||
// ----------
|
// ----------
|
||||||
// updates all minio servers and restarts them gracefully.
|
// updates all minio servers and restarts them gracefully.
|
||||||
func (a adminAPIHandlers) ServerUpdateHandler(w http.ResponseWriter, r *http.Request) {
|
func (a adminAPIHandlers) ServerUpdateHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
@ -173,7 +171,7 @@ func (a adminAPIHandlers) ServerUpdateHandler(w http.ResponseWriter, r *http.Req
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ServiceActionHandler - POST /minio/admin/v1/service?action={action}
|
// ServiceActionHandler - POST /minio/admin/v2/service?action={action}
|
||||||
// ----------
|
// ----------
|
||||||
// restarts/stops minio server gracefully. In a distributed setup,
|
// restarts/stops minio server gracefully. In a distributed setup,
|
||||||
func (a adminAPIHandlers) ServiceActionHandler(w http.ResponseWriter, r *http.Request) {
|
func (a adminAPIHandlers) ServiceActionHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
@ -262,7 +260,7 @@ type ServerInfo struct {
|
|||||||
Data *ServerInfoData `json:"data"`
|
Data *ServerInfoData `json:"data"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ServerInfoHandler - GET /minio/admin/v1/info
|
// ServerInfoHandler - GET /minio/admin/v2/info
|
||||||
// ----------
|
// ----------
|
||||||
// Get server information
|
// Get server information
|
||||||
func (a adminAPIHandlers) ServerInfoHandler(w http.ResponseWriter, r *http.Request) {
|
func (a adminAPIHandlers) ServerInfoHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
@ -303,7 +301,7 @@ func (a adminAPIHandlers) ServerInfoHandler(w http.ResponseWriter, r *http.Reque
|
|||||||
writeSuccessResponseJSON(w, jsonBytes)
|
writeSuccessResponseJSON(w, jsonBytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ServerInfoHandler - GET /minio/admin/v1/storageinfo
|
// ServerInfoHandler - GET /minio/admin/v2/storageinfo
|
||||||
// ----------
|
// ----------
|
||||||
// Get server information
|
// Get server information
|
||||||
func (a adminAPIHandlers) StorageInfoHandler(w http.ResponseWriter, r *http.Request) {
|
func (a adminAPIHandlers) StorageInfoHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
@ -355,7 +353,7 @@ type ServerNetReadPerfInfo struct {
|
|||||||
Error string `json:"error,omitempty"`
|
Error string `json:"error,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// PerfInfoHandler - GET /minio/admin/v1/performance?perfType={perfType}
|
// PerfInfoHandler - GET /minio/admin/v2/performance?perfType={perfType}
|
||||||
// ----------
|
// ----------
|
||||||
// Get all performance information based on input type
|
// Get all performance information based on input type
|
||||||
// Supported types = drive
|
// Supported types = drive
|
||||||
@ -561,7 +559,7 @@ type StartProfilingResult struct {
|
|||||||
Error string `json:"error"`
|
Error string `json:"error"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// StartProfilingHandler - POST /minio/admin/v1/profiling/start?profilerType={profilerType}
|
// StartProfilingHandler - POST /minio/admin/v2/profiling/start?profilerType={profilerType}
|
||||||
// ----------
|
// ----------
|
||||||
// Enable server profiling
|
// Enable server profiling
|
||||||
func (a adminAPIHandlers) StartProfilingHandler(w http.ResponseWriter, r *http.Request) {
|
func (a adminAPIHandlers) StartProfilingHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
@ -643,7 +641,7 @@ func (f dummyFileInfo) ModTime() time.Time { return f.modTime }
|
|||||||
func (f dummyFileInfo) IsDir() bool { return f.isDir }
|
func (f dummyFileInfo) IsDir() bool { return f.isDir }
|
||||||
func (f dummyFileInfo) Sys() interface{} { return f.sys }
|
func (f dummyFileInfo) Sys() interface{} { return f.sys }
|
||||||
|
|
||||||
// DownloadProfilingHandler - POST /minio/admin/v1/profiling/download
|
// DownloadProfilingHandler - POST /minio/admin/v2/profiling/download
|
||||||
// ----------
|
// ----------
|
||||||
// Download profiling information of all nodes in a zip format
|
// Download profiling information of all nodes in a zip format
|
||||||
func (a adminAPIHandlers) DownloadProfilingHandler(w http.ResponseWriter, r *http.Request) {
|
func (a adminAPIHandlers) DownloadProfilingHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
@ -725,7 +723,7 @@ func extractHealInitParams(vars map[string]string, qParms url.Values, r io.Reade
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// HealHandler - POST /minio/admin/v1/heal/
|
// HealHandler - POST /minio/admin/v2/heal/
|
||||||
// -----------
|
// -----------
|
||||||
// Start heal processing and return heal status items.
|
// Start heal processing and return heal status items.
|
||||||
//
|
//
|
||||||
@ -927,8 +925,8 @@ func (a adminAPIHandlers) BackgroundHealStatusHandler(w http.ResponseWriter, r *
|
|||||||
|
|
||||||
func validateAdminReq(ctx context.Context, w http.ResponseWriter, r *http.Request) ObjectLayer {
|
func validateAdminReq(ctx context.Context, w http.ResponseWriter, r *http.Request) ObjectLayer {
|
||||||
// Get current object layer instance.
|
// Get current object layer instance.
|
||||||
objectAPI := globalObjectAPI
|
objectAPI := newObjectLayerWithoutSafeModeFn()
|
||||||
if objectAPI == nil || globalNotificationSys == nil || globalIAMSys == nil {
|
if objectAPI == nil || globalNotificationSys == nil {
|
||||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL)
|
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -992,492 +990,19 @@ func toAdminAPIErr(ctx context.Context, err error) APIError {
|
|||||||
HTTPStatusCode: e.StatusCode,
|
HTTPStatusCode: e.StatusCode,
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
if err == errConfigNotFound {
|
||||||
|
apiErr = APIError{
|
||||||
|
Code: "XMinioConfigError",
|
||||||
|
Description: err.Error(),
|
||||||
|
HTTPStatusCode: http.StatusNotFound,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
apiErr = errorCodes.ToAPIErr(toAdminAPIErrCode(ctx, err))
|
apiErr = errorCodes.ToAPIErr(toAdminAPIErrCode(ctx, err))
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return apiErr
|
return apiErr
|
||||||
}
|
}
|
||||||
|
|
||||||
// RemoveUser - DELETE /minio/admin/v1/remove-user?accessKey=<access_key>
|
|
||||||
func (a adminAPIHandlers) RemoveUser(w http.ResponseWriter, r *http.Request) {
|
|
||||||
ctx := newContext(r, w, "RemoveUser")
|
|
||||||
|
|
||||||
objectAPI := validateAdminReq(ctx, w, r)
|
|
||||||
if objectAPI == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deny if WORM is enabled
|
|
||||||
if globalWORMEnabled {
|
|
||||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrMethodNotAllowed), r.URL)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
vars := mux.Vars(r)
|
|
||||||
accessKey := vars["accessKey"]
|
|
||||||
|
|
||||||
if err := globalIAMSys.DeleteUser(accessKey); err != nil {
|
|
||||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Notify all other MinIO peers to delete user.
|
|
||||||
for _, nerr := range globalNotificationSys.DeleteUser(accessKey) {
|
|
||||||
if nerr.Err != nil {
|
|
||||||
logger.GetReqInfo(ctx).SetTags("peerAddress", nerr.Host.String())
|
|
||||||
logger.LogIf(ctx, nerr.Err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListUsers - GET /minio/admin/v1/list-users
|
|
||||||
func (a adminAPIHandlers) ListUsers(w http.ResponseWriter, r *http.Request) {
|
|
||||||
ctx := newContext(r, w, "ListUsers")
|
|
||||||
|
|
||||||
objectAPI := validateAdminReq(ctx, w, r)
|
|
||||||
if objectAPI == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
allCredentials, err := globalIAMSys.ListUsers()
|
|
||||||
if err != nil {
|
|
||||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
data, err := json.Marshal(allCredentials)
|
|
||||||
if err != nil {
|
|
||||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
password := globalActiveCred.SecretKey
|
|
||||||
econfigData, err := madmin.EncryptData(password, data)
|
|
||||||
if err != nil {
|
|
||||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
writeSuccessResponseJSON(w, econfigData)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetUserInfo - GET /minio/admin/v1/user-info
|
|
||||||
func (a adminAPIHandlers) GetUserInfo(w http.ResponseWriter, r *http.Request) {
|
|
||||||
ctx := newContext(r, w, "GetUserInfo")
|
|
||||||
|
|
||||||
objectAPI := validateAdminReq(ctx, w, r)
|
|
||||||
if objectAPI == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
vars := mux.Vars(r)
|
|
||||||
name := vars["accessKey"]
|
|
||||||
|
|
||||||
userInfo, err := globalIAMSys.GetUserInfo(name)
|
|
||||||
if err != nil {
|
|
||||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
data, err := json.Marshal(userInfo)
|
|
||||||
if err != nil {
|
|
||||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
writeSuccessResponseJSON(w, data)
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateGroupMembers - PUT /minio/admin/v1/update-group-members
|
|
||||||
func (a adminAPIHandlers) UpdateGroupMembers(w http.ResponseWriter, r *http.Request) {
|
|
||||||
ctx := newContext(r, w, "UpdateGroupMembers")
|
|
||||||
|
|
||||||
objectAPI := validateAdminReq(ctx, w, r)
|
|
||||||
if objectAPI == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
defer r.Body.Close()
|
|
||||||
data, err := ioutil.ReadAll(r.Body)
|
|
||||||
if err != nil {
|
|
||||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrInvalidRequest), r.URL)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var updReq madmin.GroupAddRemove
|
|
||||||
err = json.Unmarshal(data, &updReq)
|
|
||||||
if err != nil {
|
|
||||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrInvalidRequest), r.URL)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if updReq.IsRemove {
|
|
||||||
err = globalIAMSys.RemoveUsersFromGroup(updReq.Group, updReq.Members)
|
|
||||||
} else {
|
|
||||||
err = globalIAMSys.AddUsersToGroup(updReq.Group, updReq.Members)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Notify all other MinIO peers to load group.
|
|
||||||
for _, nerr := range globalNotificationSys.LoadGroup(updReq.Group) {
|
|
||||||
if nerr.Err != nil {
|
|
||||||
logger.GetReqInfo(ctx).SetTags("peerAddress", nerr.Host.String())
|
|
||||||
logger.LogIf(ctx, nerr.Err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetGroup - /minio/admin/v1/group?group=mygroup1
|
|
||||||
func (a adminAPIHandlers) GetGroup(w http.ResponseWriter, r *http.Request) {
|
|
||||||
ctx := newContext(r, w, "GetGroup")
|
|
||||||
|
|
||||||
objectAPI := validateAdminReq(ctx, w, r)
|
|
||||||
if objectAPI == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
vars := mux.Vars(r)
|
|
||||||
group := vars["group"]
|
|
||||||
|
|
||||||
gdesc, err := globalIAMSys.GetGroupDescription(group)
|
|
||||||
if err != nil {
|
|
||||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
body, err := json.Marshal(gdesc)
|
|
||||||
if err != nil {
|
|
||||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
writeSuccessResponseJSON(w, body)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListGroups - GET /minio/admin/v1/groups
|
|
||||||
func (a adminAPIHandlers) ListGroups(w http.ResponseWriter, r *http.Request) {
|
|
||||||
ctx := newContext(r, w, "ListGroups")
|
|
||||||
|
|
||||||
objectAPI := validateAdminReq(ctx, w, r)
|
|
||||||
if objectAPI == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
groups, err := globalIAMSys.ListGroups()
|
|
||||||
if err != nil {
|
|
||||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
body, err := json.Marshal(groups)
|
|
||||||
if err != nil {
|
|
||||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
writeSuccessResponseJSON(w, body)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetGroupStatus - PUT /minio/admin/v1/set-group-status?group=mygroup1&status=enabled
|
|
||||||
func (a adminAPIHandlers) SetGroupStatus(w http.ResponseWriter, r *http.Request) {
|
|
||||||
ctx := newContext(r, w, "SetGroupStatus")
|
|
||||||
|
|
||||||
objectAPI := validateAdminReq(ctx, w, r)
|
|
||||||
if objectAPI == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
vars := mux.Vars(r)
|
|
||||||
group := vars["group"]
|
|
||||||
status := vars["status"]
|
|
||||||
|
|
||||||
var err error
|
|
||||||
if status == statusEnabled {
|
|
||||||
err = globalIAMSys.SetGroupStatus(group, true)
|
|
||||||
} else if status == statusDisabled {
|
|
||||||
err = globalIAMSys.SetGroupStatus(group, false)
|
|
||||||
} else {
|
|
||||||
err = errInvalidArgument
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Notify all other MinIO peers to reload user.
|
|
||||||
for _, nerr := range globalNotificationSys.LoadGroup(group) {
|
|
||||||
if nerr.Err != nil {
|
|
||||||
logger.GetReqInfo(ctx).SetTags("peerAddress", nerr.Host.String())
|
|
||||||
logger.LogIf(ctx, nerr.Err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetUserStatus - PUT /minio/admin/v1/set-user-status?accessKey=<access_key>&status=[enabled|disabled]
|
|
||||||
func (a adminAPIHandlers) SetUserStatus(w http.ResponseWriter, r *http.Request) {
|
|
||||||
ctx := newContext(r, w, "SetUserStatus")
|
|
||||||
|
|
||||||
objectAPI := validateAdminReq(ctx, w, r)
|
|
||||||
if objectAPI == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deny if WORM is enabled
|
|
||||||
if globalWORMEnabled {
|
|
||||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrMethodNotAllowed), r.URL)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
vars := mux.Vars(r)
|
|
||||||
accessKey := vars["accessKey"]
|
|
||||||
status := vars["status"]
|
|
||||||
|
|
||||||
// Custom IAM policies not allowed for admin user.
|
|
||||||
if accessKey == globalActiveCred.AccessKey {
|
|
||||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrInvalidRequest), r.URL)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := globalIAMSys.SetUserStatus(accessKey, madmin.AccountStatus(status)); err != nil {
|
|
||||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Notify all other MinIO peers to reload user.
|
|
||||||
for _, nerr := range globalNotificationSys.LoadUser(accessKey, false) {
|
|
||||||
if nerr.Err != nil {
|
|
||||||
logger.GetReqInfo(ctx).SetTags("peerAddress", nerr.Host.String())
|
|
||||||
logger.LogIf(ctx, nerr.Err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddUser - PUT /minio/admin/v1/add-user?accessKey=<access_key>
|
|
||||||
func (a adminAPIHandlers) AddUser(w http.ResponseWriter, r *http.Request) {
|
|
||||||
ctx := newContext(r, w, "AddUser")
|
|
||||||
|
|
||||||
objectAPI := validateAdminReq(ctx, w, r)
|
|
||||||
if objectAPI == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deny if WORM is enabled
|
|
||||||
if globalWORMEnabled {
|
|
||||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrMethodNotAllowed), r.URL)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
vars := mux.Vars(r)
|
|
||||||
accessKey := vars["accessKey"]
|
|
||||||
|
|
||||||
// Custom IAM policies not allowed for admin user.
|
|
||||||
if accessKey == globalActiveCred.AccessKey {
|
|
||||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAddUserInvalidArgument), r.URL)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if r.ContentLength > maxEConfigJSONSize || r.ContentLength == -1 {
|
|
||||||
// More than maxConfigSize bytes were available
|
|
||||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAdminConfigTooLarge), r.URL)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
password := globalActiveCred.SecretKey
|
|
||||||
configBytes, err := madmin.DecryptData(password, io.LimitReader(r.Body, r.ContentLength))
|
|
||||||
if err != nil {
|
|
||||||
logger.LogIf(ctx, err)
|
|
||||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAdminConfigBadJSON), r.URL)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var uinfo madmin.UserInfo
|
|
||||||
if err = json.Unmarshal(configBytes, &uinfo); err != nil {
|
|
||||||
logger.LogIf(ctx, err)
|
|
||||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAdminConfigBadJSON), r.URL)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = globalIAMSys.SetUser(accessKey, uinfo); err != nil {
|
|
||||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Notify all other Minio peers to reload user
|
|
||||||
for _, nerr := range globalNotificationSys.LoadUser(accessKey, false) {
|
|
||||||
if nerr.Err != nil {
|
|
||||||
logger.GetReqInfo(ctx).SetTags("peerAddress", nerr.Host.String())
|
|
||||||
logger.LogIf(ctx, nerr.Err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// InfoCannedPolicy - GET /minio/admin/v1/info-canned-policy?name={policyName}
|
|
||||||
func (a adminAPIHandlers) InfoCannedPolicy(w http.ResponseWriter, r *http.Request) {
|
|
||||||
ctx := newContext(r, w, "InfoCannedPolicy")
|
|
||||||
|
|
||||||
objectAPI := validateAdminReq(ctx, w, r)
|
|
||||||
if objectAPI == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
data, err := globalIAMSys.InfoPolicy(mux.Vars(r)["name"])
|
|
||||||
if err != nil {
|
|
||||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
w.Write(data)
|
|
||||||
w.(http.Flusher).Flush()
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListCannedPolicies - GET /minio/admin/v1/list-canned-policies
|
|
||||||
func (a adminAPIHandlers) ListCannedPolicies(w http.ResponseWriter, r *http.Request) {
|
|
||||||
ctx := newContext(r, w, "ListCannedPolicies")
|
|
||||||
|
|
||||||
objectAPI := validateAdminReq(ctx, w, r)
|
|
||||||
if objectAPI == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
policies, err := globalIAMSys.ListPolicies()
|
|
||||||
if err != nil {
|
|
||||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = json.NewEncoder(w).Encode(policies); err != nil {
|
|
||||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
w.(http.Flusher).Flush()
|
|
||||||
}
|
|
||||||
|
|
||||||
// RemoveCannedPolicy - DELETE /minio/admin/v1/remove-canned-policy?name=<policy_name>
|
|
||||||
func (a adminAPIHandlers) RemoveCannedPolicy(w http.ResponseWriter, r *http.Request) {
|
|
||||||
ctx := newContext(r, w, "RemoveCannedPolicy")
|
|
||||||
|
|
||||||
objectAPI := validateAdminReq(ctx, w, r)
|
|
||||||
if objectAPI == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
vars := mux.Vars(r)
|
|
||||||
policyName := vars["name"]
|
|
||||||
|
|
||||||
// Deny if WORM is enabled
|
|
||||||
if globalWORMEnabled {
|
|
||||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrMethodNotAllowed), r.URL)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := globalIAMSys.DeletePolicy(policyName); err != nil {
|
|
||||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Notify all other MinIO peers to delete policy
|
|
||||||
for _, nerr := range globalNotificationSys.DeletePolicy(policyName) {
|
|
||||||
if nerr.Err != nil {
|
|
||||||
logger.GetReqInfo(ctx).SetTags("peerAddress", nerr.Host.String())
|
|
||||||
logger.LogIf(ctx, nerr.Err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddCannedPolicy - PUT /minio/admin/v1/add-canned-policy?name=<policy_name>
|
|
||||||
func (a adminAPIHandlers) AddCannedPolicy(w http.ResponseWriter, r *http.Request) {
|
|
||||||
ctx := newContext(r, w, "AddCannedPolicy")
|
|
||||||
|
|
||||||
objectAPI := validateAdminReq(ctx, w, r)
|
|
||||||
if objectAPI == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
vars := mux.Vars(r)
|
|
||||||
policyName := vars["name"]
|
|
||||||
|
|
||||||
// Deny if WORM is enabled
|
|
||||||
if globalWORMEnabled {
|
|
||||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrMethodNotAllowed), r.URL)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Error out if Content-Length is missing.
|
|
||||||
if r.ContentLength <= 0 {
|
|
||||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrMissingContentLength), r.URL)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Error out if Content-Length is beyond allowed size.
|
|
||||||
if r.ContentLength > maxBucketPolicySize {
|
|
||||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrEntityTooLarge), r.URL)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
iamPolicy, err := iampolicy.ParseConfig(io.LimitReader(r.Body, r.ContentLength))
|
|
||||||
if err != nil {
|
|
||||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrMalformedPolicy), r.URL)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Version in policy must not be empty
|
|
||||||
if iamPolicy.Version == "" {
|
|
||||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrMalformedPolicy), r.URL)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = globalIAMSys.SetPolicy(policyName, *iamPolicy); err != nil {
|
|
||||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Notify all other MinIO peers to reload policy
|
|
||||||
for _, nerr := range globalNotificationSys.LoadPolicy(policyName) {
|
|
||||||
if nerr.Err != nil {
|
|
||||||
logger.GetReqInfo(ctx).SetTags("peerAddress", nerr.Host.String())
|
|
||||||
logger.LogIf(ctx, nerr.Err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetPolicyForUserOrGroup - PUT /minio/admin/v1/set-policy?policy=xxx&user-or-group=?[&is-group]
|
|
||||||
func (a adminAPIHandlers) SetPolicyForUserOrGroup(w http.ResponseWriter, r *http.Request) {
|
|
||||||
ctx := newContext(r, w, "SetPolicyForUserOrGroup")
|
|
||||||
|
|
||||||
objectAPI := validateAdminReq(ctx, w, r)
|
|
||||||
if objectAPI == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
vars := mux.Vars(r)
|
|
||||||
policyName := vars["policyName"]
|
|
||||||
entityName := vars["userOrGroup"]
|
|
||||||
isGroup := vars["isGroup"] == "true"
|
|
||||||
|
|
||||||
// Deny if WORM is enabled
|
|
||||||
if globalWORMEnabled {
|
|
||||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrMethodNotAllowed), r.URL)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := globalIAMSys.PolicyDBSet(entityName, policyName, isGroup); err != nil {
|
|
||||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Notify all other MinIO peers to reload policy
|
|
||||||
for _, nerr := range globalNotificationSys.LoadPolicyMapping(entityName, isGroup) {
|
|
||||||
if nerr.Err != nil {
|
|
||||||
logger.GetReqInfo(ctx).SetTags("peerAddress", nerr.Host.String())
|
|
||||||
logger.LogIf(ctx, nerr.Err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns true if the trace.Info should be traced,
|
// Returns true if the trace.Info should be traced,
|
||||||
// false if certain conditions are not met.
|
// false if certain conditions are not met.
|
||||||
// - input entry is not of the type *trace.Info*
|
// - input entry is not of the type *trace.Info*
|
||||||
@ -1495,7 +1020,7 @@ func mustTrace(entry interface{}, trcAll, errOnly bool) bool {
|
|||||||
return trace
|
return trace
|
||||||
}
|
}
|
||||||
|
|
||||||
// TraceHandler - POST /minio/admin/v1/trace
|
// TraceHandler - POST /minio/admin/v2/trace
|
||||||
// ----------
|
// ----------
|
||||||
// The handler sends http trace to the connected HTTP client.
|
// The handler sends http trace to the connected HTTP client.
|
||||||
func (a adminAPIHandlers) TraceHandler(w http.ResponseWriter, r *http.Request) {
|
func (a adminAPIHandlers) TraceHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
@ -1519,13 +1044,16 @@ func (a adminAPIHandlers) TraceHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
// Use buffered channel to take care of burst sends or slow w.Write()
|
// Use buffered channel to take care of burst sends or slow w.Write()
|
||||||
traceCh := make(chan interface{}, 4000)
|
traceCh := make(chan interface{}, 4000)
|
||||||
|
|
||||||
peers := getRestClients(getRemoteHosts(globalEndpoints))
|
peers := getRestClients(globalEndpoints)
|
||||||
|
|
||||||
globalHTTPTrace.Subscribe(traceCh, doneCh, func(entry interface{}) bool {
|
globalHTTPTrace.Subscribe(traceCh, doneCh, func(entry interface{}) bool {
|
||||||
return mustTrace(entry, trcAll, trcErr)
|
return mustTrace(entry, trcAll, trcErr)
|
||||||
})
|
})
|
||||||
|
|
||||||
for _, peer := range peers {
|
for _, peer := range peers {
|
||||||
|
if peer == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
peer.Trace(traceCh, doneCh, trcAll, trcErr)
|
peer.Trace(traceCh, doneCh, trcAll, trcErr)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1583,12 +1111,14 @@ func (a adminAPIHandlers) ConsoleLogHandler(w http.ResponseWriter, r *http.Reque
|
|||||||
defer close(doneCh)
|
defer close(doneCh)
|
||||||
logCh := make(chan interface{}, 4000)
|
logCh := make(chan interface{}, 4000)
|
||||||
|
|
||||||
remoteHosts := getRemoteHosts(globalEndpoints)
|
peers := getRestClients(globalEndpoints)
|
||||||
peers := getRestClients(remoteHosts)
|
|
||||||
|
|
||||||
globalConsoleSys.Subscribe(logCh, doneCh, node, limitLines, logKind, nil)
|
globalConsoleSys.Subscribe(logCh, doneCh, node, limitLines, logKind, nil)
|
||||||
|
|
||||||
for _, peer := range peers {
|
for _, peer := range peers {
|
||||||
|
if peer == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
if node == "" || strings.EqualFold(peer.host.Name, node) {
|
if node == "" || strings.EqualFold(peer.host.Name, node) {
|
||||||
peer.ConsoleLog(logCh, doneCh)
|
peer.ConsoleLog(logCh, doneCh)
|
||||||
}
|
}
|
||||||
@ -1620,7 +1150,7 @@ func (a adminAPIHandlers) ConsoleLogHandler(w http.ResponseWriter, r *http.Reque
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// KMSKeyStatusHandler - GET /minio/admin/v1/kms/key/status?key-id=<master-key-id>
|
// KMSKeyStatusHandler - GET /minio/admin/v2/kms/key/status?key-id=<master-key-id>
|
||||||
func (a adminAPIHandlers) KMSKeyStatusHandler(w http.ResponseWriter, r *http.Request) {
|
func (a adminAPIHandlers) KMSKeyStatusHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
ctx := newContext(r, w, "KMSKeyStatusHandler")
|
ctx := newContext(r, w, "KMSKeyStatusHandler")
|
||||||
|
|
||||||
@ -1702,7 +1232,7 @@ func (a adminAPIHandlers) KMSKeyStatusHandler(w http.ResponseWriter, r *http.Req
|
|||||||
writeSuccessResponseJSON(w, resp)
|
writeSuccessResponseJSON(w, resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ServerHardwareInfoHandler - GET /minio/admin/v1/hardwareinfo?Type={hwType}
|
// ServerHardwareInfoHandler - GET /minio/admin/v2/hardwareinfo?Type={hwType}
|
||||||
// ----------
|
// ----------
|
||||||
// Get all hardware information based on input type
|
// Get all hardware information based on input type
|
||||||
// Supported types = cpu
|
// Supported types = cpu
|
||||||
|
@ -90,11 +90,7 @@ func prepareAdminXLTestBed() (*adminXLTestBed, error) {
|
|||||||
globalPolicySys = NewPolicySys()
|
globalPolicySys = NewPolicySys()
|
||||||
globalPolicySys.Init(buckets, objLayer)
|
globalPolicySys.Init(buckets, objLayer)
|
||||||
|
|
||||||
globalNotificationSys, err = NewNotificationSys(globalServerConfig, globalEndpoints)
|
globalNotificationSys = NewNotificationSys(globalEndpoints)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
globalNotificationSys.Init(buckets, objLayer)
|
globalNotificationSys.Init(buckets, objLayer)
|
||||||
|
|
||||||
// Setup admin mgmt REST API handlers.
|
// Setup admin mgmt REST API handlers.
|
||||||
|
@ -660,7 +660,7 @@ func (h *healSequence) traverseAndHeal() {
|
|||||||
func (h *healSequence) healMinioSysMeta(metaPrefix string) func() error {
|
func (h *healSequence) healMinioSysMeta(metaPrefix string) func() error {
|
||||||
return func() error {
|
return func() error {
|
||||||
// Get current object layer instance.
|
// Get current object layer instance.
|
||||||
objectAPI := globalObjectAPI
|
objectAPI := newObjectLayerWithoutSafeModeFn()
|
||||||
if objectAPI == nil {
|
if objectAPI == nil {
|
||||||
return errServerNotInitialized
|
return errServerNotInitialized
|
||||||
}
|
}
|
||||||
@ -692,7 +692,7 @@ func (h *healSequence) healDiskFormat() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get current object layer instance.
|
// Get current object layer instance.
|
||||||
objectAPI := globalObjectAPI
|
objectAPI := newObjectLayerWithoutSafeModeFn()
|
||||||
if objectAPI == nil {
|
if objectAPI == nil {
|
||||||
return errServerNotInitialized
|
return errServerNotInitialized
|
||||||
}
|
}
|
||||||
@ -712,7 +712,7 @@ func (h *healSequence) healBuckets() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get current object layer instance.
|
// Get current object layer instance.
|
||||||
objectAPI := globalObjectAPI
|
objectAPI := newObjectLayerWithoutSafeModeFn()
|
||||||
if objectAPI == nil {
|
if objectAPI == nil {
|
||||||
return errServerNotInitialized
|
return errServerNotInitialized
|
||||||
}
|
}
|
||||||
@ -734,7 +734,7 @@ func (h *healSequence) healBuckets() error {
|
|||||||
// healBucket - traverses and heals given bucket
|
// healBucket - traverses and heals given bucket
|
||||||
func (h *healSequence) healBucket(bucket string) error {
|
func (h *healSequence) healBucket(bucket string) error {
|
||||||
// Get current object layer instance.
|
// Get current object layer instance.
|
||||||
objectAPI := globalObjectAPI
|
objectAPI := newObjectLayerWithoutSafeModeFn()
|
||||||
if objectAPI == nil {
|
if objectAPI == nil {
|
||||||
return errServerNotInitialized
|
return errServerNotInitialized
|
||||||
}
|
}
|
||||||
@ -771,7 +771,7 @@ func (h *healSequence) healObject(bucket, object string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get current object layer instance.
|
// Get current object layer instance.
|
||||||
objectAPI := globalObjectAPI
|
objectAPI := newObjectLayerWithoutSafeModeFn()
|
||||||
if objectAPI == nil {
|
if objectAPI == nil {
|
||||||
return errServerNotInitialized
|
return errServerNotInitialized
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,31 @@ import (
|
|||||||
xhttp "github.com/minio/minio/cmd/http"
|
xhttp "github.com/minio/minio/cmd/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func newObjectLayerWithoutSafeModeFn() ObjectLayer {
|
||||||
|
globalObjLayerMutex.Lock()
|
||||||
|
defer globalObjLayerMutex.Unlock()
|
||||||
|
return globalObjectAPI
|
||||||
|
}
|
||||||
|
|
||||||
|
func newObjectLayerFn() ObjectLayer {
|
||||||
|
globalObjLayerMutex.Lock()
|
||||||
|
defer globalObjLayerMutex.Unlock()
|
||||||
|
if globalSafeMode {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return globalObjectAPI
|
||||||
|
}
|
||||||
|
|
||||||
|
func newCachedObjectLayerFn() CacheObjectLayer {
|
||||||
|
globalObjLayerMutex.Lock()
|
||||||
|
defer globalObjLayerMutex.Unlock()
|
||||||
|
|
||||||
|
if globalSafeMode {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return globalCacheObjectAPI
|
||||||
|
}
|
||||||
|
|
||||||
// objectAPIHandler implements and provides http handlers for S3 API.
|
// objectAPIHandler implements and provides http handlers for S3 API.
|
||||||
type objectAPIHandlers struct {
|
type objectAPIHandlers struct {
|
||||||
ObjectAPI func() ObjectLayer
|
ObjectAPI func() ObjectLayer
|
||||||
@ -37,18 +62,8 @@ type objectAPIHandlers struct {
|
|||||||
func registerAPIRouter(router *mux.Router, encryptionEnabled, allowSSEKMS bool) {
|
func registerAPIRouter(router *mux.Router, encryptionEnabled, allowSSEKMS bool) {
|
||||||
// Initialize API.
|
// Initialize API.
|
||||||
api := objectAPIHandlers{
|
api := objectAPIHandlers{
|
||||||
ObjectAPI: func() ObjectLayer {
|
ObjectAPI: newObjectLayerFn,
|
||||||
if !globalSafeMode {
|
CacheAPI: newCachedObjectLayerFn,
|
||||||
return globalObjectAPI
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
CacheAPI: func() CacheObjectLayer {
|
|
||||||
if !globalSafeMode {
|
|
||||||
return globalCacheObjectAPI
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
EncryptionEnabled: func() bool {
|
EncryptionEnabled: func() bool {
|
||||||
return encryptionEnabled
|
return encryptionEnabled
|
||||||
},
|
},
|
||||||
|
@ -107,7 +107,7 @@ func startBackgroundHealing() {
|
|||||||
|
|
||||||
var objAPI ObjectLayer
|
var objAPI ObjectLayer
|
||||||
for {
|
for {
|
||||||
objAPI = globalObjectAPI
|
objAPI = newObjectLayerWithoutSafeModeFn()
|
||||||
if objAPI == nil {
|
if objAPI == nil {
|
||||||
time.Sleep(time.Second)
|
time.Sleep(time.Second)
|
||||||
continue
|
continue
|
||||||
@ -135,7 +135,7 @@ func initBackgroundHealing() {
|
|||||||
// failure error occurred.
|
// failure error occurred.
|
||||||
func bgHealDiskFormat(ctx context.Context, opts madmin.HealOpts) (madmin.HealResultItem, error) {
|
func bgHealDiskFormat(ctx context.Context, opts madmin.HealOpts) (madmin.HealResultItem, error) {
|
||||||
// Get current object layer instance.
|
// Get current object layer instance.
|
||||||
objectAPI := globalObjectAPI
|
objectAPI := newObjectLayerWithoutSafeModeFn()
|
||||||
if objectAPI == nil {
|
if objectAPI == nil {
|
||||||
return madmin.HealResultItem{}, errServerNotInitialized
|
return madmin.HealResultItem{}, errServerNotInitialized
|
||||||
}
|
}
|
||||||
@ -165,7 +165,7 @@ func bgHealDiskFormat(ctx context.Context, opts madmin.HealOpts) (madmin.HealRes
|
|||||||
// bghealBucket - traverses and heals given bucket
|
// bghealBucket - traverses and heals given bucket
|
||||||
func bgHealBucket(ctx context.Context, bucket string, opts madmin.HealOpts) (madmin.HealResultItem, error) {
|
func bgHealBucket(ctx context.Context, bucket string, opts madmin.HealOpts) (madmin.HealResultItem, error) {
|
||||||
// Get current object layer instance.
|
// Get current object layer instance.
|
||||||
objectAPI := globalObjectAPI
|
objectAPI := newObjectLayerWithoutSafeModeFn()
|
||||||
if objectAPI == nil {
|
if objectAPI == nil {
|
||||||
return madmin.HealResultItem{}, errServerNotInitialized
|
return madmin.HealResultItem{}, errServerNotInitialized
|
||||||
}
|
}
|
||||||
@ -176,7 +176,7 @@ func bgHealBucket(ctx context.Context, bucket string, opts madmin.HealOpts) (mad
|
|||||||
// bgHealObject - heal the given object and record result
|
// bgHealObject - heal the given object and record result
|
||||||
func bgHealObject(ctx context.Context, bucket, object string, opts madmin.HealOpts) (madmin.HealResultItem, error) {
|
func bgHealObject(ctx context.Context, bucket, object string, opts madmin.HealOpts) (madmin.HealResultItem, error) {
|
||||||
// Get current object layer instance.
|
// Get current object layer instance.
|
||||||
objectAPI := globalObjectAPI
|
objectAPI := newObjectLayerWithoutSafeModeFn()
|
||||||
if objectAPI == nil {
|
if objectAPI == nil {
|
||||||
return madmin.HealResultItem{}, errServerNotInitialized
|
return madmin.HealResultItem{}, errServerNotInitialized
|
||||||
}
|
}
|
||||||
|
@ -36,7 +36,7 @@ func monitorLocalDisksAndHeal() {
|
|||||||
// Wait until the object layer is ready
|
// Wait until the object layer is ready
|
||||||
var objAPI ObjectLayer
|
var objAPI ObjectLayer
|
||||||
for {
|
for {
|
||||||
objAPI = globalObjectAPI
|
objAPI = newObjectLayerWithoutSafeModeFn()
|
||||||
if objAPI == nil {
|
if objAPI == nil {
|
||||||
time.Sleep(time.Second)
|
time.Sleep(time.Second)
|
||||||
continue
|
continue
|
||||||
|
@ -51,11 +51,8 @@ import (
|
|||||||
// -- If no, make an entry
|
// -- If no, make an entry
|
||||||
// -- If yes, check if the IP of entry matches local IP. This means entry is for this instance.
|
// -- If yes, check if the IP of entry matches local IP. This means entry is for this instance.
|
||||||
// -- If IP of the entry doesn't match, this means entry is for another instance. Log an error to console.
|
// -- If IP of the entry doesn't match, this means entry is for another instance. Log an error to console.
|
||||||
func initFederatorBackend(objLayer ObjectLayer) {
|
func initFederatorBackend(buckets []BucketInfo, objLayer ObjectLayer) {
|
||||||
// Get buckets in the backend
|
if len(buckets) == 0 {
|
||||||
b, err := objLayer.ListBuckets(context.Background())
|
|
||||||
if err != nil {
|
|
||||||
logger.LogIf(context.Background(), err)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -69,21 +66,21 @@ func initFederatorBackend(objLayer ObjectLayer) {
|
|||||||
bucketSet := set.NewStringSet()
|
bucketSet := set.NewStringSet()
|
||||||
|
|
||||||
// Add buckets that are not registered with the DNS
|
// Add buckets that are not registered with the DNS
|
||||||
g := errgroup.WithNErrs(len(b))
|
g := errgroup.WithNErrs(len(buckets))
|
||||||
for index := range b {
|
for index := range buckets {
|
||||||
bucketSet.Add(b[index].Name)
|
bucketSet.Add(buckets[index].Name)
|
||||||
index := index
|
index := index
|
||||||
g.Go(func() error {
|
g.Go(func() error {
|
||||||
r, gerr := globalDNSConfig.Get(b[index].Name)
|
r, gerr := globalDNSConfig.Get(buckets[index].Name)
|
||||||
if gerr != nil {
|
if gerr != nil {
|
||||||
if gerr == dns.ErrNoEntriesFound {
|
if gerr == dns.ErrNoEntriesFound {
|
||||||
return globalDNSConfig.Put(b[index].Name)
|
return globalDNSConfig.Put(buckets[index].Name)
|
||||||
}
|
}
|
||||||
return gerr
|
return gerr
|
||||||
}
|
}
|
||||||
if globalDomainIPs.Intersection(set.CreateStringSet(getHostsSlice(r)...)).IsEmpty() {
|
if globalDomainIPs.Intersection(set.CreateStringSet(getHostsSlice(r)...)).IsEmpty() {
|
||||||
// There is already an entry for this bucket, with all IP addresses different. This indicates a bucket name collision. Log an error and continue.
|
// There is already an entry for this bucket, with all IP addresses different. This indicates a bucket name collision. Log an error and continue.
|
||||||
return fmt.Errorf("Unable to add bucket DNS entry for bucket %s, an entry exists for the same bucket. Use one of these IP addresses %v to access the bucket", b[index].Name, globalDomainIPs.ToSlice())
|
return fmt.Errorf("Unable to add bucket DNS entry for bucket %s, an entry exists for the same bucket. Use one of these IP addresses %v to access the bucket", buckets[index].Name, globalDomainIPs.ToSlice())
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}, index)
|
}, index)
|
||||||
|
@ -48,51 +48,84 @@ func validateConfig(s config.Config) error {
|
|||||||
// Disable merging env values with config for validation.
|
// Disable merging env values with config for validation.
|
||||||
env.SetEnvOff()
|
env.SetEnvOff()
|
||||||
|
|
||||||
// Enable env values upon return.
|
// Enable env values to validate KMS.
|
||||||
defer env.SetEnvOn()
|
defer env.SetEnvOn()
|
||||||
|
|
||||||
if _, err := config.LookupCreds(s[config.CredentialsSubSys][config.Default]); err != nil {
|
if _, err := config.LookupCreds(s[config.CredentialsSubSys][config.Default]); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := config.LookupRegion(s[config.RegionSubSys][config.Default]); err != nil {
|
if _, err := config.LookupRegion(s[config.RegionSubSys][config.Default]); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := config.LookupWorm(s[config.WormSubSys][config.Default]); err != nil {
|
if _, err := config.LookupWorm(s[config.WormSubSys][config.Default]); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if globalIsXL {
|
if globalIsXL {
|
||||||
if _, err := storageclass.LookupConfig(s[config.StorageClassSubSys][config.Default],
|
if _, err := storageclass.LookupConfig(s[config.StorageClassSubSys][config.Default],
|
||||||
globalXLSetDriveCount); err != nil {
|
globalXLSetDriveCount); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if _, err := etcd.LookupConfig(s[config.EtcdSubSys][config.Default], globalRootCAs); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if _, err := cache.LookupConfig(s[config.CacheSubSys][config.Default]); err != nil {
|
if _, err := cache.LookupConfig(s[config.CacheSubSys][config.Default]); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if _, err := crypto.LookupConfig(s[config.KmsVaultSubSys][config.Default]); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if _, err := compress.LookupConfig(s[config.CompressionSubSys][config.Default]); err != nil {
|
if _, err := compress.LookupConfig(s[config.CompressionSubSys][config.Default]); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
etcdCfg, err := etcd.LookupConfig(s[config.EtcdSubSys][config.Default], globalRootCAs)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if etcdCfg.Enabled {
|
||||||
|
etcdClnt, err := etcd.New(etcdCfg)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
etcdClnt.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
kmsCfg, err := crypto.LookupConfig(s[config.KmsVaultSubSys][config.Default])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if kmsCfg.Vault.Enabled {
|
||||||
|
// Set env to enable master key validation.
|
||||||
|
// this is needed only for KMS.
|
||||||
|
env.SetEnvOn()
|
||||||
|
|
||||||
|
if _, err = crypto.NewKMS(kmsCfg); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if _, err := openid.LookupConfig(s[config.IdentityOpenIDSubSys][config.Default],
|
if _, err := openid.LookupConfig(s[config.IdentityOpenIDSubSys][config.Default],
|
||||||
NewCustomHTTPTransport(), xhttp.DrainBody); err != nil {
|
NewCustomHTTPTransport(), xhttp.DrainBody); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := xldap.Lookup(s[config.IdentityLDAPSubSys][config.Default],
|
if _, err := xldap.Lookup(s[config.IdentityLDAPSubSys][config.Default],
|
||||||
globalRootCAs); err != nil {
|
globalRootCAs); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := opa.LookupConfig(s[config.PolicyOPASubSys][config.Default],
|
if _, err := opa.LookupConfig(s[config.PolicyOPASubSys][config.Default],
|
||||||
NewCustomHTTPTransport(), xhttp.DrainBody); err != nil {
|
NewCustomHTTPTransport(), xhttp.DrainBody); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := logger.LookupConfig(s); err != nil {
|
if _, err := logger.LookupConfig(s); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return notify.TestNotificationTargets(s, GlobalServiceDoneCh, globalRootCAs)
|
return notify.TestNotificationTargets(s, GlobalServiceDoneCh, globalRootCAs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,9 +19,11 @@ package cmd
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
etcd "github.com/coreos/etcd/clientv3"
|
etcd "github.com/coreos/etcd/clientv3"
|
||||||
|
jsoniter "github.com/json-iterator/go"
|
||||||
"github.com/minio/minio/cmd/config"
|
"github.com/minio/minio/cmd/config"
|
||||||
"github.com/minio/minio/cmd/logger"
|
"github.com/minio/minio/cmd/logger"
|
||||||
"github.com/minio/minio/pkg/auth"
|
"github.com/minio/minio/pkg/auth"
|
||||||
@ -97,6 +99,8 @@ func handleEncryptedConfigBackend(objAPI ObjectLayer, server bool) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
os.Unsetenv(config.EnvAccessKeyOld)
|
||||||
|
os.Unsetenv(config.EnvSecretKeyOld)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Migrating Config backend needs a retry mechanism for
|
// Migrating Config backend needs a retry mechanism for
|
||||||
@ -124,7 +128,8 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
backendEncryptedFileValue = []byte("encrypted")
|
backendEncryptedMigrationIncomplete = []byte("incomplete")
|
||||||
|
backendEncryptedMigrationComplete = []byte("encrypted")
|
||||||
)
|
)
|
||||||
|
|
||||||
func checkBackendEtcdEncrypted(ctx context.Context, client *etcd.Client) (bool, error) {
|
func checkBackendEtcdEncrypted(ctx context.Context, client *etcd.Client) (bool, error) {
|
||||||
@ -132,7 +137,7 @@ func checkBackendEtcdEncrypted(ctx context.Context, client *etcd.Client) (bool,
|
|||||||
if err != nil && err != errConfigNotFound {
|
if err != nil && err != errConfigNotFound {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
return err == nil && bytes.Equal(data, backendEncryptedFileValue), nil
|
return err == nil && bytes.Equal(data, backendEncryptedMigrationComplete), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkBackendEncrypted(objAPI ObjectLayer) (bool, error) {
|
func checkBackendEncrypted(objAPI ObjectLayer) (bool, error) {
|
||||||
@ -140,7 +145,7 @@ func checkBackendEncrypted(objAPI ObjectLayer) (bool, error) {
|
|||||||
if err != nil && err != errConfigNotFound {
|
if err != nil && err != errConfigNotFound {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
return err == nil && bytes.Equal(data, backendEncryptedFileValue), nil
|
return err == nil && bytes.Equal(data, backendEncryptedMigrationComplete), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func migrateIAMConfigsEtcdToEncrypted(client *etcd.Client) error {
|
func migrateIAMConfigsEtcdToEncrypted(client *etcd.Client) error {
|
||||||
@ -178,6 +183,9 @@ func migrateIAMConfigsEtcdToEncrypted(client *etcd.Client) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
// Once we have obtained the rotating creds
|
||||||
|
os.Unsetenv(config.EnvAccessKeyOld)
|
||||||
|
os.Unsetenv(config.EnvSecretKeyOld)
|
||||||
}
|
}
|
||||||
|
|
||||||
if encrypted {
|
if encrypted {
|
||||||
@ -191,18 +199,21 @@ func migrateIAMConfigsEtcdToEncrypted(client *etcd.Client) error {
|
|||||||
if activeCredOld.Equal(globalActiveCred) {
|
if activeCredOld.Equal(globalActiveCred) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if !activeCredOld.IsValid() {
|
logger.Info("Attempting rotation of encrypted IAM users and policies on etcd with newly supplied credentials")
|
||||||
logger.Info("Attempting a one time encrypt of all IAM users and policies on etcd")
|
|
||||||
} else {
|
} else {
|
||||||
logger.Info("Attempting a rotation of encrypted IAM users and policies on etcd with newly supplied credentials")
|
logger.Info("Attempting encryption of all IAM users and policies on etcd")
|
||||||
}
|
}
|
||||||
|
|
||||||
r, err := client.Get(ctx, minioConfigPrefix, etcd.WithPrefix(), etcd.WithKeysOnly())
|
r, err := client.Get(ctx, minioConfigPrefix, etcd.WithPrefix(), etcd.WithKeysOnly())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err = saveKeyEtcd(ctx, client, backendEncryptedFile, backendEncryptedMigrationIncomplete); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
for _, kv := range r.Kvs {
|
for _, kv := range r.Kvs {
|
||||||
var (
|
var (
|
||||||
cdata []byte
|
cdata []byte
|
||||||
@ -217,18 +228,32 @@ func migrateIAMConfigsEtcdToEncrypted(client *etcd.Client) error {
|
|||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var data []byte
|
||||||
// Is rotating of creds requested?
|
// Is rotating of creds requested?
|
||||||
if activeCredOld.IsValid() {
|
if activeCredOld.IsValid() {
|
||||||
cdata, err = madmin.DecryptData(activeCredOld.String(), bytes.NewReader(cdata))
|
data, err = madmin.DecryptData(activeCredOld.String(), bytes.NewReader(cdata))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == madmin.ErrMaliciousData {
|
if err == madmin.ErrMaliciousData {
|
||||||
|
data, err = madmin.DecryptData(globalActiveCred.String(),
|
||||||
|
bytes.NewReader(cdata))
|
||||||
|
if err != nil {
|
||||||
return config.ErrInvalidRotatingCredentialsBackendEncrypted(nil)
|
return config.ErrInvalidRotatingCredentialsBackendEncrypted(nil)
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
cencdata, err = madmin.EncryptData(globalActiveCred.String(), cdata)
|
// Attempt to unmarshal JSON content
|
||||||
|
var dummy map[string]interface{}
|
||||||
|
var json = jsoniter.ConfigCompatibleWithStandardLibrary
|
||||||
|
if err = json.Unmarshal(data, &dummy); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
cencdata, err = madmin.EncryptData(globalActiveCred.String(), data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -237,7 +262,10 @@ func migrateIAMConfigsEtcdToEncrypted(client *etcd.Client) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return saveKeyEtcd(ctx, client, backendEncryptedFile, backendEncryptedFileValue)
|
if encrypted && globalActiveCred.IsValid() {
|
||||||
|
logger.Info("Rotation complete, please make sure to unset MINIO_ACCESS_KEY_OLD and MINIO_SECRET_KEY_OLD envs")
|
||||||
|
}
|
||||||
|
return saveKeyEtcd(ctx, client, backendEncryptedFile, backendEncryptedMigrationComplete)
|
||||||
}
|
}
|
||||||
|
|
||||||
func migrateConfigPrefixToEncrypted(objAPI ObjectLayer, activeCredOld auth.Credentials, encrypted bool) error {
|
func migrateConfigPrefixToEncrypted(objAPI ObjectLayer, activeCredOld auth.Credentials, encrypted bool) error {
|
||||||
@ -252,12 +280,14 @@ func migrateConfigPrefixToEncrypted(objAPI ObjectLayer, activeCredOld auth.Crede
|
|||||||
if activeCredOld.Equal(globalActiveCred) {
|
if activeCredOld.Equal(globalActiveCred) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
logger.Info("Attempting rotation of encrypted config, IAM users and policies on MinIO with newly supplied credentials")
|
||||||
|
} else {
|
||||||
|
logger.Info("Attempting encryption of all config, IAM users and policies on MinIO backend")
|
||||||
}
|
}
|
||||||
|
|
||||||
if !activeCredOld.IsValid() {
|
err := saveConfig(context.Background(), objAPI, backendEncryptedFile, backendEncryptedMigrationIncomplete)
|
||||||
logger.Info("Attempting a one time encrypt of all config, IAM users and policies on MinIO backend")
|
if err != nil {
|
||||||
} else {
|
return err
|
||||||
logger.Info("Attempting a rotation of encrypted config, IAM users and policies on MinIO with newly supplied credentials")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var marker string
|
var marker string
|
||||||
@ -277,18 +307,31 @@ func migrateConfigPrefixToEncrypted(objAPI ObjectLayer, activeCredOld auth.Crede
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var data []byte
|
||||||
// Is rotating of creds requested?
|
// Is rotating of creds requested?
|
||||||
if activeCredOld.IsValid() {
|
if activeCredOld.IsValid() {
|
||||||
cdata, err = madmin.DecryptData(activeCredOld.String(), bytes.NewReader(cdata))
|
data, err = madmin.DecryptData(activeCredOld.String(), bytes.NewReader(cdata))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == madmin.ErrMaliciousData {
|
if err == madmin.ErrMaliciousData {
|
||||||
|
data, err = madmin.DecryptData(globalActiveCred.String(),
|
||||||
|
bytes.NewReader(cdata))
|
||||||
|
if err != nil {
|
||||||
return config.ErrInvalidRotatingCredentialsBackendEncrypted(nil)
|
return config.ErrInvalidRotatingCredentialsBackendEncrypted(nil)
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
cencdata, err = madmin.EncryptData(globalActiveCred.String(), cdata)
|
// Attempt to unmarshal JSON content
|
||||||
|
var dummy map[string]interface{}
|
||||||
|
var json = jsoniter.ConfigCompatibleWithStandardLibrary
|
||||||
|
if err = json.Unmarshal(data, &dummy); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
cencdata, err = madmin.EncryptData(globalActiveCred.String(), data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -305,5 +348,9 @@ func migrateConfigPrefixToEncrypted(objAPI ObjectLayer, activeCredOld auth.Crede
|
|||||||
marker = res.NextMarker
|
marker = res.NextMarker
|
||||||
}
|
}
|
||||||
|
|
||||||
return saveConfig(context.Background(), objAPI, backendEncryptedFile, backendEncryptedFileValue)
|
if encrypted && globalActiveCred.IsValid() {
|
||||||
|
logger.Info("Rotation complete, please make sure to unset MINIO_ACCESS_KEY_OLD and MINIO_SECRET_KEY_OLD envs")
|
||||||
|
}
|
||||||
|
|
||||||
|
return saveConfig(context.Background(), objAPI, backendEncryptedFile, backendEncryptedMigrationComplete)
|
||||||
}
|
}
|
||||||
|
@ -386,11 +386,6 @@ func (c Config) SetKVS(s string, defaultKVS map[string]KVS) error {
|
|||||||
return Error(fmt.Sprintf("invalid number of arguments %s", s))
|
return Error(fmt.Sprintf("invalid number of arguments %s", s))
|
||||||
}
|
}
|
||||||
|
|
||||||
if subSystemValue[0] == CredentialsSubSys {
|
|
||||||
return Error(fmt.Sprintf("changing '%s' sub-system values is not allowed, use ENVs instead",
|
|
||||||
subSystemValue[0]))
|
|
||||||
}
|
|
||||||
|
|
||||||
if !SubSystems.Contains(subSystemValue[0]) {
|
if !SubSystems.Contains(subSystemValue[0]) {
|
||||||
return Error(fmt.Sprintf("unknown sub-system %s", s))
|
return Error(fmt.Sprintf("unknown sub-system %s", s))
|
||||||
}
|
}
|
||||||
|
@ -31,7 +31,7 @@ import (
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
// Default values used while communicating with etcd.
|
// Default values used while communicating with etcd.
|
||||||
defaultDialTimeout = 30 * time.Second
|
defaultDialTimeout = 5 * time.Second
|
||||||
defaultDialKeepAlive = 30 * time.Second
|
defaultDialKeepAlive = 30 * time.Second
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -112,7 +112,7 @@ func LookupConfig(kv config.KVS, rootCAs *x509.CertPool) (Config, error) {
|
|||||||
return cfg, config.Error("'endpoints' key cannot be empty if you wish to enable etcd")
|
return cfg, config.Error("'endpoints' key cannot be empty if you wish to enable etcd")
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(endpoints) == 0 {
|
if len(endpoints) == 0 && !stateBool {
|
||||||
return cfg, nil
|
return cfg, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,6 +17,7 @@ package crypto
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"reflect"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/minio/minio/cmd/config"
|
"github.com/minio/minio/cmd/config"
|
||||||
@ -112,6 +113,12 @@ const (
|
|||||||
EnvKMSVaultNamespace = "MINIO_KMS_VAULT_NAMESPACE"
|
EnvKMSVaultNamespace = "MINIO_KMS_VAULT_NAMESPACE"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var defaultCfg = VaultConfig{
|
||||||
|
Auth: VaultAuth{
|
||||||
|
Type: "approle",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
// LookupConfig extracts the KMS configuration provided by environment
|
// LookupConfig extracts the KMS configuration provided by environment
|
||||||
// variables and merge them with the provided KMS configuration. The
|
// variables and merge them with the provided KMS configuration. The
|
||||||
// merging follows the following rules:
|
// merging follows the following rules:
|
||||||
@ -139,7 +146,7 @@ func LookupConfig(kvs config.KVS) (KMSConfig, error) {
|
|||||||
return kmsCfg, err
|
return kmsCfg, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !kmsCfg.Vault.IsEmpty() {
|
if kmsCfg.Vault.Enabled {
|
||||||
return kmsCfg, nil
|
return kmsCfg, nil
|
||||||
}
|
}
|
||||||
stateBool, err := config.ParseBool(env.Get(EnvKMSVaultState, kvs.Get(config.State)))
|
stateBool, err := config.ParseBool(env.Get(EnvKMSVaultState, kvs.Get(config.State)))
|
||||||
@ -166,23 +173,30 @@ func LookupConfig(kvs config.KVS) (KMSConfig, error) {
|
|||||||
vcfg.Endpoint = endpointStr
|
vcfg.Endpoint = endpointStr
|
||||||
vcfg.CAPath = env.Get(EnvKMSVaultCAPath, kvs.Get(KMSVaultCAPath))
|
vcfg.CAPath = env.Get(EnvKMSVaultCAPath, kvs.Get(KMSVaultCAPath))
|
||||||
vcfg.Auth.Type = env.Get(EnvKMSVaultAuthType, kvs.Get(KMSVaultAuthType))
|
vcfg.Auth.Type = env.Get(EnvKMSVaultAuthType, kvs.Get(KMSVaultAuthType))
|
||||||
|
if vcfg.Auth.Type == "" {
|
||||||
|
vcfg.Auth.Type = "approle"
|
||||||
|
}
|
||||||
vcfg.Auth.AppRole.ID = env.Get(EnvKMSVaultAppRoleID, kvs.Get(KMSVaultAppRoleID))
|
vcfg.Auth.AppRole.ID = env.Get(EnvKMSVaultAppRoleID, kvs.Get(KMSVaultAppRoleID))
|
||||||
vcfg.Auth.AppRole.Secret = env.Get(EnvKMSVaultAppSecretID, kvs.Get(KMSVaultAppRoleSecret))
|
vcfg.Auth.AppRole.Secret = env.Get(EnvKMSVaultAppSecretID, kvs.Get(KMSVaultAppRoleSecret))
|
||||||
vcfg.Key.Name = env.Get(EnvKMSVaultKeyName, kvs.Get(KMSVaultKeyName))
|
vcfg.Key.Name = env.Get(EnvKMSVaultKeyName, kvs.Get(KMSVaultKeyName))
|
||||||
vcfg.Namespace = env.Get(EnvKMSVaultNamespace, kvs.Get(KMSVaultNamespace))
|
vcfg.Namespace = env.Get(EnvKMSVaultNamespace, kvs.Get(KMSVaultNamespace))
|
||||||
keyVersion := env.Get(EnvKMSVaultKeyVersion, kvs.Get(KMSVaultKeyVersion))
|
if keyVersion := env.Get(EnvKMSVaultKeyVersion, kvs.Get(KMSVaultKeyVersion)); keyVersion != "" {
|
||||||
|
|
||||||
if keyVersion != "" {
|
|
||||||
vcfg.Key.Version, err = strconv.Atoi(keyVersion)
|
vcfg.Key.Version, err = strconv.Atoi(keyVersion)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return kmsCfg, fmt.Errorf("Unable to parse VaultKeyVersion value (`%s`)", keyVersion)
|
return kmsCfg, fmt.Errorf("Unable to parse VaultKeyVersion value (`%s`)", keyVersion)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if reflect.DeepEqual(vcfg, defaultCfg) {
|
||||||
|
return kmsCfg, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify all the proper settings.
|
||||||
if err = vcfg.Verify(); err != nil {
|
if err = vcfg.Verify(); err != nil {
|
||||||
return kmsCfg, err
|
return kmsCfg, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vcfg.Enabled = true
|
||||||
kmsCfg.Vault = vcfg
|
kmsCfg.Vault = vcfg
|
||||||
return kmsCfg, nil
|
return kmsCfg, nil
|
||||||
}
|
}
|
||||||
@ -191,7 +205,7 @@ func LookupConfig(kvs config.KVS) (KMSConfig, error) {
|
|||||||
func NewKMS(cfg KMSConfig) (kms KMS, err error) {
|
func NewKMS(cfg KMSConfig) (kms KMS, err error) {
|
||||||
// Lookup KMS master keys - only available through ENV.
|
// Lookup KMS master keys - only available through ENV.
|
||||||
if masterKeyLegacy := env.Get(EnvKMSMasterKeyLegacy, ""); len(masterKeyLegacy) != 0 {
|
if masterKeyLegacy := env.Get(EnvKMSMasterKeyLegacy, ""); len(masterKeyLegacy) != 0 {
|
||||||
if !cfg.Vault.IsEmpty() { // Vault and KMS master key provided
|
if cfg.Vault.Enabled { // Vault and KMS master key provided
|
||||||
return kms, errors.New("Ambiguous KMS configuration: vault configuration and a master key are provided at the same time")
|
return kms, errors.New("Ambiguous KMS configuration: vault configuration and a master key are provided at the same time")
|
||||||
}
|
}
|
||||||
kms, err = ParseMasterKey(masterKeyLegacy)
|
kms, err = ParseMasterKey(masterKeyLegacy)
|
||||||
@ -199,15 +213,14 @@ func NewKMS(cfg KMSConfig) (kms KMS, err error) {
|
|||||||
return kms, err
|
return kms, err
|
||||||
}
|
}
|
||||||
} else if masterKey := env.Get(EnvKMSMasterKey, ""); len(masterKey) != 0 {
|
} else if masterKey := env.Get(EnvKMSMasterKey, ""); len(masterKey) != 0 {
|
||||||
if !cfg.Vault.IsEmpty() { // Vault and KMS master key provided
|
if cfg.Vault.Enabled { // Vault and KMS master key provided
|
||||||
return kms, errors.New("Ambiguous KMS configuration: vault configuration and a master key are provided at the same time")
|
return kms, errors.New("Ambiguous KMS configuration: vault configuration and a master key are provided at the same time")
|
||||||
}
|
}
|
||||||
kms, err = ParseMasterKey(masterKey)
|
kms, err = ParseMasterKey(masterKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return kms, err
|
return kms, err
|
||||||
}
|
}
|
||||||
}
|
} else if cfg.Vault.Enabled {
|
||||||
if !cfg.Vault.IsEmpty() {
|
|
||||||
kms, err = NewVault(cfg.Vault)
|
kms, err = NewVault(cfg.Vault)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return kms, err
|
return kms, err
|
||||||
|
@ -18,6 +18,7 @@ package crypto
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"reflect"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/minio/minio/cmd/config"
|
"github.com/minio/minio/cmd/config"
|
||||||
@ -40,40 +41,40 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// EnvVaultEndpoint is the environment variable used to specify
|
// EnvLegacyVaultEndpoint is the environment variable used to specify
|
||||||
// the vault HTTPS endpoint.
|
// the vault HTTPS endpoint.
|
||||||
EnvVaultEndpoint = "MINIO_SSE_VAULT_ENDPOINT"
|
EnvLegacyVaultEndpoint = "MINIO_SSE_VAULT_ENDPOINT"
|
||||||
|
|
||||||
// EnvVaultAuthType is the environment variable used to specify
|
// EnvLegacyVaultAuthType is the environment variable used to specify
|
||||||
// the authentication type for vault.
|
// the authentication type for vault.
|
||||||
EnvVaultAuthType = "MINIO_SSE_VAULT_AUTH_TYPE"
|
EnvLegacyVaultAuthType = "MINIO_SSE_VAULT_AUTH_TYPE"
|
||||||
|
|
||||||
// EnvVaultAppRoleID is the environment variable used to specify
|
// EnvLegacyVaultAppRoleID is the environment variable used to specify
|
||||||
// the vault AppRole ID.
|
// the vault AppRole ID.
|
||||||
EnvVaultAppRoleID = "MINIO_SSE_VAULT_APPROLE_ID"
|
EnvLegacyVaultAppRoleID = "MINIO_SSE_VAULT_APPROLE_ID"
|
||||||
|
|
||||||
// EnvVaultAppSecretID is the environment variable used to specify
|
// EnvLegacyVaultAppSecretID is the environment variable used to specify
|
||||||
// the vault AppRole secret corresponding to the AppRole ID.
|
// the vault AppRole secret corresponding to the AppRole ID.
|
||||||
EnvVaultAppSecretID = "MINIO_SSE_VAULT_APPROLE_SECRET"
|
EnvLegacyVaultAppSecretID = "MINIO_SSE_VAULT_APPROLE_SECRET"
|
||||||
|
|
||||||
// EnvVaultKeyVersion is the environment variable used to specify
|
// EnvLegacyVaultKeyVersion is the environment variable used to specify
|
||||||
// the vault key version.
|
// the vault key version.
|
||||||
EnvVaultKeyVersion = "MINIO_SSE_VAULT_KEY_VERSION"
|
EnvLegacyVaultKeyVersion = "MINIO_SSE_VAULT_KEY_VERSION"
|
||||||
|
|
||||||
// EnvVaultKeyName is the environment variable used to specify
|
// EnvLegacyVaultKeyName is the environment variable used to specify
|
||||||
// the vault named key-ring. In the S3 context it's referred as
|
// the vault named key-ring. In the S3 context it's referred as
|
||||||
// customer master key ID (CMK-ID).
|
// customer master key ID (CMK-ID).
|
||||||
EnvVaultKeyName = "MINIO_SSE_VAULT_KEY_NAME"
|
EnvLegacyVaultKeyName = "MINIO_SSE_VAULT_KEY_NAME"
|
||||||
|
|
||||||
// EnvVaultCAPath is the environment variable used to specify the
|
// EnvLegacyVaultCAPath is the environment variable used to specify the
|
||||||
// path to a directory of PEM-encoded CA cert files. These CA cert
|
// path to a directory of PEM-encoded CA cert files. These CA cert
|
||||||
// files are used to authenticate MinIO to Vault over mTLS.
|
// files are used to authenticate MinIO to Vault over mTLS.
|
||||||
EnvVaultCAPath = "MINIO_SSE_VAULT_CAPATH"
|
EnvLegacyVaultCAPath = "MINIO_SSE_VAULT_CAPATH"
|
||||||
|
|
||||||
// EnvVaultNamespace is the environment variable used to specify
|
// EnvLegacyVaultNamespace is the environment variable used to specify
|
||||||
// vault namespace. The vault namespace is used if the enterprise
|
// vault namespace. The vault namespace is used if the enterprise
|
||||||
// version of Hashicorp Vault is used.
|
// version of Hashicorp Vault is used.
|
||||||
EnvVaultNamespace = "MINIO_SSE_VAULT_NAMESPACE"
|
EnvLegacyVaultNamespace = "MINIO_SSE_VAULT_NAMESPACE"
|
||||||
)
|
)
|
||||||
|
|
||||||
// SetKMSConfig helper to migrate from older KMSConfig to new KV.
|
// SetKMSConfig helper to migrate from older KMSConfig to new KV.
|
||||||
@ -93,7 +94,7 @@ func SetKMSConfig(s config.Config, cfg KMSConfig) {
|
|||||||
KMSVaultKeyVersion: strconv.Itoa(cfg.Vault.Key.Version),
|
KMSVaultKeyVersion: strconv.Itoa(cfg.Vault.Key.Version),
|
||||||
KMSVaultNamespace: cfg.Vault.Namespace,
|
KMSVaultNamespace: cfg.Vault.Namespace,
|
||||||
config.State: func() string {
|
config.State: func() string {
|
||||||
if !cfg.Vault.IsEmpty() {
|
if cfg.Vault.Endpoint != "" {
|
||||||
return config.StateOn
|
return config.StateOn
|
||||||
}
|
}
|
||||||
return config.StateOff
|
return config.StateOff
|
||||||
@ -141,7 +142,7 @@ func lookupConfigLegacy(kvs config.KVS) (KMSConfig, error) {
|
|||||||
return cfg, nil
|
return cfg, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
endpointStr := env.Get(EnvKMSVaultEndpoint, kvs.Get(KMSVaultEndpoint))
|
endpointStr := env.Get(EnvLegacyVaultEndpoint, "")
|
||||||
if endpointStr != "" {
|
if endpointStr != "" {
|
||||||
// Lookup Hashicorp-Vault configuration & overwrite config entry if ENV var is present
|
// Lookup Hashicorp-Vault configuration & overwrite config entry if ENV var is present
|
||||||
endpoint, err := xnet.ParseHTTPURL(endpointStr)
|
endpoint, err := xnet.ParseHTTPURL(endpointStr)
|
||||||
@ -152,25 +153,31 @@ func lookupConfigLegacy(kvs config.KVS) (KMSConfig, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
cfg.Vault.Endpoint = endpointStr
|
cfg.Vault.Endpoint = endpointStr
|
||||||
cfg.Vault.CAPath = env.Get(EnvVaultCAPath, kvs.Get(KMSVaultCAPath))
|
cfg.Vault.CAPath = env.Get(EnvLegacyVaultCAPath, "")
|
||||||
cfg.Vault.Auth.Type = env.Get(EnvVaultAuthType, kvs.Get(KMSVaultAuthType))
|
cfg.Vault.Auth.Type = env.Get(EnvLegacyVaultAuthType, "")
|
||||||
cfg.Vault.Auth.AppRole.ID = env.Get(EnvVaultAppRoleID, kvs.Get(KMSVaultAppRoleID))
|
if cfg.Vault.Auth.Type == "" {
|
||||||
cfg.Vault.Auth.AppRole.Secret = env.Get(EnvVaultAppSecretID, kvs.Get(KMSVaultAppRoleSecret))
|
cfg.Vault.Auth.Type = "approle"
|
||||||
cfg.Vault.Key.Name = env.Get(EnvVaultKeyName, kvs.Get(KMSVaultKeyName))
|
}
|
||||||
cfg.Vault.Namespace = env.Get(EnvVaultNamespace, kvs.Get(KMSVaultNamespace))
|
cfg.Vault.Auth.AppRole.ID = env.Get(EnvLegacyVaultAppRoleID, "")
|
||||||
keyVersion := env.Get(EnvVaultKeyVersion, kvs.Get(KMSVaultKeyVersion))
|
cfg.Vault.Auth.AppRole.Secret = env.Get(EnvLegacyVaultAppSecretID, "")
|
||||||
|
cfg.Vault.Key.Name = env.Get(EnvLegacyVaultKeyName, "")
|
||||||
if keyVersion != "" {
|
cfg.Vault.Namespace = env.Get(EnvLegacyVaultNamespace, "")
|
||||||
|
if keyVersion := env.Get(EnvLegacyVaultKeyVersion, ""); keyVersion != "" {
|
||||||
cfg.Vault.Key.Version, err = strconv.Atoi(keyVersion)
|
cfg.Vault.Key.Version, err = strconv.Atoi(keyVersion)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return cfg, fmt.Errorf("Invalid ENV variable: Unable to parse %s value (`%s`)",
|
return cfg, fmt.Errorf("Invalid ENV variable: Unable to parse %s value (`%s`)",
|
||||||
EnvVaultKeyVersion, keyVersion)
|
EnvLegacyVaultKeyVersion, keyVersion)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if reflect.DeepEqual(cfg.Vault, defaultCfg) {
|
||||||
|
return cfg, nil
|
||||||
|
}
|
||||||
|
|
||||||
if err = cfg.Vault.Verify(); err != nil {
|
if err = cfg.Vault.Verify(); err != nil {
|
||||||
return cfg, err
|
return cfg, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cfg.Vault.Enabled = true
|
||||||
return cfg, nil
|
return cfg, nil
|
||||||
}
|
}
|
||||||
|
@ -51,6 +51,7 @@ type VaultAppRole struct {
|
|||||||
|
|
||||||
// VaultConfig represents vault configuration.
|
// VaultConfig represents vault configuration.
|
||||||
type VaultConfig struct {
|
type VaultConfig struct {
|
||||||
|
Enabled bool `json:"-"`
|
||||||
Endpoint string `json:"endpoint"` // The vault API endpoint as URL
|
Endpoint string `json:"endpoint"` // The vault API endpoint as URL
|
||||||
CAPath string `json:"-"` // The path to PEM-encoded certificate files used for mTLS. Currently not used in config file.
|
CAPath string `json:"-"` // The path to PEM-encoded certificate files used for mTLS. Currently not used in config file.
|
||||||
Auth VaultAuth `json:"auth"` // The vault authentication configuration
|
Auth VaultAuth `json:"auth"` // The vault authentication configuration
|
||||||
@ -68,24 +69,10 @@ type vaultService struct {
|
|||||||
|
|
||||||
var _ KMS = (*vaultService)(nil) // compiler check that *vaultService implements KMS
|
var _ KMS = (*vaultService)(nil) // compiler check that *vaultService implements KMS
|
||||||
|
|
||||||
// empty/default vault configuration used to check whether a particular is empty.
|
|
||||||
var emptyVaultConfig = VaultConfig{
|
|
||||||
Auth: VaultAuth{
|
|
||||||
Type: "approle",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsEmpty returns true if the vault config struct is an
|
|
||||||
// empty configuration.
|
|
||||||
func (v *VaultConfig) IsEmpty() bool { return *v == emptyVaultConfig }
|
|
||||||
|
|
||||||
// Verify returns a nil error if the vault configuration
|
// Verify returns a nil error if the vault configuration
|
||||||
// is valid. A valid configuration is either empty or
|
// is valid. A valid configuration is either empty or
|
||||||
// contains valid non-default values.
|
// contains valid non-default values.
|
||||||
func (v *VaultConfig) Verify() (err error) {
|
func (v *VaultConfig) Verify() (err error) {
|
||||||
if v.IsEmpty() {
|
|
||||||
return // an empty configuration is valid
|
|
||||||
}
|
|
||||||
switch {
|
switch {
|
||||||
case v.Endpoint == "":
|
case v.Endpoint == "":
|
||||||
err = errors.New("crypto: missing hashicorp vault endpoint")
|
err = errors.New("crypto: missing hashicorp vault endpoint")
|
||||||
@ -107,8 +94,8 @@ func (v *VaultConfig) Verify() (err error) {
|
|||||||
// to Vault with the credentials in config and gets a client
|
// to Vault with the credentials in config and gets a client
|
||||||
// token for future api calls.
|
// token for future api calls.
|
||||||
func NewVault(config VaultConfig) (KMS, error) {
|
func NewVault(config VaultConfig) (KMS, error) {
|
||||||
if config.IsEmpty() {
|
if !config.Enabled {
|
||||||
return nil, errors.New("crypto: the hashicorp vault configuration must not be empty")
|
return nil, nil
|
||||||
}
|
}
|
||||||
if err := config.Verify(); err != nil {
|
if err := config.Verify(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -15,7 +15,6 @@
|
|||||||
package crypto
|
package crypto
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -23,17 +22,17 @@ var verifyVaultConfigTests = []struct {
|
|||||||
Config VaultConfig
|
Config VaultConfig
|
||||||
ShouldFail bool
|
ShouldFail bool
|
||||||
}{
|
}{
|
||||||
{
|
|
||||||
ShouldFail: false, // 0
|
|
||||||
Config: emptyVaultConfig,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
ShouldFail: true,
|
ShouldFail: true,
|
||||||
Config: VaultConfig{Endpoint: "https://127.0.0.1:8080"},
|
Config: VaultConfig{
|
||||||
|
Endpoint: "https://127.0.0.1:8080",
|
||||||
|
Enabled: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ShouldFail: true, // 1
|
ShouldFail: true, // 1
|
||||||
Config: VaultConfig{
|
Config: VaultConfig{
|
||||||
|
Enabled: true,
|
||||||
Endpoint: "https://127.0.0.1:8080",
|
Endpoint: "https://127.0.0.1:8080",
|
||||||
Auth: VaultAuth{Type: "unsupported"},
|
Auth: VaultAuth{Type: "unsupported"},
|
||||||
},
|
},
|
||||||
@ -41,6 +40,7 @@ var verifyVaultConfigTests = []struct {
|
|||||||
{
|
{
|
||||||
ShouldFail: true, // 2
|
ShouldFail: true, // 2
|
||||||
Config: VaultConfig{
|
Config: VaultConfig{
|
||||||
|
Enabled: true,
|
||||||
Endpoint: "https://127.0.0.1:8080",
|
Endpoint: "https://127.0.0.1:8080",
|
||||||
Auth: VaultAuth{
|
Auth: VaultAuth{
|
||||||
Type: "approle",
|
Type: "approle",
|
||||||
@ -51,6 +51,7 @@ var verifyVaultConfigTests = []struct {
|
|||||||
{
|
{
|
||||||
ShouldFail: true, // 3
|
ShouldFail: true, // 3
|
||||||
Config: VaultConfig{
|
Config: VaultConfig{
|
||||||
|
Enabled: true,
|
||||||
Endpoint: "https://127.0.0.1:8080",
|
Endpoint: "https://127.0.0.1:8080",
|
||||||
Auth: VaultAuth{
|
Auth: VaultAuth{
|
||||||
Type: "approle",
|
Type: "approle",
|
||||||
@ -61,6 +62,7 @@ var verifyVaultConfigTests = []struct {
|
|||||||
{
|
{
|
||||||
ShouldFail: true, // 4
|
ShouldFail: true, // 4
|
||||||
Config: VaultConfig{
|
Config: VaultConfig{
|
||||||
|
Enabled: true,
|
||||||
Endpoint: "https://127.0.0.1:8080",
|
Endpoint: "https://127.0.0.1:8080",
|
||||||
Auth: VaultAuth{
|
Auth: VaultAuth{
|
||||||
Type: "approle",
|
Type: "approle",
|
||||||
@ -71,6 +73,7 @@ var verifyVaultConfigTests = []struct {
|
|||||||
{
|
{
|
||||||
ShouldFail: true, // 5
|
ShouldFail: true, // 5
|
||||||
Config: VaultConfig{
|
Config: VaultConfig{
|
||||||
|
Enabled: true,
|
||||||
Endpoint: "https://127.0.0.1:8080",
|
Endpoint: "https://127.0.0.1:8080",
|
||||||
Auth: VaultAuth{
|
Auth: VaultAuth{
|
||||||
Type: "approle",
|
Type: "approle",
|
||||||
@ -82,9 +85,9 @@ var verifyVaultConfigTests = []struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestVerifyVaultConfig(t *testing.T) {
|
func TestVerifyVaultConfig(t *testing.T) {
|
||||||
for i, test := range verifyVaultConfigTests {
|
for _, test := range verifyVaultConfigTests {
|
||||||
test := test
|
test := test
|
||||||
t.Run(fmt.Sprintf("Test-%d", i), func(t *testing.T) {
|
t.Run(test.Config.Endpoint, func(t *testing.T) {
|
||||||
err := test.Config.Verify()
|
err := test.Config.Verify()
|
||||||
if test.ShouldFail && err == nil {
|
if test.ShouldFail && err == nil {
|
||||||
t.Errorf("Verify should fail but returned 'err == nil'")
|
t.Errorf("Verify should fail but returned 'err == nil'")
|
||||||
|
@ -56,7 +56,7 @@ func startDailyLifecycle() {
|
|||||||
|
|
||||||
// Wait until the object API is ready
|
// Wait until the object API is ready
|
||||||
for {
|
for {
|
||||||
objAPI = globalObjectAPI
|
objAPI = newObjectLayerWithoutSafeModeFn()
|
||||||
if objAPI == nil {
|
if objAPI == nil {
|
||||||
time.Sleep(time.Second)
|
time.Sleep(time.Second)
|
||||||
continue
|
continue
|
||||||
|
@ -553,23 +553,23 @@ func newServerCacheObjects(ctx context.Context, config cache.Config) (CacheObjec
|
|||||||
migrating: migrateSw,
|
migrating: migrateSw,
|
||||||
migMutex: sync.Mutex{},
|
migMutex: sync.Mutex{},
|
||||||
GetObjectInfoFn: func(ctx context.Context, bucket, object string, opts ObjectOptions) (ObjectInfo, error) {
|
GetObjectInfoFn: func(ctx context.Context, bucket, object string, opts ObjectOptions) (ObjectInfo, error) {
|
||||||
return globalObjectAPI.GetObjectInfo(ctx, bucket, object, opts)
|
return newObjectLayerFn().GetObjectInfo(ctx, bucket, object, opts)
|
||||||
},
|
},
|
||||||
GetObjectNInfoFn: func(ctx context.Context, bucket, object string, rs *HTTPRangeSpec, h http.Header, lockType LockType, opts ObjectOptions) (gr *GetObjectReader, err error) {
|
GetObjectNInfoFn: func(ctx context.Context, bucket, object string, rs *HTTPRangeSpec, h http.Header, lockType LockType, opts ObjectOptions) (gr *GetObjectReader, err error) {
|
||||||
return globalObjectAPI.GetObjectNInfo(ctx, bucket, object, rs, h, lockType, opts)
|
return newObjectLayerFn().GetObjectNInfo(ctx, bucket, object, rs, h, lockType, opts)
|
||||||
},
|
},
|
||||||
DeleteObjectFn: func(ctx context.Context, bucket, object string) error {
|
DeleteObjectFn: func(ctx context.Context, bucket, object string) error {
|
||||||
return globalObjectAPI.DeleteObject(ctx, bucket, object)
|
return newObjectLayerFn().DeleteObject(ctx, bucket, object)
|
||||||
},
|
},
|
||||||
DeleteObjectsFn: func(ctx context.Context, bucket string, objects []string) ([]error, error) {
|
DeleteObjectsFn: func(ctx context.Context, bucket string, objects []string) ([]error, error) {
|
||||||
errs := make([]error, len(objects))
|
errs := make([]error, len(objects))
|
||||||
for idx, object := range objects {
|
for idx, object := range objects {
|
||||||
errs[idx] = globalObjectAPI.DeleteObject(ctx, bucket, object)
|
errs[idx] = newObjectLayerFn().DeleteObject(ctx, bucket, object)
|
||||||
}
|
}
|
||||||
return errs, nil
|
return errs, nil
|
||||||
},
|
},
|
||||||
PutObjectFn: func(ctx context.Context, bucket, object string, data *PutObjReader, opts ObjectOptions) (objInfo ObjectInfo, err error) {
|
PutObjectFn: func(ctx context.Context, bucket, object string, data *PutObjReader, opts ObjectOptions) (objInfo ObjectInfo, err error) {
|
||||||
return globalObjectAPI.PutObject(ctx, bucket, object, data, opts)
|
return newObjectLayerFn().PutObject(ctx, bucket, object, data, opts)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
if migrateSw {
|
if migrateSw {
|
||||||
|
@ -222,9 +222,22 @@ func StartGateway(ctx *cli.Context, gw Gateway) {
|
|||||||
logger.FatalIf(err, "Unable to initialize gateway backend")
|
logger.FatalIf(err, "Unable to initialize gateway backend")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Re-enable logging
|
||||||
|
logger.Disable = false
|
||||||
|
|
||||||
|
// Once endpoints are finalized, initialize the new object api in safe mode.
|
||||||
|
globalObjLayerMutex.Lock()
|
||||||
|
globalSafeMode = true
|
||||||
|
globalObjectAPI = newObject
|
||||||
|
globalObjLayerMutex.Unlock()
|
||||||
|
|
||||||
// Populate existing buckets to the etcd backend
|
// Populate existing buckets to the etcd backend
|
||||||
if globalDNSConfig != nil {
|
if globalDNSConfig != nil {
|
||||||
initFederatorBackend(newObject)
|
buckets, err := newObject.ListBuckets(context.Background())
|
||||||
|
if err != nil {
|
||||||
|
logger.Fatal(err, "Unable to list buckets")
|
||||||
|
}
|
||||||
|
initFederatorBackend(buckets, newObject)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Migrate all backend configs to encrypted backend, also handles rotation as well.
|
// Migrate all backend configs to encrypted backend, also handles rotation as well.
|
||||||
@ -233,16 +246,15 @@ func StartGateway(ctx *cli.Context, gw Gateway) {
|
|||||||
logger.FatalIf(handleEncryptedConfigBackend(newObject, enableConfigOps),
|
logger.FatalIf(handleEncryptedConfigBackend(newObject, enableConfigOps),
|
||||||
"Unable to handle encrypted backend for config, iam and policies")
|
"Unable to handle encrypted backend for config, iam and policies")
|
||||||
|
|
||||||
|
// Calls all New() for all sub-systems.
|
||||||
|
newAllSubsystems()
|
||||||
|
|
||||||
// **** WARNING ****
|
// **** WARNING ****
|
||||||
// Migrating to encrypted backend should happen before initialization of any
|
// Migrating to encrypted backend should happen before initialization of any
|
||||||
// sub-systems, make sure that we do not move the above codeblock elsewhere.
|
// sub-systems, make sure that we do not move the above codeblock elsewhere.
|
||||||
|
|
||||||
if enableConfigOps {
|
if enableConfigOps {
|
||||||
// Create a new config system.
|
logger.FatalIf(globalConfigSys.Init(newObject), "Unable to initialize config system")
|
||||||
globalConfigSys = NewConfigSys()
|
|
||||||
|
|
||||||
// Load globalServerConfig from disk
|
|
||||||
logger.LogIf(context.Background(), globalConfigSys.Init(newObject))
|
|
||||||
|
|
||||||
// Start watching disk for reloading config, this
|
// Start watching disk for reloading config, this
|
||||||
// is only enabled for "NAS" gateway.
|
// is only enabled for "NAS" gateway.
|
||||||
@ -261,32 +273,20 @@ func StartGateway(ctx *cli.Context, gw Gateway) {
|
|||||||
"Unable to handle encrypted backend for iam and policies")
|
"Unable to handle encrypted backend for iam and policies")
|
||||||
}
|
}
|
||||||
|
|
||||||
if globalCacheConfig.Enabled {
|
|
||||||
// initialize the new disk cache objects.
|
|
||||||
globalCacheObjectAPI, err = newServerCacheObjects(context.Background(), globalCacheConfig)
|
|
||||||
logger.FatalIf(err, "Unable to initialize disk caching")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Re-enable logging
|
|
||||||
logger.Disable = false
|
|
||||||
|
|
||||||
// Create new IAM system.
|
|
||||||
globalIAMSys = NewIAMSys()
|
|
||||||
if enableIAMOps {
|
if enableIAMOps {
|
||||||
// Initialize IAM sys.
|
// Initialize IAM sys.
|
||||||
logger.LogIf(context.Background(), globalIAMSys.Init(newObject))
|
logger.FatalIf(globalIAMSys.Init(newObject), "Unable to initialize IAM system")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create new policy system.
|
if globalCacheConfig.Enabled {
|
||||||
globalPolicySys = NewPolicySys()
|
// initialize the new disk cache objects.
|
||||||
|
var cacheAPI CacheObjectLayer
|
||||||
|
cacheAPI, err = newServerCacheObjects(context.Background(), globalCacheConfig)
|
||||||
|
logger.FatalIf(err, "Unable to initialize disk caching")
|
||||||
|
|
||||||
// Create new lifecycle system.
|
globalObjLayerMutex.Lock()
|
||||||
globalLifecycleSys = NewLifecycleSys()
|
globalCacheObjectAPI = cacheAPI
|
||||||
|
globalObjLayerMutex.Unlock()
|
||||||
// Create new notification system.
|
|
||||||
globalNotificationSys, err = NewNotificationSys(globalServerConfig, globalEndpoints)
|
|
||||||
if err != nil {
|
|
||||||
logger.FatalIf(err, "Unable to initialize notification system")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify if object layer supports
|
// Verify if object layer supports
|
||||||
@ -294,11 +294,6 @@ func StartGateway(ctx *cli.Context, gw Gateway) {
|
|||||||
// - compression
|
// - compression
|
||||||
verifyObjectLayerFeatures("gateway "+gatewayName, newObject)
|
verifyObjectLayerFeatures("gateway "+gatewayName, newObject)
|
||||||
|
|
||||||
// Once endpoints are finalized, initialize the new object api.
|
|
||||||
globalObjLayerMutex.Lock()
|
|
||||||
globalObjectAPI = newObject
|
|
||||||
globalObjLayerMutex.Unlock()
|
|
||||||
|
|
||||||
// Prints the formatted startup message once object layer is initialized.
|
// Prints the formatted startup message once object layer is initialized.
|
||||||
if !globalCLIContext.Quiet {
|
if !globalCLIContext.Quiet {
|
||||||
mode := globalMinioModeGatewayPrefix + gatewayName
|
mode := globalMinioModeGatewayPrefix + gatewayName
|
||||||
@ -314,6 +309,11 @@ func StartGateway(ctx *cli.Context, gw Gateway) {
|
|||||||
printGatewayStartupMessage(getAPIEndpoints(), gatewayName)
|
printGatewayStartupMessage(getAPIEndpoints(), gatewayName)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Disable safe mode operation, after all initialization is over.
|
||||||
|
globalObjLayerMutex.Lock()
|
||||||
|
globalSafeMode = false
|
||||||
|
globalObjLayerMutex.Unlock()
|
||||||
|
|
||||||
// Set uptime time after object layer has initialized.
|
// Set uptime time after object layer has initialized.
|
||||||
globalBootTime = UTCNow()
|
globalBootTime = UTCNow()
|
||||||
|
|
||||||
|
@ -28,8 +28,9 @@ import (
|
|||||||
func printGatewayStartupMessage(apiEndPoints []string, backendType string) {
|
func printGatewayStartupMessage(apiEndPoints []string, backendType string) {
|
||||||
strippedAPIEndpoints := stripStandardPorts(apiEndPoints)
|
strippedAPIEndpoints := stripStandardPorts(apiEndPoints)
|
||||||
// If cache layer is enabled, print cache capacity.
|
// If cache layer is enabled, print cache capacity.
|
||||||
if globalCacheObjectAPI != nil {
|
cacheAPI := newCachedObjectLayerFn()
|
||||||
printCacheStorageInfo(globalCacheObjectAPI.StorageInfo(context.Background()))
|
if cacheAPI != nil {
|
||||||
|
printCacheStorageInfo(cacheAPI.StorageInfo(context.Background()))
|
||||||
}
|
}
|
||||||
// Prints credential.
|
// Prints credential.
|
||||||
printGatewayCommonMsg(strippedAPIEndpoints)
|
printGatewayCommonMsg(strippedAPIEndpoints)
|
||||||
|
@ -156,7 +156,7 @@ func execLeaderTasks(sets *xlSets) {
|
|||||||
func startGlobalHeal() {
|
func startGlobalHeal() {
|
||||||
var objAPI ObjectLayer
|
var objAPI ObjectLayer
|
||||||
for {
|
for {
|
||||||
objAPI = globalObjectAPI
|
objAPI = newObjectLayerWithoutSafeModeFn()
|
||||||
if objAPI == nil {
|
if objAPI == nil {
|
||||||
time.Sleep(time.Second)
|
time.Sleep(time.Second)
|
||||||
continue
|
continue
|
||||||
|
@ -52,9 +52,9 @@ func ReadinessCheckHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
func LivenessCheckHandler(w http.ResponseWriter, r *http.Request) {
|
func LivenessCheckHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
ctx := newContext(r, w, "LivenessCheckHandler")
|
ctx := newContext(r, w, "LivenessCheckHandler")
|
||||||
|
|
||||||
objLayer := globalObjectAPI
|
objLayer := newObjectLayerFn()
|
||||||
// Service not initialized yet
|
// Service not initialized yet
|
||||||
if objLayer == nil || globalSafeMode {
|
if objLayer == nil {
|
||||||
// Respond with 200 OK while server initializes to ensure a distributed cluster
|
// Respond with 200 OK while server initializes to ensure a distributed cluster
|
||||||
// is able to start on orchestration platforms like Docker Swarm.
|
// is able to start on orchestration platforms like Docker Swarm.
|
||||||
// Refer https://github.com/minio/minio/issues/8140 for more details.
|
// Refer https://github.com/minio/minio/issues/8140 for more details.
|
||||||
|
@ -49,7 +49,7 @@ func (iamOS *IAMObjectStore) getObjectAPI() ObjectLayer {
|
|||||||
if iamOS.objAPI != nil {
|
if iamOS.objAPI != nil {
|
||||||
return iamOS.objAPI
|
return iamOS.objAPI
|
||||||
}
|
}
|
||||||
return globalObjectAPI
|
return newObjectLayerWithoutSafeModeFn()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (iamOS *IAMObjectStore) setObjectAPI(objAPI ObjectLayer) {
|
func (iamOS *IAMObjectStore) setObjectAPI(objAPI ObjectLayer) {
|
||||||
|
32
cmd/iam.go
32
cmd/iam.go
@ -406,7 +406,7 @@ func (sys *IAMSys) Init(objAPI ObjectLayer) error {
|
|||||||
|
|
||||||
// DeletePolicy - deletes a canned policy from backend or etcd.
|
// DeletePolicy - deletes a canned policy from backend or etcd.
|
||||||
func (sys *IAMSys) DeletePolicy(policyName string) error {
|
func (sys *IAMSys) DeletePolicy(policyName string) error {
|
||||||
objectAPI := globalObjectAPI
|
objectAPI := newObjectLayerWithoutSafeModeFn()
|
||||||
if objectAPI == nil {
|
if objectAPI == nil {
|
||||||
return errServerNotInitialized
|
return errServerNotInitialized
|
||||||
}
|
}
|
||||||
@ -431,7 +431,7 @@ func (sys *IAMSys) DeletePolicy(policyName string) error {
|
|||||||
|
|
||||||
// InfoPolicy - expands the canned policy into its JSON structure.
|
// InfoPolicy - expands the canned policy into its JSON structure.
|
||||||
func (sys *IAMSys) InfoPolicy(policyName string) ([]byte, error) {
|
func (sys *IAMSys) InfoPolicy(policyName string) ([]byte, error) {
|
||||||
objectAPI := globalObjectAPI
|
objectAPI := newObjectLayerWithoutSafeModeFn()
|
||||||
if objectAPI == nil {
|
if objectAPI == nil {
|
||||||
return nil, errServerNotInitialized
|
return nil, errServerNotInitialized
|
||||||
}
|
}
|
||||||
@ -448,7 +448,7 @@ func (sys *IAMSys) InfoPolicy(policyName string) ([]byte, error) {
|
|||||||
|
|
||||||
// ListPolicies - lists all canned policies.
|
// ListPolicies - lists all canned policies.
|
||||||
func (sys *IAMSys) ListPolicies() (map[string][]byte, error) {
|
func (sys *IAMSys) ListPolicies() (map[string][]byte, error) {
|
||||||
objectAPI := globalObjectAPI
|
objectAPI := newObjectLayerWithoutSafeModeFn()
|
||||||
if objectAPI == nil {
|
if objectAPI == nil {
|
||||||
return nil, errServerNotInitialized
|
return nil, errServerNotInitialized
|
||||||
}
|
}
|
||||||
@ -471,7 +471,7 @@ func (sys *IAMSys) ListPolicies() (map[string][]byte, error) {
|
|||||||
|
|
||||||
// SetPolicy - sets a new name policy.
|
// SetPolicy - sets a new name policy.
|
||||||
func (sys *IAMSys) SetPolicy(policyName string, p iampolicy.Policy) error {
|
func (sys *IAMSys) SetPolicy(policyName string, p iampolicy.Policy) error {
|
||||||
objectAPI := globalObjectAPI
|
objectAPI := newObjectLayerWithoutSafeModeFn()
|
||||||
if objectAPI == nil {
|
if objectAPI == nil {
|
||||||
return errServerNotInitialized
|
return errServerNotInitialized
|
||||||
}
|
}
|
||||||
@ -492,7 +492,7 @@ func (sys *IAMSys) SetPolicy(policyName string, p iampolicy.Policy) error {
|
|||||||
|
|
||||||
// DeleteUser - delete user (only for long-term users not STS users).
|
// DeleteUser - delete user (only for long-term users not STS users).
|
||||||
func (sys *IAMSys) DeleteUser(accessKey string) error {
|
func (sys *IAMSys) DeleteUser(accessKey string) error {
|
||||||
objectAPI := globalObjectAPI
|
objectAPI := newObjectLayerWithoutSafeModeFn()
|
||||||
if objectAPI == nil {
|
if objectAPI == nil {
|
||||||
return errServerNotInitialized
|
return errServerNotInitialized
|
||||||
}
|
}
|
||||||
@ -521,7 +521,7 @@ func (sys *IAMSys) DeleteUser(accessKey string) error {
|
|||||||
|
|
||||||
// SetTempUser - set temporary user credentials, these credentials have an expiry.
|
// SetTempUser - set temporary user credentials, these credentials have an expiry.
|
||||||
func (sys *IAMSys) SetTempUser(accessKey string, cred auth.Credentials, policyName string) error {
|
func (sys *IAMSys) SetTempUser(accessKey string, cred auth.Credentials, policyName string) error {
|
||||||
objectAPI := globalObjectAPI
|
objectAPI := newObjectLayerWithoutSafeModeFn()
|
||||||
if objectAPI == nil {
|
if objectAPI == nil {
|
||||||
return errServerNotInitialized
|
return errServerNotInitialized
|
||||||
}
|
}
|
||||||
@ -561,7 +561,7 @@ func (sys *IAMSys) SetTempUser(accessKey string, cred auth.Credentials, policyNa
|
|||||||
|
|
||||||
// ListUsers - list all users.
|
// ListUsers - list all users.
|
||||||
func (sys *IAMSys) ListUsers() (map[string]madmin.UserInfo, error) {
|
func (sys *IAMSys) ListUsers() (map[string]madmin.UserInfo, error) {
|
||||||
objectAPI := globalObjectAPI
|
objectAPI := newObjectLayerWithoutSafeModeFn()
|
||||||
if objectAPI == nil {
|
if objectAPI == nil {
|
||||||
return nil, errServerNotInitialized
|
return nil, errServerNotInitialized
|
||||||
}
|
}
|
||||||
@ -587,7 +587,7 @@ func (sys *IAMSys) ListUsers() (map[string]madmin.UserInfo, error) {
|
|||||||
|
|
||||||
// GetUserInfo - get info on a user.
|
// GetUserInfo - get info on a user.
|
||||||
func (sys *IAMSys) GetUserInfo(name string) (u madmin.UserInfo, err error) {
|
func (sys *IAMSys) GetUserInfo(name string) (u madmin.UserInfo, err error) {
|
||||||
objectAPI := globalObjectAPI
|
objectAPI := newObjectLayerWithoutSafeModeFn()
|
||||||
if objectAPI == nil {
|
if objectAPI == nil {
|
||||||
return u, errServerNotInitialized
|
return u, errServerNotInitialized
|
||||||
}
|
}
|
||||||
@ -617,7 +617,7 @@ func (sys *IAMSys) GetUserInfo(name string) (u madmin.UserInfo, err error) {
|
|||||||
|
|
||||||
// SetUserStatus - sets current user status, supports disabled or enabled.
|
// SetUserStatus - sets current user status, supports disabled or enabled.
|
||||||
func (sys *IAMSys) SetUserStatus(accessKey string, status madmin.AccountStatus) error {
|
func (sys *IAMSys) SetUserStatus(accessKey string, status madmin.AccountStatus) error {
|
||||||
objectAPI := globalObjectAPI
|
objectAPI := newObjectLayerWithoutSafeModeFn()
|
||||||
if objectAPI == nil {
|
if objectAPI == nil {
|
||||||
return errServerNotInitialized
|
return errServerNotInitialized
|
||||||
}
|
}
|
||||||
@ -653,7 +653,7 @@ func (sys *IAMSys) SetUserStatus(accessKey string, status madmin.AccountStatus)
|
|||||||
|
|
||||||
// SetUser - set user credentials and policy.
|
// SetUser - set user credentials and policy.
|
||||||
func (sys *IAMSys) SetUser(accessKey string, uinfo madmin.UserInfo) error {
|
func (sys *IAMSys) SetUser(accessKey string, uinfo madmin.UserInfo) error {
|
||||||
objectAPI := globalObjectAPI
|
objectAPI := newObjectLayerWithoutSafeModeFn()
|
||||||
if objectAPI == nil {
|
if objectAPI == nil {
|
||||||
return errServerNotInitialized
|
return errServerNotInitialized
|
||||||
}
|
}
|
||||||
@ -685,7 +685,7 @@ func (sys *IAMSys) SetUser(accessKey string, uinfo madmin.UserInfo) error {
|
|||||||
|
|
||||||
// SetUserSecretKey - sets user secret key
|
// SetUserSecretKey - sets user secret key
|
||||||
func (sys *IAMSys) SetUserSecretKey(accessKey string, secretKey string) error {
|
func (sys *IAMSys) SetUserSecretKey(accessKey string, secretKey string) error {
|
||||||
objectAPI := globalObjectAPI
|
objectAPI := newObjectLayerWithoutSafeModeFn()
|
||||||
if objectAPI == nil {
|
if objectAPI == nil {
|
||||||
return errServerNotInitialized
|
return errServerNotInitialized
|
||||||
}
|
}
|
||||||
@ -724,7 +724,7 @@ func (sys *IAMSys) GetUser(accessKey string) (cred auth.Credentials, ok bool) {
|
|||||||
// AddUsersToGroup - adds users to a group, creating the group if
|
// AddUsersToGroup - adds users to a group, creating the group if
|
||||||
// needed. No error if user(s) already are in the group.
|
// needed. No error if user(s) already are in the group.
|
||||||
func (sys *IAMSys) AddUsersToGroup(group string, members []string) error {
|
func (sys *IAMSys) AddUsersToGroup(group string, members []string) error {
|
||||||
objectAPI := globalObjectAPI
|
objectAPI := newObjectLayerWithoutSafeModeFn()
|
||||||
if objectAPI == nil {
|
if objectAPI == nil {
|
||||||
return errServerNotInitialized
|
return errServerNotInitialized
|
||||||
}
|
}
|
||||||
@ -782,7 +782,7 @@ func (sys *IAMSys) AddUsersToGroup(group string, members []string) error {
|
|||||||
// RemoveUsersFromGroup - remove users from group. If no users are
|
// RemoveUsersFromGroup - remove users from group. If no users are
|
||||||
// given, and the group is empty, deletes the group as well.
|
// given, and the group is empty, deletes the group as well.
|
||||||
func (sys *IAMSys) RemoveUsersFromGroup(group string, members []string) error {
|
func (sys *IAMSys) RemoveUsersFromGroup(group string, members []string) error {
|
||||||
objectAPI := globalObjectAPI
|
objectAPI := newObjectLayerWithoutSafeModeFn()
|
||||||
if objectAPI == nil {
|
if objectAPI == nil {
|
||||||
return errServerNotInitialized
|
return errServerNotInitialized
|
||||||
}
|
}
|
||||||
@ -863,7 +863,7 @@ func (sys *IAMSys) RemoveUsersFromGroup(group string, members []string) error {
|
|||||||
|
|
||||||
// SetGroupStatus - enable/disabled a group
|
// SetGroupStatus - enable/disabled a group
|
||||||
func (sys *IAMSys) SetGroupStatus(group string, enabled bool) error {
|
func (sys *IAMSys) SetGroupStatus(group string, enabled bool) error {
|
||||||
objectAPI := globalObjectAPI
|
objectAPI := newObjectLayerWithoutSafeModeFn()
|
||||||
if objectAPI == nil {
|
if objectAPI == nil {
|
||||||
return errServerNotInitialized
|
return errServerNotInitialized
|
||||||
}
|
}
|
||||||
@ -952,7 +952,7 @@ func (sys *IAMSys) ListGroups() (r []string, err error) {
|
|||||||
// PolicyDB. This function applies only long-term users. For STS
|
// PolicyDB. This function applies only long-term users. For STS
|
||||||
// users, policy is set directly by called sys.policyDBSet().
|
// users, policy is set directly by called sys.policyDBSet().
|
||||||
func (sys *IAMSys) PolicyDBSet(name, policy string, isGroup bool) error {
|
func (sys *IAMSys) PolicyDBSet(name, policy string, isGroup bool) error {
|
||||||
objectAPI := globalObjectAPI
|
objectAPI := newObjectLayerWithoutSafeModeFn()
|
||||||
if objectAPI == nil {
|
if objectAPI == nil {
|
||||||
return errServerNotInitialized
|
return errServerNotInitialized
|
||||||
}
|
}
|
||||||
@ -1007,7 +1007,7 @@ func (sys *IAMSys) PolicyDBGet(name string, isGroup bool) ([]string, error) {
|
|||||||
return nil, errInvalidArgument
|
return nil, errInvalidArgument
|
||||||
}
|
}
|
||||||
|
|
||||||
objectAPI := globalObjectAPI
|
objectAPI := newObjectLayerWithoutSafeModeFn()
|
||||||
if objectAPI == nil {
|
if objectAPI == nil {
|
||||||
return nil, errServerNotInitialized
|
return nil, errServerNotInitialized
|
||||||
}
|
}
|
||||||
|
@ -59,7 +59,7 @@ func (sys *LifecycleSys) Get(bucketName string) (lifecycle lifecycle.Lifecycle,
|
|||||||
if globalIsGateway {
|
if globalIsGateway {
|
||||||
// When gateway is enabled, no cached value
|
// When gateway is enabled, no cached value
|
||||||
// is used to validate life cycle policies.
|
// is used to validate life cycle policies.
|
||||||
objAPI := globalObjectAPI
|
objAPI := newObjectLayerWithoutSafeModeFn()
|
||||||
if objAPI == nil {
|
if objAPI == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -83,9 +83,9 @@ func (c *minioCollector) Collect(ch chan<- prometheus.Metric) {
|
|||||||
minioVersionInfo.WithLabelValues(Version, CommitID).Set(float64(1.0))
|
minioVersionInfo.WithLabelValues(Version, CommitID).Set(float64(1.0))
|
||||||
|
|
||||||
// Fetch disk space info
|
// Fetch disk space info
|
||||||
objLayer := globalObjectAPI
|
objLayer := newObjectLayerFn()
|
||||||
// Service not initialized yet
|
// Service not initialized yet
|
||||||
if objLayer == nil || globalSafeMode {
|
if objLayer == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -628,6 +628,20 @@ func (sys *NotificationSys) ListenBucketNotification(ctx context.Context, bucket
|
|||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AddNotificationTargetsFromConfig - adds notification targets from server config.
|
||||||
|
func (sys *NotificationSys) AddNotificationTargetsFromConfig(cfg config.Config) error {
|
||||||
|
targetList, err := notify.GetNotificationTargets(cfg, GlobalServiceDoneCh, globalRootCAs)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, target := range targetList.Targets() {
|
||||||
|
if err = sys.targetList.Add(target); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// AddRemoteTarget - adds event rules map, HTTP/PeerRPC client target to bucket name.
|
// AddRemoteTarget - adds event rules map, HTTP/PeerRPC client target to bucket name.
|
||||||
func (sys *NotificationSys) AddRemoteTarget(bucketName string, target event.Target, rulesMap event.RulesMap) error {
|
func (sys *NotificationSys) AddRemoteTarget(bucketName string, target event.Target, rulesMap event.RulesMap) error {
|
||||||
if err := sys.targetList.Add(target); err != nil {
|
if err := sys.targetList.Add(target); err != nil {
|
||||||
@ -684,7 +698,7 @@ func (sys *NotificationSys) initListeners(ctx context.Context, objAPI ObjectLaye
|
|||||||
|
|
||||||
// Construct path to listener.json for the given bucket.
|
// Construct path to listener.json for the given bucket.
|
||||||
configFile := path.Join(bucketConfigPrefix, bucketName, bucketListenerConfig)
|
configFile := path.Join(bucketConfigPrefix, bucketName, bucketListenerConfig)
|
||||||
transactionConfigFile := configFile + ".transaction"
|
transactionConfigFile := configFile + "/transaction.lock"
|
||||||
|
|
||||||
// As object layer's GetObject() and PutObject() take respective lock on minioMetaBucket
|
// As object layer's GetObject() and PutObject() take respective lock on minioMetaBucket
|
||||||
// and configFile, take a transaction lock to avoid data race between readConfig()
|
// and configFile, take a transaction lock to avoid data race between readConfig()
|
||||||
@ -1098,22 +1112,14 @@ func (sys *NotificationSys) NetworkInfo() []madmin.ServerNetworkHardwareInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewNotificationSys - creates new notification system object.
|
// NewNotificationSys - creates new notification system object.
|
||||||
func NewNotificationSys(cfg config.Config, endpoints EndpointList) (*NotificationSys, error) {
|
func NewNotificationSys(endpoints EndpointList) *NotificationSys {
|
||||||
targetList, err := notify.GetNotificationTargets(cfg, GlobalServiceDoneCh, globalRootCAs)
|
|
||||||
if err != nil {
|
|
||||||
return nil, config.Errorf("Unable to start notification sub system: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
remoteHosts := getRemoteHosts(endpoints)
|
|
||||||
remoteClients := getRestClients(remoteHosts)
|
|
||||||
|
|
||||||
// bucketRulesMap/bucketRemoteTargetRulesMap are initialized by NotificationSys.Init()
|
// bucketRulesMap/bucketRemoteTargetRulesMap are initialized by NotificationSys.Init()
|
||||||
return &NotificationSys{
|
return &NotificationSys{
|
||||||
targetList: targetList,
|
targetList: event.NewTargetList(),
|
||||||
bucketRulesMap: make(map[string]event.RulesMap),
|
bucketRulesMap: make(map[string]event.RulesMap),
|
||||||
bucketRemoteTargetRulesMap: make(map[string]map[event.TargetID]event.RulesMap),
|
bucketRemoteTargetRulesMap: make(map[string]map[event.TargetID]event.RulesMap),
|
||||||
peerClients: remoteClients,
|
peerClients: getRestClients(endpoints),
|
||||||
}, nil
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type eventArgs struct {
|
type eventArgs struct {
|
||||||
@ -1264,7 +1270,7 @@ func SaveListener(objAPI ObjectLayer, bucketName string, eventNames []event.Name
|
|||||||
|
|
||||||
// Construct path to listener.json for the given bucket.
|
// Construct path to listener.json for the given bucket.
|
||||||
configFile := path.Join(bucketConfigPrefix, bucketName, bucketListenerConfig)
|
configFile := path.Join(bucketConfigPrefix, bucketName, bucketListenerConfig)
|
||||||
transactionConfigFile := configFile + ".transaction"
|
transactionConfigFile := configFile + "/transaction.lock"
|
||||||
|
|
||||||
// As object layer's GetObject() and PutObject() take respective lock on minioMetaBucket
|
// As object layer's GetObject() and PutObject() take respective lock on minioMetaBucket
|
||||||
// and configFile, take a transaction lock to avoid data race between readConfig()
|
// and configFile, take a transaction lock to avoid data race between readConfig()
|
||||||
@ -1315,7 +1321,7 @@ func RemoveListener(objAPI ObjectLayer, bucketName string, targetID event.Target
|
|||||||
|
|
||||||
// Construct path to listener.json for the given bucket.
|
// Construct path to listener.json for the given bucket.
|
||||||
configFile := path.Join(bucketConfigPrefix, bucketName, bucketListenerConfig)
|
configFile := path.Join(bucketConfigPrefix, bucketName, bucketListenerConfig)
|
||||||
transactionConfigFile := configFile + ".transaction"
|
transactionConfigFile := configFile + "/transaction.lock"
|
||||||
|
|
||||||
// As object layer's GetObject() and PutObject() take respective lock on minioMetaBucket
|
// As object layer's GetObject() and PutObject() take respective lock on minioMetaBucket
|
||||||
// and configFile, take a transaction lock to avoid data race between readConfig()
|
// and configFile, take a transaction lock to avoid data race between readConfig()
|
||||||
|
@ -693,19 +693,24 @@ func getRemoteHosts(endpoints EndpointList) []*xnet.Host {
|
|||||||
var remoteHosts []*xnet.Host
|
var remoteHosts []*xnet.Host
|
||||||
for _, hostStr := range GetRemotePeers(endpoints) {
|
for _, hostStr := range GetRemotePeers(endpoints) {
|
||||||
host, err := xnet.ParseHost(hostStr)
|
host, err := xnet.ParseHost(hostStr)
|
||||||
logger.FatalIf(err, "Unable to parse peer Host")
|
if err != nil {
|
||||||
|
logger.LogIf(context.Background(), err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
remoteHosts = append(remoteHosts, host)
|
remoteHosts = append(remoteHosts, host)
|
||||||
}
|
}
|
||||||
|
|
||||||
return remoteHosts
|
return remoteHosts
|
||||||
}
|
}
|
||||||
|
|
||||||
func getRestClients(peerHosts []*xnet.Host) []*peerRESTClient {
|
func getRestClients(endpoints EndpointList) []*peerRESTClient {
|
||||||
|
peerHosts := getRemoteHosts(endpoints)
|
||||||
restClients := make([]*peerRESTClient, len(peerHosts))
|
restClients := make([]*peerRESTClient, len(peerHosts))
|
||||||
for i, host := range peerHosts {
|
for i, host := range peerHosts {
|
||||||
client, err := newPeerRESTClient(host)
|
client, err := newPeerRESTClient(host)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.LogIf(context.Background(), err)
|
logger.LogIf(context.Background(), err)
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
restClients[i] = client
|
restClients[i] = client
|
||||||
}
|
}
|
||||||
|
@ -42,12 +42,8 @@ type peerRESTServer struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func getServerInfo() (*ServerInfoData, error) {
|
func getServerInfo() (*ServerInfoData, error) {
|
||||||
if globalBootTime.IsZero() {
|
objLayer := newObjectLayerWithoutSafeModeFn()
|
||||||
return nil, errServerNotInitialized
|
if objLayer == nil {
|
||||||
}
|
|
||||||
|
|
||||||
objLayer := globalObjectAPI
|
|
||||||
if objLayer == nil || globalSafeMode {
|
|
||||||
return nil, errServerNotInitialized
|
return nil, errServerNotInitialized
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -166,7 +162,7 @@ func (s *peerRESTServer) DeletePolicyHandler(w http.ResponseWriter, r *http.Requ
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
objAPI := globalObjectAPI
|
objAPI := newObjectLayerWithoutSafeModeFn()
|
||||||
if objAPI == nil {
|
if objAPI == nil {
|
||||||
s.writeErrorResponse(w, errServerNotInitialized)
|
s.writeErrorResponse(w, errServerNotInitialized)
|
||||||
return
|
return
|
||||||
@ -194,7 +190,7 @@ func (s *peerRESTServer) LoadPolicyHandler(w http.ResponseWriter, r *http.Reques
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
objAPI := globalObjectAPI
|
objAPI := newObjectLayerWithoutSafeModeFn()
|
||||||
if objAPI == nil {
|
if objAPI == nil {
|
||||||
s.writeErrorResponse(w, errServerNotInitialized)
|
s.writeErrorResponse(w, errServerNotInitialized)
|
||||||
return
|
return
|
||||||
@ -222,7 +218,7 @@ func (s *peerRESTServer) LoadPolicyMappingHandler(w http.ResponseWriter, r *http
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
objAPI := globalObjectAPI
|
objAPI := newObjectLayerWithoutSafeModeFn()
|
||||||
if objAPI == nil {
|
if objAPI == nil {
|
||||||
s.writeErrorResponse(w, errServerNotInitialized)
|
s.writeErrorResponse(w, errServerNotInitialized)
|
||||||
return
|
return
|
||||||
@ -251,7 +247,7 @@ func (s *peerRESTServer) DeleteUserHandler(w http.ResponseWriter, r *http.Reques
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
objAPI := globalObjectAPI
|
objAPI := newObjectLayerWithoutSafeModeFn()
|
||||||
if objAPI == nil {
|
if objAPI == nil {
|
||||||
s.writeErrorResponse(w, errServerNotInitialized)
|
s.writeErrorResponse(w, errServerNotInitialized)
|
||||||
return
|
return
|
||||||
@ -279,7 +275,7 @@ func (s *peerRESTServer) LoadUserHandler(w http.ResponseWriter, r *http.Request)
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
objAPI := globalObjectAPI
|
objAPI := newObjectLayerWithoutSafeModeFn()
|
||||||
if objAPI == nil {
|
if objAPI == nil {
|
||||||
s.writeErrorResponse(w, errServerNotInitialized)
|
s.writeErrorResponse(w, errServerNotInitialized)
|
||||||
return
|
return
|
||||||
@ -329,7 +325,7 @@ func (s *peerRESTServer) LoadGroupHandler(w http.ResponseWriter, r *http.Request
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
objAPI := globalObjectAPI
|
objAPI := newObjectLayerWithoutSafeModeFn()
|
||||||
if objAPI == nil {
|
if objAPI == nil {
|
||||||
s.writeErrorResponse(w, errServerNotInitialized)
|
s.writeErrorResponse(w, errServerNotInitialized)
|
||||||
return
|
return
|
||||||
@ -539,7 +535,7 @@ func (s *peerRESTServer) ReloadFormatHandler(w http.ResponseWriter, r *http.Requ
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
objAPI := globalObjectAPI
|
objAPI := newObjectLayerWithoutSafeModeFn()
|
||||||
if objAPI == nil {
|
if objAPI == nil {
|
||||||
s.writeErrorResponse(w, errServerNotInitialized)
|
s.writeErrorResponse(w, errServerNotInitialized)
|
||||||
return
|
return
|
||||||
|
@ -69,7 +69,7 @@ func (sys *PolicySys) IsAllowed(args policy.Args) bool {
|
|||||||
if globalIsGateway {
|
if globalIsGateway {
|
||||||
// When gateway is enabled, no cached value
|
// When gateway is enabled, no cached value
|
||||||
// is used to validate bucket policies.
|
// is used to validate bucket policies.
|
||||||
objAPI := globalObjectAPI
|
objAPI := newObjectLayerFn()
|
||||||
if objAPI != nil {
|
if objAPI != nil {
|
||||||
config, err := objAPI.GetBucketPolicy(context.Background(), args.BucketName)
|
config, err := objAPI.GetBucketPolicy(context.Background(), args.BucketName)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
@ -180,53 +180,99 @@ func serverHandleEnvVars() {
|
|||||||
handleCommonEnvVars()
|
handleCommonEnvVars()
|
||||||
}
|
}
|
||||||
|
|
||||||
func initAllSubsystems(newObject ObjectLayer) {
|
func newAllSubsystems() {
|
||||||
|
// Create new notification system and initialize notification targets
|
||||||
|
globalNotificationSys = NewNotificationSys(globalEndpoints)
|
||||||
|
|
||||||
// Create a new config system.
|
// Create a new config system.
|
||||||
globalConfigSys = NewConfigSys()
|
globalConfigSys = NewConfigSys()
|
||||||
|
|
||||||
// Initialize config system.
|
|
||||||
if err := globalConfigSys.Init(newObject); err != nil {
|
|
||||||
logger.Fatal(err, "Unable to initialize config system")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create new IAM system.
|
// Create new IAM system.
|
||||||
globalIAMSys = NewIAMSys()
|
globalIAMSys = NewIAMSys()
|
||||||
|
|
||||||
if err := globalIAMSys.Init(newObject); err != nil {
|
// Create new policy system.
|
||||||
logger.Fatal(err, "Unable to initialize IAM system")
|
globalPolicySys = NewPolicySys()
|
||||||
|
|
||||||
|
// Create new lifecycle system.
|
||||||
|
globalLifecycleSys = NewLifecycleSys()
|
||||||
}
|
}
|
||||||
|
|
||||||
buckets, err := newObject.ListBuckets(context.Background())
|
func initSafeModeInit(buckets []BucketInfo) (err error) {
|
||||||
|
defer func() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Fatal(err, "Unable to list buckets")
|
// Enable logger
|
||||||
|
logger.Disable = false
|
||||||
|
|
||||||
|
// Prints the formatted startup message in safe mode operation.
|
||||||
|
printStartupSafeModeMessage(getAPIEndpoints(), err)
|
||||||
|
|
||||||
|
// Initialization returned error reaching safe mode and
|
||||||
|
// not proceeding waiting for admin action.
|
||||||
|
handleSignals()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
newObject := newObjectLayerWithoutSafeModeFn()
|
||||||
|
|
||||||
|
// Calls New() for all sub-systems.
|
||||||
|
newAllSubsystems()
|
||||||
|
|
||||||
|
// Migrate all backend configs to encrypted backend, also handles rotation as well.
|
||||||
|
if err = handleEncryptedConfigBackend(newObject, true); err != nil {
|
||||||
|
return fmt.Errorf("Unable to handle encrypted backend for config, iam and policies: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create new notification system and initialize notification targets
|
// **** WARNING ****
|
||||||
globalNotificationSys, err = NewNotificationSys(globalServerConfig, globalEndpoints)
|
// Migrating to encrypted backend should happen before initialization of any
|
||||||
if err != nil {
|
// sub-systems, make sure that we do not move the above codeblock elsewhere.
|
||||||
logger.Fatal(err, "Unable to initialize notification targets")
|
|
||||||
|
// Validate and initialize all subsystems.
|
||||||
|
if err = initAllSubsystems(buckets, newObject); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if globalEtcdClient != nil {
|
||||||
|
// **** WARNING ****
|
||||||
|
// Migrating to encrypted backend on etcd should happen before initialization of
|
||||||
|
// IAM sub-systems, make sure that we do not move the above codeblock elsewhere.
|
||||||
|
if err = migrateIAMConfigsEtcdToEncrypted(globalEtcdClient); err != nil {
|
||||||
|
return fmt.Errorf("Unable to handle encrypted backend for iam and policies: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func initAllSubsystems(buckets []BucketInfo, newObject ObjectLayer) (err error) {
|
||||||
|
// Initialize config system.
|
||||||
|
if err = globalConfigSys.Init(newObject); err != nil {
|
||||||
|
return fmt.Errorf("Unable to initialize config system: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = globalNotificationSys.AddNotificationTargetsFromConfig(globalServerConfig); err != nil {
|
||||||
|
return fmt.Errorf("Unable to initialize notification target(s) from config: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = globalIAMSys.Init(newObject); err != nil {
|
||||||
|
return fmt.Errorf("Unable to initialize IAM system: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize notification system.
|
// Initialize notification system.
|
||||||
if err = globalNotificationSys.Init(buckets, newObject); err != nil {
|
if err = globalNotificationSys.Init(buckets, newObject); err != nil {
|
||||||
logger.Fatal(err, "Unable to initialize notification system")
|
return fmt.Errorf("Unable to initialize notification system: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create new policy system.
|
|
||||||
globalPolicySys = NewPolicySys()
|
|
||||||
|
|
||||||
// Initialize policy system.
|
// Initialize policy system.
|
||||||
if err = globalPolicySys.Init(buckets, newObject); err != nil {
|
if err = globalPolicySys.Init(buckets, newObject); err != nil {
|
||||||
logger.Fatal(err, "Unable to initialize policy system")
|
return fmt.Errorf("Unable to initialize policy system; %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create new lifecycle system.
|
|
||||||
globalLifecycleSys = NewLifecycleSys()
|
|
||||||
|
|
||||||
// Initialize lifecycle system.
|
// Initialize lifecycle system.
|
||||||
if err = globalLifecycleSys.Init(buckets, newObject); err != nil {
|
if err = globalLifecycleSys.Init(buckets, newObject); err != nil {
|
||||||
logger.Fatal(err, "Unable to initialize lifecycle system")
|
return fmt.Errorf("Unable to initialize lifecycle system: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// serverMain handler called for 'minio server' command.
|
// serverMain handler called for 'minio server' command.
|
||||||
@ -327,42 +373,33 @@ func serverMain(ctx *cli.Context) {
|
|||||||
globalTLSCerts.Stop()
|
globalTLSCerts.Stop()
|
||||||
|
|
||||||
globalHTTPServer.Shutdown()
|
globalHTTPServer.Shutdown()
|
||||||
logger.FatalIf(err, "Unable to initialize backend")
|
logger.Fatal(err, "Unable to initialize backend")
|
||||||
}
|
|
||||||
|
|
||||||
// Populate existing buckets to the etcd backend
|
|
||||||
if globalDNSConfig != nil {
|
|
||||||
initFederatorBackend(newObject)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Re-enable logging
|
// Re-enable logging
|
||||||
logger.Disable = false
|
logger.Disable = false
|
||||||
|
|
||||||
// Migrate all backend configs to encrypted backend, also handles rotation as well.
|
// Once endpoints are finalized, initialize the new object api in safe mode.
|
||||||
logger.FatalIf(handleEncryptedConfigBackend(newObject, true),
|
globalObjLayerMutex.Lock()
|
||||||
"Unable to handle encrypted backend for config, iam and policies")
|
globalSafeMode = true
|
||||||
|
globalObjectAPI = newObject
|
||||||
|
globalObjLayerMutex.Unlock()
|
||||||
|
|
||||||
// **** WARNING ****
|
buckets, err := newObject.ListBuckets(context.Background())
|
||||||
// Migrating to encrypted backend should happen before initialization of any
|
if err != nil {
|
||||||
// sub-systems, make sure that we do not move the above codeblock elsewhere.
|
logger.Fatal(err, "Unable to list buckets")
|
||||||
|
|
||||||
// Validate and initialize all subsystems.
|
|
||||||
initAllSubsystems(newObject)
|
|
||||||
|
|
||||||
if globalEtcdClient != nil {
|
|
||||||
// **** WARNING ****
|
|
||||||
// Migrating to encrypted backend on etcd should happen before initialization of
|
|
||||||
// IAM sub-systems, make sure that we do not move the above codeblock elsewhere.
|
|
||||||
logger.FatalIf(migrateIAMConfigsEtcdToEncrypted(globalEtcdClient),
|
|
||||||
"Unable to handle encrypted backend for iam and policies")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if globalCacheConfig.Enabled {
|
// Populate existing buckets to the etcd backend
|
||||||
logger.StartupMessage(color.Red(color.Bold("Disk caching is recommended only for gateway deployments")))
|
if globalDNSConfig != nil {
|
||||||
|
initFederatorBackend(buckets, newObject)
|
||||||
|
}
|
||||||
|
|
||||||
// initialize the new disk cache objects.
|
initSafeModeInit(buckets)
|
||||||
globalCacheObjectAPI, err = newServerCacheObjects(context.Background(), globalCacheConfig)
|
|
||||||
logger.FatalIf(err, "Unable to initialize disk caching")
|
if globalCacheConfig.Enabled {
|
||||||
|
msg := color.RedBold("Disk caching is disabled in 'server' mode, 'caching' is only supported in gateway deployments")
|
||||||
|
logger.StartupMessage(msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
initDailyLifecycle()
|
initDailyLifecycle()
|
||||||
@ -373,14 +410,17 @@ func serverMain(ctx *cli.Context) {
|
|||||||
initGlobalHeal()
|
initGlobalHeal()
|
||||||
}
|
}
|
||||||
|
|
||||||
globalObjectAPI = newObject
|
// Disable safe mode operation, after all initialization is over.
|
||||||
|
globalObjLayerMutex.Lock()
|
||||||
|
globalSafeMode = false
|
||||||
|
globalObjLayerMutex.Unlock()
|
||||||
|
|
||||||
// Prints the formatted startup message once object layer is initialized.
|
// Prints the formatted startup message once object layer is initialized.
|
||||||
printStartupMessage(getAPIEndpoints())
|
printStartupMessage(getAPIEndpoints())
|
||||||
|
|
||||||
if globalActiveCred.Equal(auth.DefaultCredentials) {
|
if globalActiveCred.Equal(auth.DefaultCredentials) {
|
||||||
msg := fmt.Sprintf("Detected default credentials '%s', please change the credentials immediately using 'MINIO_ACCESS_KEY' and 'MINIO_SECRET_KEY'", globalActiveCred)
|
msg := fmt.Sprintf("Detected default credentials '%s', please change the credentials immediately using 'MINIO_ACCESS_KEY' and 'MINIO_SECRET_KEY'", globalActiveCred)
|
||||||
logger.StartupMessage(color.Red(color.Bold(msg)))
|
logger.StartupMessage(color.RedBold(msg))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set uptime time after object layer has initialized.
|
// Set uptime time after object layer has initialized.
|
||||||
|
@ -32,6 +32,7 @@ import (
|
|||||||
// Documentation links, these are part of message printing code.
|
// Documentation links, these are part of message printing code.
|
||||||
const (
|
const (
|
||||||
mcQuickStartGuide = "https://docs.min.io/docs/minio-client-quickstart-guide"
|
mcQuickStartGuide = "https://docs.min.io/docs/minio-client-quickstart-guide"
|
||||||
|
mcAdminQuickStartGuide = "https://docs.min.io/docs/minio-admin-complete-guide.html"
|
||||||
goQuickStartGuide = "https://docs.min.io/docs/golang-client-quickstart-guide"
|
goQuickStartGuide = "https://docs.min.io/docs/golang-client-quickstart-guide"
|
||||||
jsQuickStartGuide = "https://docs.min.io/docs/javascript-client-quickstart-guide"
|
jsQuickStartGuide = "https://docs.min.io/docs/javascript-client-quickstart-guide"
|
||||||
javaQuickStartGuide = "https://docs.min.io/docs/java-client-quickstart-guide"
|
javaQuickStartGuide = "https://docs.min.io/docs/java-client-quickstart-guide"
|
||||||
@ -45,16 +46,75 @@ func getFormatStr(strLen int, padding int) string {
|
|||||||
return "%" + formatStr
|
return "%" + formatStr
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prints the formatted startup message.
|
func printStartupSafeModeMessage(apiEndpoints []string, err error) {
|
||||||
func printStartupMessage(apiEndPoints []string) {
|
logStartupMessage(color.RedBold("Server startup failed with '%v'", err))
|
||||||
|
logStartupMessage(color.RedBold("Server switching to safe mode"))
|
||||||
|
logStartupMessage(color.RedBold("Please use 'mc admin' commands to fix this issue"))
|
||||||
|
|
||||||
strippedAPIEndpoints := stripStandardPorts(apiEndPoints)
|
// Object layer is initialized then print StorageInfo in safe mode.
|
||||||
// If cache layer is enabled, print cache capacity.
|
objAPI := newObjectLayerWithoutSafeModeFn()
|
||||||
if globalCacheObjectAPI != nil {
|
if objAPI != nil {
|
||||||
printCacheStorageInfo(globalCacheObjectAPI.StorageInfo(context.Background()))
|
if msg := getStorageInfoMsgSafeMode(objAPI.StorageInfo(context.Background())); msg != "" {
|
||||||
|
logStartupMessage(msg)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get saved credentials.
|
||||||
|
cred := globalActiveCred
|
||||||
|
|
||||||
|
// Get saved region.
|
||||||
|
region := globalServerRegion
|
||||||
|
|
||||||
|
strippedAPIEndpoints := stripStandardPorts(apiEndpoints)
|
||||||
|
|
||||||
|
apiEndpointStr := strings.Join(strippedAPIEndpoints, " ")
|
||||||
|
|
||||||
|
// Colorize the message and print.
|
||||||
|
logStartupMessage(color.Red("Endpoint: ") + color.Bold(fmt.Sprintf(getFormatStr(len(apiEndpointStr), 1), apiEndpointStr)))
|
||||||
|
if color.IsTerminal() && !globalCLIContext.Anonymous {
|
||||||
|
logStartupMessage(color.Red("AccessKey: ") + color.Bold(fmt.Sprintf("%s ", cred.AccessKey)))
|
||||||
|
logStartupMessage(color.Red("SecretKey: ") + color.Bold(fmt.Sprintf("%s ", cred.SecretKey)))
|
||||||
|
if region != "" {
|
||||||
|
logStartupMessage(color.Red("Region: ") + color.Bold(fmt.Sprintf(getFormatStr(len(region), 3), region)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prints `mc` cli configuration message chooses
|
||||||
|
// first endpoint as default.
|
||||||
|
alias := "myminio"
|
||||||
|
endPoint := strippedAPIEndpoints[0]
|
||||||
|
|
||||||
|
// Configure 'mc', following block prints platform specific information for minio client admin commands.
|
||||||
|
if color.IsTerminal() {
|
||||||
|
logStartupMessage(color.RedBold("\nCommand-line Access: ") + mcAdminQuickStartGuide)
|
||||||
|
if runtime.GOOS == globalWindowsOSName {
|
||||||
|
mcMessage := fmt.Sprintf("> mc.exe config host add %s %s %s %s --api s3v4", alias,
|
||||||
|
endPoint, cred.AccessKey, cred.SecretKey)
|
||||||
|
logStartupMessage(fmt.Sprintf(getFormatStr(len(mcMessage), 3), mcMessage))
|
||||||
|
mcMessage = fmt.Sprintf("> mc.exe admin --help")
|
||||||
|
logStartupMessage(fmt.Sprintf(getFormatStr(len(mcMessage), 3), mcMessage))
|
||||||
|
} else {
|
||||||
|
mcMessage := fmt.Sprintf("$ mc config host add %s %s %s %s --api s3v4", alias,
|
||||||
|
endPoint, cred.AccessKey, cred.SecretKey)
|
||||||
|
logStartupMessage(fmt.Sprintf(getFormatStr(len(mcMessage), 3), mcMessage))
|
||||||
|
mcMessage = fmt.Sprintf("$ mc admin --help")
|
||||||
|
logStartupMessage(fmt.Sprintf(getFormatStr(len(mcMessage), 3), mcMessage))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prints the formatted startup message.
|
||||||
|
func printStartupMessage(apiEndpoints []string) {
|
||||||
|
|
||||||
|
strippedAPIEndpoints := stripStandardPorts(apiEndpoints)
|
||||||
|
// If cache layer is enabled, print cache capacity.
|
||||||
|
cachedObjAPI := newCachedObjectLayerFn()
|
||||||
|
if cachedObjAPI != nil {
|
||||||
|
printCacheStorageInfo(cachedObjAPI.StorageInfo(context.Background()))
|
||||||
|
}
|
||||||
|
|
||||||
// Object layer is initialized then print StorageInfo.
|
// Object layer is initialized then print StorageInfo.
|
||||||
objAPI := globalObjectAPI
|
objAPI := newObjectLayerFn()
|
||||||
if objAPI != nil {
|
if objAPI != nil {
|
||||||
printStorageInfo(objAPI.StorageInfo(context.Background()))
|
printStorageInfo(objAPI.StorageInfo(context.Background()))
|
||||||
}
|
}
|
||||||
@ -183,6 +243,16 @@ func printObjectAPIMsg() {
|
|||||||
logStartupMessage(color.Blue(" .NET: ") + fmt.Sprintf(getFormatStr(len(dotnetQuickStartGuide), 6), dotnetQuickStartGuide))
|
logStartupMessage(color.Blue(" .NET: ") + fmt.Sprintf(getFormatStr(len(dotnetQuickStartGuide), 6), dotnetQuickStartGuide))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get formatted disk/storage info message.
|
||||||
|
func getStorageInfoMsgSafeMode(storageInfo StorageInfo) string {
|
||||||
|
var msg string
|
||||||
|
if storageInfo.Backend.Type == BackendErasure {
|
||||||
|
diskInfo := fmt.Sprintf(" %d Online, %d Offline. ", storageInfo.Backend.OnlineDisks.Sum(), storageInfo.Backend.OfflineDisks.Sum())
|
||||||
|
msg += color.Red("Status:") + fmt.Sprintf(getFormatStr(len(diskInfo), 8), diskInfo)
|
||||||
|
}
|
||||||
|
return msg
|
||||||
|
}
|
||||||
|
|
||||||
// Get formatted disk/storage info message.
|
// Get formatted disk/storage info message.
|
||||||
func getStorageInfoMsg(storageInfo StorageInfo) string {
|
func getStorageInfoMsg(storageInfo StorageInfo) string {
|
||||||
var msg string
|
var msg string
|
||||||
|
@ -55,7 +55,7 @@ func handleSignals() {
|
|||||||
// send signal to various go-routines that they need to quit.
|
// send signal to various go-routines that they need to quit.
|
||||||
close(GlobalServiceDoneCh)
|
close(GlobalServiceDoneCh)
|
||||||
|
|
||||||
if objAPI := globalObjectAPI; objAPI != nil {
|
if objAPI := newObjectLayerWithoutSafeModeFn(); objAPI != nil {
|
||||||
oerr = objAPI.Shutdown(context.Background())
|
oerr = objAPI.Shutdown(context.Background())
|
||||||
logger.LogIf(context.Background(), oerr)
|
logger.LogIf(context.Background(), oerr)
|
||||||
}
|
}
|
||||||
@ -66,7 +66,7 @@ func handleSignals() {
|
|||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case err := <-globalHTTPServerErrorCh:
|
case err := <-globalHTTPServerErrorCh:
|
||||||
if objAPI := globalObjectAPI; objAPI != nil {
|
if objAPI := newObjectLayerWithoutSafeModeFn(); objAPI != nil {
|
||||||
objAPI.Shutdown(context.Background())
|
objAPI.Shutdown(context.Background())
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -370,11 +370,7 @@ func UnstartedTestServer(t TestErrHandler, instanceType string) TestServer {
|
|||||||
globalPolicySys = NewPolicySys()
|
globalPolicySys = NewPolicySys()
|
||||||
globalPolicySys.Init(buckets, objLayer)
|
globalPolicySys.Init(buckets, objLayer)
|
||||||
|
|
||||||
globalNotificationSys, err = NewNotificationSys(globalServerConfig, testServer.Disks)
|
globalNotificationSys = NewNotificationSys(testServer.Disks)
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Unable to initialize notification system %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
globalNotificationSys.Init(buckets, objLayer)
|
globalNotificationSys.Init(buckets, objLayer)
|
||||||
|
|
||||||
globalLifecycleSys = NewLifecycleSys()
|
globalLifecycleSys = NewLifecycleSys()
|
||||||
@ -1640,10 +1636,10 @@ func newTestObjectLayer(endpoints EndpointList) (newObject ObjectLayer, err erro
|
|||||||
globalIAMSys.Init(xl)
|
globalIAMSys.Init(xl)
|
||||||
|
|
||||||
globalPolicySys = NewPolicySys()
|
globalPolicySys = NewPolicySys()
|
||||||
globalNotificationSys, err = NewNotificationSys(globalServerConfig, endpoints)
|
globalPolicySys.Init(nil, xl)
|
||||||
if err != nil {
|
|
||||||
return xl, err
|
globalNotificationSys = NewNotificationSys(endpoints)
|
||||||
}
|
globalNotificationSys.Init(nil, xl)
|
||||||
|
|
||||||
return xl, nil
|
return xl, nil
|
||||||
}
|
}
|
||||||
|
@ -62,18 +62,8 @@ const specialAssets = "index_bundle.*.js|loader.css|logo.svg|firefox.png|safari.
|
|||||||
func registerWebRouter(router *mux.Router) error {
|
func registerWebRouter(router *mux.Router) error {
|
||||||
// Initialize Web.
|
// Initialize Web.
|
||||||
web := &webAPIHandlers{
|
web := &webAPIHandlers{
|
||||||
ObjectAPI: func() ObjectLayer {
|
ObjectAPI: newObjectLayerFn,
|
||||||
if !globalSafeMode {
|
CacheAPI: newCachedObjectLayerFn,
|
||||||
return globalObjectAPI
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
CacheAPI: func() CacheObjectLayer {
|
|
||||||
if !globalSafeMode {
|
|
||||||
return globalCacheObjectAPI
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize a new json2 codec.
|
// Initialize a new json2 codec.
|
||||||
|
@ -839,12 +839,12 @@ func (xl xlObjects) AbortMultipartUpload(ctx context.Context, bucket, object, up
|
|||||||
// get Quorum for this object
|
// get Quorum for this object
|
||||||
_, writeQuorum, err := objectQuorumFromMeta(ctx, xl, partsMetadata, errs)
|
_, writeQuorum, err := objectQuorumFromMeta(ctx, xl, partsMetadata, errs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return toObjectErr(err, bucket, object)
|
return toObjectErr(err, bucket, object, uploadID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cleanup all uploaded parts.
|
// Cleanup all uploaded parts.
|
||||||
if err = xl.deleteObject(ctx, minioMetaMultipartBucket, uploadIDPath, writeQuorum, false); err != nil {
|
if err = xl.deleteObject(ctx, minioMetaMultipartBucket, uploadIDPath, writeQuorum, false); err != nil {
|
||||||
return toObjectErr(err, bucket, object)
|
return toObjectErr(err, bucket, object, uploadID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Successfully purged.
|
// Successfully purged.
|
||||||
|
@ -18,11 +18,11 @@ package cmd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"hash/crc32"
|
"hash/crc32"
|
||||||
"path"
|
"path"
|
||||||
|
|
||||||
jsoniter "github.com/json-iterator/go"
|
|
||||||
"github.com/minio/minio/cmd/logger"
|
"github.com/minio/minio/cmd/logger"
|
||||||
"github.com/minio/minio/pkg/sync/errgroup"
|
"github.com/minio/minio/pkg/sync/errgroup"
|
||||||
)
|
)
|
||||||
@ -117,7 +117,6 @@ func hashOrder(key string, cardinality int) []int {
|
|||||||
|
|
||||||
// Constructs xlMetaV1 using `jsoniter` lib.
|
// Constructs xlMetaV1 using `jsoniter` lib.
|
||||||
func xlMetaV1UnmarshalJSON(ctx context.Context, xlMetaBuf []byte) (xlMeta xlMetaV1, err error) {
|
func xlMetaV1UnmarshalJSON(ctx context.Context, xlMetaBuf []byte) (xlMeta xlMetaV1, err error) {
|
||||||
var json = jsoniter.ConfigCompatibleWithStandardLibrary
|
|
||||||
err = json.Unmarshal(xlMetaBuf, &xlMeta)
|
err = json.Unmarshal(xlMetaBuf, &xlMeta)
|
||||||
return xlMeta, err
|
return xlMeta, err
|
||||||
}
|
}
|
||||||
|
@ -54,7 +54,9 @@ export MINIO_SECRET_KEY_OLD=minio123
|
|||||||
minio server /data
|
minio server /data
|
||||||
```
|
```
|
||||||
|
|
||||||
Once the migration is complete and server has started successfully remove `MINIO_ACCESS_KEY_OLD` and `MINIO_SECRET_KEY_OLD` environment variables, restart the server.
|
Once the migration is complete, server will automatically unset the `MINIO_ACCESS_KEY_OLD` and `MINIO_SECRET_KEY_OLD` with in the process namespace.
|
||||||
|
|
||||||
|
> **NOTE: Make sure to remove `MINIO_ACCESS_KEY_OLD` and `MINIO_SECRET_KEY_OLD` in scripts or service files before next service restarts of the server to avoid double encryption of your existing contents.**
|
||||||
|
|
||||||
#### Region
|
#### Region
|
||||||
| Field | Type | Description |
|
| Field | Type | Description |
|
||||||
|
@ -37,6 +37,13 @@ var (
|
|||||||
return fmt.Sprint
|
return fmt.Sprint
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
RedBold = func() func(format string, a ...interface{}) string {
|
||||||
|
if IsTerminal() {
|
||||||
|
return color.New(color.FgRed, color.Bold).SprintfFunc()
|
||||||
|
}
|
||||||
|
return fmt.Sprintf
|
||||||
|
}()
|
||||||
|
|
||||||
Red = func() func(format string, a ...interface{}) string {
|
Red = func() func(format string, a ...interface{}) string {
|
||||||
if IsTerminal() {
|
if IsTerminal() {
|
||||||
return color.New(color.FgRed).SprintfFunc()
|
return color.New(color.FgRed).SprintfFunc()
|
||||||
|
@ -36,15 +36,17 @@ type TargetList struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add - adds unique target to target list.
|
// Add - adds unique target to target list.
|
||||||
func (list *TargetList) Add(target Target) error {
|
func (list *TargetList) Add(targets ...Target) error {
|
||||||
list.Lock()
|
list.Lock()
|
||||||
defer list.Unlock()
|
defer list.Unlock()
|
||||||
|
|
||||||
|
for _, target := range targets {
|
||||||
if _, ok := list.targets[target.ID()]; ok {
|
if _, ok := list.targets[target.ID()]; ok {
|
||||||
return fmt.Errorf("target %v already exists", target.ID())
|
return fmt.Errorf("target %v already exists", target.ID())
|
||||||
}
|
}
|
||||||
|
|
||||||
list.targets[target.ID()] = target
|
list.targets[target.ID()] = target
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -102,6 +104,19 @@ func (list *TargetList) Remove(targetids ...TargetID) <-chan TargetIDErr {
|
|||||||
return errCh
|
return errCh
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Targets - list all targets
|
||||||
|
func (list *TargetList) Targets() []Target {
|
||||||
|
list.RLock()
|
||||||
|
defer list.RUnlock()
|
||||||
|
|
||||||
|
targets := []Target{}
|
||||||
|
for _, tgt := range list.targets {
|
||||||
|
targets = append(targets, tgt)
|
||||||
|
}
|
||||||
|
|
||||||
|
return targets
|
||||||
|
}
|
||||||
|
|
||||||
// List - returns available target IDs.
|
// List - returns available target IDs.
|
||||||
func (list *TargetList) List() []TargetID {
|
func (list *TargetList) List() []TargetID {
|
||||||
list.RLock()
|
list.RLock()
|
||||||
|
Loading…
Reference in New Issue
Block a user