diff --git a/cmd/generic-handlers.go b/cmd/generic-handlers.go
index 7c9985e4b..05f83519a 100644
--- a/cmd/generic-handlers.go
+++ b/cmd/generic-handlers.go
@@ -245,6 +245,12 @@ func isAdminReq(r *http.Request) bool {
return strings.HasPrefix(r.URL.Path, adminPathPrefix)
}
+// Check to allow access to the reserved "bucket" `/minio` for KMS
+// API requests.
+func isKMSReq(r *http.Request) bool {
+ return strings.HasPrefix(r.URL.Path, kmsPathPrefix)
+}
+
// Supported Amz date headers.
var amzDateHeaders = []string{
// Do not chane this order, x-amz-date value should be
@@ -428,12 +434,11 @@ func setRequestValidityHandler(h http.Handler) http.Handler {
// For all other requests reject access to reserved buckets
bucketName, _ := request2BucketObjectName(r)
if isMinioReservedBucket(bucketName) || isMinioMetaBucket(bucketName) {
- if !guessIsRPCReq(r) && !guessIsBrowserReq(r) && !guessIsHealthCheckReq(r) && !guessIsMetricsReq(r) && !isAdminReq(r) {
+ if !guessIsRPCReq(r) && !guessIsBrowserReq(r) && !guessIsHealthCheckReq(r) && !guessIsMetricsReq(r) && !isAdminReq(r) && !isKMSReq(r) {
if ok {
tc.funcName = "handler.ValidRequest"
tc.responseRecorder.LogErrBody = true
}
-
writeErrorResponse(r.Context(), w, errorCodes.ToAPIErr(ErrAllAccessDisabled), r.URL)
return
}
diff --git a/cmd/kms-handlers.go b/cmd/kms-handlers.go
new file mode 100644
index 000000000..73ea2e5d8
--- /dev/null
+++ b/cmd/kms-handlers.go
@@ -0,0 +1,607 @@
+// Copyright (c) 2015-2022 MinIO, Inc.
+//
+// This file is part of MinIO Object Storage stack
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+
+package cmd
+
+import (
+ "crypto/subtle"
+ "encoding/json"
+ "net/http"
+ "time"
+
+ "github.com/minio/kes"
+ "github.com/minio/madmin-go"
+ "github.com/minio/minio/internal/kms"
+ "github.com/minio/minio/internal/logger"
+ iampolicy "github.com/minio/pkg/iam/policy"
+)
+
+// KMSStatusHandler - GET /minio/kms/v1/status
+func (a kmsAPIHandlers) KMSStatusHandler(w http.ResponseWriter, r *http.Request) {
+ ctx := newContext(r, w, "KMSStatus")
+ defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r))
+ objectAPI, _ := validateAdminReq(ctx, w, r, iampolicy.KMSStatusAction)
+ if objectAPI == nil {
+ return
+ }
+
+ if GlobalKMS == nil {
+ writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrKMSNotConfigured), r.URL)
+ return
+ }
+
+ stat, err := GlobalKMS.Stat(ctx)
+ if err != nil {
+ writeCustomErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrInternalError), err.Error(), r.URL)
+ return
+ }
+
+ status := madmin.KMSStatus{
+ Name: stat.Name,
+ DefaultKeyID: stat.DefaultKey,
+ Endpoints: make(map[string]madmin.ItemState, len(stat.Endpoints)),
+ }
+ for _, endpoint := range stat.Endpoints {
+ status.Endpoints[endpoint] = madmin.ItemOnline // TODO(aead): Implement an online check for mTLS
+ }
+
+ resp, err := json.Marshal(status)
+ if err != nil {
+ writeCustomErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrInternalError), err.Error(), r.URL)
+ return
+ }
+ writeSuccessResponseJSON(w, resp)
+}
+
+// KMSCreateKeyHandler - POST /minio/kms/v1/key/create?key-id=
+func (a kmsAPIHandlers) KMSCreateKeyHandler(w http.ResponseWriter, r *http.Request) {
+ ctx := newContext(r, w, "KMSCreateKey")
+ defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r))
+
+ objectAPI, _ := validateAdminReq(ctx, w, r, iampolicy.KMSCreateKeyAction)
+ if objectAPI == nil {
+ return
+ }
+
+ if GlobalKMS == nil {
+ writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrKMSNotConfigured), r.URL)
+ return
+ }
+
+ manager, ok := GlobalKMS.(kms.KeyManager)
+ if !ok {
+ writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrNotImplemented), r.URL)
+ return
+ }
+
+ if err := manager.CreateKey(ctx, r.Form.Get("key-id")); err != nil {
+ writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
+ return
+ }
+ writeSuccessResponseHeadersOnly(w)
+}
+
+// KMSDeleteKeyHandler - DELETE /minio/kms/v1/key/delete?key-id=
+func (a kmsAPIHandlers) KMSDeleteKeyHandler(w http.ResponseWriter, r *http.Request) {
+ ctx := newContext(r, w, "KMSDeleteKey")
+ defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r))
+
+ objectAPI, _ := validateAdminReq(ctx, w, r, iampolicy.KMSDeleteKeyAction)
+ if objectAPI == nil {
+ return
+ }
+
+ if GlobalKMS == nil {
+ writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrKMSNotConfigured), r.URL)
+ return
+ }
+ manager, ok := GlobalKMS.(kms.KeyManager)
+ if !ok {
+ writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrNotImplemented), r.URL)
+ return
+ }
+ if err := manager.DeleteKey(ctx, r.Form.Get("key-id")); err != nil {
+ writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
+ return
+ }
+ writeSuccessResponseHeadersOnly(w)
+}
+
+// KMSListKeysHandler - GET /minio/kms/v1/key/list?pattern=
+func (a kmsAPIHandlers) KMSListKeysHandler(w http.ResponseWriter, r *http.Request) {
+ ctx := newContext(r, w, "KMSListKeys")
+ defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r))
+
+ objectAPI, _ := validateAdminReq(ctx, w, r, iampolicy.KMSListKeysAction)
+ if objectAPI == nil {
+ return
+ }
+
+ if GlobalKMS == nil {
+ writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrKMSNotConfigured), r.URL)
+ return
+ }
+ manager, ok := GlobalKMS.(kms.KeyManager)
+ if !ok {
+ writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrNotImplemented), r.URL)
+ return
+ }
+ keys, err := manager.ListKeys(ctx, r.Form.Get("pattern"))
+ if err != nil {
+ writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
+ return
+ }
+ values, err := keys.Values(0)
+ if err != nil {
+ writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
+ return
+ }
+ if res, err := json.Marshal(values); err != nil {
+ writeCustomErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrInternalError), err.Error(), r.URL)
+ } else {
+ writeSuccessResponseJSON(w, res)
+ }
+}
+
+type importKeyRequest struct {
+ Bytes string
+}
+
+// KMSImportKeyHandler - POST /minio/kms/v1/key/import?key-id=
+func (a kmsAPIHandlers) KMSImportKeyHandler(w http.ResponseWriter, r *http.Request) {
+ ctx := newContext(r, w, "KMSImportKey")
+ defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r))
+
+ objectAPI, _ := validateAdminReq(ctx, w, r, iampolicy.KMSImportKeyAction)
+ if objectAPI == nil {
+ return
+ }
+
+ if GlobalKMS == nil {
+ writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrKMSNotConfigured), r.URL)
+ return
+ }
+ manager, ok := GlobalKMS.(kms.KeyManager)
+ if !ok {
+ writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrNotImplemented), r.URL)
+ return
+ }
+ var request importKeyRequest
+ if err := json.NewDecoder(r.Body).Decode(&request); err != nil {
+ writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
+ return
+ }
+ if err := manager.ImportKey(ctx, r.Form.Get("key-id"), []byte(request.Bytes)); err != nil {
+ writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
+ return
+ }
+ writeSuccessResponseHeadersOnly(w)
+}
+
+// KMSKeyStatusHandler - GET /minio/kms/v1/key/status?key-id=
+func (a kmsAPIHandlers) KMSKeyStatusHandler(w http.ResponseWriter, r *http.Request) {
+ ctx := newContext(r, w, "KMSKeyStatus")
+
+ defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r))
+ objectAPI, _ := validateAdminReq(ctx, w, r, iampolicy.KMSKeyStatusAction)
+ if objectAPI == nil {
+ return
+ }
+
+ if GlobalKMS == nil {
+ writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrKMSNotConfigured), r.URL)
+ return
+ }
+
+ stat, err := GlobalKMS.Stat(ctx)
+ if err != nil {
+ writeCustomErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrInternalError), err.Error(), r.URL)
+ return
+ }
+
+ keyID := r.Form.Get("key-id")
+ if keyID == "" {
+ keyID = stat.DefaultKey
+ }
+ response := madmin.KMSKeyStatus{
+ KeyID: keyID,
+ }
+
+ kmsContext := kms.Context{"MinIO admin API": "KMSKeyStatusHandler"} // Context for a test key operation
+ // 1. Generate a new key using the KMS.
+ key, err := GlobalKMS.GenerateKey(ctx, keyID, kmsContext)
+ if err != nil {
+ response.EncryptionErr = err.Error()
+ resp, err := json.Marshal(response)
+ if err != nil {
+ writeCustomErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrInternalError), err.Error(), r.URL)
+ return
+ }
+ writeSuccessResponseJSON(w, resp)
+ return
+ }
+
+ // 2. Verify that we can indeed decrypt the (encrypted) key
+ decryptedKey, err := GlobalKMS.DecryptKey(key.KeyID, key.Ciphertext, kmsContext)
+ if err != nil {
+ response.DecryptionErr = err.Error()
+ resp, err := json.Marshal(response)
+ if err != nil {
+ writeCustomErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrInternalError), err.Error(), r.URL)
+ return
+ }
+ writeSuccessResponseJSON(w, resp)
+ return
+ }
+
+ // 3. Compare generated key with decrypted key
+ if subtle.ConstantTimeCompare(key.Plaintext, decryptedKey) != 1 {
+ response.DecryptionErr = "The generated and the decrypted data key do not match"
+ resp, err := json.Marshal(response)
+ if err != nil {
+ writeCustomErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrInternalError), err.Error(), r.URL)
+ return
+ }
+ writeSuccessResponseJSON(w, resp)
+ return
+ }
+
+ resp, err := json.Marshal(response)
+ if err != nil {
+ writeCustomErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrInternalError), err.Error(), r.URL)
+ return
+ }
+ writeSuccessResponseJSON(w, resp)
+}
+
+// KMSDescribePolicyHandler - GET /minio/kms/v1/policy/describe?policy=
+func (a kmsAPIHandlers) KMSDescribePolicyHandler(w http.ResponseWriter, r *http.Request) {
+ ctx := newContext(r, w, "KMSDescribePolicy")
+ defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r))
+
+ objectAPI, _ := validateAdminReq(ctx, w, r, iampolicy.KMSDescribePolicyAction)
+ if objectAPI == nil {
+ return
+ }
+
+ if GlobalKMS == nil {
+ writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrKMSNotConfigured), r.URL)
+ return
+ }
+ manager, ok := GlobalKMS.(kms.PolicyManager)
+ if !ok {
+ writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrNotImplemented), r.URL)
+ return
+ }
+ policy, err := manager.DescribePolicy(ctx, r.Form.Get("policy"))
+ if err != nil {
+ writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
+ return
+ }
+ p, err := json.Marshal(policy)
+ if err != nil {
+ writeCustomErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrInternalError), err.Error(), r.URL)
+ return
+ }
+ writeSuccessResponseJSON(w, p)
+}
+
+type assignPolicyRequest struct {
+ Identity string
+}
+
+// KMSAssignPolicyHandler - POST /minio/kms/v1/policy/assign?policy=
+func (a kmsAPIHandlers) KMSAssignPolicyHandler(w http.ResponseWriter, r *http.Request) {
+ ctx := newContext(r, w, "KMSAssignPolicy")
+ defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r))
+
+ objectAPI, _ := validateAdminReq(ctx, w, r, iampolicy.KMSAssignPolicyAction)
+ if objectAPI == nil {
+ return
+ }
+
+ if GlobalKMS == nil {
+ writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrKMSNotConfigured), r.URL)
+ return
+ }
+ manager, ok := GlobalKMS.(kms.PolicyManager)
+ if !ok {
+ writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrNotImplemented), r.URL)
+ return
+ }
+ var request assignPolicyRequest
+ if err := json.NewDecoder(r.Body).Decode(&request); err != nil {
+ writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
+ return
+ }
+ err := manager.AssignPolicy(ctx, r.Form.Get("policy"), request.Identity)
+ if err != nil {
+ writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
+ return
+ }
+ writeSuccessResponseHeadersOnly(w)
+}
+
+// KMSSetPolicyHandler - POST /minio/kms/v1/policy/policy?policy=
+func (a kmsAPIHandlers) KMSSetPolicyHandler(w http.ResponseWriter, r *http.Request) {
+ ctx := newContext(r, w, "KMSSetPolicy")
+ defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r))
+
+ objectAPI, _ := validateAdminReq(ctx, w, r, iampolicy.KMSSetPolicyAction)
+ if objectAPI == nil {
+ return
+ }
+
+ if GlobalKMS == nil {
+ writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrKMSNotConfigured), r.URL)
+ return
+ }
+ manager, ok := GlobalKMS.(kms.PolicyManager)
+ if !ok {
+ writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrNotImplemented), r.URL)
+ return
+ }
+ var policy kes.Policy
+ if err := json.NewDecoder(r.Body).Decode(&policy); err != nil {
+ writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
+ return
+ }
+ if err := manager.SetPolicy(ctx, r.Form.Get("policy"), &policy); err != nil {
+ writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
+ return
+ }
+ writeSuccessResponseHeadersOnly(w)
+}
+
+// KMSDeletePolicyHandler - DELETE /minio/kms/v1/policy/delete?policy=
+func (a kmsAPIHandlers) KMSDeletePolicyHandler(w http.ResponseWriter, r *http.Request) {
+ ctx := newContext(r, w, "KMSDeletePolicy")
+ defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r))
+
+ objectAPI, _ := validateAdminReq(ctx, w, r, iampolicy.KMSDeletePolicyAction)
+ if objectAPI == nil {
+ return
+ }
+
+ if GlobalKMS == nil {
+ writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrKMSNotConfigured), r.URL)
+ return
+ }
+
+ manager, ok := GlobalKMS.(kms.PolicyManager)
+ if !ok {
+ writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrNotImplemented), r.URL)
+ return
+ }
+ if err := manager.DeletePolicy(ctx, r.Form.Get("policy")); err != nil {
+ writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
+ return
+ }
+ writeSuccessResponseHeadersOnly(w)
+}
+
+// KMSListPoliciesHandler - GET /minio/kms/v1/policy/list?pattern=
+func (a kmsAPIHandlers) KMSListPoliciesHandler(w http.ResponseWriter, r *http.Request) {
+ ctx := newContext(r, w, "KMSListPolicies")
+ defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r))
+
+ objectAPI, _ := validateAdminReq(ctx, w, r, iampolicy.KMSListPoliciesAction)
+ if objectAPI == nil {
+ return
+ }
+
+ if GlobalKMS == nil {
+ writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrKMSNotConfigured), r.URL)
+ return
+ }
+ manager, ok := GlobalKMS.(kms.PolicyManager)
+ if !ok {
+ writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrNotImplemented), r.URL)
+ return
+ }
+ policies, err := manager.ListPolicies(ctx, r.Form.Get("pattern"))
+ if err != nil {
+ writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
+ return
+ }
+ values, err := policies.Values(0)
+ if err != nil {
+ writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
+ return
+ }
+ if res, err := json.Marshal(values); err != nil {
+ writeCustomErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrInternalError), err.Error(), r.URL)
+ } else {
+ writeSuccessResponseJSON(w, res)
+ }
+}
+
+// KMSGetPolicyHandler - GET /minio/kms/v1/policy/get?policy=
+func (a kmsAPIHandlers) KMSGetPolicyHandler(w http.ResponseWriter, r *http.Request) {
+ ctx := newContext(r, w, "KMSGetPolicy")
+ defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r))
+
+ objectAPI, _ := validateAdminReq(ctx, w, r, iampolicy.KMSGetPolicyAction)
+ if objectAPI == nil {
+ return
+ }
+
+ if GlobalKMS == nil {
+ writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrKMSNotConfigured), r.URL)
+ return
+ }
+ manager, ok := GlobalKMS.(kms.PolicyManager)
+ if !ok {
+ writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrNotImplemented), r.URL)
+ return
+ }
+ policy, err := manager.GetPolicy(ctx, r.Form.Get("policy"))
+ if err != nil {
+ writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
+ return
+ }
+
+ if p, err := json.Marshal(policy); err != nil {
+ writeCustomErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrInternalError), err.Error(), r.URL)
+ } else {
+ writeSuccessResponseJSON(w, p)
+ }
+}
+
+// KMSDescribeIdentityHandler - GET /minio/kms/v1/identity/describe?identity=
+func (a kmsAPIHandlers) KMSDescribeIdentityHandler(w http.ResponseWriter, r *http.Request) {
+ ctx := newContext(r, w, "KMSDescribeIdentity")
+ defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r))
+
+ objectAPI, _ := validateAdminReq(ctx, w, r, iampolicy.KMSDescribeIdentityAction)
+ if objectAPI == nil {
+ return
+ }
+
+ if GlobalKMS == nil {
+ writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrKMSNotConfigured), r.URL)
+ return
+ }
+ manager, ok := GlobalKMS.(kms.IdentityManager)
+ if !ok {
+ writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrNotImplemented), r.URL)
+ return
+ }
+ identity, err := manager.DescribeIdentity(ctx, r.Form.Get("identity"))
+ if err != nil {
+ writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
+ return
+ }
+ i, err := json.Marshal(identity)
+ if err != nil {
+ writeCustomErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrInternalError), err.Error(), r.URL)
+ return
+ }
+ writeSuccessResponseJSON(w, i)
+}
+
+type describeSelfIdentityResponse struct {
+ Policy *kes.Policy `json:"policy"`
+ PolicyName string `json:"policyName"`
+ Identity string `json:"identity"`
+ IsAdmin bool `json:"isAdmin"`
+ CreatedAt time.Time `json:"createdAt"`
+ CreatedBy string `json:"createdBy"`
+}
+
+// KMSDescribeSelfIdentityHandler - GET /minio/kms/v1/identity/describe-self
+func (a kmsAPIHandlers) KMSDescribeSelfIdentityHandler(w http.ResponseWriter, r *http.Request) {
+ ctx := newContext(r, w, "KMSDescribeSelfIdentity")
+ defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r))
+
+ objectAPI, _ := validateAdminReq(ctx, w, r, iampolicy.KMSDescribeSelfIdentityAction)
+ if objectAPI == nil {
+ return
+ }
+
+ if GlobalKMS == nil {
+ writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrKMSNotConfigured), r.URL)
+ return
+ }
+ manager, ok := GlobalKMS.(kms.IdentityManager)
+ if !ok {
+ writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrNotImplemented), r.URL)
+ return
+ }
+ identity, policy, err := manager.DescribeSelfIdentity(ctx)
+ if err != nil {
+ writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
+ return
+ }
+ res := &describeSelfIdentityResponse{
+ Policy: policy,
+ PolicyName: identity.Policy,
+ Identity: identity.Identity.String(),
+ IsAdmin: identity.IsAdmin,
+ CreatedAt: identity.CreatedAt,
+ CreatedBy: identity.CreatedBy.String(),
+ }
+ i, err := json.Marshal(res)
+ if err != nil {
+ writeCustomErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrInternalError), err.Error(), r.URL)
+ return
+ }
+ writeSuccessResponseJSON(w, i)
+}
+
+// KMSDeleteIdentityHandler - DELETE /minio/kms/v1/identity/delete?identity=
+func (a kmsAPIHandlers) KMSDeleteIdentityHandler(w http.ResponseWriter, r *http.Request) {
+ ctx := newContext(r, w, "KMSDeleteIdentity")
+ defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r))
+
+ objectAPI, _ := validateAdminReq(ctx, w, r, iampolicy.KMSDeleteIdentityAction)
+ if objectAPI == nil {
+ return
+ }
+
+ if GlobalKMS == nil {
+ writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrKMSNotConfigured), r.URL)
+ return
+ }
+ manager, ok := GlobalKMS.(kms.IdentityManager)
+ if !ok {
+ writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrNotImplemented), r.URL)
+ return
+ }
+
+ if err := manager.DeleteIdentity(ctx, r.Form.Get("policy")); err != nil {
+ writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
+ return
+ }
+ writeSuccessResponseHeadersOnly(w)
+}
+
+// KMSListIdentitiesHandler - GET /minio/kms/v1/identity/list?pattern=
+func (a kmsAPIHandlers) KMSListIdentitiesHandler(w http.ResponseWriter, r *http.Request) {
+ ctx := newContext(r, w, "KMSListIdentities")
+ defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r))
+
+ objectAPI, _ := validateAdminReq(ctx, w, r, iampolicy.KMSListIdentitiesAction)
+ if objectAPI == nil {
+ return
+ }
+
+ if GlobalKMS == nil {
+ writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrKMSNotConfigured), r.URL)
+ return
+ }
+ manager, ok := GlobalKMS.(kms.IdentityManager)
+ if !ok {
+ writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrNotImplemented), r.URL)
+ return
+ }
+ identities, err := manager.ListIdentities(ctx, r.Form.Get("pattern"))
+ if err != nil {
+ writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
+ return
+ }
+ values, err := identities.Values(0)
+ if err != nil {
+ writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
+ return
+ }
+ if res, err := json.Marshal(values); err != nil {
+ writeCustomErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrInternalError), err.Error(), r.URL)
+ } else {
+ writeSuccessResponseJSON(w, res)
+ }
+}
diff --git a/cmd/kms-router.go b/cmd/kms-router.go
new file mode 100644
index 000000000..fc4f06438
--- /dev/null
+++ b/cmd/kms-router.go
@@ -0,0 +1,80 @@
+// Copyright (c) 2015-2022 MinIO, Inc.
+//
+// This file is part of MinIO Object Storage stack
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+
+package cmd
+
+import (
+ "net/http"
+
+ "github.com/gorilla/mux"
+ "github.com/klauspost/compress/gzhttp"
+ "github.com/klauspost/compress/gzip"
+ "github.com/minio/minio/internal/logger"
+)
+
+const (
+ kmsPathPrefix = minioReservedBucketPath + "/kms"
+ kmsAPIVersion = "v1"
+ kmsAPIVersionPrefix = SlashSeparator + kmsAPIVersion
+)
+
+type kmsAPIHandlers struct{}
+
+// registerKMSRouter - Registers KMS APIs
+func registerKMSRouter(router *mux.Router) {
+ kmsAPI := kmsAPIHandlers{}
+ kmsRouter := router.PathPrefix(kmsPathPrefix).Subrouter()
+
+ KMSVersions := []string{
+ kmsAPIVersionPrefix,
+ }
+
+ gz, err := gzhttp.NewWrapper(gzhttp.MinSize(1000), gzhttp.CompressionLevel(gzip.BestSpeed))
+ if err != nil {
+ // Static params, so this is very unlikely.
+ logger.Fatal(err, "Unable to initialize server")
+ }
+
+ for _, version := range KMSVersions {
+ // KMS Status APIs
+ kmsRouter.Methods(http.MethodGet).Path(version + "/status").HandlerFunc(gz(httpTraceAll(kmsAPI.KMSStatusHandler)))
+ // KMS Key APIs
+ kmsRouter.Methods(http.MethodPost).Path(version+"/key/create").HandlerFunc(gz(httpTraceAll(kmsAPI.KMSCreateKeyHandler))).Queries("key-id", "{key-id:.*}")
+ kmsRouter.Methods(http.MethodPost).Path(version+"/key/import").HandlerFunc(gz(httpTraceAll(kmsAPI.KMSImportKeyHandler))).Queries("key-id", "{key-id:.*}")
+ kmsRouter.Methods(http.MethodDelete).Path(version+"/key/delete").HandlerFunc(gz(httpTraceAll(kmsAPI.KMSDeleteKeyHandler))).Queries("key-id", "{key-id:.*}")
+ kmsRouter.Methods(http.MethodGet).Path(version+"/key/list").HandlerFunc(gz(httpTraceAll(kmsAPI.KMSListKeysHandler))).Queries("pattern", "{pattern:.*}")
+ kmsRouter.Methods(http.MethodGet).Path(version + "/key/status").HandlerFunc(gz(httpTraceAll(kmsAPI.KMSKeyStatusHandler)))
+
+ // KMS Policy APIs
+ kmsRouter.Methods(http.MethodPost).Path(version+"/policy/set").HandlerFunc(gz(httpTraceAll(kmsAPI.KMSSetPolicyHandler))).Queries("policy", "{policy:.*}")
+ kmsRouter.Methods(http.MethodPost).Path(version+"/policy/assign").HandlerFunc(gz(httpTraceAll(kmsAPI.KMSAssignPolicyHandler))).Queries("policy", "{policy:.*}")
+ kmsRouter.Methods(http.MethodGet).Path(version+"/policy/describe").HandlerFunc(gz(httpTraceAll(kmsAPI.KMSDescribePolicyHandler))).Queries("policy", "{policy:.*}")
+ kmsRouter.Methods(http.MethodGet).Path(version+"/policy/get").HandlerFunc(gz(httpTraceAll(kmsAPI.KMSGetPolicyHandler))).Queries("policy", "{policy:.*}")
+ kmsRouter.Methods(http.MethodDelete).Path(version+"/policy/delete").HandlerFunc(gz(httpTraceAll(kmsAPI.KMSDeletePolicyHandler))).Queries("policy", "{policy:.*}")
+ kmsRouter.Methods(http.MethodGet).Path(version+"/policy/list").HandlerFunc(gz(httpTraceAll(kmsAPI.KMSListPoliciesHandler))).Queries("pattern", "{pattern:.*}")
+
+ // KMS Identity APIs
+ kmsRouter.Methods(http.MethodGet).Path(version+"/identity/describe").HandlerFunc(gz(httpTraceAll(kmsAPI.KMSDescribeIdentityHandler))).Queries("identity", "{identity:.*}")
+ kmsRouter.Methods(http.MethodGet).Path(version + "/identity/describe-self").HandlerFunc(gz(httpTraceAll(kmsAPI.KMSDescribeSelfIdentityHandler)))
+ kmsRouter.Methods(http.MethodDelete).Path(version+"/identity/delete").HandlerFunc(gz(httpTraceAll(kmsAPI.KMSDeleteIdentityHandler))).Queries("identity", "{identity:.*}")
+ kmsRouter.Methods(http.MethodGet).Path(version+"/identity/list").HandlerFunc(gz(httpTraceAll(kmsAPI.KMSListIdentitiesHandler))).Queries("pattern", "{pattern:.*}")
+ }
+
+ // If none of the routes match add default error handler routes
+ kmsRouter.NotFoundHandler = httpTraceAll(errorResponseHandler)
+ kmsRouter.MethodNotAllowedHandler = httpTraceAll(methodNotAllowedHandler("KMS"))
+}
diff --git a/cmd/routers.go b/cmd/routers.go
index ed044d626..d04dac112 100644
--- a/cmd/routers.go
+++ b/cmd/routers.go
@@ -89,6 +89,9 @@ func configureServerHandler(endpointServerPools EndpointServerPools) (http.Handl
// Add STS router always.
registerSTSRouter(router)
+ // Add KMS router
+ registerKMSRouter(router)
+
// Add API router
registerAPIRouter(router)
diff --git a/internal/kms/kes.go b/internal/kms/kes.go
index ad4323daa..4695c4ef8 100644
--- a/internal/kms/kes.go
+++ b/internal/kms/kes.go
@@ -314,10 +314,10 @@ func (c *kesClient) ListPolicies(ctx context.Context, pattern string) (*kes.Poli
}
// SetPolicy creates or updates a policy.
-func (c *kesClient) SetPolicy(ctx context.Context, policy, data string) error {
+func (c *kesClient) SetPolicy(ctx context.Context, policy string, policyItem *kes.Policy) error {
c.lock.RLock()
defer c.lock.RUnlock()
- return c.client.SetPolicy(ctx, policy, &kes.Policy{Allow: []string{"*"}, Info: kes.PolicyInfo{Name: "my-app2"}})
+ return c.client.SetPolicy(ctx, policy, policyItem)
}
// GetPolicy gets a policy from KMS.
diff --git a/internal/kms/policy-manager.go b/internal/kms/policy-manager.go
index 865de405b..d4c872632 100644
--- a/internal/kms/policy-manager.go
+++ b/internal/kms/policy-manager.go
@@ -37,7 +37,7 @@ type PolicyManager interface {
AssignPolicy(ctx context.Context, policy, identity string) error
// SetPolicy creates or updates a policy.
- SetPolicy(ctx context.Context, policy, data string) error
+ SetPolicy(ctx context.Context, policy string, policyItem *kes.Policy) error
// GetPolicy gets a policy from KMS.
GetPolicy(ctx context.Context, policy string) (*kes.Policy, error)