mirror of
https://github.com/pgsty/minio.git
synced 2026-10-31 21:02:06 +03:00
fix: keep streaming trailers visible after replication headers are stripped
A request that does not earn replication trust continues with a clone whose internal replication headers are removed. r.Clone copies the Trailer map, but the streaming body reader created from the original request fills the original map, so a trailing checksum was never seen by the hash reader and PutObject and UploadPart with STREAMING-UNSIGNED-PAYLOAD-TRAILER failed with XAmzContentChecksumMismatch whenever an untrusted X-Minio-Source-* header was present. Share the trailer map with the clone, as the Snowball path already does for its per-entry requests, and cover both handlers with a test. The marker evaluation that was copied into six handlers now lives in evaluateReplicationTrust so the rule (a declared replica without the replication permission is rejected; trust needs the exact marker plus the permission) is defined once. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PvgysXDmhPBBimCReYtA8q Signed-off-by: Feng Ruohang <rh@vonng.com>
This commit is contained in:
@@ -70,6 +70,25 @@ func replicationPermissionAllowed(ctx context.Context, r *http.Request, bucket,
|
||||
return authorizeRequest(ctx, r, action) == ErrNone
|
||||
}
|
||||
|
||||
// evaluateReplicationTrust decides whether a request may carry replication
|
||||
// semantics for the given action. A request that declares itself a replica
|
||||
// without holding the replication permission is rejected. trusted reports that
|
||||
// the exact marker came from a permitted principal; replica additionally
|
||||
// requires the request to declare REPLICA status.
|
||||
func evaluateReplicationTrust(ctx context.Context, r *http.Request, bucket, object string, action policy.Action) (trusted, replica bool, s3Err APIErrorCode) {
|
||||
rawReplica := hasReplicaStatus(r.Header)
|
||||
markerExact := hasReplicationMarker(r.Header)
|
||||
permitted := false
|
||||
if rawReplica || markerExact {
|
||||
permitted = replicationPermissionAllowed(ctx, r, bucket, object, action)
|
||||
}
|
||||
if rawReplica && !permitted {
|
||||
return false, false, ErrAccessDenied
|
||||
}
|
||||
trusted = markerExact && permitted
|
||||
return trusted, trusted && rawReplica, ErrNone
|
||||
}
|
||||
|
||||
// replicationRequestHeaders are internal request controls. They are removed
|
||||
// only after signature verification when a request has not earned replication
|
||||
// trust. Public S3/SSE/checksum headers, proxy loop guards, and replication
|
||||
@@ -110,6 +129,9 @@ func hasReplicationRequestHeaders(h http.Header) bool {
|
||||
func cloneRequestWithoutReplicationHeaders(ctx context.Context, r *http.Request) *http.Request {
|
||||
clone := r.Clone(ctx)
|
||||
stripReplicationRequestHeaders(clone.Header)
|
||||
// A streaming body reader built from the original request fills r.Trailer
|
||||
// as the body is consumed; the checksum reader must observe that same map.
|
||||
clone.Trailer = r.Trailer
|
||||
return clone
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user