Commit Graph

316 Commits

Author SHA1 Message Date
Ben McClelland
873148a5c4 fix: add Host header to HTTP test requests for fasthttp v1.70.0 compatibility
fasthttp v1.70.0 now enforces the HTTP/1.1 requirement of exactly
one Host header, rejecting requests that omit it. Fix tests that
were failing due to missing host.
2026-04-13 19:13:58 -07:00
niksis02
b473aa0545 fix: move versionId validation to backend
Closes #1813

We use a specific `versionId` format(`ulid` package) to generate versionIds in posix, which is not compatible to S3. The versionId validation was performed in frontend which is a potential source of failure for s3 proxy configured on an s3 service which doesn't use ulid for versionId generation(e.g. aws S3). These changes move the specific `ulid` versionId validation to posix to not force any specific versionId format in the gateway.
2026-04-07 01:56:51 +04:00
Ben McClelland
71ae9bf045 Merge pull request #2005 from versity/sis/copyobject-expected-source-bucket-owner
feat: implement x-amz-source-expected-bucket-owner for CopyObject and UploadPartCopy
2026-04-02 08:25:50 -07:00
niksis02
052f2364cc feat: implement x-amz-source-expected-bucket-owner for CopyObject and UploadPartCopy
Closes #1897

Extract the `X-Amz-Source-Expected-Bucket-Owner` header for CopyObject and UploadPartCopy. Verify the source bucket owner in the backend and if the provided access key id doesn't match, return an `AccessDenied` error.
2026-04-01 21:44:33 +04:00
niksis02
3d47e7fed1 fix: add x-amz-bucket-region to ListObjects(V2) response headers.
Fixes #1814

The `x-amz-bucket-region` is not mentioned in AWS S3 documentation, however s3 sends it in all ListObjects(V2) successful responses. The header is now added.
2026-04-01 15:22:33 +04:00
Ben McClelland
e0209ebab4 Merge pull request #1997 from versity/sis/copyobject-threshold
fix: enforce 5gb copy source object size threshold.
2026-03-31 12:27:11 -07:00
Ben McClelland
89ab7a4f2b Merge pull request #1982 from versity/sis/POST-object
feat: add browser-based POST object upload support
2026-03-31 12:25:00 -07:00
niksis02
59002b2650 feat: implement integration tests for browser-based POST object 2026-03-31 22:47:04 +04:00
niksis02
285d130a47 feat: add browser-based POST object upload support
Closes #1648
Fixes #1980
Fixes #1981

This PR implements browser-based POST object uploads for S3-compatible form uploads. It adds support for handling `multipart/form-data` object uploads submitted from browsers, including streaming multipart parsing so file content is not buffered in memory, POST policy decoding and evaluation, SigV4-based form authorization, and integration with the existing `PutObject` backend flow. The implementation covers the full browser POST upload path, including validation of required form fields, credential scope and request date checks, signature verification, metadata extraction from `x-amz-meta-*` fields, checksum field parsing, object tagging conversion from XML into the query-string format expected by `PutObject`, and browser-compatible success handling through `success_action_status` and `success_action_redirect`. It also wires the new flow into the router and metrics layer and adds POST-specific error handling and debug logging across policy parsing, multipart parsing, and POST authorization. AWS S3 also accepts the `redirect` form field alongside `success_action_redirect`, but since AWS has marked `redirect` as deprecated and is planning to remove it, this gateway intentionally does not support it.
2026-03-31 22:44:54 +04:00
niksis02
bbe246e8ec fix: enforce 5gb copy source object size threshold.
Fixes #1896

Enforces the S3 `5 GiB` copy source size limit across the posix and azure
backends for `CopyObject` and `UploadPartCopy`, returning `InvalidRequest` when
the source object exceeds the threshold.

The limit is now configurable via `--copy-object-threshold`
(`VGW_COPY_OBJECT_THRESHOLD`, default 5 GiB).
A new `--mp-max-parts flag` (`VGW_MP_MAX_PARTS`, default `10000`) has been added to make multipart upload parts number limit configurable.

No integration test has been added, as GitHub Actions cannot reliably
handle large objects.
2026-03-31 22:44:03 +04:00
niksis02
3b17f05d17 feat: support response header overrides in HeadObject
Closes #1967

