Commit Graph

19 Commits

Author SHA1 Message Date
Ben McClelland e137e8d375 fix: connection early termination resulting in internal error
When the connection terminates before all bytes read, we were
getting an io.ErrUnexpectedEOF that was not being handled as
a standard io.EOF resulting in an internal error being raised.
Translate io.ErrUnexpectedEOF to io.EOF so that we return the
normal errors for unexpected content. Add a log message so
that its clear the error is due to the connection being
terminated before all data sent and not the fault of the
gateway.
2026-06-01 10:12:41 -07:00
niksis02 9f786b3c2c feat: global error refactoring
Fixes #2123
Fixes #2120
Fixes #2116
Fixes #2111
Fixes #2108
Fixes #2086
Fixes #2085
Fixes #2083
Fixes #2081
Fixes #2080
Fixes #2073
Fixes #2072
Fixes #2071
Fixes #2069
Fixes #2044
Fixes #2043
Fixes #2042
Fixes #2041
Fixes #2040
Fixes #2039
Fixes #2036
Fixes #2035
Fixes #2034
Fixes #2028
Fixes #2020
Fixes #1842
Fixes #1810
Fixes #1780
Fixes #1775
Fixes #1736
Fixes #1705
Fixes #1663
Fixes #1645
Fixes #1583
Fixes #1526
Fixes #1514
Fixes #1493
Fixes #1487
Fixes #959
Fixes #779
Closes #823
Closes #85

Refactor global S3 error handling around structured error types and centralized XML response generation.

All S3 errors now share the common APIError base for the fields every error has: Code, HTTP status code, and Message. Non-traditional errors that need AWS-compatible XML fields now have dedicated typed errors in the s3err package. Each typed error implements the shared S3Error behavior so controllers and middleware can handle errors consistently while still emitting error-specific XML fields.

Add a dedicated InvalidArgumentError type because InvalidArgument is used widely across request validation, auth, copy source handling, object lock validation, multipart validation, and header parsing. The new InvalidArgument path uses explicit InvalidArgErrorCode constants with predefined descriptions and ArgumentName values, keeping call sites readable while preserving the correct InvalidArgument XML shape and optional ArgumentValue.

New structured errors added in s3err:
- `AccessForbiddenError`: Method, ResourceType
- `BadDigestError`: CalculatedDigest, ExpectedDigest
- `BucketError`: BucketName
- `ContentSHA256MismatchError`: ClientComputedContentSHA256, S3ComputedContentSHA256
- `EntityTooLargeError`: ProposedSize, MaxSizeAllowed
- `EntityTooSmallError`: ProposedSize, MinSizeAllowed
- `ExpiredPresignedURLError`: ServerTime, XAmzExpires, Expires
- `InvalidAccessKeyIdError`: AWSAccessKeyId
- `InvalidArgumentError`: Description, ArgumentName, ArgumentValue
- `InvalidChunkSizeError`: Chunk, BadChunkSize
- `InvalidDigestError`: ContentMD5
- `InvalidLocationConstraintError`: LocationConstraint
- `InvalidPartError`: UploadId, PartNumber, ETag
- `InvalidRangeError`: RangeRequested, ActualObjectSize
- `InvalidTagError`: TagKey, TagValue
- `KeyTooLongError`: Size, MaxSizeAllowed
- `MetadataTooLargeError`: Size, MaxSizeAllowed
- `MethodNotAllowedError`: Method, ResourceType, AllowedMethods
- `NoSuchUploadError`: UploadId
- `NoSuchVersionError`: Key, VersionId
- `NotImplementedError`: Header, AdditionalMessage
- `PreconditionFailedError`: Condition
- `RequestTimeTooSkewedError`: RequestTime, ServerTime, MaxAllowedSkewMilliseconds
- `SignatureDoesNotMatchError`: AWSAccessKeyId, StringToSign, SignatureProvided, StringToSignBytes, CanonicalRequest, CanonicalRequestBytes

Fix CompleteMultipartUpload validation in the Azure backend so missing or empty `ETag` values return the appropriate S3 error instead of allowing a gateway panic.

Fix presigned authentication expiration validation to compare server time in `UTC`, matching the `UTC` timestamp used by presigned URL signing.

Add request ID and host ID support across S3 requests. Each request now receives AWS S3-like identifiers, returned in response headers as `x-amz-request-id` and `x-amz-id-2` and included in all XML error responses as RequestId and HostId. The generated ID structure is designed to resemble AWS S3 request IDs and host IDs.

The request signature calculation/validation for streaming uploads was previously delayed until the request body was fully read, both for Authorization header authentication and presigned URLs.
Now, the signature is validated immediately in the authorization middlewares without reading the request body, since the signature calculation itself does not depend on the request body. Instead, only the `x-amz-content-sha256` SHA-256 hash calculation is delayed.
2026-05-21 23:49:34 +04:00
niksis02 cc54aad003 feat: adds integration tests for STREAMING-AWS4-HMAC-SHA256-PAYLOAD-TRAILER requests 2025-12-26 21:16:01 +04:00
niksis02 807399459d feat: adds integration tests for STREAMING-AWS4-HMAC-SHA256-PAYLOAD requests 2025-12-23 02:31:27 +04:00
niksis02 9eaaeedd28 fix: bunch of fixes in signed streaming requests
Fixes #1683
Fixes #1684
Fixes #1685
Fixes #1690
Fixes #1691
Fixes #1692
Fixes #1694
Fixes #1695

This PR primarily focuses on error handling and checksum calculation for signed streaming requests of type `STREAMING-AWS4-HMAC-SHA256-PAYLOAD` and `STREAMING-AWS4-HMAC-SHA256-PAYLOAD-TRAILER`.

It fixes the error type returned when the payload encoding is incorrect: the correct `IncompleteBody` error is now returned.

Chunk size validation has been added, enforcing the rule that only the final chunk may be smaller than 8192 bytes.

The `x-amz-trailer` header value is now validated against the checksum trailer present in the payload. For `STREAMING-AWS4-HMAC-SHA256-PAYLOAD-TRAILER` requests, if no checksum trailer is provided in the payload, the gateway now returns an `IncompleteBody` error.

If there is a mismatch between the `x-amz-trailer` header and the checksum trailer in the payload, or if the checksum header key in the payload is invalid, a `MalformedTrailer` error is returned.

The `x-amz-decoded-content-length` header value is now compared against the actual decoded payload length, and an `IncompleteBody` error is returned if there is a mismatch.

Finally, the double checksum calculation issue has been fixed. For `STREAMING-AWS4-HMAC-SHA256-PAYLOAD-TRAILER` requests, the trailing checksum is now parsed from the request payload and stored in the backend, instead of being recalculated by the backend.
2025-12-16 23:15:34 +04:00
Ben McClelland 40da4a31d3 chore: cleanup unused constants
We have some leftover constants from some previous changes. This
just cleans up all that are no longer needed.
2025-10-09 12:19:00 -07:00
Ben McClelland 743707b9ae Merge pull request #1509 from versity/ben/chunk-panic
fix: panic in signed-chunk-reader with incorrect debug string
2025-09-02 14:06:34 -07:00
Ben McClelland 488a9ac1bb fix: panic in signed-chunk-reader with incorrect debug string
The following panic was triggered when mc client (that uses
chunked uploads) would upload a 171164 byte file. This likely
could have been hit with other sizes as well, but this size
was able to reliably reproduce the issue.

panic: runtime error: slice bounds out of range [:2] with capacity 1

goroutine 66 [running]:
github.com/versity/versitygw/s3api/utils.(*ChunkReader).parseChunkHeaderBytes(0x14000276200, {0x14000167fff?, 0x14000103180?, 0x200000003?})
	versitygw/s3api/utils/signed-chunk-reader.go:372 +0xe54
github.com/versity/versitygw/s3api/utils.(*ChunkReader).parseAndRemoveChunkInfo(0x14000276200, {0x14000167fff, 0x1, 0x1})
	versitygw/s3api/utils/signed-chunk-reader.go:251 +0x50
github.com/versity/versitygw/s3api/utils.(*ChunkReader).Read(0x14000276200, {0x14000160000, 0x14000056c00?, 0x8000})
	versitygw/s3api/utils/signed-chunk-reader.go:126 +0x188
io.(*teeReader).Read(0x140000b09c0, {0x14000160000, 0x105e7b368?, 0x8000})
	/usr/local/go/src/io/io.go:628 +0x34
...

The reproducer is:
% truncate -s 171764 testfile
% mc cp testfile gwtest/mybucket/testfile
mc: <ERROR> Failed to copy `/Users/ben/repo/s3perf/tools/testfile`. Put "http://127.0.0.1:7070/mybucket/testfile": dial tcp 127.0.0.1:7070: connect: connection refused

The panic can happen because the capacity of header ([]byte) at
the point of the debuglog line can be less than 2, but we were
trying to always send the first 2 bytes to the debug log.
2025-09-02 08:30:03 -07:00
Ben McClelland 24b1c45db3 cleanup: move debuglogger to top level for full project access
The debuglogger should be a top level module since we expect
all modules within the project to make use of this. If its
hidden in s3api, then contributors are less likely to make
use of this outside of s3api.
2025-09-01 20:02:02 -07:00
niksis02 23b5e60854 feat: adds debug logging for chunk readers.
Closes #1221

Adds debug logging for `signed`/`unsigned` chunk readers.
Adds the `debuglogger.Infof` log method, which prints out green info logs with `[INFO]:` prefix.
The debug logging inclues some chunk details: size, signature, trailers. It also prints out stash/release stash operations.
The error cases are logged with standart yellow `[DEBUG]:` prefix.
The `String to sign` block in signed chunk reader is logged in purple horizontal borders with title.
2025-05-08 00:22:01 +04:00
Ben McClelland a9fcf63063 feat: cleanup calling of debuglogger with managed debug setting 2025-05-02 17:05:59 -07:00
niksis02 6fa58db6ab fix: fixes the signed chunk encoding reader stashing.
Fixes #1238

The signed chunk reader stashes the header bytes if it can't fully parse the chunk header. On the next `io.Reader` call, the stash is combined with the new buffer data to attempt parsing the header again. The stashing logic was broken due to the premature removal of the first two header bytes (`\r\n`). As a result, the stash was incomplete, leading to parsing issues on subsequent calls.

These changes fix the stashing logic and correct the buffer offset calculation in `parseChunkHeaderBytes`.
2025-04-25 19:06:57 +04:00
niksis02 bbb5a22c89 feat: makes debug loggin prettier. Adds missing logs in FE and utility functions
Added missing debug logs in the `front-end` and `utility` functions.
Enhanced debug logging with the following improvements:

- Each debug message is now prefixed with [DEBUG] and appears in color.
- The full request URL is printed at the beginning of each debug log block.
- Request/response details are wrapped in framed sections for better readability.
- Headers are displayed in a colored box.
- XML request/response bodies are pretty-printed with indentation and color.
2025-04-17 22:46:05 +04:00
niksis02 7d4076b944 fix: Fixes the signed chunk trailer encoding to return proper api errors for invalid and incorrect checksums.
Fixes #1165

The signed chunk encoding with trailers should return api error for:

1. Invalid checksum - `(InvalidRequest) Value for x-amz-checksum-x trailing header is invalid.`
2. Incorrect checksum - `(BadDigest) The x you specified did not match the calculated checksum.`

Where `x` could be crc32, crc32c, sha1 ...
2025-03-29 01:46:45 +04:00
niksis02 a4b7feaa70 feat: Implements signed chunk encoding with trailer in the gateway.
Closes #1159
Fixes #1161

Implements signed chunk encoding with trailers in the gateway.
The signed encoding (both with and without trailers) is now handled by the `ChunkReader`.
Fixes the `ChunkReader` implementation to validate encoding headers byte by byte.

The chunk encoding with trailers follows the general signed chunk encoding pattern, but the final chunk includes the trailing signature (`x-amz-trailing-signature`) and the checksum header (`x-amz-checksum-x`, where `x` can be `crc32`, `crc32c`, `sha1`, `sha256`, or `crc64nvme`).

Adds validation for the `X-Amz-Trailer` header.
2025-03-27 20:56:49 +04:00
niksis02 4496711695 fix: Adds check for the final chunk signature in signed chunk encoding without trailing headers.
Fixes #1147

The final chunk header with 0 length, contains the last signature in signed chunk encoding implementation.
Added this last signature verification in the signed chunk encoding without trailers.
2025-03-20 18:19:14 +04:00
Ben McClelland 3b1fcf2f08 fix: chunk encoding with incorrect chunk signature
We were getting errors such as:
2025/02/07 19:24:28 Internal Error, write object data: write exceeds content length 87

whenever the chunk encoding did not have the correct chunk
signatures. The issue was that the chunk encoding reader
was reading from the underlying reader and then passing the full
buffer read back to the caller if the underlying reader returned
an error. This meant that we were not processing the chunk
headers within the buffer due to the higher level error, and
would possibly hand back the longer unprocessed chunk encoded
stream to the caller that was in turn trying to write to the
object file exceeding the content length limit.

Fixes #1056
2025-02-07 19:33:10 -08:00
Ben McClelland 748912fb3d fix: prevent panic with malformed chunk encoding
An invalid chunk encoding, or parse errors leading to parsing
invalid data can lead to a server panic if the chunk header
remaining is determined to be larger than the max buffer size.

This was previously seen when the chunk trailer checksums were
used by the client without the support from the server side
for this encoding.  Example panic:

 panic: runtime error: slice bounds out of range [4088:1024]

 goroutine 5 [running]:
 github.com/versity/versitygw/s3api/utils.(*ChunkReader).parseChunkHeaderBytes(0xc0003c4280, {0xc0000e6000?, 0x3000?, 0x423525?})
 	/home/tester/s3api/utils/chunk-reader.go:242 +0x492
 github.com/versity/versitygw/s3api/utils.(*ChunkReader).parseAndRemoveChunkInfo(0xc0003c4280, {0xc0000e6000, 0x3000, 0x8000})
 	/home/tester/s3api/utils/chunk-reader.go:170 +0x20b
 github.com/versity/versitygw/s3api/utils.(*ChunkReader).Read(0xc0003c4280, {0xc0000e6000, 0xc0000b41e0?, 0x8000})
 	/home/tester/s3api/utils/chunk-reader.go:91 +0x11e

This fix will validate the data length before copying into the
temporary buffer to prevent a panic and instead just return
an error.
2025-01-31 16:22:25 -08:00
niksis02 da3c6211bd feat: Implements streaming unsigned payload reader with trailers 2025-01-31 13:29:34 -08:00