github.com/minio/madmin-go/v2@v2.2.1/kms-commands.go (about) 1 // 2 // Copyright (c) 2015-2022 MinIO, Inc. 3 // 4 // This file is part of MinIO Object Storage stack 5 // 6 // This program is free software: you can redistribute it and/or modify 7 // it under the terms of the GNU Affero General Public License as 8 // published by the Free Software Foundation, either version 3 of the 9 // License, or (at your option) any later version. 10 // 11 // This program is distributed in the hope that it will be useful, 12 // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 // GNU Affero General Public License for more details. 15 // 16 // You should have received a copy of the GNU Affero General Public License 17 // along with this program. If not, see <http://www.gnu.org/licenses/>. 18 // 19 20 package madmin 21 22 import ( 23 "context" 24 "encoding/json" 25 "net/http" 26 "net/url" 27 "time" 28 ) 29 30 // KMSStatus contains various informations about 31 // the KMS connected to a MinIO server - like 32 // the KMS endpoints and the default key ID. 33 type KMSStatus struct { 34 Name string `json:"name"` // Name or type of the KMS 35 DefaultKeyID string `json:"default-key-id"` // The key ID used when no explicit key is specified 36 Endpoints map[string]ItemState `json:"endpoints"` // List of KMS endpoints and their status (online/offline) 37 State KMSState `json:"state"` // Current KMS server state 38 } 39 40 // KMSState is a KES server status snapshot. 41 type KMSState struct { 42 Version string 43 KeyStoreLatency time.Duration 44 KeyStoreReachable bool 45 KeystoreAvailable bool 46 47 OS string 48 Arch string 49 UpTime time.Duration 50 CPUs int 51 UsableCPUs int 52 HeapAlloc uint64 53 StackAlloc uint64 54 } 55 56 // KMSKeyInfo contains key metadata 57 type KMSKeyInfo struct { 58 CreatedAt string `json:"createdAt"` 59 CreatedBy string `json:"createdBy"` 60 Name string `json:"name"` 61 } 62 63 // KMSPolicyInfo contains policy metadata 64 type KMSPolicyInfo struct { 65 CreatedAt string `json:"created_at"` 66 CreatedBy string `json:"created_by"` 67 Name string `json:"name"` 68 } 69 70 // KMSIdentityInfo contains policy metadata 71 type KMSIdentityInfo struct { 72 CreatedAt string `json:"createdAt"` 73 CreatedBy string `json:"createdBy"` 74 Identity string `json:"identity"` 75 Policy string `json:"policy"` 76 Error string `json:"error"` 77 } 78 79 // KMSDescribePolicy contains policy metadata 80 type KMSDescribePolicy struct { 81 Name string `json:"name"` 82 CreatedAt string `json:"created_at"` 83 CreatedBy string `json:"created_by"` 84 } 85 86 // KMSPolicy represents a KMS policy 87 type KMSPolicy struct { 88 Allow []string `json:"allow"` 89 Deny []string `json:"deny"` 90 } 91 92 // KMSDescribeIdentity contains identity metadata 93 type KMSDescribeIdentity struct { 94 Policy string `json:"policy"` 95 Identity string `json:"identity"` 96 IsAdmin bool `json:"isAdmin"` 97 CreatedAt string `json:"createdAt"` 98 CreatedBy string `json:"createdBy"` 99 } 100 101 // KMSDescribeSelfIdentity describes the identity issuing the request 102 type KMSDescribeSelfIdentity struct { 103 Policy *KMSPolicy `json:"policy"` 104 PolicyName string `json:"policyName"` 105 Identity string `json:"identity"` 106 IsAdmin bool `json:"isAdmin"` 107 CreatedAt string `json:"createdAt"` 108 CreatedBy string `json:"createdBy"` 109 } 110 111 type KMSMetrics struct { 112 RequestOK int64 `json:"kes_http_request_success"` 113 RequestErr int64 `json:"kes_http_request_error"` 114 RequestFail int64 `json:"kes_http_request_failure"` 115 RequestActive int64 `json:"kes_http_request_active"` 116 117 AuditEvents int64 `json:"kes_log_audit_events"` 118 ErrorEvents int64 `json:"kes_log_error_events"` 119 120 LatencyHistogram map[int64]int64 `json:"kes_http_response_time"` 121 122 UpTime int64 `json:"kes_system_up_time"` 123 CPUs int64 `json:"kes_system_num_cpu"` 124 UsableCPUs int64 `json:"kes_system_num_cpu_used"` 125 126 Threads int64 `json:"kes_system_num_threads"` 127 HeapAlloc int64 `json:"kes_system_mem_heap_used"` 128 HeapObjects int64 `json:"kes_system_mem_heap_objects"` 129 StackAlloc int64 `json:"kes_system_mem_stack_used"` 130 } 131 132 type KMSAPI struct { 133 Method string 134 Path string 135 MaxBody int64 136 Timeout int64 137 } 138 139 type KMSVersion struct { 140 Version string `json:"version"` 141 } 142 143 // KMSStatus returns status information about the KMS connected 144 // to the MinIO server, if configured. 145 func (adm *AdminClient) KMSStatus(ctx context.Context) (KMSStatus, error) { 146 // GET /minio/kms/v1/status 147 resp, err := adm.doKMSRequest(ctx, "/status", http.MethodGet, nil, map[string]string{}) 148 if err != nil { 149 return KMSStatus{}, err 150 } 151 defer closeResponse(resp) 152 if resp.StatusCode != http.StatusOK { 153 return KMSStatus{}, httpRespToErrorResponse(resp) 154 } 155 var status KMSStatus 156 if err = json.NewDecoder(resp.Body).Decode(&status); err != nil { 157 return KMSStatus{}, err 158 } 159 return status, nil 160 } 161 162 // KMSMetrics returns metrics about the KMS connected 163 // to the MinIO server, if configured. 164 func (adm *AdminClient) KMSMetrics(ctx context.Context) (*KMSMetrics, error) { 165 // GET /minio/kms/v1/metrics 166 resp, err := adm.doKMSRequest(ctx, "/metrics", http.MethodGet, nil, map[string]string{}) 167 if err != nil { 168 return nil, err 169 } 170 defer closeResponse(resp) 171 if resp.StatusCode != http.StatusOK { 172 return nil, httpRespToErrorResponse(resp) 173 } 174 var metrics KMSMetrics 175 if err = json.NewDecoder(resp.Body).Decode(&metrics); err != nil { 176 return nil, err 177 } 178 return &metrics, nil 179 } 180 181 // KMSAPIs returns a list of supported API endpoints in the KMS connected 182 // to the MinIO server, if configured. 183 func (adm *AdminClient) KMSAPIs(ctx context.Context) ([]KMSAPI, error) { 184 // GET /minio/kms/v1/apis 185 resp, err := adm.doKMSRequest(ctx, "/apis", http.MethodGet, nil, map[string]string{}) 186 if err != nil { 187 return nil, err 188 } 189 defer closeResponse(resp) 190 if resp.StatusCode != http.StatusOK { 191 return nil, httpRespToErrorResponse(resp) 192 } 193 var apis []KMSAPI 194 if err = json.NewDecoder(resp.Body).Decode(&apis); err != nil { 195 return nil, err 196 } 197 return apis, nil 198 } 199 200 // KMSVersion returns a list of supported API endpoints in the KMS connected 201 // to the MinIO server, if configured. 202 func (adm *AdminClient) KMSVersion(ctx context.Context) (*KMSVersion, error) { 203 // GET /minio/kms/v1/version 204 resp, err := adm.doKMSRequest(ctx, "/version", http.MethodGet, nil, map[string]string{}) 205 if err != nil { 206 return nil, err 207 } 208 defer closeResponse(resp) 209 if resp.StatusCode != http.StatusOK { 210 return nil, httpRespToErrorResponse(resp) 211 } 212 var version KMSVersion 213 if err = json.NewDecoder(resp.Body).Decode(&version); err != nil { 214 return nil, err 215 } 216 return &version, nil 217 } 218 219 // CreateKey tries to create a new master key with the given keyID 220 // at the KMS connected to a MinIO server. 221 func (adm *AdminClient) CreateKey(ctx context.Context, keyID string) error { 222 // POST /minio/kms/v1/key/create?key-id=<keyID> 223 resp, err := adm.doKMSRequest(ctx, "/key/create", http.MethodPost, nil, map[string]string{"key-id": keyID}) 224 if err != nil { 225 return err 226 } 227 defer closeResponse(resp) 228 if resp.StatusCode != http.StatusOK { 229 return httpRespToErrorResponse(resp) 230 } 231 return nil 232 } 233 234 // DeleteKey tries to delete a key with the given keyID 235 // at the KMS connected to a MinIO server. 236 func (adm *AdminClient) DeleteKey(ctx context.Context, keyID string) error { 237 // DELETE /minio/kms/v1/key/delete?key-id=<keyID> 238 resp, err := adm.doKMSRequest(ctx, "/key/delete", http.MethodDelete, nil, map[string]string{"key-id": keyID}) 239 if err != nil { 240 return err 241 } 242 defer closeResponse(resp) 243 if resp.StatusCode != http.StatusOK { 244 return httpRespToErrorResponse(resp) 245 } 246 return nil 247 } 248 249 // ImportKey tries to import a cryptographic key 250 // at the KMS connected to a MinIO server. 251 func (adm *AdminClient) ImportKey(ctx context.Context, keyID string, content []byte) error { 252 // POST /minio/kms/v1/key/import?key-id=<keyID> 253 resp, err := adm.doKMSRequest(ctx, "/key/import", http.MethodPost, content, map[string]string{"key-id": keyID}) 254 if err != nil { 255 return err 256 } 257 defer closeResponse(resp) 258 if resp.StatusCode != http.StatusOK { 259 return httpRespToErrorResponse(resp) 260 } 261 return nil 262 } 263 264 // ListKeys tries to get all key names that match the specified pattern 265 func (adm *AdminClient) ListKeys(ctx context.Context, pattern string) ([]KMSKeyInfo, error) { 266 // GET /minio/kms/v1/key/list?pattern=<pattern> 267 resp, err := adm.doKMSRequest(ctx, "/key/list", http.MethodGet, nil, map[string]string{"pattern": pattern}) 268 if err != nil { 269 return nil, err 270 } 271 defer closeResponse(resp) 272 if resp.StatusCode != http.StatusOK { 273 return nil, httpRespToErrorResponse(resp) 274 } 275 var results []KMSKeyInfo 276 if err = json.NewDecoder(resp.Body).Decode(&results); err != nil { 277 return nil, err 278 } 279 return results, nil 280 } 281 282 // GetKeyStatus requests status information about the key referenced by keyID 283 // from the KMS connected to a MinIO by performing a Admin-API request. 284 // It basically hits the `/minio/admin/v3/kms/key/status` API endpoint. 285 func (adm *AdminClient) GetKeyStatus(ctx context.Context, keyID string) (*KMSKeyStatus, error) { 286 // GET /minio/kms/v1/key/status?key-id=<keyID> 287 resp, err := adm.doKMSRequest(ctx, "/key/status", http.MethodGet, nil, map[string]string{"key-id": keyID}) 288 if err != nil { 289 return nil, err 290 } 291 defer closeResponse(resp) 292 if resp.StatusCode != http.StatusOK { 293 return nil, httpRespToErrorResponse(resp) 294 } 295 var keyInfo KMSKeyStatus 296 if err = json.NewDecoder(resp.Body).Decode(&keyInfo); err != nil { 297 return nil, err 298 } 299 return &keyInfo, nil 300 } 301 302 // KMSKeyStatus contains some status information about a KMS master key. 303 // The MinIO server tries to access the KMS and perform encryption and 304 // decryption operations. If the MinIO server can access the KMS and 305 // all master key operations succeed it returns a status containing only 306 // the master key ID but no error. 307 type KMSKeyStatus struct { 308 KeyID string `json:"key-id"` 309 EncryptionErr string `json:"encryption-error,omitempty"` // An empty error == success 310 DecryptionErr string `json:"decryption-error,omitempty"` // An empty error == success 311 } 312 313 // SetKMSPolicy tries to create or update a policy 314 // at the KMS connected to a MinIO server. 315 func (adm *AdminClient) SetKMSPolicy(ctx context.Context, policy string, content []byte) error { 316 // POST /minio/kms/v1/policy/set?policy=<policy> 317 resp, err := adm.doKMSRequest(ctx, "/policy/set", http.MethodPost, content, map[string]string{"policy": policy}) 318 if err != nil { 319 return err 320 } 321 defer closeResponse(resp) 322 if resp.StatusCode != http.StatusOK { 323 return httpRespToErrorResponse(resp) 324 } 325 return nil 326 } 327 328 // AssignPolicy tries to assign a policy to an identity 329 // at the KMS connected to a MinIO server. 330 func (adm *AdminClient) AssignPolicy(ctx context.Context, policy string, content []byte) error { 331 // POST /minio/kms/v1/policy/assign?policy=<policy> 332 resp, err := adm.doKMSRequest(ctx, "/policy/assign", http.MethodPost, content, map[string]string{"policy": policy}) 333 if err != nil { 334 return err 335 } 336 defer closeResponse(resp) 337 if resp.StatusCode != http.StatusOK { 338 return httpRespToErrorResponse(resp) 339 } 340 return nil 341 } 342 343 // DescribePolicy tries to describe a KMS policy 344 func (adm *AdminClient) DescribePolicy(ctx context.Context, policy string) (*KMSDescribePolicy, error) { 345 // GET /minio/kms/v1/policy/describe?policy=<policy> 346 resp, err := adm.doKMSRequest(ctx, "/policy/describe", http.MethodGet, nil, map[string]string{"policy": policy}) 347 if err != nil { 348 return nil, err 349 } 350 defer closeResponse(resp) 351 if resp.StatusCode != http.StatusOK { 352 return nil, httpRespToErrorResponse(resp) 353 } 354 var dp KMSDescribePolicy 355 if err = json.NewDecoder(resp.Body).Decode(&dp); err != nil { 356 return nil, err 357 } 358 return &dp, nil 359 } 360 361 // GetPolicy tries to get a KMS policy 362 func (adm *AdminClient) GetPolicy(ctx context.Context, policy string) (*KMSPolicy, error) { 363 // GET /minio/kms/v1/policy/get?policy=<policy> 364 resp, err := adm.doKMSRequest(ctx, "/policy/get", http.MethodGet, nil, map[string]string{"policy": policy}) 365 if err != nil { 366 return nil, err 367 } 368 defer closeResponse(resp) 369 if resp.StatusCode != http.StatusOK { 370 return nil, httpRespToErrorResponse(resp) 371 } 372 var p KMSPolicy 373 if err = json.NewDecoder(resp.Body).Decode(&p); err != nil { 374 return nil, err 375 } 376 return &p, nil 377 } 378 379 // ListPolicies tries to get all policies that match the specified pattern 380 func (adm *AdminClient) ListPolicies(ctx context.Context, pattern string) ([]KMSPolicyInfo, error) { 381 // GET /minio/kms/v1/policy/list?pattern=<pattern> 382 resp, err := adm.doKMSRequest(ctx, "/policy/list", http.MethodGet, nil, map[string]string{"pattern": pattern}) 383 if err != nil { 384 return nil, err 385 } 386 defer closeResponse(resp) 387 if resp.StatusCode != http.StatusOK { 388 return nil, httpRespToErrorResponse(resp) 389 } 390 var results []KMSPolicyInfo 391 if err = json.NewDecoder(resp.Body).Decode(&results); err != nil { 392 return nil, err 393 } 394 return results, nil 395 } 396 397 // DeletePolicy tries to delete a policy 398 // at the KMS connected to a MinIO server. 399 func (adm *AdminClient) DeletePolicy(ctx context.Context, policy string) error { 400 // DELETE /minio/kms/v1/policy/delete?policy=<policy> 401 resp, err := adm.doKMSRequest(ctx, "/policy/delete", http.MethodDelete, nil, map[string]string{"policy": policy}) 402 if err != nil { 403 return err 404 } 405 defer closeResponse(resp) 406 if resp.StatusCode != http.StatusOK { 407 return httpRespToErrorResponse(resp) 408 } 409 return nil 410 } 411 412 // DescribeIdentity tries to describe a KMS identity 413 func (adm *AdminClient) DescribeIdentity(ctx context.Context, identity string) (*KMSDescribeIdentity, error) { 414 // GET /minio/kms/v1/identity/describe?identity=<identity> 415 resp, err := adm.doKMSRequest(ctx, "/identity/describe", http.MethodGet, nil, map[string]string{"identity": identity}) 416 if err != nil { 417 return nil, err 418 } 419 defer closeResponse(resp) 420 if resp.StatusCode != http.StatusOK { 421 return nil, httpRespToErrorResponse(resp) 422 } 423 var i KMSDescribeIdentity 424 if err = json.NewDecoder(resp.Body).Decode(&i); err != nil { 425 return nil, err 426 } 427 return &i, nil 428 } 429 430 // DescribeSelfIdentity tries to describe the identity issuing the request. 431 func (adm *AdminClient) DescribeSelfIdentity(ctx context.Context) (*KMSDescribeSelfIdentity, error) { 432 // GET /minio/kms/v1/identity/describe-self 433 resp, err := adm.doKMSRequest(ctx, "/identity/describe-self", http.MethodGet, nil, map[string]string{}) 434 if err != nil { 435 return nil, err 436 } 437 defer closeResponse(resp) 438 if resp.StatusCode != http.StatusOK { 439 return nil, httpRespToErrorResponse(resp) 440 } 441 var si KMSDescribeSelfIdentity 442 if err = json.NewDecoder(resp.Body).Decode(&si); err != nil { 443 return nil, err 444 } 445 return &si, nil 446 } 447 448 // ListIdentities tries to get all identities that match the specified pattern 449 func (adm *AdminClient) ListIdentities(ctx context.Context, pattern string) ([]KMSIdentityInfo, error) { 450 // GET /minio/kms/v1/identity/list?pattern=<pattern> 451 if pattern == "" { // list identities does not default to * 452 pattern = "*" 453 } 454 resp, err := adm.doKMSRequest(ctx, "/identity/list", http.MethodGet, nil, map[string]string{"pattern": pattern}) 455 if err != nil { 456 return nil, err 457 } 458 defer closeResponse(resp) 459 if resp.StatusCode != http.StatusOK { 460 return nil, httpRespToErrorResponse(resp) 461 } 462 var results []KMSIdentityInfo 463 if err = json.NewDecoder(resp.Body).Decode(&results); err != nil { 464 return nil, err 465 } 466 return results, nil 467 } 468 469 // DeleteIdentity tries to delete a identity 470 // at the KMS connected to a MinIO server. 471 func (adm *AdminClient) DeleteIdentity(ctx context.Context, identity string) error { 472 // DELETE /minio/kms/v1/identity/delete?identity=<identity> 473 resp, err := adm.doKMSRequest(ctx, "/identity/delete", http.MethodDelete, nil, map[string]string{"identity": identity}) 474 if err != nil { 475 return err 476 } 477 defer closeResponse(resp) 478 if resp.StatusCode != http.StatusOK { 479 return httpRespToErrorResponse(resp) 480 } 481 return nil 482 } 483 484 func (adm *AdminClient) doKMSRequest(ctx context.Context, path, method string, content []byte, values map[string]string) (*http.Response, error) { 485 qv := url.Values{} 486 for key, value := range values { 487 qv.Set(key, value) 488 } 489 reqData := requestData{ 490 relPath: kmsAPIPrefix + path, 491 queryValues: qv, 492 isKMS: true, 493 content: content, 494 } 495 return adm.executeMethod(ctx, method, reqData) 496 }