Repository: minio/minio (language: go)

Issue:
Incompatiblity of anonymous PUT request to public bucket with S3 Java SDK version 2.30
When I start minio via Docker locally with a public bucket and I try to put a file there using the latest version of the S3 Java SDK and anonymous credentials, I am getting this exception:
```
software.amazon.awssdk.services.s3.model.S3Exception: Missing fields in request. (Service: S3, Status Code: 400, Request ID: ...
```

## Expected Behavior
The file should be added.

## Current Behavior
I am getting the above exception.

## Steps to Reproduce (for bugs)
```
docker run -p 9000:9000 -p9001:9001 --restart=unless-stopped --name minio -e MINIO_DEFAULT_BUCKETS=my-bucket:public bitnami/minio@sha256:ca31735c5a8e6a85de77c63bea09d8780a788ca9bbf7f1d730610c5da84a5796
```

```java
S3AsyncClient asyncClient = S3AsyncClient.builder()
        .endpointOverride(URI.create("http://localhost:9000"))
        .forcePathStyle(true)
        .credentialsProvider(AnonymousCredentialsProvider.create())
        .build();

final var input = new ByteArrayInputStream("file content".getBytes());
PutObjectRequest putObjectRequest = PutObjectRequest.builder()
        .bucket("my-bucket")
        .key("/test/filename")
        .metadata(Map.of())
        .contentType("text/plain")
        .build();

asyncClient.putObject(putObjectRequest, AsyncRequestBody.fromInputStream(
        input,
        (long) input.available(),
        Executors.newSingleThreadExecutor())
).get();
```

On v2.30.31 of the Java SDK, I am getting this stack trace:
```
Exception in thread "main" java.util.concurrent.ExecutionException: software.amazon.awssdk.services.s3.model.S3Exception: Missing fields in request. (Service: S3, Status Code: 400, Request ID: 18298B10D2319065, Extended Request ID: dd9025bab4ad464b049177c95eb6ebf374d3b3fd1af9251148b658df7ac2e3e8) (SDK Attempt Count: 1)
	at java.base/java.util.concurrent.CompletableFuture.reportGet(CompletableFuture.java:396)
	at java.base/java.util.concurrent.CompletableFuture.get(CompletableFuture.java:2073)
        ...
Caused by: software.amazon.awssdk.services.s3.model.S3Exception: Missing fields in request. (Service: S3, Status Code: 400, Request ID: 18298B10D2319065, Extended Request ID: dd9025bab4ad464b049177c95eb6ebf374d3b3fd1af9251148b658df7ac2e3e8) (SDK Attempt Count: 1)
	at software.amazon.awssdk.services.s3.model.S3Exception$BuilderImpl.build(S3Exception.java:113)
	at software.amazon.awssdk.services.s3.model.S3Exception$BuilderImpl.build(S3Exception.java:61)
	at software.amazon.awssdk.core.internal.http.pipeline.stages.utils.RetryableStageHelper.retryPolicyDisallowedRetryException(RetryableStageHelper.java:168)
	at software.amazon.awssdk.core.internal.http.pipeline.stages.AsyncRetryableStage$RetryingExecutor.maybeAttemptExecute(AsyncRetryableStage.java:135)
Caused by: software.amazon.awssdk.services.s3.model.S3Exception: Missing fields in request. (Service: S3, Status Code: 400, Request ID: 18298B10D2319065, Extended Request ID: dd9025bab4ad464b049177c95eb6ebf374d3b3fd1af9251148b658df7ac2e3e8) (SDK Attempt Count: 1)

	at software.amazon.awssdk.core.internal.http.pipeline.stages.AsyncRetryableStage$RetryingExecutor.maybeRetryExecute(AsyncRetryableStage.java:152)
	at software.amazon.awssdk.core.internal.http.pipeline.stages.AsyncRetryableStage$RetryingExecutor.lambda$attemptExecute$1(AsyncRetryableStage.java:123)
	at java.base/java.util.concurrent.CompletableFuture.uniWhenComplete(CompletableFuture.java:863)
	at java.base/java.util.concurrent.CompletableFuture$UniWhenComplete.tryFire(CompletableFuture.java:841)
	at java.base/java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:510)
	at java.base/java.util.concurrent.CompletableFuture.complete(CompletableFuture.java:2179)
	at software.amazon.awssdk.core.internal.http.pipeline.stages.MakeAsyncHttpRequestStage.lambda$execute$0(MakeAsyncHttpRequestStage.java:110)
	at java.base/java.util.concurrent.CompletableFuture.uniWhenComplete(CompletableFuture.java:863)
	at java.base/java.util.concurrent.CompletableFuture$UniWhenComplete.tryFire(CompletableFuture.java:841)
	at java.base/java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:510)
	at java.base/java.util.concurrent.CompletableFuture.complete(CompletableFuture.java:2179)
	at software.amazon.awssdk.core.internal.http.pipeline.stages.MakeAsyncHttpRequestStage.completeResponseFuture(MakeAsyncHttpRequestStage.java:253)
	at software.amazon.awssdk.core.internal.http.pipeline.stages.MakeAsyncHttpRequestStage.lambda$executeHttpRequest$3(MakeAsyncHttpRequestStage.java:167)
	at java.base/java.util.concurrent.CompletableFuture.uniHandle(CompletableFuture.java:934)
	at java.base/java.util.concurrent.CompletableFuture$UniHandle.tryFire(CompletableFuture.java:911)
	at java.base/java.util.concurrent.CompletableFuture$Completion.run(CompletableFuture.java:482)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642)
	at java.base/java.lang.Thread.run(Thread.java:1583)
```

