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 }