Commit graph

2,802 commits

Author SHA1 Message Date
Arthur Carcano
7d97b2b96e Ignore flaky test_items_and_indices 2026-05-01 21:36:17 +00:00
Arthur Carcano
a5650ea303 Ignore typos in fuzz/ 2026-05-01 21:36:17 +00:00
Arthur Carcano
322da7242b Post review fixes 2026-05-01 21:36:17 +00:00
Arthur Carcano
6a097e7de3 Add MPU table 2026-05-01 21:36:17 +00:00
Arthur Carcano
6ddae5397c Add version table fuzz 2026-05-01 21:36:17 +00:00
Arthur Carcano
9a18259419 Add rust toolchain toml in fuzz dir 2026-05-01 21:36:17 +00:00
Arthur Carcano
ade4d07bb5 Set up fuzz infrastructure 2026-05-01 21:36:17 +00:00
Alex
0a5282d918 Merge pull request 'Add garage health CLI subcommand' (#1373) from Arlen2/garage:1354_health-check_cmd into main-v2
Reviewed-on: https://git.deuxfleurs.fr/Deuxfleurs/garage/pulls/1373
2026-05-01 19:48:14 +00:00
Alex Auvolat
12012916b7 simplify the garage health subcommand 2026-05-01 21:32:50 +02:00
Paul FLORENCE
9fa4e03748 Add health-check command to garage CLI
This command is used to check the local node health.

Related to https://git.deuxfleurs.fr/Deuxfleurs/garage/issues/1354
2026-05-01 21:32:50 +02:00
Alex
f7be222471 Merge pull request 'admin api: return full layout computation statistics as json (fix #1428)' (#1435) from fix-1428 into main-v2
Reviewed-on: https://git.deuxfleurs.fr/Deuxfleurs/garage/pulls/1435
2026-05-01 17:53:39 +00:00
Alex Auvolat
5e9380820e admin api: update OpenApi schema 2026-05-01 17:53:39 +00:00
Alex Auvolat
62349a6559 admin api: return full layout computation statistics as json (fix #1428) 2026-05-01 17:53:39 +00:00
Alex Auvolat
ada0c8ab70 admin api: add fields to GetNodeInfo result (fix #1429) (#1434)
Reviewed-on: https://git.deuxfleurs.fr/Deuxfleurs/garage/pulls/1434
2026-05-01 16:57:27 +00:00
Alex Auvolat
7bc7f33f43 bg vars: return "never" when scrub never ran (fix #1421) (#1430)
Reviewed-on: https://git.deuxfleurs.fr/Deuxfleurs/garage/pulls/1430
2026-05-01 15:06:02 +00:00
Alex Auvolat
be203494c5 set some flaky tests as #[ignore] (#1432)
Reviewed-on: https://git.deuxfleurs.fr/Deuxfleurs/garage/pulls/1432
2026-05-01 14:44:28 +00:00
Alex Auvolat
3c983ac5e0 admin api: properly eliminate irrelevant role deletions (fix #1427) (#1431)
Reviewed-on: https://git.deuxfleurs.fr/Deuxfleurs/garage/pulls/1431
2026-05-01 14:40:34 +00:00
Minkyu Kim
a2c797000f fix(cors): return single matching origin instead of multiple values in Access-Control-Allow-Origin (#1419)
## Title
fix(cors): return single matching origin instead of multiple values in `Access-Control-Allow-Origin`

## Summary
This PR fixes bucket CORS responses when a single CORS rule contains multiple `AllowedOrigins`.

Previously, Garage returned the configured origins as a comma-separated list in `Access-Control-Allow-Origin`, for example:

```http
Access-Control-Allow-Origin: https://app.example.test, https://admin.example.test
```

This is not the expected browser-facing behavior.
When a request origin matches a configured rule, the response should reflect **only the matching request origin**, unless the rule contains `*`.

## What changed
- `Access-Control-Allow-Origin` now behaves as follows:
  - returns `*` when the matched rule contains a wildcard origin
  - otherwise returns the request `Origin` as a **single value**
- added `Vary: Origin` when ACAO reflects the request origin
- added preflight-specific `Vary` handling in the preflight path for:
  - `Origin`
  - `Access-Control-Request-Method`
  - `Access-Control-Request-Headers`

## Scope
This change applies to shared bucket CORS handling paths, including:
- S3 API responses
- K2V API responses
- S3 POST object responses
- web bucket responses
- preflight (`OPTIONS`) bucket CORS responses

This does **not** change admin API fixed CORS behavior.

## Reproduction
A direct repro script is included:

```bash
./script/test-cors-multi-origin.sh
```

It exercises two cases against a direct single-node Garage instance:

1. **single-origin control**
2. **multi-origin repro**

Before this fix, the multi-origin case returned a comma-separated ACAO value.

After this fix, both cases reflect only the request origin.

## Example behavior

### Before
```http
Access-Control-Allow-Origin: https://app.example.test, https://admin.example.test
```

### After
```http
Access-Control-Allow-Origin: https://app.example.test
```

## Tests
Added/updated tests in `src/api/common/cors.rs` for:
- single-origin control
- multiple allowed origins reflecting the request origin
- wildcard origin preserving `*`
- preserving existing `Vary` values while appending `Origin`

## Validation
Used for validation:

```bash
cargo test -p garage_api_common cors::tests -- --nocapture
cargo build -p garage --bin garage
./script/test-cors-multi-origin.sh
```

## Reproducibility
For reviewers who want to validate behavior by commit:

- Before fix: `aa368e4b`
  - includes the direct repro script and the regression test setup
  - multi-origin ACAO is reproduced as a comma-separated value

- After fix: `f630eb92`
  - reflects only the matching request origin
  - preserves wildcard behavior
  - adds `Vary: Origin` and preflight-specific `Vary` handling

Branch:
- `fix/cors-multiple-allow-origin`

Base used during validation:
- `74ad3bf8` (`main-v2`)

Closes Deuxfleurs/garage#1149

Reviewed-on: https://git.deuxfleurs.fr/Deuxfleurs/garage/pulls/1419
2026-04-28 14:48:02 +00:00
Austin Drummond
80f9335950 collapse sequential whitespace in canonical SigV4 header values (#1424)
## Summary

Garage's SigV4 canonical-request builder trims leading/trailing whitespace from signed header values but does not collapse sequential internal whitespace, which the SigV4 spec requires:

> Convert sequential spaces to a single space.

— https://docs.aws.amazon.com/IAM/latest/UserGuide/create-signed-request.html

AWS SDKs apply this normalization before computing the signature, but transmit the raw value on the wire. The receiver must therefore apply the same normalization when reconstructing the canonical request, otherwise the recomputed hash differs and the request is rejected as `Invalid signature`.

Same class of canonicalization-drift bug as #1155 / !1382, but on the canonical-headers axis rather than the canonical-URI axis.

## Reproduction

Surfaces in practice with `gitlab-runner`'s S3 cache uploader. I was in the midst of migrating my runner cache from AWS S3 to garage, but I noticed some shared runner caches were no longer uploading.

I was using `sha256sum | sha256sum` to compute my cache keys, which leaves a trailing `  -` on the value. Once GitLab appends `-protected` for protected branches the resulting `x-amz-meta-cachekey` header value contains internal sequential whitespace and triggers the mismatch:

```
x-amz-meta-cachekey:php-  --protected
                              ^^
                              two spaces, preserved by Garage
```

Without the fix the included regression test (`test_presigned_put_with_user_metadata`) fails with HTTP 403; with the fix it returns 200.

`aws-cli` is unaffected because it signs `Content-Type` rather than user metadata, so the specific code path with whitespace-bearing signed header values isn't exercised.

## Fix

In `canonical_request` (`src/api/common/signature/payload.rs`), replace the `.trim()` call on the joined header value with the full SigV4 normalization — `split_whitespace().collect::<Vec<_>>().join(" ")` — which both trims edges and collapses internal runs.

## Tests

* New regression test `test_presigned_put_with_user_metadata` covering a  presigned PUT whose `x-amz-meta-*` value contains internal sequential whitespace.
* Full integration suite passes: `40 passed; 0 failed; 2 ignored`.
* `garage_api_common` unit tests pass: `18 passed; 0 failed`.

## Notes

* Backwards-compatible: any signature that validated before still validates, because clients are spec-required to collapse on their side; Garage was only rejecting requests where the client had collapsed correctly but Garage hadn't.
* No config or migration changes.
* Fix applies to both presigned-URL and Authorization-header code paths since they share the canonical-request builder.

Reviewed-on: https://git.deuxfleurs.fr/Deuxfleurs/garage/pulls/1424
Reviewed-by: Alex <lx@deuxfleurs.fr>
2026-04-27 21:15:23 +00:00
maximilien
d217a3f15d add SECURITY.md (#1423)
Add some instructions to report security issues with garage.

Reviewed-on: https://git.deuxfleurs.fr/Deuxfleurs/garage/pulls/1423
2026-04-27 07:33:27 +00:00
Alex Auvolat
063bf8258b write CONTRIBUTING.md file, first iteration (#1406)
Merging this first version as a baseline. For future work: write a security.md document to explain how to report security vulnerabilities, and split off the release process in a separate releasing.md document

Reviewed-on: https://git.deuxfleurs.fr/Deuxfleurs/garage/pulls/1406
2026-04-26 10:35:17 +00:00
Alex
1d66240495 Merge pull request 'Update dependencies post-2.3.0 release' (#1415) from update-dependencies into main-v2
Reviewed-on: https://git.deuxfleurs.fr/Deuxfleurs/garage/pulls/1415
2026-04-23 20:42:22 +00:00
Alex Auvolat
d977ca4a24 fix new cargo clippy lints 2026-04-23 22:21:15 +02:00
Alex Auvolat
1cdaccbc3d update rust-overlay and use rust 1.95.0 2026-04-23 21:17:27 +02:00
Alex Auvolat
8e38680ef5 update dependencies post-2.3.0 release and update to rust 1.91.1 2026-04-23 21:11:19 +02:00
bnjoroge1
393c4bb2f6 cli: hide secret env values in help (#1418)
Closes #1417.

Co-authored-by: bnjoroge1 <bnjoroge1@users.noreply.git.deuxfleurs.fr>
Reviewed-on: https://git.deuxfleurs.fr/Deuxfleurs/garage/pulls/1418
2026-04-23 18:52:54 +00:00
Arthur Carcano
74ad3bf887 Replace the existential lifetime in sqlite adapter with a static one (#1407)
Reviewed-on: https://git.deuxfleurs.fr/Deuxfleurs/garage/pulls/1407
Reviewed-by: Alex <lx@deuxfleurs.fr>
2026-04-20 09:28:46 +00:00
Yureka
7c18abb664 fix: prevent depending on aws-lc via reqwest (#1412)
Otherwise the rustls dependency might be built with both aws-lc and ring backends,
leading to the following error in the k2v_client tests when
consul-discovery feature is enabled (including the reqwest dependency):

```
Could not automatically determine the process-level CryptoProvider from Rustls crate features.
Call CryptoProvider::install_default() before this point to select a provider manually, or make sure exactly one of the 'aws-lc-rs' and 'ring' features is enabled.
See the documentation of the CryptoProvider type for more information.
```

Co-authored-by: Yureka <yuka@yuka.dev>
Reviewed-on: https://git.deuxfleurs.fr/Deuxfleurs/garage/pulls/1412
Reviewed-by: Alex <lx@deuxfleurs.fr>
2026-04-20 09:28:21 +00:00
maximilien
1dffcca430 Merge pull request 'helm: make garage.toml bind addresses configurable via values' (#1383) from giottolino/garage:helm-configurable-bind-addrs into main-v2
Reviewed-on: https://git.deuxfleurs.fr/Deuxfleurs/garage/pulls/1383
2026-04-19 22:39:59 +00:00
gi8
5a8ee9f640 helm: make garage.toml bind addresses configurable via values 2026-04-19 22:39:59 +00:00
Alex Auvolat
7b119c0b4f bump version number to v2.3.0 v2.3.0 2026-04-16 18:34:27 +02:00
Alex Auvolat
02d5e67698 db: avoid iterating bounded from empty slice (fix #1401) (#1408)
Reviewed-on: https://git.deuxfleurs.fr/Deuxfleurs/garage/pulls/1408
Co-authored-by: Alex Auvolat <lx@deuxfleurs.fr>
Co-committed-by: Alex Auvolat <lx@deuxfleurs.fr>
2026-04-16 16:33:28 +00:00
maximilien
854280e957 Merge pull request 'helm: Conditionally skip CRD management RBAC rule' (#1248) from boris.m/garage:feat/drop-crd-management-rbac-rule into main-v2
Reviewed-on: https://git.deuxfleurs.fr/Deuxfleurs/garage/pulls/1248
Reviewed-by: maximilien <git@mricher.fr>
2026-04-16 16:22:17 +00:00
B Marinov
9ea2b1d628 helm: Conditionally skip CRD management RBAC rule
Remove rule permitting changes to CRDs when garage.kubernetesSkipCrd is  set to true.
2026-04-16 16:22:17 +00:00
maximilien
7b7548a4f7 Merge pull request 'Fix helm existing configmap volume ref in workload' (#1388) from PhilleZi/garage:fix-helm-existing-configmap into main-v2
Reviewed-on: https://git.deuxfleurs.fr/Deuxfleurs/garage/pulls/1388
Reviewed-by: maximilien <git@mricher.fr>
2026-04-16 16:20:27 +00:00
Philip Zingmark
a2e410f8b6 Fix helm existing configmap volume ref in workload 2026-04-16 16:20:01 +00:00
Alex
690729ccdb Merge pull request 'fix: bound known_addrs growth and add TCP connect timeout' (#1345) from rajsinghtech/garage:fix/peering-stale-addr-reconnection into main-v2
Reviewed-on: https://git.deuxfleurs.fr/Deuxfleurs/garage/pulls/1345
2026-04-15 11:42:38 +00:00
Alex Auvolat
ff743453b6 garage_net: make pruning logic simpler and add test 2026-04-15 11:42:38 +00:00
Raj Singh
f34a7db48a fix: bound known_addrs growth
known_addrs in PeerInfoInternal is append-only — addresses accumulate
via add_addr() and PeerList gossip but are never removed. In dynamic
environments (k8s pod restarts, DHCP, NAT traversal), this list grows
unboundedly with stale addresses.

Combined with sequential iteration in try_connect() and no TCP connect
timeout in netapp.rs, each unreachable address blocks reconnection for
the kernel's TCP SYN timeout (75-130s on Linux). With 10+ stale
addresses, worst-case reconnection exceeds 750s — a full outage for
replication_factor=3 clusters.

This commit contains the two following changes:

1. Address failure tracking and pruning (peering.rs): Track consecutive
   connection failures per address in PeerInfoInternal. After 3 failures,
   prune from known_addrs. Reset count when address is re-advertised via
   gossip or incoming connection. Prevents unbounded list growth.

2. Shuffle before connecting (peering.rs): Randomize address order in
   try_connect() so the valid address (often appended last) gets a fair
   chance instead of always trying stale addresses first.
2026-04-15 11:42:38 +00:00
Raj Singh
3a355b1617 fix: add TCP connect timeout
known_addrs in PeerInfoInternal is append-only — addresses accumulate
via add_addr() and PeerList gossip but are never removed. In dynamic
environments (k8s pod restarts, DHCP, NAT traversal), this list grows
unboundedly with stale addresses.

Combined with sequential iteration in try_connect() and no TCP connect
timeout in netapp.rs, each unreachable address blocks reconnection for
the kernel's TCP SYN timeout (75-130s on Linux). With 10+ stale
addresses, worst-case reconnection exceeds 750s — a full outage for
replication_factor=3 clusters.

This patches includes a first change to fix this issue:

1. TCP connect timeout (netapp.rs): Wrap TcpStream::connect() in
   tokio::time::timeout(10s). Caps per-address attempt from 75-130s
   to 10s, reducing worst-case 10-addr reconnection from ~750s to ~100s.
2026-04-15 11:42:38 +00:00
Alex
0b5e82a18b Merge pull request 'Cherry-pick #1396 for main-v2' (#1404) from fix-starvation into main-v2
Reviewed-on: https://git.deuxfleurs.fr/Deuxfleurs/garage/pulls/1404
2026-04-15 10:35:22 +00:00
Gauthier Zirnhelt
2798667345 Fix the LifecycleWorker being uncooperative (#1396)
## Summary

This PR ensures that the `LifecycleWorker` yields at least once to the Tokio scheduler in between each batch of 100 objects.

## Problem being solved

I'm administrating a Garage cluster which has been experiencing timeouts on all endpoints while the lifecycle worker is running at midnight UTC : `Ping timeout` error messages and even requests eventually failing due to `Could not reach quorum ...`.

I have found that this happens while the lifecycle worker is working on a big bucket (containing millions of objects) with a lifecycle rule that applies to very few objects.
The `process_object()` function does not hit any `await`:
- `last_bucket` is always the same, so the `bucket_table` is not read asynchronously
- no transaction is made on the `object_table` because my lifecycle rule (almost) never applies to any object

The first commit in this PR adds an executable which reproduces the problem that I've been experiencing in a self-contained way : the lifecycle worker starves the Tokio scheduler so much that no other task is able to run (or very rarely).
To run it : `cargo run -p garage_model --bin lifecycle-starvation-test`.
This commit can be dropped post-review, as it's only useful to demonstrate the starvation.

The error messages completely stopped after adding the extra yield to the nodes of my cluster.
The duration of the lifecycle worker task does not appear to have changed at all from what I can see (looking at the timestamps produced either by the self-contained binary or by each of my nodes with the `Lifecycle worker finished` message).

## Note

An other potential fix would have been to force the `WorkerProcessor` to yield before re-enqueuing a busy task, but this would have affected all Garage workers even though it's only the `LifecycleWorker` being uncooperative.

Reviewed-on: https://git.deuxfleurs.fr/Deuxfleurs/garage/pulls/1396
Reviewed-by: Alex <lx@deuxfleurs.fr>
Co-authored-by: Gauthier Zirnhelt <gauthier.zirnhelt@insimo.fr>
Co-committed-by: Gauthier Zirnhelt <gauthier.zirnhelt@insimo.fr>
2026-04-15 12:13:18 +02:00
Alex
b1660f0cba Merge pull request 'document known issues' (#1379) from doc-known-issues into main-v2
Reviewed-on: https://git.deuxfleurs.fr/Deuxfleurs/garage/pulls/1379
2026-04-15 10:11:39 +00:00
Alex Auvolat
dfb20ba87f doc: write details of known issues 2026-04-15 10:11:39 +00:00
maximilien
7279cb9113 Add comment on tags 2026-04-15 10:11:39 +00:00
Alex Auvolat
56cb89d153 wip: list known issues in documentation 2026-04-15 10:11:39 +00:00
Armael
6fd9bba0cb WebsiteConfiguration: do not emit empty XML attributes for absent values (#1391)
This fixes a regression wrt garage-v1, likely caused by the version upgrade of quick_xml.

Currently, garage-v2 will emit empty ErrorDocument/IndexDocument/RedirectAllRequestsTo attributes in the response of GetBucketWebsite if there are no corresponding values.
This is somewhat wrong; at least, the S3 documentation for RedirectAllRequestsTo (https://docs.aws.amazon.com/AmazonS3/latest/API/API_RedirectAllRequestsTo.html) writes that it has a required HostName field. So emitting an empty RedirectAllRequestsTo is invalid.

This PR skips emitting XML attributes for these parameters if they contain no value.

Co-authored-by: Armaël Guéneau <armael.gueneau@ens-lyon.org>
Reviewed-on: https://git.deuxfleurs.fr/Deuxfleurs/garage/pulls/1391
Co-authored-by: Armael <armael@noreply.localhost>
Co-committed-by: Armael <armael@noreply.localhost>
2026-04-13 13:59:32 +00:00
Jul Lang
f9605fae78 fix typo (#1402)
found by [typos](https://github.com/crate-ci/typos)

Reviewed-on: https://git.deuxfleurs.fr/Deuxfleurs/garage/pulls/1402
Co-authored-by: Jul Lang <jullanggit@proton.me>
Co-committed-by: Jul Lang <jullanggit@proton.me>
2026-04-13 12:12:57 +00:00
Armael
9969c3e599 Fix: correctly parse CORS website configuration with no rules (#1392)
This is a port of #1320 on top of the main-v2 branch.

Co-authored-by: Armaël Guéneau <armael.gueneau@ens-lyon.org>
Reviewed-on: https://git.deuxfleurs.fr/Deuxfleurs/garage/pulls/1392
Co-authored-by: Armael <armael@noreply.localhost>
Co-committed-by: Armael <armael@noreply.localhost>
2026-03-22 17:09:16 +00:00
Alex
a69a8d3b21 Merge pull request 'force uri encoding before check signature' (#1382) from gwenlg/garage:signature_doesnt_match_1155 into main-v2
Reviewed-on: https://git.deuxfleurs.fr/Deuxfleurs/garage/pulls/1382
Reviewed-by: Alex <lx@deuxfleurs.fr>
2026-03-22 10:59:43 +00:00