storj.io/minio@v0.0.0-20230509071714-0cbc90f649b1/cmd/admin-handlers-config-kv.go (about)

     1  /*
     2   * MinIO Cloud Storage, (C) 2019 MinIO, Inc.
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *     http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  package cmd
    18  
    19  import (
    20  	"bytes"
    21  	"context"
    22  	"encoding/json"
    23  	"io"
    24  	"net/http"
    25  	"strconv"
    26  	"strings"
    27  
    28  	"github.com/gorilla/mux"
    29  
    30  	"storj.io/minio/cmd/config"
    31  	"storj.io/minio/cmd/config/cache"
    32  	xldap "storj.io/minio/cmd/config/identity/ldap"
    33  	"storj.io/minio/cmd/config/identity/openid"
    34  	"storj.io/minio/cmd/config/policy/opa"
    35  	"storj.io/minio/cmd/config/storageclass"
    36  	"storj.io/minio/cmd/logger"
    37  	"storj.io/minio/pkg/auth"
    38  	iampolicy "storj.io/minio/pkg/iam/policy"
    39  	"storj.io/minio/pkg/madmin"
    40  )
    41  
    42  func validateAdminReqConfigKV(ctx context.Context, w http.ResponseWriter, r *http.Request) (auth.Credentials, ObjectLayer) {
    43  	// Get current object layer instance.
    44  	objectAPI := newObjectLayerFn()
    45  	if objectAPI == nil {
    46  		writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL)
    47  		return auth.Credentials{}, nil
    48  	}
    49  
    50  	// Validate request signature.
    51  	cred, adminAPIErr := checkAdminRequestAuth(ctx, r, iampolicy.ConfigUpdateAdminAction, "")
    52  	if adminAPIErr != ErrNone {
    53  		writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(adminAPIErr), r.URL)
    54  		return cred, nil
    55  	}
    56  
    57  	return cred, objectAPI
    58  }
    59  
    60  // DelConfigKVHandler - DELETE /minio/admin/v3/del-config-kv
    61  func (a adminAPIHandlers) DelConfigKVHandler(w http.ResponseWriter, r *http.Request) {
    62  	ctx := NewContext(r, w, "DeleteConfigKV")
    63  
    64  	defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r))
    65  
    66  	cred, objectAPI := validateAdminReqConfigKV(ctx, w, r)
    67  	if objectAPI == nil {
    68  		return
    69  	}
    70  
    71  	if r.ContentLength > maxEConfigJSONSize || r.ContentLength == -1 {
    72  		// More than maxConfigSize bytes were available
    73  		writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAdminConfigTooLarge), r.URL)
    74  		return
    75  	}
    76  
    77  	password := cred.SecretKey
    78  	kvBytes, err := madmin.DecryptData(password, io.LimitReader(r.Body, r.ContentLength))
    79  	if err != nil {
    80  		logger.LogIf(ctx, err, logger.Application)
    81  		writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAdminConfigBadJSON), r.URL)
    82  		return
    83  	}
    84  
    85  	cfg, err := readServerConfig(ctx, objectAPI)
    86  	if err != nil {
    87  		writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
    88  		return
    89  	}
    90  
    91  	if err = cfg.DelFrom(bytes.NewReader(kvBytes)); err != nil {
    92  		writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
    93  		return
    94  	}
    95  
    96  	if err = saveServerConfig(ctx, objectAPI, cfg); err != nil {
    97  		writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
    98  		return
    99  	}
   100  }
   101  
   102  // SetConfigKVHandler - PUT /minio/admin/v3/set-config-kv
   103  func (a adminAPIHandlers) SetConfigKVHandler(w http.ResponseWriter, r *http.Request) {
   104  	ctx := NewContext(r, w, "SetConfigKV")
   105  
   106  	defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r))
   107  
   108  	cred, objectAPI := validateAdminReqConfigKV(ctx, w, r)
   109  	if objectAPI == nil {
   110  		return
   111  	}
   112  
   113  	if r.ContentLength > maxEConfigJSONSize || r.ContentLength == -1 {
   114  		// More than maxConfigSize bytes were available
   115  		writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAdminConfigTooLarge), r.URL)
   116  		return
   117  	}
   118  
   119  	password := cred.SecretKey
   120  	kvBytes, err := madmin.DecryptData(password, io.LimitReader(r.Body, r.ContentLength))
   121  	if err != nil {
   122  		logger.LogIf(ctx, err, logger.Application)
   123  		writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAdminConfigBadJSON), r.URL)
   124  		return
   125  	}
   126  
   127  	cfg, err := readServerConfig(ctx, objectAPI)
   128  	if err != nil {
   129  		writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
   130  		return
   131  	}
   132  
   133  	dynamic, err := cfg.ReadConfig(bytes.NewReader(kvBytes))
   134  	if err != nil {
   135  		writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
   136  		return
   137  	}
   138  
   139  	if err = validateConfig(cfg, objectAPI.SetDriveCounts()); err != nil {
   140  		writeCustomErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAdminConfigBadJSON), err.Error(), r.URL)
   141  		return
   142  	}
   143  
   144  	// Update the actual server config on disk.
   145  	if err = saveServerConfig(ctx, objectAPI, cfg); err != nil {
   146  		writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
   147  		return
   148  	}
   149  
   150  	// Write to the config input KV to history.
   151  	if err = saveServerConfigHistory(ctx, objectAPI, kvBytes); err != nil {
   152  		writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
   153  		return
   154  	}
   155  
   156  	if dynamic {
   157  		// Apply dynamic values.
   158  		if err := applyDynamicConfig(GlobalContext, objectAPI, cfg); err != nil {
   159  			writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
   160  			return
   161  		}
   162  		GlobalNotificationSys.SignalService(serviceReloadDynamic)
   163  		// If all values were dynamic, tell the client.
   164  		w.Header().Set(madmin.ConfigAppliedHeader, madmin.ConfigAppliedTrue)
   165  	}
   166  	writeSuccessResponseHeadersOnly(w)
   167  }
   168  
   169  // GetConfigKVHandler - GET /minio/admin/v3/get-config-kv?key={key}
   170  func (a adminAPIHandlers) GetConfigKVHandler(w http.ResponseWriter, r *http.Request) {
   171  	ctx := NewContext(r, w, "GetConfigKV")
   172  
   173  	defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r))
   174  
   175  	cred, objectAPI := validateAdminReqConfigKV(ctx, w, r)
   176  	if objectAPI == nil {
   177  		return
   178  	}
   179  
   180  	cfg := globalServerConfig
   181  	if newObjectLayerFn() == nil {
   182  		var err error
   183  		cfg, err = getValidConfig(objectAPI)
   184  		if err != nil {
   185  			writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
   186  			return
   187  		}
   188  	}
   189  
   190  	vars := mux.Vars(r)
   191  	var buf = &bytes.Buffer{}
   192  	cw := config.NewConfigWriteTo(cfg, vars["key"])
   193  	if _, err := cw.WriteTo(buf); err != nil {
   194  		writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
   195  		return
   196  	}
   197  
   198  	password := cred.SecretKey
   199  	econfigData, err := madmin.EncryptData(password, buf.Bytes())
   200  	if err != nil {
   201  		writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
   202  		return
   203  	}
   204  
   205  	writeSuccessResponseJSON(w, econfigData)
   206  }
   207  
   208  func (a adminAPIHandlers) ClearConfigHistoryKVHandler(w http.ResponseWriter, r *http.Request) {
   209  	ctx := NewContext(r, w, "ClearConfigHistoryKV")
   210  
   211  	defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r))
   212  
   213  	_, objectAPI := validateAdminReqConfigKV(ctx, w, r)
   214  	if objectAPI == nil {
   215  		return
   216  	}
   217  
   218  	vars := mux.Vars(r)
   219  	restoreID := vars["restoreId"]
   220  	if restoreID == "" {
   221  		writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrInvalidRequest), r.URL)
   222  		return
   223  	}
   224  	if restoreID == "all" {
   225  		chEntries, err := listServerConfigHistory(ctx, objectAPI, false, -1)
   226  		if err != nil {
   227  			writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
   228  			return
   229  		}
   230  		for _, chEntry := range chEntries {
   231  			if err = delServerConfigHistory(ctx, objectAPI, chEntry.RestoreID); err != nil {
   232  				writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
   233  				return
   234  			}
   235  		}
   236  	} else {
   237  		if err := delServerConfigHistory(ctx, objectAPI, restoreID); err != nil {
   238  			writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
   239  			return
   240  		}
   241  	}
   242  }
   243  
   244  // RestoreConfigHistoryKVHandler - restores a config with KV settings for the given KV id.
   245  func (a adminAPIHandlers) RestoreConfigHistoryKVHandler(w http.ResponseWriter, r *http.Request) {
   246  	ctx := NewContext(r, w, "RestoreConfigHistoryKV")
   247  
   248  	defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r))
   249  
   250  	_, objectAPI := validateAdminReqConfigKV(ctx, w, r)
   251  	if objectAPI == nil {
   252  		return
   253  	}
   254  
   255  	vars := mux.Vars(r)
   256  	restoreID := vars["restoreId"]
   257  	if restoreID == "" {
   258  		writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrInvalidRequest), r.URL)
   259  		return
   260  	}
   261  
   262  	kvBytes, err := readServerConfigHistory(ctx, objectAPI, restoreID)
   263  	if err != nil {
   264  		writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
   265  		return
   266  	}
   267  
   268  	cfg, err := readServerConfig(ctx, objectAPI)
   269  	if err != nil {
   270  		writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
   271  		return
   272  	}
   273  
   274  	if _, err = cfg.ReadConfig(bytes.NewReader(kvBytes)); err != nil {
   275  		writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
   276  		return
   277  	}
   278  
   279  	if err = validateConfig(cfg, objectAPI.SetDriveCounts()); err != nil {
   280  		writeCustomErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAdminConfigBadJSON), err.Error(), r.URL)
   281  		return
   282  	}
   283  
   284  	if err = saveServerConfig(ctx, objectAPI, cfg); err != nil {
   285  		writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
   286  		return
   287  	}
   288  
   289  	delServerConfigHistory(ctx, objectAPI, restoreID)
   290  }
   291  
   292  // ListConfigHistoryKVHandler - lists all the KV ids.
   293  func (a adminAPIHandlers) ListConfigHistoryKVHandler(w http.ResponseWriter, r *http.Request) {
   294  	ctx := NewContext(r, w, "ListConfigHistoryKV")
   295  
   296  	defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r))
   297  
   298  	cred, objectAPI := validateAdminReqConfigKV(ctx, w, r)
   299  	if objectAPI == nil {
   300  		return
   301  	}
   302  
   303  	vars := mux.Vars(r)
   304  	count, err := strconv.Atoi(vars["count"])
   305  	if err != nil {
   306  		writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
   307  		return
   308  	}
   309  
   310  	chEntries, err := listServerConfigHistory(ctx, objectAPI, true, count)
   311  	if err != nil {
   312  		writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
   313  		return
   314  	}
   315  
   316  	data, err := json.Marshal(chEntries)
   317  	if err != nil {
   318  		writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
   319  		return
   320  	}
   321  
   322  	password := cred.SecretKey
   323  	econfigData, err := madmin.EncryptData(password, data)
   324  	if err != nil {
   325  		writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
   326  		return
   327  	}
   328  
   329  	writeSuccessResponseJSON(w, econfigData)
   330  }
   331  
   332  // HelpConfigKVHandler - GET /minio/admin/v3/help-config-kv?subSys={subSys}&key={key}
   333  func (a adminAPIHandlers) HelpConfigKVHandler(w http.ResponseWriter, r *http.Request) {
   334  	ctx := NewContext(r, w, "HelpConfigKV")
   335  
   336  	defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r))
   337  
   338  	_, objectAPI := validateAdminReqConfigKV(ctx, w, r)
   339  	if objectAPI == nil {
   340  		return
   341  	}
   342  
   343  	vars := mux.Vars(r)
   344  
   345  	subSys := vars["subSys"]
   346  	key := vars["key"]
   347  
   348  	_, envOnly := r.URL.Query()["env"]
   349  
   350  	rd, err := GetHelp(subSys, key, envOnly)
   351  	if err != nil {
   352  		writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
   353  		return
   354  	}
   355  
   356  	json.NewEncoder(w).Encode(rd)
   357  	w.(http.Flusher).Flush()
   358  }
   359  
   360  // SetConfigHandler - PUT /minio/admin/v3/config
   361  func (a adminAPIHandlers) SetConfigHandler(w http.ResponseWriter, r *http.Request) {
   362  	ctx := NewContext(r, w, "SetConfig")
   363  
   364  	defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r))
   365  
   366  	cred, objectAPI := validateAdminReqConfigKV(ctx, w, r)
   367  	if objectAPI == nil {
   368  		return
   369  	}
   370  
   371  	if r.ContentLength > maxEConfigJSONSize || r.ContentLength == -1 {
   372  		// More than maxConfigSize bytes were available
   373  		writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAdminConfigTooLarge), r.URL)
   374  		return
   375  	}
   376  
   377  	password := cred.SecretKey
   378  	kvBytes, err := madmin.DecryptData(password, io.LimitReader(r.Body, r.ContentLength))
   379  	if err != nil {
   380  		logger.LogIf(ctx, err, logger.Application)
   381  		writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAdminConfigBadJSON), r.URL)
   382  		return
   383  	}
   384  
   385  	cfg := newServerConfig()
   386  	if _, err = cfg.ReadConfig(bytes.NewReader(kvBytes)); err != nil {
   387  		writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
   388  		return
   389  	}
   390  
   391  	if err = validateConfig(cfg, objectAPI.SetDriveCounts()); err != nil {
   392  		writeCustomErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAdminConfigBadJSON), err.Error(), r.URL)
   393  		return
   394  	}
   395  
   396  	// Update the actual server config on disk.
   397  	if err = saveServerConfig(ctx, objectAPI, cfg); err != nil {
   398  		writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
   399  		return
   400  	}
   401  
   402  	// Write to the config input KV to history.
   403  	if err = saveServerConfigHistory(ctx, objectAPI, kvBytes); err != nil {
   404  		writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
   405  		return
   406  	}
   407  
   408  	writeSuccessResponseHeadersOnly(w)
   409  }
   410  
   411  // GetConfigHandler - GET /minio/admin/v3/config
   412  // Get config.json of this minio setup.
   413  func (a adminAPIHandlers) GetConfigHandler(w http.ResponseWriter, r *http.Request) {
   414  	ctx := NewContext(r, w, "GetConfig")
   415  
   416  	defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r))
   417  
   418  	cred, objectAPI := validateAdminReqConfigKV(ctx, w, r)
   419  	if objectAPI == nil {
   420  		return
   421  	}
   422  
   423  	cfg, err := readServerConfig(ctx, objectAPI)
   424  	if err != nil {
   425  		writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
   426  		return
   427  	}
   428  
   429  	var s strings.Builder
   430  	hkvs := config.HelpSubSysMap[""]
   431  	var count int
   432  	for _, hkv := range hkvs {
   433  		count += len(cfg[hkv.Key])
   434  	}
   435  	for _, hkv := range hkvs {
   436  		v := cfg[hkv.Key]
   437  		for target, kv := range v {
   438  			off := kv.Get(config.Enable) == config.EnableOff
   439  			switch hkv.Key {
   440  			case config.CacheSubSys:
   441  				off = !cache.Enabled(kv)
   442  			case config.StorageClassSubSys:
   443  				off = !storageclass.Enabled(kv)
   444  			case config.PolicyOPASubSys:
   445  				off = !opa.Enabled(kv)
   446  			case config.IdentityOpenIDSubSys:
   447  				off = !openid.Enabled(kv)
   448  			case config.IdentityLDAPSubSys:
   449  				off = !xldap.Enabled(kv)
   450  			}
   451  			if off {
   452  				s.WriteString(config.KvComment)
   453  				s.WriteString(config.KvSpaceSeparator)
   454  			}
   455  			s.WriteString(hkv.Key)
   456  			if target != config.Default {
   457  				s.WriteString(config.SubSystemSeparator)
   458  				s.WriteString(target)
   459  			}
   460  			s.WriteString(config.KvSpaceSeparator)
   461  			s.WriteString(kv.String())
   462  			count--
   463  			if count > 0 {
   464  				s.WriteString(config.KvNewline)
   465  			}
   466  		}
   467  	}
   468  
   469  	password := cred.SecretKey
   470  	econfigData, err := madmin.EncryptData(password, []byte(s.String()))
   471  	if err != nil {
   472  		writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
   473  		return
   474  	}
   475  
   476  	writeSuccessResponseJSON(w, econfigData)
   477  }