It works in version 2.29.52; there was a change to how checksums are handled in v2.30.X of the SDK.

In version 2.30.X, it works if I either specify some credentials, or calculate the checksum explicitly:
```java
                .credentialsProvider(() -> AwsBasicCredentials.create("minioadmin", "minioadmin"))
```
or
```java
        Crc32Checksum crc32Checksum = new Crc32Checksum();
        crc32Checksum.update(data, 0, data.length);
        String checksum = Base64.getEncoder().encodeToString(crc32Checksum.getChecksumBytes());

        // ...

        PutObjectRequest putObjectRequest = PutObjectRequest.builder()
        // ...
                .checksumAlgorithm(ChecksumAlgorithm.CRC32)
                .checksumCRC32(checksum)
        // ...
```

## Context
Trying to use minio for testing before deploying code to run against S3.

## Your Environment
This exact docker image:
bitnami/minio@sha256:ca31735c5a8e6a85de77c63bea09d8780a788ca9bbf7f1d730610c5da84a5796
Running on WSL:
Linux <Device-Name> 5.15.167.4-microsoft-standard-WSL2 #1 SMP Tue Nov 5 00:21:55 UTC 2024 x86_64 x86_64 x86_64 GNU/Linux


Discussion / hints:
@klauspost this is related to checksum types not being available for public buckets. 
@harshavardhana What?

@jonathanbrem-dt Please provide...

A) The exact version you are using. Maybe use `mc admin info ....`
B) A full trace of the failing call. Either from the client side or `mc admin trace -v ALIAS`.

Closing this as no reproducer is shared. 
```
my admin info ...
```
returns:
```
●  localhost:9000
   Uptime: 2 minutes
   Version: 2025-02-28T09:55:16Z
   Network: 1/1 OK
   Drives: 1/1 OK
   Pool: 1

┌──────┬────────────────────────┬─────────────────────┬──────────────┐
│ Pool │ Drives Usage           │ Erasure stripe size │ Erasure sets │
│ 1st  │ 11.5% (total: 956 GiB) │ 1                   │ 1            │
└──────┴────────────────────────┴─────────────────────┴──────────────┘

0 B Used, 1 Bucket, 0 Objects
1 drive online, 0 drives offline, EC:0
```

and the trace:
```
admin trace -v ...
```
returns:

