mirror of
https://github.com/minio/minio.git
synced 2024-12-24 22:25:54 -05:00
Implement KMS handlers (#15737)
This commit is contained in:
parent
57cfdfd8fb
commit
3307aa1260
@ -245,6 +245,12 @@ func isAdminReq(r *http.Request) bool {
|
|||||||
return strings.HasPrefix(r.URL.Path, adminPathPrefix)
|
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.
|
// Supported Amz date headers.
|
||||||
var amzDateHeaders = []string{
|
var amzDateHeaders = []string{
|
||||||
// Do not chane this order, x-amz-date value should be
|
// 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
|
// For all other requests reject access to reserved buckets
|
||||||
bucketName, _ := request2BucketObjectName(r)
|
bucketName, _ := request2BucketObjectName(r)
|
||||||
if isMinioReservedBucket(bucketName) || isMinioMetaBucket(bucketName) {
|
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 {
|
if ok {
|
||||||
tc.funcName = "handler.ValidRequest"
|
tc.funcName = "handler.ValidRequest"
|
||||||
tc.responseRecorder.LogErrBody = true
|
tc.responseRecorder.LogErrBody = true
|
||||||
}
|
}
|
||||||
|
|
||||||
writeErrorResponse(r.Context(), w, errorCodes.ToAPIErr(ErrAllAccessDisabled), r.URL)
|
writeErrorResponse(r.Context(), w, errorCodes.ToAPIErr(ErrAllAccessDisabled), r.URL)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
607
cmd/kms-handlers.go
Normal file
607
cmd/kms-handlers.go
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
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=<master-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=<master-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=<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=<master-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=<master-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=<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=<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=<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=<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=<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=<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=<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=<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=<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)
|
||||||
|
}
|
||||||
|
}
|
80
cmd/kms-router.go
Normal file
80
cmd/kms-router.go
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
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"))
|
||||||
|
}
|
@ -89,6 +89,9 @@ func configureServerHandler(endpointServerPools EndpointServerPools) (http.Handl
|
|||||||
// Add STS router always.
|
// Add STS router always.
|
||||||
registerSTSRouter(router)
|
registerSTSRouter(router)
|
||||||
|
|
||||||
|
// Add KMS router
|
||||||
|
registerKMSRouter(router)
|
||||||
|
|
||||||
// Add API router
|
// Add API router
|
||||||
registerAPIRouter(router)
|
registerAPIRouter(router)
|
||||||
|
|
||||||
|
@ -314,10 +314,10 @@ func (c *kesClient) ListPolicies(ctx context.Context, pattern string) (*kes.Poli
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SetPolicy creates or updates a policy.
|
// 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()
|
c.lock.RLock()
|
||||||
defer c.lock.RUnlock()
|
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.
|
// GetPolicy gets a policy from KMS.
|
||||||
|
@ -37,7 +37,7 @@ type PolicyManager interface {
|
|||||||
AssignPolicy(ctx context.Context, policy, identity string) error
|
AssignPolicy(ctx context.Context, policy, identity string) error
|
||||||
|
|
||||||
// SetPolicy creates or updates a policy.
|
// 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 gets a policy from KMS.
|
||||||
GetPolicy(ctx context.Context, policy string) (*kes.Policy, error)
|
GetPolicy(ctx context.Context, policy string) (*kes.Policy, error)
|
||||||
|
Loading…
Reference in New Issue
Block a user