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  }