```
localhost:9000 [REQUEST s3.PutObject] [2025-03-12T15:01:29.849] [Client IP: 172.17.0.1]
localhost:9000 PUT /my-bucket//test/filename
localhost:9000 Proto: HTTP/1.1
localhost:9000 Host: localhost:9000
localhost:9000 Amz-Sdk-Request: attempt=1; max=4
localhost:9000 Content-Encoding: aws-chunked
localhost:9000 Content-Length: 53
localhost:9000 X-Amz-Decoded-Content-Length: 12
localhost:9000 X-Amz-Sdk-Checksum-Algorithm: CRC32
localhost:9000 Amz-Sdk-Invocation-Id: 4f75fe1b-39e9-bd8c-f2d8-48e5e55bf6f3
localhost:9000 Content-Type: text/plain
localhost:9000 Expect: 100-continue
localhost:9000 User-Agent: aws-sdk-java/2.30.35 md/io#async md/http#NettyNio ua/2.1 os/Linux#5.15.167.4-microsoft-standard-WSL2 lang/java#21.0.5 md/OpenJDK_64-Bit_Server_VM#21.0.5+11-LTS md/vendor#Eclipse_Adoptium md/en_ cfg/auth-source#anon m/D,N,N
localhost:9000 X-Amz-Content-Sha256: STREAMING-UNSIGNED-PAYLOAD-TRAILER
localhost:9000 X-Amz-Trailer: x-amz-checksum-crc32
localhost:9000 <BLOB>
localhost:9000 [RESPONSE] [2025-03-12T15:01:29.849] [ Duration 154µs TTFB 139.936µs ↑ 199 B  ↓ 349 B ]
localhost:9000 400 Bad Request
localhost:9000 Accept-Ranges: bytes
localhost:9000 Strict-Transport-Security: max-age=31536000; includeSubDomains
localhost:9000 Vary: Origin,Accept-Encoding
localhost:9000 X-Amz-Request-Id: 182C130A11749171
localhost:9000 X-Content-Type-Options: nosniff
localhost:9000 X-Ratelimit-Remaining: 6331
localhost:9000 Content-Length: 349
localhost:9000 Content-Type: application/xml
localhost:9000 Server: MinIO
localhost:9000 X-Amz-Id-2: dd9025bab4ad464b049177c95eb6ebf374d3b3fd1af9251148b658df7ac2e3e8
localhost:9000 X-Ratelimit-Limit: 6331
localhost:9000 X-Xss-Protection: 1; mode=block
localhost:9000 <?xml version="1.0" encoding="UTF-8"?>
<Error><Code>MissingFields</Code><Message>Missing fields in request.</Message><Key>test/filename</Key><BucketName>my-bucket</BucketName><Resource>/my-bucket//test/filename</Resource><RequestId>182C130A11749171</RequestId><HostId>dd9025bab4ad464b049177c95eb6ebf374d3b3fd1af9251148b658df7ac2e3e8</HostId></Error>
localhost:9000
```

If in the Java code I set
```java
                .credentialsProvider(() -> AwsBasicCredentials.create("minioadmin", "minioadmin"))
```

Then I get the trace:
```
localhost:9000 [REQUEST s3.PutObject] [2025-03-12T15:05:53.201] [Client IP: 172.17.0.1]
localhost:9000 PUT /my-bucket//test/filename
localhost:9000 Proto: HTTP/1.1
localhost:9000 Host: localhost:9000
localhost:9000 Amz-Sdk-Invocation-Id: 2907b2e5-0f9d-971c-abb7-d9acc00f3d92
localhost:9000 Content-Encoding: aws-chunked
localhost:9000 Content-Length: 53
localhost:9000 Expect: 100-continue
localhost:9000 X-Amz-Content-Sha256: STREAMING-UNSIGNED-PAYLOAD-TRAILER
localhost:9000 X-Amz-Decoded-Content-Length: 12
localhost:9000 Amz-Sdk-Request: attempt=1; max=4
localhost:9000 Authorization: AWS4-HMAC-SHA256 Credential=minioadmin/20250312/us-east-1/s3/aws4_request, SignedHeaders=amz-sdk-invocation-id;amz-sdk-request;content-encoding;content-length;content-type;host;x-amz-content-sha256;x-amz-date;x-amz-decoded-content-length;x-amz-sdk-checksum-algorithm;x-amz-trailer, Signature=a071a8bfa80c49a7591e18a19bc48ac0751000226cbbfdc3680e09de2fead212
localhost:9000 X-Amz-Sdk-Checksum-Algorithm: CRC32
localhost:9000 User-Agent: aws-sdk-java/2.30.35 md/io#async md/http#NettyNio ua/2.1 os/Linux#5.15.167.4-microsoft-standard-WSL2 lang/java#21.0.5 md/OpenJDK_64-Bit_Server_VM#21.0.5+11-LTS md/vendor#Eclipse_Adoptium md/en_ m/D,N,N
localhost:9000 Content-Type: text/plain
localhost:9000 X-Amz-Date: 20250312T140553Z
localhost:9000 X-Amz-Trailer: x-amz-checksum-crc32
localhost:9000 <BLOB>
localhost:9000 [RESPONSE] [2025-03-12T15:05:53.217] [ Duration 16.786ms TTFB 16.745718ms ↑ 277 B  ↓ 0 B ]
localhost:9000 200 OK
localhost:9000 Vary: Origin,Accept-Encoding
localhost:9000 X-Amz-Checksum-Crc32: 0NMKrg==
localhost:9000 X-Amz-Id-2: dd9025bab4ad464b049177c95eb6ebf374d3b3fd1af9251148b658df7ac2e3e8
localhost:9000 X-Content-Type-Options: nosniff
localhost:9000 X-Ratelimit-Remaining: 6331
localhost:9000 X-Xss-Protection: 1; mode=block
localhost:9000 Content-Length: 0
localhost:9000 ETag: "d10b4c3ff123b26dc068d43a8bef2d23"
localhost:9000 Strict-Transport-Security: max-age=31536000; includeSubDomains
localhost:9000 X-Amz-Request-Id: 182C134762763609
localhost:9000 X-Ratelimit-Limit: 6331
localhost:9000 Accept-Ranges: bytes
localhost:9000 Server: MinIO
localhost:9000 <BLOB>
localhost:9000
```