Add support for response header override query parameters(`response-cache-control`, `response-content-disposition`, `response-content-encoding`, `response-content-language`, `response-content-type`, `response-expires`) in `HeadObject`. Anonymous requests with override params are rejected with `ErrAnonymousResponseHeaders`.
2026-03-31 17:19:03 +04:00
Ben McClelland
97cc6bf23b chore: run go modernize tool
This is a fixup of the codebase using:
go run golang.org/x/tools/go/analysis/passes/modernize/cmd/modernize@latest -fix ./...

This has no bahvior changes, and only updates safe changes for
modern go features.
2026-03-10 09:47:37 -07:00
niksis02
21a636b3b5 fix: add request headers and metadata headers limit
Fixes #1606

According to AWS documentation:
> *“The PUT request header is limited to 8 KB in size. Within the PUT request header, the user-defined metadata is limited to 2 KB in size. The size of user-defined metadata is measured by taking the sum of the number of bytes in the UTF-8 encoding of each key and value.”*

Based on this, object metadata size is now limited to **2 KB** for all object upload operations (`PutObject`, `CopyObject`, and `CreateMultipartUpload`).

Fixes handling of metadata HTTP headers when the same header appears multiple times with different casing or even if they are identical. According to S3 behavior, these headers must be merged into a single lower-cased metadata key, with values concatenated using commas.

Example:

```
x-amz-meta-Key: value1
x-amz-meta-kEy: value2
x-amz-meta-keY: value3
```

Translated to:

```
key: value1,value2,value3
```

This PR also introduces an **8 KB limit for request headers**. Although the S3 documentation explicitly mentions the 8 KB limit only for **PUT requests**, in practice this limit applies to **all requests**.

To enforce the header size limit, the Fiber configuration option `ReadBufferSize` is used. This parameter defines the maximum number of bytes read when parsing an incoming request. Note that this limit does not apply strictly to request headers only, since request parsing also includes other parts of the request line (e.g., the HTTP method, protocol string, and version such as `HTTP/1.1`). So `ReadBufferSize` is effectively a limit for request headers size, but not the exact limit.
2026-03-06 23:25:49 +04:00
niksis02
5c918f3682 feat: revert ignore object ACL behavior
The logic to return a `NotImplemented` error on object upload operations, when any ACL header is present has been removed. Now all object ACL headers are by default ignored. The `-noacl` flag is preserved to disabled bucket ACLs.

**Testing**
The Put/Get object ACL tests are moved to `NotImplemented` integration tests group as a default gateway behavior. The existing `_acl_not_supported` tests are modified to expect no error, when ACLs are used on object uploads.
2026-03-02 19:30:57 +04:00
niksis02
5ae791b154 feat: configuration option to disable ACLs
Closes #1847

This PR introduces a global optional gateway CLI flag `--disable-acl` (`VGW_DISABLE_ACL`) to disable ACL handling. When this flag is enabled, the gateway ignores all ACL-related headers, particularly in `CreateBucket`, `PutObject`, `CopyObject`, and `CreateMultipartUpload`.

`GetBucketAcl` behavior is unchanged simply returning the bucket ACL config.
There's no change in object ACL actions(`PutObjectACL`, `GetObjectACL`). They return a`NotImplemented` error as before.

A new custom error is added for PutBucketAcl calls when ACLs are disabled at the gateway level. Its HTTP status code and error code match AWS S3’s behavior, with only a slightly different error message.

In the access-control checker, ACL evaluation is fully bypassed. If ACLs are disabled only the bucket owner gets access to the bucket and all grantee checks are ignored.

The PR also includes minor refactoring of the S3 API server and router. The growing list of parameters passed to the router’s Init method has been consolidated into fields within the router struct, initialized during router construction. Parameters not needed by the S3 server are no longer stored in the server configuration and are instead forwarded directly to the router.
2026-02-27 20:04:13 +04:00
niksis02
6fafc15d08 fix: fixes PutBucketCors CORSRules validation
Fixes #1870
Fixes #1863

