mirror of
https://github.com/versity/versitygw.git
synced 2026-09-29 03:05:34 +00:00
fix: role last-used tracking, and record S3 requests in last-used metadata
Role last-used tracking was missing entirely - `GetRole` returned a `RoleLastUsed` element that nothing ever wrote, rendering the zero time instead of the empty element AWS returns for an unused role - and access key last-used only ever saw the `IAM`/`STS` control plane, so a credential used exclusively against the S3 gateway reported as never used. Roles now record a use whenever a request authenticates with one of their session credentials, through a new `Storer.RecordRoleUsage` mirroring `RecordAccessKeyUsage`, gated on the session's role still being the one it was minted against so a session outliving its role can't attribute its use to a same-named replacement. `LastUsedDate` became a `*time.Time` so an unused role renders as an empty element. Both records now cover the S3 data plane as well: the gateway sends its configured region and `s3` on evaluate-policy and the IAM service records the caller there, so `GetAccessKeyLastUsed's` `ServiceName` is now iam, sts or s3. That call was chosen over derive-signing-key, which runs before signature verification and takes its region and service from the caller's own `Authorization` header - recording there would let anyone who knows an access key id refresh and poison another identity's audit record. Requests denied by a bucket policy or made against a public bucket are not recorded, since neither reaches identity-policy evaluation. To keep per-request recording affordable, an update is skipped while the stored record has the same service and region and is under a minute old; a change of either is written through immediately. Assuming a role is not a use, a request denied by an identity policy is, and both successful and denied S3 requests update the record. Also moves the `OIDC-dependent` tests into the `s3-iam-session` group so runoidctests.sh runs a single group.
This commit is contained in:
@@ -1368,6 +1368,7 @@ func TestIAMGetRole(ts *TestState) {
|
||||
ts.Run(IAMGetRole_long_role_name)
|
||||
ts.Run(IAMGetRole_non_existing_role)
|
||||
ts.Run(IAMGetRole_success)
|
||||
ts.Run(IAMGetRole_role_last_used_never_used)
|
||||
}
|
||||
|
||||
func TestIAMListRoles(ts *TestState) {
|
||||
@@ -1620,7 +1621,6 @@ func TestIAMAssumeRoleWithWebIdentity(ts *TestState) {
|
||||
ts.Run(IAMAssumeRoleWithWebIdentity_oaud_condition_mismatch)
|
||||
ts.Run(IAMAssumeRoleWithWebIdentity_issuer_trailing_slash_mismatch)
|
||||
ts.Run(IAMAssumeRoleWithWebIdentity_issuer_scheme_mismatch)
|
||||
ts.Run(IAMAssumeRoleWithWebIdentity_github_oidc_live)
|
||||
}
|
||||
|
||||
func TestIAMGetCallerIdentity(ts *TestState) {
|
||||
@@ -1737,8 +1737,14 @@ func TestS3IAMAccessControl(ts *TestState) {
|
||||
ts.Run(S3IAMAccessControl_condition_multiple_keys_anded)
|
||||
ts.Run(S3IAMAccessControl_inactive_and_deleted_credentials)
|
||||
ts.Run(S3IAMAccessControl_bucket_policy_unknown_principal_rejected)
|
||||
ts.Run(S3IAMAccessControl_access_key_last_used_records_s3)
|
||||
}
|
||||
|
||||
// TestS3IAMSessionAccessControl is the one group the OIDC workflow runs, so
|
||||
// it carries every test that needs a real, signed ID token — including the
|
||||
// IAM/STS-endpoint ones below, which are not S3 access-control tests but
|
||||
// have the same GitHub-OIDC prerequisite. Every test here skips itself
|
||||
// outside a job that can mint a token.
|
||||
func TestS3IAMSessionAccessControl(ts *TestState) {
|
||||
ts.Run(S3IAMSession_role_policy_allows)
|
||||
ts.Run(S3IAMSession_role_without_policy_denied)
|
||||
@@ -1760,6 +1766,9 @@ func TestS3IAMSessionAccessControl(ts *TestState) {
|
||||
ts.Run(S3IAMSession_delete_objects_authorizes_each_key)
|
||||
ts.Run(S3IAMSession_condition_identity_keys)
|
||||
ts.Run(S3IAMSession_get_caller_identity_matches_s3_principal)
|
||||
ts.Run(S3IAMSession_AssumeRoleWithWebIdentity_github_oidc_live)
|
||||
ts.Run(S3IAMSession_GetRole_role_last_used_recorded)
|
||||
ts.Run(S3IAMSession_role_last_used_records_s3)
|
||||
}
|
||||
|
||||
func TestIAM(ts *TestState) {
|
||||
@@ -2136,6 +2145,9 @@ func GetIntTests() IntTests {
|
||||
"S3IAMSession_role_policy_explicit_deny_wins": S3IAMSession_role_policy_explicit_deny_wins,
|
||||
"S3IAMSession_role_without_policy_denied": S3IAMSession_role_without_policy_denied,
|
||||
"S3IAMSession_role_policy_allows": S3IAMSession_role_policy_allows,
|
||||
"S3IAMSession_AssumeRoleWithWebIdentity_github_oidc_live": S3IAMSession_AssumeRoleWithWebIdentity_github_oidc_live,
|
||||
"S3IAMSession_GetRole_role_last_used_recorded": S3IAMSession_GetRole_role_last_used_recorded,
|
||||
"S3IAMSession_role_last_used_records_s3": S3IAMSession_role_last_used_records_s3,
|
||||
"S3IAMAccessControl_retention_extension_needs_no_bypass": S3IAMAccessControl_retention_extension_needs_no_bypass,
|
||||
"S3IAMAccessControl_delete_objects_authorizes_each_key": S3IAMAccessControl_delete_objects_authorizes_each_key,
|
||||
"S3IAMAccessControl_delete_objects_version_needs_separate_permission": S3IAMAccessControl_delete_objects_version_needs_separate_permission,
|
||||
@@ -2168,6 +2180,7 @@ func GetIntTests() IntTests {
|
||||
"S3IAMAccessControl_condition_multiple_keys_anded": S3IAMAccessControl_condition_multiple_keys_anded,
|
||||
"S3IAMAccessControl_inactive_and_deleted_credentials": S3IAMAccessControl_inactive_and_deleted_credentials,
|
||||
"S3IAMAccessControl_bucket_policy_unknown_principal_rejected": S3IAMAccessControl_bucket_policy_unknown_principal_rejected,
|
||||
"S3IAMAccessControl_access_key_last_used_records_s3": S3IAMAccessControl_access_key_last_used_records_s3,
|
||||
"Authentication_invalid_auth_header": Authentication_invalid_auth_header,
|
||||
"Authentication_unsupported_signature_version": Authentication_unsupported_signature_version,
|
||||
"Authentication_missing_components": Authentication_missing_components,
|
||||
@@ -2405,6 +2418,7 @@ func GetIntTests() IntTests {
|
||||
"IAMGetRole_long_role_name": IAMGetRole_long_role_name,
|
||||
"IAMGetRole_non_existing_role": IAMGetRole_non_existing_role,
|
||||
"IAMGetRole_success": IAMGetRole_success,
|
||||
"IAMGetRole_role_last_used_never_used": IAMGetRole_role_last_used_never_used,
|
||||
"IAMListRoles_invalid_path_prefix": IAMListRoles_invalid_path_prefix,
|
||||
"IAMListRoles_long_path_prefix": IAMListRoles_long_path_prefix,
|
||||
"IAMListRoles_invalid_max_items": IAMListRoles_invalid_max_items,
|
||||
@@ -2594,7 +2608,6 @@ func GetIntTests() IntTests {
|
||||
"IAMAssumeRoleWithWebIdentity_oaud_condition_mismatch": IAMAssumeRoleWithWebIdentity_oaud_condition_mismatch,
|
||||
"IAMAssumeRoleWithWebIdentity_issuer_trailing_slash_mismatch": IAMAssumeRoleWithWebIdentity_issuer_trailing_slash_mismatch,
|
||||
"IAMAssumeRoleWithWebIdentity_issuer_scheme_mismatch": IAMAssumeRoleWithWebIdentity_issuer_scheme_mismatch,
|
||||
"IAMAssumeRoleWithWebIdentity_github_oidc_live": IAMAssumeRoleWithWebIdentity_github_oidc_live,
|
||||
"IAMGetCallerIdentity_root_success": IAMGetCallerIdentity_root_success,
|
||||
"IAMGetCallerIdentity_user_success": IAMGetCallerIdentity_user_success,
|
||||
"IAMGetCallerIdentity_unknown_access_key": IAMGetCallerIdentity_unknown_access_key,
|
||||
|
||||
Reference in New Issue
Block a user