And if I switch to the 2.29.52 version of the Java SDK but stick with anonymous credentials, the trace is:

```
localhost:9000 [REQUEST s3.PutObject] [2025-03-12T15:10:47.359] [Client IP: 172.17.0.1]
localhost:9000 PUT /my-bucket//test/filename
localhost:9000 Proto: HTTP/1.1
localhost:9000 Host: localhost:9000
localhost:9000 Expect: 100-continue
localhost:9000 User-Agent: aws-sdk-java/2.29.52 md/io#async md/http#NettyNio ua/2.1 os/Linux#5.15.167.4-microsoft-standard-WSL2 lang/java#21.0.5 md/OpenJDK_64-Bit_Server_VM#21.0.5+11-LTS md/vendor#Eclipse_Adoptium md/en_ cfg/auth-source#anon m/D,N,N
localhost:9000 Amz-Sdk-Invocation-Id: 0e276ea1-724a-e55d-588f-70ecca541a8d
localhost:9000 Amz-Sdk-Request: attempt=1; max=4
localhost:9000 Content-Length: 12
localhost:9000 Content-Type: text/plain
localhost:9000 <BLOB>
localhost:9000 [RESPONSE] [2025-03-12T15:10:47.372] [ Duration 12.687ms TTFB 12.645992ms ↑ 101 B  ↓ 0 B ]
localhost:9000 200 OK
localhost:9000 Content-Length: 0
localhost:9000 ETag: "d10b4c3ff123b26dc068d43a8bef2d23"
localhost:9000 Server: MinIO
localhost:9000 Vary: Origin,Accept-Encoding
localhost:9000 X-Amz-Id-2: dd9025bab4ad464b049177c95eb6ebf374d3b3fd1af9251148b658df7ac2e3e8
localhost:9000 X-Amz-Request-Id: 182C138BDFA960C0
localhost:9000 X-Xss-Protection: 1; mode=block
localhost:9000 Accept-Ranges: bytes
localhost:9000 Strict-Transport-Security: max-age=31536000; includeSubDomains
localhost:9000 X-Content-Type-Options: nosniff
localhost:9000 X-Ratelimit-Limit: 6331
localhost:9000 X-Ratelimit-Remaining: 6331
localhost:9000 <BLOB>
localhost:9000
```
Okay so anonymous request has some missing fields not populated by the AWS SDKs

```localhost:9000 [REQUEST s3.PutObject] [2025-03-12T15:01:29.849] [Client IP: 172.17.0.1]
localhost:9000 PUT /my-bucket//test/filename
localhost:9000 Proto: HTTP/1.1
localhost:9000 Host: localhost:9000
localhost:9000 Amz-Sdk-Request: attempt=1; max=4
localhost:9000 Content-Encoding: aws-chunked
localhost:9000 Content-Length: 53
localhost:9000 X-Amz-Decoded-Content-Length: 12
localhost:9000 X-Amz-Sdk-Checksum-Algorithm: CRC32
localhost:9000 Amz-Sdk-Invocation-Id: 4f75fe1b-39e9-bd8c-f2d8-48e5e55bf6f3
localhost:9000 Content-Type: text/plain
localhost:9000 Expect: 100-continue
localhost:9000 User-Agent: aws-sdk-java/2.30.35 md/io#async md/http#NettyNio ua/2.1 os/Linux#5.15.167.4-microsoft-standard-WSL2 lang/java#21.0.5 md/OpenJDK_64-Bit_Server_VM#21.0.5+11-LTS md/vendor#Eclipse_Adoptium md/en_ cfg/auth-source#anon m/D,N,N
localhost:9000 X-Amz-Content-Sha256: STREAMING-UNSIGNED-PAYLOAD-TRAILER
localhost:9000 X-Amz-Trailer: x-amz-checksum-crc32
localhost:9000 <BLOB>
localhost:9000 [RESPONSE] [2025-03-12T15:01:29.849] [ Duration 154µs TTFB 139.936µs ↑ 199 B  ↓ 349 B ]
localhost:9000 400 Bad Request
localhost:9000 Accept-Ranges: bytes
localhost:9000 Strict-Transport-Security: max-age=31536000; includeSubDomains
localhost:9000 Vary: Origin,Accept-Encoding
localhost:9000 X-Amz-Request-Id: 182C130A11749171
localhost:9000 X-Content-Type-Options: nosniff
localhost:9000 X-Ratelimit-Remaining: 6331
localhost:9000 Content-Length: 349
localhost:9000 Content-Type: application/xml
localhost:9000 Server: MinIO
localhost:9000 X-Amz-Id-2: dd9025bab4ad464b049177c95eb6ebf374d3b3fd1af9251148b658df7ac2e3e8
localhost:9000 X-Ratelimit-Limit: 6331
localhost:9000 X-Xss-Protection: 1; mode=block
localhost:9000 <?xml version="1.0" encoding="UTF-8"?>
<Error><Code>MissingFields</Code><Message>Missing fields in request.</Message><Key>test/filename</Key><BucketName>my-bucket</BucketName><Resource>/my-bucket//test/filename</Resource><RequestId>182C130A11749171</RequestId><HostId>dd9025bab4ad464b049177c95eb6ebf374d3b3fd1af9251148b658df7ac2e3e8</HostId></Error>
localhost:9000
```