A validation has been added to **PutBucketCors** for `CORSRule.AllowedOrigins`. The `AllowedOrigins` list can no longer be empty—otherwise a **MalformedXML** error is returned. Additionally, each origin is now validated to ensure it does not contain more than one wildcard.

A similar validation has been added for `AllowedMethods`. The list must not be empty, or a **MalformedXML** error is returned. Previously, empty method values (e.g., `[]string{""}`) were incorrectly treated as valid. This has been fixed, and an **UnsupportedCORSMethod** error is now returned.
2026-02-24 16:59:38 +04:00
niksis02
7fb3dedecc feat: adds Location, x-amz-bucket-arn response headers in CreateBucket
Closes #1873
2026-02-20 13:02:51 +04:00
Ben McClelland
a81f9e5152 Merge pull request #1871 from versity/sis/create-bucket-private-canned-acl
fix: correct private canned ACL behavior on bucket creation
2026-02-18 09:55:36 -08:00
niksis02
f1577fd00b fix: correct private canned ACL behavior on bucket creation
Fixes #1869

Generally, when object ownership is not explicitly specified during bucket creation, it defaults to `BucketOwnerEnforced`. With `BucketOwnerEnforced`, ACLs are disabled and any attempt to set one results in an `InvalidBucketAclWithObjectOwnership` error.

However, there is an edge case. When the `private` canned ACL is used during bucket creation—which is effectively the default ACL for all buckets—`BucketOwnerEnforced` is still permitted. Moreover, if no explicit object ownership is specified together with the `private` canned ACL, the ownership defaults to `BucketOwnerPreferred`.

This fix also resolves the issue with rclone bucket creation, since rclone sends `x-amz-acl: private` by default:

```
rclone mkdir vgw:test
```
2026-02-18 20:32:05 +04:00
niksis02
46bcc8af35 fix: fixes object default Content-Type
Fixes #1849

If no `Content-Type` is provided during object upload, S3 defaults it to `application/octet-stream`. This behavior was missing in the gateway, causing backends to persist an empty `Content-Type`, which Fiber then overrides with its default `text/plain`. The behavior has now been corrected for the object upload operations: `PutObject`, `CreateMultipartUpload`, and `CopyObject`.
2026-02-18 01:44:52 +04:00
Ben McClelland
a420d8b340 Merge pull request #1856 from versity/sis/error-response-content-type
fix: adds application/xml Content-Type to error responses
2026-02-16 12:53:26 -08:00
niksis02
2232efd328 fix: adds application/xml Content-Type to error responses
Fixes #1852
Fixes #1821

Fiber used to return the `text/plain` default `Content-Type` for error responses, because it wasn't explicitly set. Now for all error responses the `application/xml` content type is set.
2026-02-16 21:54:11 +04:00
niksis02
68755ca700 fix: replace debuglogger.Logf("Internal Error, %v", err) with debuglogger.InternalError(err) 2026-02-13 21:03:58 +04:00
niksis02
89aa822a40 fix: fixes DeleteObject if-match quoted comparison
Fixes #1835

If-Match in DeleteObject is a precondition header that compares the client-provided ETag with the server-side ETag before deleting the object. Previously, the comparison failed when the client sent an unquoted ETag, because server ETags are stored with quotes. The implementation now trims quotes from both the input ETag and the server ETag before comparison to avoid mismatches. Both quoted and unquoted ETags are valid according to S3.
2026-02-11 16:45:36 +04:00
niksis02
2365f9f1ae fix: fixes list-limiters parsing and validation
Fixes #1809
Fixes #1806
Fixes #1804
Fixes #1794

This PR focuses on correcting so-called "list-limiter" parsing and validation. The affected limiters include: `max-keys`, `max-uploads`, `max-parts`, `max-buckets`, `max-uploads` and `part-number-marker`. When a limiter value is outside the integer range, a specific `InvalidArgument` error is now returned. If the value is a valid integer but negative, a different `InvalidArgument` error is produced.

`max-buckets` has its own validation rules: completely invalid values and values outside the allowed range (`1 <= input <= 10000`) return distinct errors. For `ListObjectVersions`, negative `max-keys` values follow S3’s special-case behavior and return a different `InvalidArgument` error message.

