mirror of
https://github.com/minio/minio.git
synced 2025-11-07 12:52:58 -05:00
Allow a KMS Action to specify keys in the Resources of a policy (#20079)
This commit is contained in:
@@ -24,6 +24,7 @@ import (
|
||||
|
||||
"github.com/minio/kms-go/kes"
|
||||
"github.com/minio/madmin-go/v3"
|
||||
"github.com/minio/minio/internal/auth"
|
||||
"github.com/minio/minio/internal/kms"
|
||||
"github.com/minio/minio/internal/logger"
|
||||
"github.com/minio/pkg/v3/policy"
|
||||
@@ -56,7 +57,7 @@ func (a kmsAPIHandlers) KMSStatusHandler(w http.ResponseWriter, r *http.Request)
|
||||
writeSuccessResponseJSON(w, resp)
|
||||
}
|
||||
|
||||
// KMSMetricsHandler - POST /minio/kms/v1/metrics
|
||||
// KMSMetricsHandler - GET /minio/kms/v1/metrics
|
||||
func (a kmsAPIHandlers) KMSMetricsHandler(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := newContext(r, w, "KMSMetrics")
|
||||
defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r))
|
||||
@@ -83,7 +84,7 @@ func (a kmsAPIHandlers) KMSMetricsHandler(w http.ResponseWriter, r *http.Request
|
||||
}
|
||||
}
|
||||
|
||||
// KMSAPIsHandler - POST /minio/kms/v1/apis
|
||||
// KMSAPIsHandler - GET /minio/kms/v1/apis
|
||||
func (a kmsAPIHandlers) KMSAPIsHandler(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := newContext(r, w, "KMSAPIs")
|
||||
defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r))
|
||||
@@ -114,7 +115,7 @@ type versionResponse struct {
|
||||
Version string `json:"version"`
|
||||
}
|
||||
|
||||
// KMSVersionHandler - POST /minio/kms/v1/version
|
||||
// KMSVersionHandler - GET /minio/kms/v1/version
|
||||
func (a kmsAPIHandlers) KMSVersionHandler(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := newContext(r, w, "KMSVersion")
|
||||
defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r))
|
||||
@@ -159,7 +160,20 @@ func (a kmsAPIHandlers) KMSCreateKeyHandler(w http.ResponseWriter, r *http.Reque
|
||||
return
|
||||
}
|
||||
|
||||
if err := GlobalKMS.CreateKey(ctx, &kms.CreateKeyRequest{Name: r.Form.Get("key-id")}); err != nil {
|
||||
keyID := r.Form.Get("key-id")
|
||||
|
||||
// Ensure policy allows the user to create this key name
|
||||
cred, owner, s3Err := validateAdminSignature(ctx, r, "")
|
||||
if s3Err != ErrNone {
|
||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(s3Err), r.URL)
|
||||
return
|
||||
}
|
||||
if !checkKMSActionAllowed(r, owner, cred, policy.KMSCreateKeyAction, keyID) {
|
||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAccessDenied), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
if err := GlobalKMS.CreateKey(ctx, &kms.CreateKeyRequest{Name: keyID}); err != nil {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
@@ -171,6 +185,9 @@ func (a kmsAPIHandlers) KMSListKeysHandler(w http.ResponseWriter, r *http.Reques
|
||||
ctx := newContext(r, w, "KMSListKeys")
|
||||
defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r))
|
||||
|
||||
// This only checks if the action (kms:ListKeys) is allowed, it does not check
|
||||
// each key name against the policy's Resources. We check that below, once
|
||||
// we have the list of key names from the KMS.
|
||||
objectAPI, _ := validateAdminReq(ctx, w, r, policy.KMSListKeysAction)
|
||||
if objectAPI == nil {
|
||||
return
|
||||
@@ -180,7 +197,7 @@ func (a kmsAPIHandlers) KMSListKeysHandler(w http.ResponseWriter, r *http.Reques
|
||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrKMSNotConfigured), r.URL)
|
||||
return
|
||||
}
|
||||
names, _, err := GlobalKMS.ListKeyNames(ctx, &kms.ListRequest{
|
||||
allKeyNames, _, err := GlobalKMS.ListKeyNames(ctx, &kms.ListRequest{
|
||||
Prefix: r.Form.Get("pattern"),
|
||||
})
|
||||
if err != nil {
|
||||
@@ -188,8 +205,24 @@ func (a kmsAPIHandlers) KMSListKeysHandler(w http.ResponseWriter, r *http.Reques
|
||||
return
|
||||
}
|
||||
|
||||
values := make([]kes.KeyInfo, 0, len(names))
|
||||
for _, name := range names {
|
||||
// Get the cred and owner for checking authz below.
|
||||
cred, owner, s3Err := validateAdminSignature(ctx, r, "")
|
||||
if s3Err != ErrNone {
|
||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(s3Err), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
// Now we have all the key names, for each of them, check whether the policy grants permission for
|
||||
// the user to list it.
|
||||
keyNames := []string{}
|
||||
for _, name := range allKeyNames {
|
||||
if checkKMSActionAllowed(r, owner, cred, policy.KMSListKeysAction, name) {
|
||||
keyNames = append(keyNames, name)
|
||||
}
|
||||
}
|
||||
|
||||
values := make([]kes.KeyInfo, 0, len(keyNames))
|
||||
for _, name := range keyNames {
|
||||
values = append(values, kes.KeyInfo{
|
||||
Name: name,
|
||||
})
|
||||
@@ -224,6 +257,17 @@ func (a kmsAPIHandlers) KMSKeyStatusHandler(w http.ResponseWriter, r *http.Reque
|
||||
KeyID: keyID,
|
||||
}
|
||||
|
||||
// Ensure policy allows the user to get this key's status
|
||||
cred, owner, s3Err := validateAdminSignature(ctx, r, "")
|
||||
if s3Err != ErrNone {
|
||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(s3Err), r.URL)
|
||||
return
|
||||
}
|
||||
if !checkKMSActionAllowed(r, owner, cred, policy.KMSKeyStatusAction, keyID) {
|
||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAccessDenied), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
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, &kms.GenerateKeyRequest{Name: keyID, AssociatedData: kmsContext})
|
||||
@@ -274,3 +318,16 @@ func (a kmsAPIHandlers) KMSKeyStatusHandler(w http.ResponseWriter, r *http.Reque
|
||||
}
|
||||
writeSuccessResponseJSON(w, resp)
|
||||
}
|
||||
|
||||
// checkKMSActionAllowed checks for authorization for a specific action on a resource.
|
||||
func checkKMSActionAllowed(r *http.Request, owner bool, cred auth.Credentials, action policy.KMSAction, resource string) bool {
|
||||
return globalIAMSys.IsAllowed(policy.Args{
|
||||
AccountName: cred.AccessKey,
|
||||
Groups: cred.Groups,
|
||||
Action: policy.Action(action),
|
||||
ConditionValues: getConditionValues(r, "", cred),
|
||||
IsOwner: owner,
|
||||
Claims: cred.Claims,
|
||||
BucketName: resource, // overloading BucketName as that's what the policy engine uses to assemble a Resource.
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user