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  }