Additionally, `GetObjectAttributes` now follows S3 semantics for `x-amz-max-parts`: S3 ignores invalid values, so the gateway now matches that behavior.
2026-02-06 14:21:56 +04:00
Ben McClelland
01fc142c1e fix: correct spelling for debuglogger.InternalError() (#1784) 2026-01-24 06:44:54 -08:00
niksis02
8569b158f0 fix: return not implemented in object actions, if acl header is present
Fixes #1767
Fixes #1773

As object ACLs are not supported in the gateway, any attempt to set an ACL during object creation must return a NotImplemented error. A check has now been added to `PutObject`, `CopyObject`, and `CreateMultipartUpload` to detect any ACL-related headers and return a NotImplemented error accordingly.
2026-01-23 17:03:03 +04:00
niksis02
f6225aa968 fix: fixes some write operations blocking in read-only mode
Fixes #1765
Fixes #1771

This PR addresses two issues:

1. CreateBucket was previously allowed when the gateway was running in read-only mode. It is now correctly blocked.
2. Write operations were permitted on public buckets in read-only mode because the public access checks in `auth.VerifyAccess` were evaluated before the read-only check. The read-only check now takes precedence, and all write operations on public buckets are blocked.
2026-01-21 13:51:46 +04:00
niksis02
2561ef9708 feat: implements admin CreateBucket endpoint/cli command
Closes #1731

Implements the admin `CreateBucket` (`PATCH /:bucket/create`) endpoint and CLI command, which create a new bucket with the provided owner access key ID. The endpoint internally calls the S3 `CreateBucket` API, storing the new owner information in the request context under the `bucket-owner` key. This value is then retrieved by the S3 API layer and the backends.

The endpoint uses the custom `x-vgw-owner` HTTP header to pass the bucket owner access key ID.

The admin CLI command mirrors `aws s3api create-bucket` and supports all flags implemented by the gateway (for example, `--create-bucket-configuration`, `--acl`, `--object-ownership`, etc.).
2026-01-12 14:32:52 +04:00
Ben McClelland
d446102f69 feat: add option for default global cors allow origin headers
There is some desire to have a web dashboard for the gateway. So
that we dont have to proxy all requests through the webserver
and expose credentials over the wire, the better approach would
be to enable CORS headers to allow browser requests directly to
the s3/admin service.

The default for these headers is off, so that they are only
enabled for instances that specfically want to support this
workload.
2026-01-08 16:23:23 -08:00
niksis02
8e0eec0201 fix: return null in GetBucketLocation for us-east-1
Fixes #1643

`GetBucketLocation` in S3 returns empty `LocationConstraint` if the bucket is in the `us-east-1` region. This fix returns empty `LocationConstraint` if the gateway region is `us-east-1`.
2026-01-05 16:24:01 +04:00
Ben McClelland
0cfacfc049 Merge pull request #1730 from versity/sis/create-bucket-location-constraint-us-east-1
fix: fixes CreateBucket LocationConstraint validation
2026-01-03 20:52:57 -08:00
niksis02
d0158420ee fix: fixes CreateBucket LocationConstraint validation
Fixes #1654
Fixes #1644

CreateBucket `LocationConstraint` rejects empty values with an `InvalidLocationConstraint` error.
The `us-east-1` `LocationConstraint` is considered invalid because it is the default value and must not be present in the `CreateBucketConfiguration` request body.

This PR fixes both issues by returning `InvalidLocationConstraint` in both cases.
2026-01-03 23:21:19 +04:00
niksis02
12e1308d1f fix: adds versionId in put/get/delete object tagging actions response.
Fixes #1698

`PutObjectTagging`, `GetObjectTagging` and `DeleteObjectTagging` return the `x-amz-version-id` in the response headers. The PR adds this header in the responses.
2026-01-02 23:46:52 +04:00
Ben McClelland
f835ef1772 Merge pull request #1719 from versity/sis/putbucketpolicy-status
fix: fixes the PutBucketPolicy response status
2025-12-31 00:29:33 -08:00
niksis02
39ee175484 fix: fixes the PutBucketPolicy response status
Fixes #1712

A successful `PutBucketPolicy` call returns `204`(No Content) http status. This PR fixes the response status code, by sending correct `204`, insted of `200`.
2025-12-29 21:10:52 +04:00
niksis02
f467b896d8 feat: adds Location in CompleteMultipartUpload response
Closes #1714

There is a `Location` field in the `CompleteMultipartUpload` result that represents the newly created object URL. This PR adds this property to the `CompleteMultipartUpload` response, generating it dynamically in either host-style or path-style format, depending on the gateway configuration.
2025-12-29 13:39:54 +04:00
niksis02
05f8225577 feat: adds missing versioning-related bucket policy actions
Closes #1635

Some S3 actions have dedicated bucket policy actions and require explicit policy permissions when operating on object versions. These actions were missing in the gateway: `GetObjectVersionTagging`, `PutObjectVersionTagging`, `DeleteObjectVersionTagging`, `DeleteObjectVersion`, and `GetObjectVersionAttributes`.

The logic for these actions is straightforward — if the incoming request includes the `versionId` query parameter, S3 enforces the corresponding bucket policy action that includes `version`.

This PR adds support for these missing actions in the gateway.
2025-11-12 23:53:27 +04:00
niksis02
eae11b44c5 fix: adds versionId validation for object level actions
Fixes #1630

S3 returns `InvalidArgument: Invalid version id specified` for invalid version IDs in object-level actions that accept `versionId` as a query parameter. The `versionId` in S3 follows a specific structure, and if the input string doesn’t match this structure, the error is returned. In the gateway, the `versionId` is generated using the `ulid` package, which also has a defined structure. This PR adds validation for object-level operations that work with object versions by using the ULID parser.

These actions include: `HeadObject`, `GetObject`, `PutObjectTagging`, `GetObjectTagging`, `DeleteObjectTagging`, `PutObjectLegalHold`, `GetObjectLegalHold`, `PutObjectRetention`, `GetObjectRetention`, `DeleteObject`, `CopyObject`, `UploadPartCopy`, and `GetObjectAttributes`.
2025-11-11 22:23:50 +04:00
niksis02
5c3cef65e2 fix: fixes s3 event and access logs sending in ProcessController
Fixes #1620

S3 events were not correctly sent by the gateway. For example, actions that don’t include a response payload caused the event emission to be skipped. This has now been fixed — S3 events are sent regardless of whether the response has a payload or if any parsing issues occur later in the `ProcessController` during body parsing.

The fix also ensures that S3 events are sent **only** for specific actions, as intended, instead of being sent for all actions.

Additionally, this update improves server access log handling by covering cases where errors occur during response payload parsing, ensuring such errors are properly reflected in the access logs.
2025-11-06 20:20:18 +04:00
niksis02
77459720ba feat: adds x-amz-tagging-count support for HeadObject
Closes #1346

`GetObject` and `HeadObject` return the `x-amz-tagging-count` header in the response, which specifies the number of tags associated with the object. This was already supported for `GetObject`, but missing for `HeadObject`. This implementation adds support for `HeadObject` in `azure` and `posix` and updates the integration tests to cover this functionality for `GetObject`.
2025-11-05 20:30:50 +04:00
niksis02
8d2eeebce3 feat: adds tagging support for object versions in posix
Closes #1343

Object version tagging support was previously missing in the gateway. The support is added with this PR. If versioning is not enabled at the gateway level and a user attempts to put, get, or delete object version tags, the gateway returns an `InvalidArgument`(Invalid versionId)
2025-11-04 23:51:22 +04:00
Ben McClelland
efe4ccb5ec Merge pull request #1613 from versity/sis/copyobject-non-empty-body
fix: adds request body check for CopyObject and UploadPartCopy
2025-11-04 11:39:56 -08:00
niksis02
9a01185be9 fix: adds request body check for CopyObject and UploadPartCopy
Fixes #1242

S3 returns a specific error for actions that expect an empty request body but receive a non-empty one. Such actions include **CopyObject** and **UploadPartCopy**, which are HTTP PUT requests with no request body. This implementation adds a check for these actions and returns the corresponding error.
2025-11-04 00:22:33 +04:00
niksis02
7744dacced fix: adds validation for bucket canned ACL
Fixes #1379

Adds validation for bucket canned ACLs in `CreateBucket` and `PutBucketAcl`. The gateway supports three values: `private`, `public-read`, and `public-read-write`. All other values (including `authenticated-read`, which is not supported) are considered invalid and result in an `InvalidArgument` error with an empty error message.
2025-11-03 22:59:06 +04:00
niksis02
9bde1ddb3a feat: implements tagging support for CreateBucket
Closes #1595

This implementation diverges from AWS S3 behavior. The `CreateBucket` request body is no longer ignored. Based on the S3 request body schema, the gateway parses only the `LocationConstraint` and `Tags` fields. If the `LocationConstraint` does not match the gateway’s region, it returns an `InvalidLocationConstraint` error.

In AWS S3, tagging during bucket creation is supported only for directory buckets. The gateway extends this support to general-purpose buckets.

If the request body is malformed, the gateway returns a `MalformedXML` error.
2025-10-31 00:59:56 +04:00
Ben McClelland
326de3b010 Merge pull request #1505 from versity/ben/head-bucket-err
fix: head object should set X-Amz-Bucket-Region on access denied
2025-10-28 14:41:48 -07:00
Ben McClelland
ee67b41a98 fix: head object should set X-Amz-Bucket-Region on access denied
The HeadObject API states that the x-amz-bucket-region header
will still get set for an access denied error to correctly indicate
region of bucket. This is needed due to the way polices work
across regions in aws, and some apps rely on this behavior.

See notes in GetBucketLocation:
In a bucket's home Region, calls to the GetBucketLocation operation
are governed by the bucket's policy. In other Regions, the bucket
policy doesn't apply, which means that cross-account access won't
be authorized. However, calls to the HeadBucket operation always
return the bucket’s location through an HTTP response header,
whether access to the bucket is authorized or not. Therefore, we
recommend using the HeadBucket operation for bucket Region
discovery and to avoid using the GetBucketLocation operation.

Fixes #1500
2025-10-28 19:50:01 +04:00
niksis02
12f4920c8d feat: implements checksum calculation for all actions
Closes #1549
Fixes #1593
Fixes #1521
Fixes #1427
Fixes #1311
Fixes #1301
Fixes #1040

This PR primarily focuses on checksum calculation within the gateway, but it also includes several related fixes and improvements.

It introduces a middleware responsible for handling and calculating checksums for the `x-amz-checksum-*` headers and `Content-MD5`. The middleware is applied only to actions that expect a request body or checksum headers. It also enforces validation for actions that require a non-empty request body, returning an error if the body is missing. Similarly, it returns an error for actions where at least one checksum header (`Content-MD5` or `x-amz-checksum-*`) is required but none is provided.
The implementation is based on [https://gist.github.com/niksis02/eec3198f03e561a0998d67af75c648d7](the reference table), tested directly against S3:

It also fixes the error case where the `x-amz-sdk-checksum-algorithm` header is present but no corresponding `x-amz-checksum-*` or `x-amz-trailer` header is included.

Additionally, the PR improves validation for the `x-amz-content-sha256` header. For actions that require this header, an error is now returned when it’s missing. For actions that don’t require it, the middleware no longer enforces its presence. Following the common S3 pattern, the header remains mandatory for admin routes.

Finally, the `x-amz-content-sha256` header is now optional for anonymous requests, as it is not required in that case.
2025-10-25 01:58:03 +04:00
niksis02
24679a82ac fix: fixes the composite checksums in CompleteMultipartUpload
Fixes #1359

The composite checksums in **CompleteMultipartUpload** generally follow the format `checksum-<number_of_parts>`. Previously, the gateway treated composite checksums as regular checksums without distinguishing between the two formats.

In S3, the `x-amz-checksum-*` headers accept both plain checksum values and the `checksum-<number_of_parts>` format. However, after a successful `CompleteMultipartUpload` request, the final checksum is always stored with the part number included.

This implementation adds support for parsing both formats—checksums with and without the part number. From now on, composite checksums are consistently stored with the part number included.

Additionally, two integration tests are added:

* One verifies the final composite checksum with part numbers.
* Another ensures invalid composite checksums are correctly rejected.
2025-10-17 16:45:07 +04:00