Add session policy support to IAM (#8338)
* Add session policy support to IAM - Implement policy evaluation for session tokens in policy_engine.go - Add session_policy field to session claims for tracking applied policies - Update STS service to include session policies in token generation - Add IAM integration tests for session policy validation - Update IAM manager to support policy attachment to sessions - Extend S3 API STS endpoint to handle session policy restrictions * fix: optimize session policy evaluation and add documentation * sts: add NormalizeSessionPolicy helper for inline session policies * sts: support inline session policies for AssumeRoleWithWebIdentity and credential-based flows * s3api: parse and normalize Policy parameter for STS HTTP handlers * tests: add session policy unit tests and integration tests for inline policy downscoping * tests: add s3tables STS inline policy integration * iam: handle user principals and validate tokens * sts: enforce inline session policy size limit * tests: harden s3tables STS integration config * iam: clarify principal policy resolution errors * tests: improve STS integration endpoint selection
This commit is contained in:
@@ -165,12 +165,25 @@ func (h *STSHandlers) handleAssumeRoleWithWebIdentity(w http.ResponseWriter, r *
|
||||
return
|
||||
}
|
||||
|
||||
sessionPolicyJSON, err := sts.NormalizeSessionPolicy(r.FormValue("Policy"))
|
||||
if err != nil {
|
||||
h.writeSTSErrorResponse(w, r, STSErrMalformedPolicyDocument,
|
||||
fmt.Errorf("invalid Policy document: %w", err))
|
||||
return
|
||||
}
|
||||
|
||||
var sessionPolicyPtr *string
|
||||
if sessionPolicyJSON != "" {
|
||||
sessionPolicyPtr = &sessionPolicyJSON
|
||||
}
|
||||
|
||||
// Build request for STS service
|
||||
request := &sts.AssumeRoleWithWebIdentityRequest{
|
||||
RoleArn: roleArn,
|
||||
WebIdentityToken: webIdentityToken,
|
||||
RoleSessionName: roleSessionName,
|
||||
DurationSeconds: durationSeconds,
|
||||
Policy: sessionPolicyPtr,
|
||||
}
|
||||
|
||||
// Call STS service
|
||||
@@ -216,6 +229,8 @@ func (h *STSHandlers) handleAssumeRoleWithWebIdentity(w http.ResponseWriter, r *
|
||||
|
||||
// handleAssumeRole handles the AssumeRole API action
|
||||
// This requires AWS Signature V4 authentication
|
||||
// Inline session policies (Policy parameter) are supported for AssumeRole,
|
||||
// AssumeRoleWithWebIdentity, and AssumeRoleWithLDAPIdentity.
|
||||
func (h *STSHandlers) handleAssumeRole(w http.ResponseWriter, r *http.Request) {
|
||||
// Extract parameters from form
|
||||
roleArn := r.FormValue("RoleArn")
|
||||
@@ -290,8 +305,16 @@ func (h *STSHandlers) handleAssumeRole(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
// Parse optional inline session policy for downscoping
|
||||
sessionPolicyJSON, err := sts.NormalizeSessionPolicy(r.FormValue("Policy"))
|
||||
if err != nil {
|
||||
h.writeSTSErrorResponse(w, r, STSErrMalformedPolicyDocument,
|
||||
fmt.Errorf("invalid Policy document: %w", err))
|
||||
return
|
||||
}
|
||||
|
||||
// Generate common STS components
|
||||
stsCreds, assumedUser, err := h.prepareSTSCredentials(roleArn, roleSessionName, durationSeconds, nil)
|
||||
stsCreds, assumedUser, err := h.prepareSTSCredentials(roleArn, roleSessionName, durationSeconds, sessionPolicyJSON, nil)
|
||||
if err != nil {
|
||||
h.writeSTSErrorResponse(w, r, STSErrInternalError, err)
|
||||
return
|
||||
@@ -420,12 +443,19 @@ func (h *STSHandlers) handleAssumeRoleWithLDAPIdentity(w http.ResponseWriter, r
|
||||
return
|
||||
}
|
||||
|
||||
sessionPolicyJSON, err := sts.NormalizeSessionPolicy(r.FormValue("Policy"))
|
||||
if err != nil {
|
||||
h.writeSTSErrorResponse(w, r, STSErrMalformedPolicyDocument,
|
||||
fmt.Errorf("invalid Policy document: %w", err))
|
||||
return
|
||||
}
|
||||
|
||||
// Generate common STS components with LDAP-specific claims
|
||||
modifyClaims := func(claims *sts.STSSessionClaims) {
|
||||
claims.WithIdentityProvider("ldap", identity.UserID, identity.Provider)
|
||||
}
|
||||
|
||||
stsCreds, assumedUser, err := h.prepareSTSCredentials(roleArn, roleSessionName, durationSeconds, modifyClaims)
|
||||
stsCreds, assumedUser, err := h.prepareSTSCredentials(roleArn, roleSessionName, durationSeconds, sessionPolicyJSON, modifyClaims)
|
||||
if err != nil {
|
||||
h.writeSTSErrorResponse(w, r, STSErrInternalError, err)
|
||||
return
|
||||
@@ -445,7 +475,7 @@ func (h *STSHandlers) handleAssumeRoleWithLDAPIdentity(w http.ResponseWriter, r
|
||||
|
||||
// prepareSTSCredentials extracts common shared logic for credential generation
|
||||
func (h *STSHandlers) prepareSTSCredentials(roleArn, roleSessionName string,
|
||||
durationSeconds *int64, modifyClaims func(*sts.STSSessionClaims)) (STSCredentials, *AssumedRoleUser, error) {
|
||||
durationSeconds *int64, sessionPolicy string, modifyClaims func(*sts.STSSessionClaims)) (STSCredentials, *AssumedRoleUser, error) {
|
||||
|
||||
// Calculate duration
|
||||
duration := time.Hour // Default 1 hour
|
||||
@@ -479,6 +509,10 @@ func (h *STSHandlers) prepareSTSCredentials(roleArn, roleSessionName string,
|
||||
WithSessionName(roleSessionName).
|
||||
WithRoleInfo(roleArn, fmt.Sprintf("%s:%s", roleName, roleSessionName), assumedRoleArn)
|
||||
|
||||
if sessionPolicy != "" {
|
||||
claims.WithSessionPolicy(sessionPolicy)
|
||||
}
|
||||
|
||||
// Apply custom claims if provided (e.g., LDAP identity)
|
||||
if modifyClaims != nil {
|
||||
modifyClaims(claims)
|
||||
@@ -582,13 +616,14 @@ type LDAPIdentityResult struct {
|
||||
type STSErrorCode string
|
||||
|
||||
const (
|
||||
STSErrAccessDenied STSErrorCode = "AccessDenied"
|
||||
STSErrExpiredToken STSErrorCode = "ExpiredTokenException"
|
||||
STSErrInvalidAction STSErrorCode = "InvalidAction"
|
||||
STSErrInvalidParameterValue STSErrorCode = "InvalidParameterValue"
|
||||
STSErrMissingParameter STSErrorCode = "MissingParameter"
|
||||
STSErrSTSNotReady STSErrorCode = "ServiceUnavailable"
|
||||
STSErrInternalError STSErrorCode = "InternalError"
|
||||
STSErrAccessDenied STSErrorCode = "AccessDenied"
|
||||
STSErrExpiredToken STSErrorCode = "ExpiredTokenException"
|
||||
STSErrInvalidAction STSErrorCode = "InvalidAction"
|
||||
STSErrInvalidParameterValue STSErrorCode = "InvalidParameterValue"
|
||||
STSErrMalformedPolicyDocument STSErrorCode = "MalformedPolicyDocument"
|
||||
STSErrMissingParameter STSErrorCode = "MissingParameter"
|
||||
STSErrSTSNotReady STSErrorCode = "ServiceUnavailable"
|
||||
STSErrInternalError STSErrorCode = "InternalError"
|
||||
)
|
||||
|
||||
// stsErrorResponses maps error codes to HTTP status and messages
|
||||
@@ -596,13 +631,14 @@ var stsErrorResponses = map[STSErrorCode]struct {
|
||||
HTTPStatusCode int
|
||||
Message string
|
||||
}{
|
||||
STSErrAccessDenied: {http.StatusForbidden, "Access Denied"},
|
||||
STSErrExpiredToken: {http.StatusBadRequest, "Token has expired"},
|
||||
STSErrInvalidAction: {http.StatusBadRequest, "Invalid action"},
|
||||
STSErrInvalidParameterValue: {http.StatusBadRequest, "Invalid parameter value"},
|
||||
STSErrMissingParameter: {http.StatusBadRequest, "Missing required parameter"},
|
||||
STSErrSTSNotReady: {http.StatusServiceUnavailable, "STS service not ready"},
|
||||
STSErrInternalError: {http.StatusInternalServerError, "Internal error"},
|
||||
STSErrAccessDenied: {http.StatusForbidden, "Access Denied"},
|
||||
STSErrExpiredToken: {http.StatusBadRequest, "Token has expired"},
|
||||
STSErrInvalidAction: {http.StatusBadRequest, "Invalid action"},
|
||||
STSErrInvalidParameterValue: {http.StatusBadRequest, "Invalid parameter value"},
|
||||
STSErrMalformedPolicyDocument: {http.StatusBadRequest, "Malformed policy document"},
|
||||
STSErrMissingParameter: {http.StatusBadRequest, "Missing required parameter"},
|
||||
STSErrSTSNotReady: {http.StatusServiceUnavailable, "STS service not ready"},
|
||||
STSErrInternalError: {http.StatusInternalServerError, "Internal error"},
|
||||
}
|
||||
|
||||
// STSErrorResponse is the XML error response format
|
||||
|
||||
Reference in New Issue
Block a user