1159 Commits

Author SHA1 Message Date
Zhengchao An b2a376c2d2 Merge commit from fork
* fix(admin): bound IAM import archive expansion

MAX_IAM_IMPORT_SIZE caps the compressed upload at 10 MB, but every member of the
archive was then read with read_to_end into an unbounded Vec. Deflate ratios well
above 100:1 are easy to construct, so a small authorized upload could expand
without limit across the seven members ImportIam reads.

Add a shared expansion budget (MAX_IAM_IMPORT_EXPANDED_SIZE, 10x the compressed
cap) drawn down by every member, and route all seven reads through one helper
that reads a byte past the remaining budget to detect overrun. Sharing the budget
bounds the archive as a whole rather than letting each member spend the full
limit independently.

Covers R03-CAN-024 through R03-CAN-030 plus R04-CAN-077 (backlog #1471) — one
fix rather than seven, since all seven call sites were byte-identical.

* fix(kms): confine local key paths and refuse silent key replacement

Local KMS key identifiers arrive from request input — the `name` tag on CreateKey,
the `keyId` body field or query parameter on DeleteKey — and were joined onto
`key_dir` with no validation. An identifier such as `../../tmp/evil` escaped the
configured directory, making key creation a constrained arbitrary-file write and
`DeleteKey` with `force_immediate` a cross-directory delete.

Validate in `master_key_path` and make it fallible, so every filesystem path in
this backend inherits the guard: decode_stored_key, load_master_key,
save_master_key, create_key and delete_key all derive their paths there. The rule
is containment rather than a character allowlist, so identifiers already in use
keep resolving; only separators, NUL, absolute paths and non-single-component
forms are refused. Note `.` and `..` are contained rather than refused — the
`.key` suffix turns them into the ordinary filenames `..key` and `...key`.

Separately, `LocalKmsBackend::create_key` had no existence check, while the
sibling `KmsClient::create_key` has always had one. Since `save_master_key`
renames over its destination, creating a key under an existing name silently
replaced its material and destroyed the ability to decrypt everything wrapped
under it — and the backend path is the one the admin API uses. It now returns
KeyAlreadyExists, matching StaticKmsBackend.

Covers R03-CAN-072, R03-CAN-073 and R07-CAN-103 (backlog #1475). R03-CAN-073
needed no separate change: delete_key routes both its load and its remove_file
through master_key_path.

* fix(swift): bound SLO manifest reads to the 2 MiB manifest limit

The three Swift SLO handlers that load a stored manifest (handle_slo_get,
handle_slo_get_manifest, handle_slo_delete) read the `<object>.slo-manifest`
object to EOF with AsyncReadExt::read_to_end. That key is predictable and
writable through the ordinary object PUT path, so a tenant can replace the
manifest with an arbitrarily large object and then make the server allocate
its full size on every SLO GET, multipart-manifest=get, or
multipart-manifest=delete request - a memory amplification bounded only by
the stored object size (CWE-400 / CWE-770). The 2 MiB manifest limit that
handle_slo_put enforces was not applied on the read side.

Introduce MAX_SLO_MANIFEST_SIZE (the existing 2 MiB PUT limit, now a named
constant) and a shared read_manifest_bytes helper that reads through a
`take(limit + 1)` and rejects anything larger, so an oversized manifest is
refused instead of being buffered first. All three call sites go through the
helper. handle_slo_put now checks the size before parsing the JSON.

Regression tests: test_read_manifest_bytes_rejects_oversized_manifest and
test_read_manifest_bytes_stops_reading_oversized_manifest (which asserts the
reader is not consumed past the limit), plus a boundary test that a manifest
at exactly 2 MiB is still accepted.

* fix(protocols): authorize every object in FTPS/WebDAV recursive deletes

The FTPS and WebDAV gateways authorized only the container before a
recursive delete and then destroyed everything inside it without a
further check:

- FTPS RMD (and DELE on a bucket path ending in '/') cleared
  s3:DeleteBucket, then delete_bucket_recursively listed the bucket and
  deleted every object.
- WebDAV DELETE on a bucket did the same via its own
  delete_bucket_recursively.
- WebDAV DELETE on a directory cleared s3:DeleteObject for the directory
  marker key ("dir/") only, then listed that prefix and deleted every
  child under it.

A principal holding s3:DeleteBucket (or s3:DeleteObject on a single
marker key) could therefore erase objects it had no s3:DeleteObject
permission for, and the operation reported success.

Deletion stays recursive - that is the expected behaviour for these
protocols - but each object now clears s3:DeleteObject on its own key
before it is removed, and the enumeration clears s3:ListBucket. A denial
aborts the whole operation with access denied rather than being skipped,
so the caller can never be told the delete succeeded while objects were
left behind or removed without authorization.

The test double gained shared-state cloning, delete_object/delete_bucket
call logs, and list/delete queue helpers so the regression tests can
observe that nothing is deleted once a deny lands.

* fix(server,ecstore): bound TLS handshakes and remote volume RPC waits

Three call sites let an unauthenticated client or a misbehaving peer hold
server resources with no deadline.

TLS listener (R03-CAN-035): process_connection awaited
`acceptor.accept(socket)` with no bound. A client that opens a TCP
connection and never finishes the handshake parks a Tokio task and a socket
forever, and the connection cap (RUSTFS_API_MAX_CONNECTIONS) is unlimited by
default, so nothing else sheds it. The handshake now runs under
accept_tls_with_deadline(), reusing the existing HTTP/1 header-read budget —
the established slow-client bound for the pre-request phase — and the
expiry is recorded through the same log/metric path as a handshake error,
under a new TIMEOUT failure kind.

Remote disk RPCs (R03-CAN-049, R03-CAN-050): list_volumes and delete_volume
passed Duration::ZERO, which execute_with_timeout treats as "no deadline",
so a peer that accepts the request and never answers stalls the coordinator
(and, for delete_volume, the bucket-deletion workflow). Both now pass
get_max_timeout_duration(), matching every sibling method in the file.

Regression tests: a silent TLS peer must be shed by the handshake deadline;
list_volumes/delete_volume against a peer that completes the TCP connect and
then goes silent must fail with DiskError::Timeout instead of hanging.

* fix(security): stop leaking signed headers and bound OIDC/KMS credentials

Three independent hygiene fixes found by the security review.

R03-CAN-018 (crates/signer): try_get_canonical_headers and get_signed_headers
logged the complete header map at DEBUG before signing. Runtime callers pass
session credentials and SSE-C key material through these headers, so anyone
able to raise the log level (or read DEBUG logs) recovered
X-Amz-Security-Token and SSE-C keys verbatim. The statements were debugging
leftovers with no operational value and are deleted rather than redacted.

R03-CAN-014 (crates/iam): the OIDC HTTP adapter buffered provider responses
with an unbounded Response::bytes(), so a configured, compromised or
attacker-pointed IdP endpoint could stream an arbitrarily large or endless
body into memory (the ValidateOidcConfig admin handler lets a ServerInfo
caller choose the endpoint). Responses are now read incrementally and fail
closed past MAX_OIDC_RESPONSE_SIZE, and the already SSRF-hardened client
builder gains request and connect timeouts so a stalled provider cannot pin
the calling task indefinitely.

R07-CAN-105 (helm): the Vault KMS token was serialized into the chart
ConfigMap, exposing it to every subject allowed to get ConfigMaps in the
namespace. It now renders into a dedicated Secret that the Deployment and
StatefulSet consume via envFrom; the Secret is separate from the main
credentials Secret so it also works when secret.existingSecret is set.

Regression tests:
- rustfs-signer: signing_never_logs_signed_header_material
- rustfs-iam: oidc_response_body_past_the_limit_is_rejected,
  oidc_response_body_at_the_limit_is_accepted
- scripts/test_helm_templates.sh: KMS token must never render in plaintext

* fix(webdav): enforce body limit, request timeout and connection cap

The configured WebDAV maximum body size was enforced from Content-Length, so a
chunked request declared no length and bypassed it entirely. The configured
request timeout was never applied to the connection at all, and the accept loop
spawned a task per connection with no bound, so an unauthenticated client could
hold resources indefinitely and in unbounded number.

Enforce the limit on bytes actually read rather than the declared length, apply
the configured timeout to the request, and bound accepted connections with a new
RUSTFS_WEBDAV_MAX_CONNECTIONS (default 1024) surfaced in the config report.

Covers R03-CAN-051, R03-CAN-052, R03-CAN-067, R04-CAN-089, R05-CAN-094 and
R05-CAN-097 (backlog #1471, #1474).

* fix(security): stop STS credentials from crossing the parent trust boundary

Two related credential-boundary holes let a short-lived STS credential act
with the full, unrestricted authority of the long-term user it was minted
from.

AddUser (R03-CAN-021, CWE-269/863): should_check_deny_only relaxes the admin
policy check to deny-only when a Console/STS session targets the IAM user it
represents. Nothing then stopped that session from calling AddUser with its
own parent's access key, so the handler wrote an attacker-chosen secret key
and status over the parent's stored Credentials via create_user ->
save_user_identity. A session that expires in minutes became permanent
control of the account. AddUser now rejects any temp or service-account
requester whose resolved parent equals the target access key, resolving the
parent the same way should_check_deny_only does (parent_user field, else the
JWT `parent` claim, since some stores persist the parent only in the token).

FTPS/SFTP/WebDAV password auth (R04-CAN-086, CWE-287/862): these protocols
looked the access key up with check_key, which falls back to the STS account
cache, and then compared only the stored secret. An STS access key plus
secret therefore authenticated with no session token presented and no
session-policy claims applied - the holder got the parent's full permissions.
Password authentication now rejects temporary credentials before the secret
comparison. The discriminator is is_temp() && !is_service_account(), the same
one IamCache::update_user_with_claims uses to route an identity into the STS
cache, so service accounts - which resolve policy from stored IAM state
rather than a client-presented token - keep working over these protocols.

Regression tests cover both predicates and pin the guards to their call
sites so neither can be dropped without a test failure.
2026-07-27 00:22:50 +08:00
houseme 0ea7f17fd7 test(ilm): cover transition budget partial records (#5305)
Co-authored-by: heihutu <heihutu@gmail.com>
2026-07-27 00:15:58 +08:00
houseme fc6dfa891a test(ilm): cover heartbeat backpressure status (#5303)
Co-authored-by: heihutu <heihutu@gmail.com>
2026-07-26 14:41:18 +00:00
houseme 994678cfcf test(ilm): cover unknown checkpoint counters (#5299)
Co-authored-by: heihutu <heihutu@gmail.com>
2026-07-26 14:33:21 +00:00
houseme 0711a6f4fe test(ilm): cover manual transition journal replay matrix (#5300)
Co-authored-by: heihutu <heihutu@gmail.com>
2026-07-26 13:41:32 +00:00
houseme 0a25d25e68 test(ilm): cover cancelled continuation resume (#5294)
Co-authored-by: heihutu <heihutu@gmail.com>
2026-07-26 13:40:36 +00:00
houseme 009dd93788 test(ilm): cover restart unknown readback (#5298)
Co-authored-by: heihutu <heihutu@gmail.com>
2026-07-26 13:15:25 +00:00
houseme 7cacd1f558 feat(ilm): persist manual transition task journals (#5296)
* feat(ilm): persist manual transition task journal

Co-Authored-By: heihutu <heihutu@gmail.com>

* fix(ilm): reconcile manual task journals on recovery

Co-Authored-By: heihutu <heihutu@gmail.com>

---------

Co-authored-by: heihutu <heihutu@gmail.com>
2026-07-26 12:38:41 +00:00
houseme c3242f83ba test(tier): cover reporter committed prepare replay (#5297)
Add a deterministic four-hot AddTier regression for the reporter path where one peer has already durably committed prepare replay and the remaining peers still need commit fanout before local publish.

Co-authored-by: heihutu <heihutu@gmail.com>
2026-07-26 12:37:14 +00:00
houseme 09a991e735 test(perf): cover GET fallback metric probes (#5291)
* test(perf): cover GET fallback metric probes

Co-Authored-By: heihutu <heihutu@gmail.com>

* test(perf): prefer format fallback labels

Co-Authored-By: heihutu <heihutu@gmail.com>

* test(ilm): remove duplicate manual transition import

Co-Authored-By: heihutu <heihutu@gmail.com>

---------

Co-authored-by: heihutu <heihutu@gmail.com>
2026-07-26 11:44:53 +00:00
houseme 14c249c266 test(ilm): cover durable checkpoint persistence (#5288)
* test(ilm): cover durable checkpoint persistence

Co-Authored-By: heihutu <heihutu@gmail.com>

* style: format checkpoint persistence imports

Co-Authored-By: heihutu <heihutu@gmail.com>

---------

Co-authored-by: heihutu <heihutu@gmail.com>
2026-07-26 11:32:23 +00:00
houseme 5acc52b7f9 test(ilm): cover admission scope parallelism (#5293)
* test(ilm): cover cross-bucket admission scope

Co-Authored-By: heihutu <heihutu@gmail.com>

* fix(ilm): remove duplicate transition progress import

Co-Authored-By: heihutu <heihutu@gmail.com>

---------

Co-authored-by: heihutu <heihutu@gmail.com>
2026-07-26 11:30:08 +00:00
houseme 26663e0d5d test(ilm): cover durable progress checkpoint renewal (#5287)
Add regression coverage for durable manual transition progress checkpoints so page progress persists the report, queue snapshot, and renewed scope admission lease together.

Co-authored-by: heihutu <heihutu@gmail.com>
2026-07-26 10:34:08 +00:00
houseme caeaa4bf34 test(ilm): cover durable transition progress recovery (#5286)
Co-authored-by: heihutu <heihutu@gmail.com>
2026-07-26 10:06:24 +00:00
houseme 05f52beea3 test(ilm): cover cancelled worker counter persistence (#5285)
* test(ilm): cover combined manual transition failures

Co-Authored-By: heihutu <heihutu@gmail.com>

* test(ecstore): route transition matrix imports

Co-Authored-By: heihutu <heihutu@gmail.com>

* test(ilm): satisfy transition matrix lint

Co-Authored-By: heihutu <heihutu@gmail.com>

* test(ilm): cover cancelled worker counter persistence

Add a focused manual transition job matrix regression for cancel-requested terminal records that already contain mixed worker completion and failure counters. The test verifies cancelled state reduction preserves worker counters and survives encode/decode.

Co-Authored-By: heihutu <heihutu@gmail.com>

---------

Co-authored-by: heihutu <heihutu@gmail.com>
2026-07-26 10:01:03 +00:00
houseme e6706bb94a test(ilm): cover combined manual transition failures (#5279)
* test(ilm): cover combined manual transition failures

Co-Authored-By: heihutu <heihutu@gmail.com>

* test(ecstore): route transition matrix imports

Co-Authored-By: heihutu <heihutu@gmail.com>

* test(ilm): satisfy transition matrix lint

Co-Authored-By: heihutu <heihutu@gmail.com>

---------

Co-authored-by: heihutu <heihutu@gmail.com>
2026-07-26 17:14:01 +08:00
houseme a609b92b3c fix(ilm): retry transient scope admission races (#5280)
Co-authored-by: heihutu <heihutu@gmail.com>
2026-07-26 17:13:53 +08:00
Zhengchao An 4e353fb3c8 refactor(storage): resolve staged dead code in store module (#5283)
refactor(storage): resolve staged dead code in store module (#730)

Drop the store module's blanket #![allow(dead_code)] and resolve every
unit it was masking, instead of keeping them staged:

- write_persistent_key_only_index: the keys-only writer wrapper only had
  test callers; the in-process rebuild flow already exists and uses the
  _with_metadata variant (prepare -> rebuild, triggered lazily from the
  opt-in listing path). Tests call _with_metadata directly now.
- ListIndexLifecycle::recover_after_restart / mark_corrupt: production
  derives index health per request from the persisted artifacts (index
  file + namespace mutation journal), and restart recovery already lives
  in load_persistent_key_only_index's journal restore. The persisted-
  lifecycle design these transitions served was superseded, and Corrupt
  had no detector, so the never-constructed Corrupt state/reason
  variants go with them.
- record_list_objects_index_opt_in_fallback (+ helpers): superseded by
  the inline per-site fallback recording in the opt-in listing path; the
  recorder recomputed health with a hardcoded None provider, so it could
  only ever report a disabled-based reason.
- Provider-contract traits (Generation/Page/PageIterator/KeyLookup):
  provider dispatch went the ListObjectsIndexProviderKind enum route;
  only ListMetadataIndexHealth is live (dyn in source-mode selection)
  and stays, minus its unused is_healthy default.
- Delete the unused list_quorum_from_env/RUSTFS_API_LIST_QUORUM pair,
  fold should_resume_local_decommission and
  should_auto_start_rebalance_after_recovered_meta into their surviving
  primitives, drop get_disk_via_endpoint, and cfg(test) the remaining
  test-only conveniences.
2026-07-26 08:54:12 +00:00
houseme 058f81c61f fix(ilm): deduplicate manual transition worker results (#5278)
* fix(ilm): fail closed on unsafe manual job recovery

Mark expired manual transition jobs Unknown when recovery cannot prove queued worker outcomes are durable. Enforce ETag-guarded admission release with exact delete preconditions so stale releasers cannot remove a replacement admission, while still allowing drained cancelled jobs to terminate.

Co-Authored-By: heihutu <heihutu@gmail.com>

* style(ilm): format manual recovery imports

Apply rustfmt import ordering after merging main into the manual recovery branch.

Co-Authored-By: heihutu <heihutu@gmail.com>

* fix(ilm): deduplicate manual transition worker results

Persist per-task manual transition worker result markers before applying job counters so duplicate worker completion reports are no-ops. Reconcile persisted markers during drained-queue lease renewal and recovery to restore marker-before-record crash windows. Fail closed when persisted worker result markers are corrupt.

Co-Authored-By: heihutu <heihutu@gmail.com>

---------

Co-authored-by: heihutu <heihutu@gmail.com>
2026-07-26 08:28:33 +00:00
houseme b4e3c7117e fix(ilm): fail closed on unsafe manual recovery (#5276)
fix(ilm): fail closed on unsafe manual job recovery

Mark expired manual transition jobs Unknown when recovery cannot prove queued worker outcomes are durable. Enforce ETag-guarded admission release with exact delete preconditions so stale releasers cannot remove a replacement admission, while still allowing drained cancelled jobs to terminate.

Co-authored-by: heihutu <heihutu@gmail.com>
2026-07-26 07:36:58 +00:00
houseme 23f4683f20 test(ilm): cover queue terminal partial reports (#5277)
Cover manual transition terminal job records for queue closed and queue send timeout outcomes so backpressure summaries remain partial with the expected counters and queue snapshots.

Co-authored-by: heihutu <heihutu@gmail.com>
2026-07-26 07:21:37 +00:00
houseme 12b7f22fca test(ilm): cover stale transition admission reclaim (#5273)
Co-authored-by: heihutu <heihutu@gmail.com>
2026-07-26 06:55:36 +00:00
houseme a047bbfcfb test(ilm): cover manual transition worker failure summary (#5272)
Co-authored-by: heihutu <heihutu@gmail.com>
2026-07-26 06:45:06 +00:00
houseme 02ad75e552 test(tier): cover committed prepare replay fanout (#5270)
Co-authored-by: heihutu <heihutu@gmail.com>
2026-07-26 13:41:37 +08:00
houseme 21c85481b8 test(ilm): cover unknown manual transition worker loss (#5269)
test(ilm): cover unknown state after lost worker result

Co-authored-by: heihutu <heihutu@gmail.com>
2026-07-26 13:41:26 +08:00
houseme 1397a4e7ca feat(ilm): expose lifecycle expiry scanner counters (#5262)
Co-authored-by: heihutu <heihutu@gmail.com>
2026-07-26 12:11:03 +08:00
houseme 78dd2d40d3 test(ilm): cover deterministic manual transition cancel (#5263)
Add a focused ecstore regression that exercises active manual transition cancellation through the existing cancel_check hook after scan progress has been made. The test verifies the cancelled report, dry-run counters, and opaque resume cursor without relying on wall-clock timing.

Co-authored-by: heihutu <heihutu@gmail.com>
2026-07-26 04:03:19 +00:00
houseme 342f9f94bc test(ilm): cover manual transition queue pressure status (#5265)
Co-authored-by: heihutu <heihutu@gmail.com>
2026-07-26 04:02:44 +00:00
houseme 92f72c3912 fix(ilm): normalize cancelled manual job reports (#5255)
Ensure cancelled durable manual transition jobs expose a cancelled report both when worker results drain after a cancel request and when legacy persisted records are decoded.

Co-authored-by: heihutu <heihutu@gmail.com>
2026-07-26 03:18:53 +00:00
houseme 6fa2d06731 fix(ilm): honor explicit object lock metadata (#5253)
Co-authored-by: heihutu <heihutu@gmail.com>
2026-07-26 02:57:37 +00:00
harry han 03af8e472b fix(lifecycle): stop tier free-version recovery walk-timeout loop (#5194)
The background tier free-version recovery walk pinned a hardcoded 60s
total wall-clock timeout that overrides every operator knob, so any
bucket whose healthy full walk exceeds 60s fails forever; the failed
run's duration was also subtracted from the next 60s tick, restarting
the walk immediately and pinning CPU and disk I/O.

- Drop the total wall-clock budget on the recovery walk
  (walkdir_timeout: Duration::ZERO) and inherit the operator-tunable
  drive stall budget (RUSTFS_DRIVE_WALKDIR_STALL_TIMEOUT_SECS) for
  per-call progress, so hung disks still fail fast.
- Back off failed runs from completion time: 60s doubling to a 600s
  cap, reset on success; never subtract the failed run's duration.
- Add RUSTFS_TIER_FREE_VERSION_RECOVERY_ENABLED (default true) to opt
  out of the recovery worker on deployments with no remote tiers;
  invalid values warn and fail open.

Fixes #5130

Co-authored-by: claude <claude@ehdtn.com>
2026-07-26 02:54:33 +00:00
houseme 6d8e196f36 feat(ilm): trace lifecycle expiry execution (#5250)
Co-authored-by: heihutu <heihutu@gmail.com>
2026-07-26 01:58:48 +00:00
Zhengchao An 5c99ca1328 fix(ecstore): fail fast on missing RPC secret in distributed startup (#5248)
When endpoints include remote nodes and no internode RPC secret is
resolvable, every remote format read fails client-side with "No valid
auth token" and startup dies ~2 minutes later with the misleading
"store init failed to load formats after 10 retries: erasure read
quorum" error (issues #4939, #5153).

Add a preflight to ECStore init that resolves the RPC secret once
before any disk opens: if any endpoint is non-local and resolution
fails, abort immediately with the operator-facing remediation message.
The preflight also emits the "RPC auth secret resolution failed" log
line so the log-analyzer rpc-secret-resolution rule keeps firing for
this scenario. Single-node (all-local) topologies never invoke the
resolver.
2026-07-26 00:28:38 +00:00
唐小鸭 233865d172 feat(kms): introduce KMS unavailability error and enhance data key handling (#5184) 2026-07-26 04:04:35 +08:00
houseme 77b5e1b64c fix(ilm): report manual transition tier failures (#5238)
Co-authored-by: heihutu <heihutu@gmail.com>
2026-07-25 18:02:37 +00:00
houseme 23a5db4012 fix(ilm): preserve lifecycle version groups (#5239)
* fix(ilm): evaluate complete version groups

Co-Authored-By: heihutu <heihutu@gmail.com>

* feat(ilm): log replication expiry blocks

Co-Authored-By: heihutu <heihutu@gmail.com>

* fix(ilm): diagnose incomplete noncurrent chains

Co-Authored-By: heihutu <heihutu@gmail.com>

* test(ilm): cover purge-pending version groups

Add regression coverage for lifecycle-only version listing so purge-pending versions stay present for ILM evaluation while the public ListObjectVersions projection remains filtered.

Refs rustfs/backlog#1500

Co-Authored-By: heihutu <heihutu@gmail.com>

* feat(ilm): log lifecycle evaluation failures

Emit structured lifecycle_evaluation_failed events when version group loading or evaluation fails during immediate and existing-object expiry scans.

Refs rustfs/backlog#1503

Co-Authored-By: heihutu <heihutu@gmail.com>

* test(ilm): cover incomplete noncurrent chains

Pin the fail-closed behavior for noncurrent expiration when successor_mod_time is missing and reuse a stable structured event name for that skip path.

Refs rustfs/backlog#1502

Co-Authored-By: heihutu <heihutu@gmail.com>

* test(ilm): cover one-day noncurrent expiry boundary

Add a runtime regression test proving NoncurrentVersionExpiration Days=1 stays inactive before expected_expiry_time and deletes exactly at the computed due boundary.

Refs rustfs/backlog#1504

Co-Authored-By: heihutu <heihutu@gmail.com>

* fix(ilm): keep transition checks after incomplete expiry

Co-Authored-By: heihutu <heihutu@gmail.com>

---------

Co-authored-by: heihutu <heihutu@gmail.com>
2026-07-25 17:42:02 +00:00
houseme 516f7fecc1 fix(tier): converge config after peer recovery (#5240)
Retry peer tier config reloads after committed mutations so recovered nodes converge without requiring a second admin change.

Co-authored-by: heihutu <heihutu@gmail.com>
2026-07-25 17:25:37 +00:00
houseme 1e95e6d311 fix(ilm): harden durable transition admission (#5235)
Co-authored-by: heihutu <heihutu@gmail.com>
2026-07-25 15:46:55 +00:00
houseme 3cbe3d6b94 fix(tier): reconcile committed mutation replay (#5230)
Co-authored-by: heihutu <heihutu@gmail.com>
2026-07-25 15:20:54 +00:00
Zhengchao An 61d4e04d65 feat(rpc): bind canonical body digest into internode mutating disk RPC signatures (#5234)
* feat(rpc): bind canonical body digest into internode mutating disk RPC signatures

Binds a domain-separated, length-prefixed canonical request-body digest into the
v2 HMAC signature scope for every mutating NodeService disk RPC, so an on-path
attacker on the default-plaintext internode channel can no longer tamper with a
mutation payload (or strip the msgpack `_bin` field to force the JSON fallback
decode) without invalidating the signature.

Covers 13 mutating disk RPCs: RenameData, DeleteVersion, DeleteVersions,
WriteMetadata, UpdateMetadata, WriteAll, Delete, DeletePaths, RenameFile,
RenamePart, DeleteVolume, MakeVolume, MakeVolumes. The digest covers both the
msgpack `_bin` payloads and their JSON compatibility copies. Gated fail-open by
default (RUSTFS_INTERNODE_RPC_BODY_DIGEST_STRICT) with a convergence counter, so
rolling upgrades are byte-for-byte unaffected; the replay-cache capacity is now
configurable and overflow fails closed with a metric.

Refs https://github.com/rustfs/backlog/issues/1327

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(rpc): satisfy architecture-migration compat-marker guard

Put the removal condition on the RUSTFS_COMPAT_TODO marker line itself, and
stop backticking env-var/metric names in the cleanup-register entry so the
guard's id extractor only sees the task-id.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 22:56:27 +08:00
houseme 6974963e20 feat(ilm): add durable manual transition job store (#5229)
* feat(ilm): add manual transition job route contract

Refs #1479

Co-Authored-By: heihutu <heihutu@gmail.com>

* feat(ilm): add durable manual transition job store

Co-Authored-By: heihutu <heihutu@gmail.com>

* fix(ilm): harden durable transition job cancellation

Co-Authored-By: heihutu <heihutu@gmail.com>

---------

Co-authored-by: heihutu <heihutu@gmail.com>
2026-07-25 14:54:14 +00:00
houseme 7ab0955f8b test(e2e): stabilize tier and storage class checks (#5228)
Co-authored-by: heihutu <heihutu@gmail.com>
2026-07-25 14:21:52 +00:00
houseme 2cf5fd6bfc fix(getobject): avoid duplicate downstream-close metrics (#5231)
Co-authored-by: heihutu <heihutu@gmail.com>
2026-07-25 14:13:47 +00:00
houseme 5988606e68 test(internode): extend fallback and transition coverage (#5232)
Add decode-error metrics for internode msgpack/json compatibility paths, extend the mixed fallback e2e assertions for transitioned multipart partNumber reads, and cover manual transition async status polling plus inactive-owner status behavior.

Co-authored-by: heihutu <heihutu@gmail.com>
2026-07-25 13:26:23 +00:00
cxymds f52dde87d1 fix(object): make version undo preconditions atomic (#5225)
* fix(object): make version undo preconditions atomic

* fix(object): fence metadata-only undo copies

* fix(object): order versions by commit time
2026-07-25 19:26:50 +08:00
houseme 8e83087ba4 fix(tier): handle mutation intent CAS races (#5227)
* test(scripts): expand internode grpc ab env coverage

* fix(tier): handle mutation intent CAS races

Make tier mutation intent advancement retry idempotently when an If-Match CAS loses to a matching committed peer update.

Expand four-node inline fallback E2E coverage for mixed msgpack controls, transition readiness evidence, and per-node environment overrides.

Co-Authored-By: heihutu <heihutu@gmail.com>

---------

Co-authored-by: heihutu <heihutu@gmail.com>
2026-07-25 11:10:13 +00:00
Henry Guo a63b79004c fix(scanner): make distributed usage convergence authoritative (#5151)
* fix(scanner): make distributed usage cycles authoritative

* fix(scanner): close distributed refresh races

* fix(config): align scanner reload integration

* fix(admin): scope config test helpers

* fix(scanner): harden distributed usage convergence

* fix(scanner): preserve rolling activity compatibility

* fix(admin): expose non-secret optional config values

* fix(scanner): acknowledge distributed dirty usage

* fix(ecstore): make bucket mutations cancellation safe

* fix(scanner): preserve pending dirty acknowledgements

* test(obs): account for superseded scanner metric

* fix(api): reject excess detached bucket mutations

* test: close scanner convergence coverage gaps

* fix(scanner): make path tracking cleanup one-shot

---------

Co-authored-by: Henry Guo <marshawcoco@users.noreply.github.com>
Co-authored-by: houseme <housemecn@gmail.com>
2026-07-25 18:45:16 +08:00
houseme 2dc4d0b651 refactor(getobject): scope downstream close tagging (#5224)
Co-authored-by: heihutu <heihutu@gmail.com>
2026-07-25 09:36:19 +00:00
houseme 2ee111ad8b feat(ilm): add durable manual transition jobs (#5223)
Refs #1479

Co-authored-by: heihutu <heihutu@gmail.com>
2026-07-25 09:12:44 +00:00
houseme 9ddb30139d fix(getobject): distinguish downstream output closure (#5220)
fix(getobject): preserve internal broken pipe failures

Co-authored-by: heihutu <heihutu@gmail.com>
2026-07-25 08:22:20 +00:00