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