mirror of
https://github.com/pgsty/minio.git
synced 2026-09-26 04:45:59 +03:00
fix: keep the CORS lookup fail-closed until bucket metadata is loaded
Restore the startup guard removed by the previous cleanup: while bucket metadata is still loading, a non-resident name may be a bucket with a restrictive CORS document, so the request gets no CORS answer instead of the global policy. After startup a non-resident name still falls back to the global policy without any metadata I/O; the separate load-failure set stays removed. 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:
+4
-3
@@ -789,9 +789,10 @@ func corsHandler(handler http.Handler) http.Handler {
|
|||||||
if bucket, _ := request2BucketObjectName(r); bucket != "" && globalBucketMetadataSys != nil {
|
if bucket, _ := request2BucketObjectName(r); bucket != "" && globalBucketMetadataSys != nil {
|
||||||
// Resident-only lookup: this runs before authentication with a
|
// Resident-only lookup: this runs before authentication with a
|
||||||
// client-supplied path segment as the bucket name, so it must
|
// client-supplied path segment as the bucket name, so it must
|
||||||
// never load or cache metadata. A bucket with a stored CORS
|
// never load or cache metadata. While startup loading is still
|
||||||
// document that failed to parse gets no CORS headers; any other
|
// running, and for a bucket whose stored CORS document failed to
|
||||||
// non-resident name falls back to the global policy below.
|
// parse, the request gets no CORS headers; any other non-resident
|
||||||
|
// name falls back to the global policy below.
|
||||||
cfg, _, err := globalBucketMetadataSys.GetResidentCorsConfig(bucket)
|
cfg, _, err := globalBucketMetadataSys.GetResidentCorsConfig(bucket)
|
||||||
if err == nil && cfg != nil {
|
if err == nil && cfg != nil {
|
||||||
if applyBucketCors(w, r, cfg) {
|
if applyBucketCors(w, r, cfg) {
|
||||||
|
|||||||
@@ -598,15 +598,15 @@ func testBucketCorsUnknownBucketDoesNotGrowMetadata(obj ObjectLayer, _ string, _
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBucketCorsStartupMissUsesGlobalFallbackWithoutIO(t *testing.T) {
|
func TestBucketCorsStartupMissFailsClosedWithoutIO(t *testing.T) {
|
||||||
ExecObjectLayerAPITest(ExecObjectLayerAPITestArgs{
|
ExecObjectLayerAPITest(ExecObjectLayerAPITestArgs{
|
||||||
t: t,
|
t: t,
|
||||||
objAPITest: testBucketCorsStartupMissUsesGlobalFallbackWithoutIO,
|
objAPITest: testBucketCorsStartupMissFailsClosedWithoutIO,
|
||||||
endpoints: []string{"GetBucketCors"},
|
endpoints: []string{"GetBucketCors"},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func testBucketCorsStartupMissUsesGlobalFallbackWithoutIO(obj ObjectLayer, _ string, _ string, _ http.Handler, _ auth.Credentials, t *testing.T) {
|
func testBucketCorsStartupMissFailsClosedWithoutIO(obj ObjectLayer, _ string, _ string, _ http.Handler, _ auth.Credentials, t *testing.T) {
|
||||||
oldObjectAPI := newObjectLayerFn()
|
oldObjectAPI := newObjectLayerFn()
|
||||||
oldMetadataSys := globalBucketMetadataSys
|
oldMetadataSys := globalBucketMetadataSys
|
||||||
counting := &corsLookupCountingObjectLayer{ObjectLayer: obj}
|
counting := &corsLookupCountingObjectLayer{ObjectLayer: obj}
|
||||||
@@ -630,8 +630,8 @@ func testBucketCorsStartupMissUsesGlobalFallbackWithoutIO(obj ObjectLayer, _ str
|
|||||||
if !innerCalled || rec.Code != http.StatusNoContent {
|
if !innerCalled || rec.Code != http.StatusNoContent {
|
||||||
t.Fatalf("startup miss did not reach inner handler: called=%v status=%d", innerCalled, rec.Code)
|
t.Fatalf("startup miss did not reach inner handler: called=%v status=%d", innerCalled, rec.Code)
|
||||||
}
|
}
|
||||||
if got := rec.Header().Get("Access-Control-Allow-Origin"); got != "https://app.example.com" {
|
if got := rec.Header().Get("Access-Control-Allow-Origin"); got != "" {
|
||||||
t.Fatalf("startup miss did not fall back to the global policy: %q", got)
|
t.Fatalf("startup miss used permissive global CORS: %q", got)
|
||||||
}
|
}
|
||||||
if got := counting.getObjectNInfoCalls.Load(); got != 0 {
|
if got := counting.getObjectNInfoCalls.Load(); got != 0 {
|
||||||
t.Fatalf("startup miss performed %d metadata reads", got)
|
t.Fatalf("startup miss performed %d metadata reads", got)
|
||||||
|
|||||||
@@ -400,18 +400,23 @@ func (sys *BucketMetadataSys) GetSSEConfig(bucket string) (*bucketsse.BucketSSEC
|
|||||||
// GetResidentCorsConfig returns the CORS configuration of a bucket whose
|
// GetResidentCorsConfig returns the CORS configuration of a bucket whose
|
||||||
// metadata is already resident in memory. It runs before authentication for
|
// metadata is already resident in memory. It runs before authentication for
|
||||||
// every Origin-bearing request with a client-supplied path segment, so it
|
// every Origin-bearing request with a client-supplied path segment, so it
|
||||||
// never loads or caches metadata. A name that is not resident, including any
|
// never loads or caches metadata. Until startup loading has completed, a
|
||||||
// real bucket while startup loading is still in progress, reports
|
// non-resident name may still be a bucket with a restrictive document, so it
|
||||||
// errConfigNotFound and the caller applies the global CORS policy exactly as
|
// reports errBucketMetadataNotInitialized and gets no CORS answer. After
|
||||||
// releases without per-bucket CORS did.
|
// that, a non-resident name reports errConfigNotFound and the caller applies
|
||||||
|
// the global CORS policy exactly as releases without per-bucket CORS did.
|
||||||
func (sys *BucketMetadataSys) GetResidentCorsConfig(bucket string) (*cors.Config, time.Time, error) {
|
func (sys *BucketMetadataSys) GetResidentCorsConfig(bucket string) (*cors.Config, time.Time, error) {
|
||||||
if isReservedOrInvalidBucket(bucket, true) {
|
if isReservedOrInvalidBucket(bucket, true) {
|
||||||
return nil, time.Time{}, errConfigNotFound
|
return nil, time.Time{}, errConfigNotFound
|
||||||
}
|
}
|
||||||
sys.RLock()
|
sys.RLock()
|
||||||
meta, ok := sys.metadataMap[bucket]
|
meta, ok := sys.metadataMap[bucket]
|
||||||
|
initialized := sys.initialized
|
||||||
sys.RUnlock()
|
sys.RUnlock()
|
||||||
if !ok {
|
if !ok {
|
||||||
|
if !initialized {
|
||||||
|
return nil, time.Time{}, errBucketMetadataNotInitialized
|
||||||
|
}
|
||||||
return nil, time.Time{}, errConfigNotFound
|
return nil, time.Time{}, errConfigNotFound
|
||||||
}
|
}
|
||||||
if meta.corsConfigErr != nil {
|
if meta.corsConfigErr != nil {
|
||||||
|
|||||||
Reference in New Issue
Block a user