@klauspost 
May be we are expecting more headers.. 
> localhost:9000 Proto: HTTP/1.1
> localhost:9000 Host: localhost:9000
> localhost:9000 Amz-Sdk-Request: attempt=1; max=4
> localhost:9000 Content-Encoding: aws-chunked
> localhost:9000 Content-Length: 53
> localhost:9000 X-Amz-Decoded-Content-Length: 12
> localhost:9000 X-Amz-Sdk-Checksum-Algorithm: CRC32
> localhost:9000 Amz-Sdk-Invocation-Id: 4f75fe1b-39e9-bd8c-f2d8-48e5e55bf6f3
> localhost:9000 Content-Type: text/plain
> localhost:9000 Expect: 100-continue
> localhost:9000 User-Agent: aws-sdk-java/2.30.35 md/io#async md/http#NettyNio ua/2.1 os/Linux#5.15.167.4-microsoft-standard-WSL2 lang/java#21.0.5 md/OpenJDK_64-Bit_Server_VM#21.0.5+11-LTS md/vendor#Eclipse_Adoptium md/en_ cfg/auth-source#anon m/D,N,N
> localhost:9000 X-Amz-Content-Sha256: STREAMING-UNSIGNED-PAYLOAD-TRAILER
> localhost:9000 X-Amz-Trailer: x-amz-checksum-crc32

this looks like a bug in the aws-sdk-java-v2 they are not supposed to send 

> localhost:9000 X-Amz-Content-Sha256: STREAMING-UNSIGNED-PAYLOAD-TRAILER

https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-auth-using-authorization-header.html this is part of signature v4 requests however, you have anonymous credentials set. 
@harshavardhana I would kinda expect that to be there.

So `aws-chunked` says it is chunked, `STREAMING-UNSIGNED-PAYLOAD-TRAILER` that it is without signatures. Fine - and `X-Amz-Trailer: x-amz-checksum-crc32` should provide the checksum in the chunked format trailer.

So it looks legit enough to me. We may have a check missing or assuming something for one of this set of combinations.

I will see if I can set up a repro that does this combination of params, when I get some bandwidth.
@klauspost it may be that unsigned payloads are supported with anonymous credentials which is strange but we have to extend that for minio-go as well

As all things incorrectly documented, or not documented at all :-) 

Fix patch:
diff --git a/cmd/auth-handler.go b/cmd/auth-handler.go
index dd489591073c1..53d285e4f2aee 100644
--- a/cmd/auth-handler.go
+++ b/cmd/auth-handler.go
@@ -754,8 +754,14 @@ func isPutActionAllowed(ctx context.Context, atype authType, bucketName, objectN
 		return ErrSignatureVersionNotSupported
 	case authTypeSignedV2, authTypePresignedV2:
 		cred, owner, s3Err = getReqAccessKeyV2(r)
-	case authTypeStreamingSigned, authTypePresigned, authTypeSigned, authTypeStreamingSignedTrailer, authTypeStreamingUnsignedTrailer:
+	case authTypeStreamingSigned, authTypePresigned, authTypeSigned, authTypeStreamingSignedTrailer:
 		cred, owner, s3Err = getReqAccessKeyV4(r, region, serviceS3)
+	case authTypeStreamingUnsignedTrailer:
+		cred, owner, s3Err = getReqAccessKeyV4(r, region, serviceS3)
+		if s3Err == ErrMissingFields {
+			// Could be anonymous. cred + owner is zero value.
+			s3Err = ErrNone
+		}
 	}
 	if s3Err != ErrNone {
 		return s3Err


Test patch: