Commit Graph

602 Commits

Author SHA1 Message Date
dependabot[bot] f24377b1e9 chore(deps): bump actions/checkout from 6 to 7
Bumps [actions/checkout](https://github.com/actions/checkout) from 6 to 7.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v6...v7)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

test: README updates, speed up some file integrity checks
2026-06-23 20:34:26 -03:00
Ben McClelland 27f04ad5ea feat: add windows functional test coverage and fix some windows behavior
This change adds Windows functional test execution in CI and updates
backend handling so windows filesystem error/path semantics map correctly
to expected S3 outcomes.

The only meta supported on windows right now is sidecar, so the tests
in windows mode also skip sidecar skips.

Future work is to address the skips and/or more clearly document
the unsupported/incompatible behavior on windows.

The windows support will still remain best effort, but these tests
should at least flag when future changes introduce incompatible
behavior on windows.
2026-06-18 11:34:05 -07:00
Ben McClelland 7bde76e982 Merge pull request #2195 from versity/sis/object-lock-default-retention-too-large
fix: validate object lock default retention upper limits
2026-06-17 15:35:03 -07:00
Ben McClelland 3311c7f3c1 Merge pull request #2194 from versity/sis/asterisk-read-preconditions
fix: support asterisk read preconditions
2026-06-17 15:32:44 -07:00
Luke McCrone 2fa0a2df89 test: ListObjectVersions query tests 2026-06-17 16:42:17 -03:00
Ben McClelland dd6e7de6c6 Merge pull request #2184 from versity/sis/empty-content-md5
fix: reject empty Content-MD5 on PUT operations
2026-06-16 21:06:16 -07:00
Ben McClelland ee2364e70c Merge pull request #2182 from versity/sis/fiber-migration-v3
feat: migrate Fiber to v3.3.0
2026-06-16 21:00:09 -07:00
niksis02 67af0afa81 fix: validate object lock default retention upper limits
Fixes #2187

Enforce maximum default retention periods when parsing PutObjectLockConfiguration requests. Reject Days values greater than 36500 and Years values greater than 100 with InvalidArgument errors.
2026-06-16 22:48:51 +04:00
niksis02 6df901ba42 fix: support asterisk read preconditions
Fixes #2185

Add asterisk handling for `If-Match` and `If-None-Match` read preconditions across `GetObject`, `HeadObject`, `CopyObject`, and `UploadPartCopy`.

`If-Match`: `*` now matches any `ETag`, while `If-None-Match`: `*` returns `304 Not Modified`.
2026-06-16 21:36:45 +04:00
niksis02 12fb5a6594 fix: reject empty Content-MD5 on PUT operations
Fixes #2179

Treat a present but empty `Content-MD5` header as an invalid digest instead of handling it as if the header were absent. This makes PUT operations return `InvalidDigest` for empty `Content-MD5` values while preserving existing behavior for missing headers.
2026-06-15 18:53:42 +04:00
niksis02 4d391cabc8 feat: migrate Fiber to v3.3.0
Fixes #2180
Fixes #2181

Migrate the gateway from Fiber v2 to Fiber v3.3.0 and update the affected server, middleware, handler, controller, and test code for the new APIs.

Replace the deprecated Fiber filesystem middleware used by the WebUI with the Fiber v3 static middleware, serving the embedded WebUI assets from an fs.Sub filesystem.

Fix the request header limit handling regression by adding a temporary handler for Fiber v3/fasthttp small-buffer errors so oversized request headers return the expected regulated S3 error response.

Fix the debuglogger panic by reworking the boxed key/value formatter used for debug request and response dumps. The formatter now handles long header keys and values without producing invalid wrap widths, negative padding, or out-of-range string slices.
2026-06-15 14:48:31 +04:00
Luke McCrone 3a35e37b5c test: jailing, logging, executable cleanups 2026-06-10 12:46:27 -03:00
niksis02 f08f76fea4 feat: support x-amz-website-redirect-location
Integrate x-amz-website-redirect-location across object metadata flows so uploads, copies, multipart creation, HEAD, and GET preserve and return redirect locations, and website hosting applies object-level redirects from the stored value.
2026-06-10 12:41:55 +04:00
niksis02 1625c5963e feat: improve static website hosting support
Enhances the static website hosting implementation with more complete S3-compatible behavior across request handling, backend storage, validation, CORS, and errors.

Adds dedicated website endpoint handling for GET, HEAD, and OPTIONS requests, including index document resolution, error document serving, redirect-all support, pre-fetch and post-error routing rules, query string preservation in redirects, public access checks before object reads, and method-not-allowed responses.

Improves error handling for website responses by returning S3-compatible HTML error bodies with request IDs, host IDs, x-amz-error-code, x-amz-error-message, and specialized error fields. This also fixes website-related validation errors to return more accurate S3-style error codes and messages, including invalid redirect protocols, invalid HTTP redirect/error codes, conflicting routing rule replacements, routing rule limits, and oversized website configuration requests.

Adds website CORS support for GET, HEAD, and OPTIONS preflight requests, including bucket CORS lookup through website host bucket resolution, allowed origin/method/header validation, exposed header handling, ETag exposure, Vary headers, max-age handling, and CORS access-denied responses.

Adds debug logging around website configuration parsing, validation failures, CORS checks, backend lookup failures, and internal website error paths to make failures easier to diagnose.

Adds compressed website configuration storage so larger configs fit backend metadata limits, including gzip storage for POSIX extended attributes and base64-encoded compressed metadata for Azure. Also adds Azure PutBucketWebsite, GetBucketWebsite, and DeleteBucketWebsite support.

Adds and expands test coverage for website config validation, S3-compatible HTML error bodies, website routing behavior, public access enforcement, HEAD behavior, CORS handling, PutBucketWebsite limits, and end-to-end website hosting through a Docker-based dnsmasq test setup and CI workflow.
2026-06-10 12:41:55 +04:00
Marc Singer 375c2764d5 Add website integration tests and remove NotImplemented stubs
Replace PutBucketWebsite, GetBucketWebsite, DeleteBucketWebsite
NotImplemented test stubs with comprehensive integration tests covering:
- non-existing bucket errors
- validation (empty suffix, suffix with slash, invalid protocol, mutual
  exclusion of RedirectAllRequestsTo and IndexDocument)
- successful put/get round-trips for both index+error and redirect-all configs
- delete idempotency and verification

Signed-off-by: Marc Singer <marc@singer.gg>

Add error document serving, routing rules, and integration tests

Implement Features 1 and 2 of S3 static website hosting:

- WebsiteErrorDocument controller wrapper intercepts 4xx errors on
  website-enabled buckets and serves the configured error document or
  evaluates post-request routing rules (error code match redirects)
- ResolveWebsiteIndex middleware now caches parsed WebsiteConfiguration
  in context, handles RedirectAllRequestsTo, evaluates pre-request
  routing rules (key prefix match redirects), and rewrites directory
  keys for index document
- MatchPreRequestRule and MatchPostRequestRule methods on
  WebsiteConfiguration for routing rule evaluation
- 14 unit tests for routing rule matching
- 7 integration tests covering error document, routing rules,
  redirect-all, and index document behavior

Signed-off-by: Marc Singer <marc@singer.gg>

Add separate website hosting endpoint with virtual-host routing

Signed-off-by: Marc Singer <marc@singer.gg>

Support catch-all mode for website endpoint when --website-domain is omitted

Signed-off-by: Marc Singer <marc@singer.gg>
2026-06-10 12:41:51 +04:00
Luke McCrone f826f13a64 test: jailing work (base) 2026-06-09 14:55:47 -03:00
niksis02 577470214d fix: enforce required SignedHeaders validation for SigV4 requests
Validate required signed headers for both Authorization-header SigV4 requests and presigned URLs. The required signed header set is now `host` plus every incoming header with the `x-amz-` prefix.

During request reconstruction, signed headers and explicitly ignored headers are copied into the generated request used for signature verification. If an incoming `x-amz-*` header is present but missing from the client-provided `SignedHeaders`, return `AccessDenied` with a `HeadersNotSigned` field. The `host` header remains part of the canonical request and signed header calculation.

Previously, a client could sign a request without an S3 control header and then add that header after signing. For example, a presigned `PUT` URL could be generated with only `host` signed, then the actual request could include an unsigned `X-Amz-Tagging` or `X-Amz-Copy-Source` header. Because the verifier reconstructed the request only from `SignedHeaders`, that extra header was omitted from signature calculation and could pass authentication even though it changed the request semantics. This is now rejected with `AccessDenied`.

Expose v4 helper methods for checking required and ignored headers, and update canonical header signing so ignored headers can still be included when a client explicitly lists them in `SignedHeaders`, while `Authorization` remains excluded from signature calculation.
2026-05-30 21:16:26 +04:00
Luke McCrone 179fdc0352 test: ListMultipartUpload query tests, related cleanup 2026-05-28 09:30:59 -03:00
Ben McClelland 2bc4da944f Merge pull request #2153 from versity/sis/policy-key-normalization
fix: normalize object keys during bucket policy evaluation
2026-05-27 20:17:25 -07:00
niksis02 cd0b4e6d9d fix: normalize object keys during bucket policy evaluation
Object key validation allowed internal parent-directory segments such as `public/../private.txt`. Bucket policy and auth checks evaluated the raw key, so a policy allowing bucket/public/* could match the request while posix backend later resolved the key with `filepath.Join` and accessed `bucket/private.txt`.

Add backend-specific object key normalization to close that mismatch. The Backend interface now exposes `NormalizeObjectKey` so authorization can evaluate resources using the same key shape a backend will use for storage access.

Backends that do not collapse object paths, including Azure and the S3 proxy, inherit `BackendUnsupported.NormalizeObjectKey`. That implementation returns the input key unchanged, avoiding unnecessary normalization and keeping policy evaluation unpolluted for object stores where ../ is part of the key name.

posix/scoutfs normalize keys with filepath.Join so policy resources and request keys are compared after internal dot segments are collapsed.

Bucket policy evaluation now normalizes both the incoming object key and object resource patterns from the policy before matching. Object lock governance bypass policy checks use the same backend normalizer as well, so retention and legal hold authorization cannot diverge from backend path resolution.
2026-05-27 22:20:39 +04:00
niksis02 4ef090dbfc fix: fix empty ownership control rules panic
Fix the gateway panic when validating malformed bucket ownership controls bodies with no rules. The handler now checks the rules count before indexing the first rule.
2026-05-23 04:18:06 +04:00
niksis02 eecc1a779c fix: reject invalid PostObject keys
Validate multipart PostObject key fields with the existing object name rules so path traversal and degenerate names return BadRequest. This prevents crafted object keys from escaping the gateway root.
2026-05-22 14:48:31 +04:00
niksis02 ed1ad6b623 fix: honor explicit public bucket policy deny
Distinguish public bucket policy no-match from explicit deny during anonymous access checks. This preserves ACL fallback only for requests that are not allowed by policy, while ensuring a matching Deny statement short-circuits authorization and returns AccessDenied even when a public ACL would otherwise grant access.
2026-05-22 00:01:24 +04: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
Luke McCrone 48f79a6171 test: ListObjects util code reorg, return value/multipart fixes 2026-05-20 14:30:41 -03:00
Luke McCrone fd8e4cd69c test: HeadObject query tests, more util code changes 2026-05-14 11:15:15 -03:00
Ben McClelland 2c0844ad88 fix: ignore implicit directories for Get/HeadObject
Directories that exist on the filesystem but were not explicitly created
via S3 (put-object with a key ending in '/') do not have an etag
value. ListObjectsV2 already uses the presence of this attribute to
decide whether to include a directory as an object. GetObject and
HeadObject were not performing this check, so they would successfully
return directories that ListObjectsV2 would not list.

Add the etag attribute check in GetObject and HeadObject: if a
directory path is requested but has no etag xattr, return 404. This
makes all three operations agree on which directories are S3 objects.

Fixes #2130
2026-05-12 18:56:00 -07:00
Luke McCrone 37d6ca03ed test: util code cleanup 2026-05-11 16:34:54 -03:00
Luke McCrone 6608c53612 test: DeleteObjects payload testing 2026-05-08 14:46:54 -03:00
Ben McClelland 12f3d3a1e2 Merge pull request #2117 from versity/test/dockerfile_prep
test: dockerfile, run script updates, README updates
2026-05-06 13:39:00 -07:00
Ben McClelland c6e795f91f Merge pull request #2114 from versity/sis/unsupported-sigv2
fix: reject SigV2 requests
2026-05-06 13:38:14 -07:00
niksis02 e69d073273 fix: reject SigV2 requests
The gateway currently supports only Signature Version 4 (SigV4) authorization. Deprecated AWS SigV2 requests are now rejected with an AWS-specific `InvalidRequest` error for both Authorization-header requests and query-string requests(presigned URLs).

This also fixes SigV4 Authorization-header handling for date headers. SigV4 accepts two date headers: `Date` and `X-Amz-Date`. `X-Amz-Date` takes precedence, but when it is missing, `Date` should be used. The gateway now uses the `Date` header with lower precedence when `X-Amz-Date` is not present. No SDK integration test was added for this case because the SDK always sets `X-Amz-Date`, and this behavior is not configurable.
2026-05-06 23:43:10 +04:00
Luke McCrone 0136a0ba84 test: dockerfile, run script updates, README updates 2026-05-06 14:44:50 -03:00
Luke McCrone 317767b15b test: add test tags 2026-05-05 19:07:56 -03:00
Ben McClelland 21da1d7e70 Merge pull request #2097 from versity/dependabot/go_modules/dev-dependencies-3789651bc5
chore(deps): bump the dev-dependencies group with 7 updates
2026-05-04 16:24:32 -07:00
Ben McClelland bb3cdd9cb6 fix: skip integration tests not compatible in sidecar
A few tests are not yet compatible with sidecar mode. Add a test
option to skip these when running sidecar tests.
2026-05-04 16:05:29 -07:00
niksis02 d2fa265fb8 feat: support sha512, md5, xxhash3, xxhash64, xxhash128 data integrity checksums
Integrate the new S3 checksum types in the gateway, including `SHA512`, `MD5`, `XXHASH64`, `XXHASH3`, and `XXHASH128`. This adds checksum calculation, validation, schema handling, and test coverage for the expanded checksum support.

These external packages have been used:
- `github.com/zeebo/xxh3` for `XXHASH3` and `XXHASH128`
- `github.com/cespare/xxhash/v2` for `XXHASH64`

Adjust integration tests because `aws-sdk-go-v2/service/s3` does not support automatic checksum calculation for the new checksum algorithms and returns an SDK-level error when only the checksum algorithm is provided. Only precalculated checksum values are acceptable for these checksum types.

References:
- `https://github.com/aws/aws-sdk-go-v2/issues/3404`
- `https://github.com/aws/aws-sdk-go-v2/issues/3403`
2026-05-04 08:50:39 -07:00
Luke McCrone 9a21c5a1e0 test: GetObject - add query tests, go/bats improved header/object reply handling 2026-04-28 21:10:09 -03:00
niksis02 8d5b2be0b2 fix: check PutObjectTagging/LegalHold/Retention permissions on PutObject,CopyObject and CreateMultipartUpload
Fixes #1986

When a client includes tagging, legal hold, or retention headers in a PutObject, CopyObject or CreateMultipartUpload request, the corresponding bucket policy permissions must be verified in addition to s3:PutObject:

`X-Amz-Tagging` - `s3:PutObjectTagging`
`X-Amz-Object-Lock-Legal-Hold` - `s3:PutObjectLegalHold`
`X-Amz-Object-Lock-Mode` - `s3:PutObjectRetention`

Previously, only s3:PutObject was checked, allowing users to set tagging, legal hold, and retention without having the required permissions. Now each action permission is check, if user tries to add them.

For CopyObject these permissions are checked on destination object.
2026-04-28 01:05:34 +04:00
alice nodelman 79d03b2cb4 Merge branch 'main' into test/bats_tagging 2026-04-22 15:00:49 -07:00
Ben McClelland 0767d87387 test: add github actions functional tests for posix sidecar option
Enough people are making use of sidecar that we need to add
a CI test to make sure we have some coverage with this mode.

This add a couple small functional test fixes found wtih
enabling sidecar tests as well.
2026-04-22 13:23:46 -07:00
Ben McClelland db77882ec6 test: disable mc client tests from test matrix
The dl.min.io download site has been having stability issues
possibly related to github action runners getting rate limited.
Disable these for now until we can find a better place to host
this client.
2026-04-22 11:29:26 -07:00
Luke McCrone 29f378ea5e test: initial bats test tagging system, shellcheck fix 2026-04-21 18:48:40 -03:00
niksis02 d6fb9547b8 fix: correct 206 Partial Content response status for ranged GetObject and HeadObject
Fixes #2052
Fixes #2056
Fixes #2057

Previously, GetObject and HeadObject used the request's `Range` header to determine the response status code, which caused incorrect 206 responses for invalid Range header values.

The status is now driven by whether res.ContentRange is set in the response, rather than by the presence of a range in the request. Backends (posix and azure) now set Content-Range for PartNumber=1 on non-multipart objects, skipping zero-size objects where no range applies.

HeadObject was also fixed to return 206 when Content-Range is present, and to only return checksums when the full object is requested.
2026-04-21 02:13:04 +04:00
Ben McClelland 8533bc1b60 Merge pull request #2054 from versity/sis/racing-complete-mp
fix: make CompleteMultipartUpload idempotent and add part-number support to GetObject/HeadObject
2026-04-20 10:23:09 -07:00
niksis02 62e8cddbc7 fix: make CompleteMultipartUpload idempotent and add part-number support to GetObject/HeadObject
Closes #1064

Use the multipart ETag as the in-progress directory suffix instead of the static `.inprogress` marker so that concurrent CompleteMultipartUpload calls for the same upload ID are all treated as successful (idempotent) rather than racing, where only one succeeded and the rest returned NoSuchUpload.

After finalizing the multipart upload, store an `mp-metadata` xattr on the assembled object that records the upload ID and cumulative byte offsets for each part. GetObject and HeadObject now use this metadata to serve individual part ranges via the `partNumber` query parameter, returning a successful response instead of returning NotImplemented.

Add two new S3 error codes:
- `ErrInvalidPartNumberRange` (416 RequestedRangeNotSatisfiable) — returned
  when the requested part number exceeds the number of parts in the upload.
- `ErrRangeAndPartNumber` (400 BadRequest) — returned when both a Range header
  and a partNumber query parameter are specified on the same request.
2026-04-20 20:45:58 +04:00
niksis02 b905554e06 fix: fix azure multipart upload objects masking
As multipart uploads are translated to blobs in azure blob storage, they were visible in ListObjects(V2) as complete objects. Now the blobs with multipart prefix are filtered out during listing.

The listing logic is rewritten client-side to implement proper S3 semantics: flat blob enumeration with manual delimiter handling, correct truncation (IsTruncated only set when more items genuinely exist beyond maxKeys), and StartAfter/Marker/ContinuationToken applied via the lexicographic max of both constraints in ListObjectsV2.

For the same reason bucket deletion was not allowed. Now multipart objects are explicitly checked on bucket deletion and any pending multipart upload doesn't block the bucket deletion anymore.
2026-04-16 23:56:18 +04:00
Ben McClelland 6d3688adf9 Merge pull request #2013 from versity/test/rest_list_objects_v2_queries
Test/rest list objects v2 queries
2026-04-15 13:32:40 -07:00
Ben McClelland 2b918d585e chore: fix spellings and unused function args
No logic changes, just janitorial cleanup
2026-04-15 10:32:14 -07:00
Luke McCrone 1632c8c006 test: ListObjectsV2 query updates, file updates, bash cleanup 2026-04-14 18:50:10 -03:00