mirror of
https://github.com/minio/minio.git
synced 2024-12-26 15:15:55 -05:00
Add the policy name to the audit logs tags when doing policy-based API calls. Add retention settings to tags (#20638)
* Add the policy name to the audit log tags when doing policy-based API calls * Audit log the retention settings requested in the API call * Audit log of retention on PutObjectRetention API path too
This commit is contained in:
parent
c07e5b49d4
commit
d202fdd022
@ -37,6 +37,7 @@ import (
|
|||||||
"github.com/minio/madmin-go/v3"
|
"github.com/minio/madmin-go/v3"
|
||||||
"github.com/minio/minio/internal/auth"
|
"github.com/minio/minio/internal/auth"
|
||||||
"github.com/minio/minio/internal/config/dns"
|
"github.com/minio/minio/internal/config/dns"
|
||||||
|
"github.com/minio/minio/internal/logger"
|
||||||
"github.com/minio/mux"
|
"github.com/minio/mux"
|
||||||
xldap "github.com/minio/pkg/v3/ldap"
|
xldap "github.com/minio/pkg/v3/ldap"
|
||||||
"github.com/minio/pkg/v3/policy"
|
"github.com/minio/pkg/v3/policy"
|
||||||
@ -1579,6 +1580,7 @@ func (a adminAPIHandlers) InfoCannedPolicy(w http.ResponseWriter, r *http.Reques
|
|||||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, errTooManyPolicies), r.URL)
|
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, errTooManyPolicies), r.URL)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
setReqInfoPolicyName(ctx, name)
|
||||||
|
|
||||||
policyDoc, err := globalIAMSys.InfoPolicy(name)
|
policyDoc, err := globalIAMSys.InfoPolicy(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -1682,6 +1684,7 @@ func (a adminAPIHandlers) RemoveCannedPolicy(w http.ResponseWriter, r *http.Requ
|
|||||||
|
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
policyName := vars["name"]
|
policyName := vars["name"]
|
||||||
|
setReqInfoPolicyName(ctx, policyName)
|
||||||
|
|
||||||
if err := globalIAMSys.DeletePolicy(ctx, policyName, true); err != nil {
|
if err := globalIAMSys.DeletePolicy(ctx, policyName, true); err != nil {
|
||||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||||
@ -1714,6 +1717,7 @@ func (a adminAPIHandlers) AddCannedPolicy(w http.ResponseWriter, r *http.Request
|
|||||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAdminResourceInvalidArgument), r.URL)
|
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAdminResourceInvalidArgument), r.URL)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
setReqInfoPolicyName(ctx, policyName)
|
||||||
|
|
||||||
// Error out if Content-Length is missing.
|
// Error out if Content-Length is missing.
|
||||||
if r.ContentLength <= 0 {
|
if r.ContentLength <= 0 {
|
||||||
@ -1779,6 +1783,7 @@ func (a adminAPIHandlers) SetPolicyForUserOrGroup(w http.ResponseWriter, r *http
|
|||||||
policyName := vars["policyName"]
|
policyName := vars["policyName"]
|
||||||
entityName := vars["userOrGroup"]
|
entityName := vars["userOrGroup"]
|
||||||
isGroup := vars["isGroup"] == "true"
|
isGroup := vars["isGroup"] == "true"
|
||||||
|
setReqInfoPolicyName(ctx, policyName)
|
||||||
|
|
||||||
if !isGroup {
|
if !isGroup {
|
||||||
ok, _, err := globalIAMSys.IsTempUser(entityName)
|
ok, _, err := globalIAMSys.IsTempUser(entityName)
|
||||||
@ -1864,7 +1869,7 @@ func (a adminAPIHandlers) SetPolicyForUserOrGroup(w http.ResponseWriter, r *http
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListPolicyMappingEntities - GET /minio/admin/v3/idp/builtin/polciy-entities?policy=xxx&user=xxx&group=xxx
|
// ListPolicyMappingEntities - GET /minio/admin/v3/idp/builtin/policy-entities?policy=xxx&user=xxx&group=xxx
|
||||||
func (a adminAPIHandlers) ListPolicyMappingEntities(w http.ResponseWriter, r *http.Request) {
|
func (a adminAPIHandlers) ListPolicyMappingEntities(w http.ResponseWriter, r *http.Request) {
|
||||||
ctx := r.Context()
|
ctx := r.Context()
|
||||||
|
|
||||||
@ -1966,6 +1971,7 @@ func (a adminAPIHandlers) AttachDetachPolicyBuiltin(w http.ResponseWriter, r *ht
|
|||||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
setReqInfoPolicyName(ctx, strings.Join(addedOrRemoved, ","))
|
||||||
|
|
||||||
respBody := madmin.PolicyAssociationResp{
|
respBody := madmin.PolicyAssociationResp{
|
||||||
UpdatedAt: updatedAt,
|
UpdatedAt: updatedAt,
|
||||||
@ -2812,3 +2818,10 @@ func commonAddServiceAccount(r *http.Request, ldap bool) (context.Context, auth.
|
|||||||
|
|
||||||
return ctx, cred, opts, createReq, targetUser, APIError{}
|
return ctx, cred, opts, createReq, targetUser, APIError{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// setReqInfoPolicyName will set the given policyName as a tag on the context's request info,
|
||||||
|
// so that it appears in audit logs.
|
||||||
|
func setReqInfoPolicyName(ctx context.Context, policyName string) {
|
||||||
|
reqInfo := logger.GetReqInfo(ctx)
|
||||||
|
reqInfo.SetTags("policyName", policyName)
|
||||||
|
}
|
||||||
|
@ -1809,6 +1809,10 @@ func (api objectAPIHandlers) PutBucketObjectLockConfigHandler(w http.ResponseWri
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Audit log tags.
|
||||||
|
reqInfo := logger.GetReqInfo(ctx)
|
||||||
|
reqInfo.SetTags("retention", config.String())
|
||||||
|
|
||||||
configData, err := xml.Marshal(config)
|
configData, err := xml.Marshal(config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL)
|
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL)
|
||||||
|
@ -2899,6 +2899,8 @@ func (api objectAPIHandlers) PutObjectRetentionHandler(w http.ResponseWriter, r
|
|||||||
writeErrorResponse(ctx, w, apiErr, r.URL)
|
writeErrorResponse(ctx, w, apiErr, r.URL)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
reqInfo := logger.GetReqInfo(ctx)
|
||||||
|
reqInfo.SetTags("retention", objRetention.String())
|
||||||
|
|
||||||
opts, err := getOpts(ctx, r, bucket, object)
|
opts, err := getOpts(ctx, r, bucket, object)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -237,6 +237,25 @@ type Config struct {
|
|||||||
} `xml:"Rule,omitempty"`
|
} `xml:"Rule,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// String returns the human readable format of object lock configuration, used in audit logs.
|
||||||
|
func (config Config) String() string {
|
||||||
|
parts := []string{
|
||||||
|
fmt.Sprintf("Enabled: %v", config.Enabled()),
|
||||||
|
}
|
||||||
|
if config.Rule != nil {
|
||||||
|
if config.Rule.DefaultRetention.Mode != "" {
|
||||||
|
parts = append(parts, fmt.Sprintf("Mode: %s", config.Rule.DefaultRetention.Mode))
|
||||||
|
}
|
||||||
|
if config.Rule.DefaultRetention.Days != nil {
|
||||||
|
parts = append(parts, fmt.Sprintf("Days: %d", *config.Rule.DefaultRetention.Days))
|
||||||
|
}
|
||||||
|
if config.Rule.DefaultRetention.Years != nil {
|
||||||
|
parts = append(parts, fmt.Sprintf("Years: %d", *config.Rule.DefaultRetention.Years))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return strings.Join(parts, ", ")
|
||||||
|
}
|
||||||
|
|
||||||
// Enabled returns true if config.ObjectLockEnabled is set to Enabled
|
// Enabled returns true if config.ObjectLockEnabled is set to Enabled
|
||||||
func (config *Config) Enabled() bool {
|
func (config *Config) Enabled() bool {
|
||||||
return config.ObjectLockEnabled == Enabled
|
return config.ObjectLockEnabled == Enabled
|
||||||
@ -349,6 +368,10 @@ type ObjectRetention struct {
|
|||||||
RetainUntilDate RetentionDate `xml:"RetainUntilDate,omitempty"`
|
RetainUntilDate RetentionDate `xml:"RetainUntilDate,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (o ObjectRetention) String() string {
|
||||||
|
return fmt.Sprintf("Mode: %s, RetainUntilDate: %s", o.Mode, o.RetainUntilDate.Time)
|
||||||
|
}
|
||||||
|
|
||||||
// Maximum 4KiB size per object retention config.
|
// Maximum 4KiB size per object retention config.
|
||||||
const maxObjectRetentionSize = 1 << 12
|
const maxObjectRetentionSize = 1 << 12
|
||||||
|
|
||||||
|
@ -611,3 +611,72 @@ func TestFilterObjectLockMetadata(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestToString(t *testing.T) {
|
||||||
|
days := uint64(30)
|
||||||
|
daysPtr := &days
|
||||||
|
years := uint64(2)
|
||||||
|
yearsPtr := &years
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
c Config
|
||||||
|
want string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "happy case",
|
||||||
|
c: Config{
|
||||||
|
ObjectLockEnabled: "Enabled",
|
||||||
|
},
|
||||||
|
want: "Enabled: true",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "with default retention days",
|
||||||
|
c: Config{
|
||||||
|
ObjectLockEnabled: "Enabled",
|
||||||
|
Rule: &struct {
|
||||||
|
DefaultRetention DefaultRetention `xml:"DefaultRetention"`
|
||||||
|
}{
|
||||||
|
DefaultRetention: DefaultRetention{
|
||||||
|
Mode: RetGovernance,
|
||||||
|
Days: daysPtr,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: "Enabled: true, Mode: GOVERNANCE, Days: 30",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "with default retention years",
|
||||||
|
c: Config{
|
||||||
|
ObjectLockEnabled: "Enabled",
|
||||||
|
Rule: &struct {
|
||||||
|
DefaultRetention DefaultRetention `xml:"DefaultRetention"`
|
||||||
|
}{
|
||||||
|
DefaultRetention: DefaultRetention{
|
||||||
|
Mode: RetCompliance,
|
||||||
|
Years: yearsPtr,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: "Enabled: true, Mode: COMPLIANCE, Years: 2",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "disabled case",
|
||||||
|
c: Config{
|
||||||
|
ObjectLockEnabled: "Disabled",
|
||||||
|
},
|
||||||
|
want: "Enabled: false",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "empty case",
|
||||||
|
c: Config{},
|
||||||
|
want: "Enabled: false",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
got := tt.c.String()
|
||||||
|
if got != tt.want {
|
||||||
|
t.Errorf("test: %s, got: '%v', want: '%v'", tt.name, got, tt.want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user