Commit Graph
1844 Commits
Author SHA1 Message Date
Zhengchao AnandGitHub 7f19e9a465 Merge commit from fork
docs/testing/security-regressions.md requires every fixed advisory to map to a
named, greppable regression test. Rename the tests added with the fixes to carry
their advisory id so `rg -i ghsa` finds them, and add the four rows to the
advisory -> test map.

Adds the FTPS MKD regression test that was missing. It primes the dummy backend
with a successful create_bucket, so the assertion distinguishes "denied at the
authorization boundary" from "backend refused" — without the queued success an
unconfigured create_bucket fails on its own and the test would pass even with
the authorization check removed. Verified it fails when the check is reverted.

DummyBackend gains a Debug impl (FtpsDriver's trait bounds require it) and a
queue_create_bucket_ok helper.

Records in the CI-execution map why ghsa_g3vq_* runs in the default pass despite
sitting behind the ftps feature: the rustfs crate defaults to ["ftps", "webdav"]
and cargo unifies features across the workspace build, so the test executes
there even though `cargo test -p rustfs-protocols` alone would skip it.
2026-07-26 07:10:50 +08:00
Zhengchao AnandGitHub 92f83bfe15 Merge commit from fork
* fix(policy): quantify negated string conditions per value

ForAllValues:/ForAnyValue: negated string operators computed the positive
quantified match and then negated the aggregate. That yields NOT(all match)
and NOT(any match), which is the semantics of the *other* quantifier, so
ForAllValues:StringNotEquals and ForAnyValue:StringNotEquals were exactly
transposed. The same applied to StringNotEqualsIgnoreCase, StringNotLike,
ArnNotEquals and ArnNotLike.

Introduce an explicit Quantifier and push negation into the per-value
predicate for the qualified forms, so ForAllValues requires every request
value to satisfy the operator and ForAnyValue requires at least one.
Unqualified operators keep negating the aggregate, preserving AWS
single-valued-key semantics. Absent keys now follow AWS: ForAllValues is
vacuously satisfied, ForAnyValue is not.

A request value set that is fully contained in or fully disjoint from the
policy set cannot distinguish the two quantifiers, which is why the existing
cases missed this; the new tests use partially overlapping sets.

* fix(auth): keep request headers out of server-derived condition keys

get_condition_values folded every remaining request header into the policy
condition map. HeaderMap lowercases header names, and the server-derived keys
userid, username, principaltype, versionid and signatureversion are lowercase
too, so a header of the same name collided with them. The collision branch used
extend(), and non-quantified string operators match if ANY value in the vector
matches, so sending `userid: admin` was enough to satisfy a condition on
aws:userid. The jwt:/ldap: claim keys were reachable the same way whenever the
credential carried no such claim.

groups was worse than an append: the header loop ran before the cred.groups
block, which is gated on !args.contains_key("groups"), so a `groups:` header
both injected a value and suppressed the credential's real group list.

Resolve claims and group membership before merging headers, then skip any header
naming a key the server already derived or a well-known identity/context key.
The reserved set comes from KeyName so it tracks the key registry; s3:x-amz-*
keys stay mergeable because they mirror request headers by design.

* fix(access): gate the ListBucketVersions fallback on public-access checks

An anonymous request for ListObjectVersions that the bucket policy does not
grant directly falls back to re-checking the grant as s3:ListBucket. That
fallback returned Ok(()) straight away, skipping the two gates the direct grant
passes through: deny_anonymous_table_data_plane_if_needed and the
RestrictPublicBuckets check on the bucket's public-access block.

So a bucket whose policy allows anonymous s3:ListBucket kept serving anonymous
version listings after an operator enabled RestrictPublicBuckets, even though
the equivalent GetObject was correctly denied.

Fold the fallback into policy_allowed so both routes reach the same gates.

* fix(ftps): authorize MKD against the CreateBucket boundary

FTPS MKD creates a bucket but ran no authorization check, so any principal that
could open an FTPS session could create buckets regardless of policy. Every
other operation in this driver authorizes first — LIST, RETR, STOR, DELE and
RMD all call authorize_operation — and the WebDAV gateway checks
S3Action::CreateBucket on the equivalent path.

Add the matching check so MKD clears the same boundary as an S3 CreateBucket.
2026-07-26 06:14:01 +08:00
Zhengchao AnandGitHub 258b7d6f06 fix(kms): do not include secret value in static KMS format error (#5243)
The malformed-format error message in build_static_kms_config embedded the
raw env var value. The most likely misconfiguration is setting
RUSTFS_KMS_STATIC_SECRET_KEY to the bare base64 key without the <key-name>:
prefix, in which case the full secret key material would be written to
startup logs. Drop the value from the message, matching the equivalent
parsing error in KmsConfig::from_env().
2026-07-25 21:24:34 +00:00
唐小鸭andGitHub 99e1f5fbd2 feat(kms): add static single-key backend (#5222) 2026-07-26 04:12:56 +08:00
Zhengchao AnandGitHub c984bc7251 fix(s3): return Cache-Control on GET object (#5241) 2026-07-26 04:12:11 +08:00
唐小鸭andGitHub 233865d172 feat(kms): introduce KMS unavailability error and enhance data key handling (#5184) 2026-07-26 04:04:35 +08:00
77b5e1b64c fix(ilm): report manual transition tier failures (#5238)
Co-authored-by: heihutu <[email protected]>
2026-07-25 18:02:37 +00:00
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 <[email protected]>
2026-07-25 17:25:37 +00:00
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 <[email protected]>

* 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 <[email protected]>

---------

Co-authored-by: Claude Opus 4.8 <[email protected]>
2026-07-25 22:56:27 +08:00
6974963e20 feat(ilm): add durable manual transition job store (#5229)
* feat(ilm): add manual transition job route contract

Refs #1479

Co-Authored-By: heihutu <[email protected]>

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

Co-Authored-By: heihutu <[email protected]>

* fix(ilm): harden durable transition job cancellation

Co-Authored-By: heihutu <[email protected]>

---------

Co-authored-by: heihutu <[email protected]>
2026-07-25 14:54:14 +00:00
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 <[email protected]>
2026-07-25 13:26:23 +00:00
ffde6c43ee feat(ilm): add manual transition job route contract (#5221)
Refs #1479

Co-authored-by: heihutu <[email protected]>
2026-07-25 12:22:46 +00:00
cxymdsandGitHub 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
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 <[email protected]>
Co-authored-by: houseme <[email protected]>
2026-07-25 18:45:16 +08:00
GatewayJandGitHub 0364523dad fix(s3select): enforce query and resource limits (#5028)
* fix(s3select): enforce query and resource limits

* fix(s3select): close query resource limit gaps

* fix(s3select): preserve timeout and stream invariants

* fix(s3select): enforce staged query limits

* fix(s3select): preserve policy error compatibility

* fix(s3select): bound error source traversal
2026-07-25 18:44:53 +08:00
2dc4d0b651 refactor(getobject): scope downstream close tagging (#5224)
Co-authored-by: heihutu <[email protected]>
2026-07-25 09:36:19 +00:00
2ee111ad8b feat(ilm): add durable manual transition jobs (#5223)
Refs #1479

Co-authored-by: heihutu <[email protected]>
2026-07-25 09:12:44 +00:00
9ddb30139d fix(getobject): distinguish downstream output closure (#5220)
fix(getobject): preserve internal broken pipe failures

Co-authored-by: heihutu <[email protected]>
2026-07-25 08:22:20 +00:00
ffd1b94e1f feat(rpc): add server-side internode v2 signature verification (fail-open, method-path binding groundwork) (#5160)
* feat(rpc): gate internode legacy signature fallback behind a convergence counter and strict env

Add the backlog#1327 Plan-A rollout infrastructure on top of the already-merged v2 target-bound internode gRPC authentication: a rustfs_system_network_internode_signature_v1_fallback_total counter that increments only when a request without any v2 auth headers is accepted through the legacy constant-target signature, and a RUSTFS_INTERNODE_RPC_SIGNATURE_STRICT env (default false, compile-time asserted) that, when enabled later, closes the legacy fallback path. Default behavior is fail-open and byte-identical for legacy-only peers; requests carrying v2 headers are verified as v2 with no downgrade exactly as before.

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

* ci: fix internode auth test lint failures

* perf(ecstore): cache internode sig strict env

---------

Co-authored-by: houseme <[email protected]>
2026-07-25 08:17:49 +00:00
cxymdsandGitHub 59361ed786 feat(site-replication): make repair operations durable (#5217)
* feat(site-replication): make repair operations durable

* fix(site-replication): serialize durable repair execution
2026-07-25 10:35:37 +08:00
housemeandGitHub dfb0a20048 test(rpc): add corrupt-json decode coverage (#5216)
* test(rpc): add corrupt-json decode coverage

* test(ilm): assert manual transition status-less contract
2026-07-25 01:03:59 +00:00
cxymdsandGitHub 7320d7fab2 fix(replication): make resync starts atomic (#5215) 2026-07-25 08:58:06 +08:00
Zhengchao AnandGitHub 28d19db9fc fix(console): handle apple touch icon paths (#5214) 2026-07-25 08:06:48 +08:00
cxymdsandGitHub 45b675c641 fix(replication): report authoritative backlog metrics (#5209) 2026-07-25 01:32:50 +08:00
cxymdsandGitHub 05caec0bd5 feat(replication): structure active check results (#5208) 2026-07-25 01:28:03 +08:00
cxymdsandGitHub eaa17e0441 feat(admin): add encrypted diagnostic archives (#5207) 2026-07-25 01:19:47 +08:00
cxymdsandGitHub 2b6cc0ee08 fix(admin): redact and seal config history (#5206) 2026-07-24 23:54:24 +08:00
cxymdsandGitHub 938f7296f9 fix(admin): report truthful diagnostic capabilities (#5205) 2026-07-24 23:33:45 +08:00
187a060919 test(internode): pin msgpack gate rollback coverage (#5203)
Add test coverage for the msgpack-only request/fleet confirmation truth table and exact request JSON policy manifest.

Pin request and response rollback behavior so removing either gate restores JSON compatibility while both gates enter msgpack-only for eligible fields.

Co-authored-by: heihutu <[email protected]>
2026-07-24 15:16:23 +00:00
cda443bd81 feat(ilm): reject overlapping manual transition runs (#5202)
Co-authored-by: heihutu <[email protected]>
2026-07-24 14:56:42 +00:00
44b1916103 fix(getobject): enrich stream error context (#5201)
Co-authored-by: heihutu <[email protected]>
2026-07-24 14:47:56 +00:00
cxymdsandGitHub fc43b149c5 fix(admin): restore complete config history snapshots (#5196) 2026-07-24 21:28:12 +08:00
cxymdsandGitHub fa235e9018 fix(s3): list multipart uploads by bucket prefix (#5195)
* fix(s3): list multipart uploads by bucket prefix

* fix(s3): preserve exact-key crash reclamation
2026-07-24 21:28:06 +08:00
beb807ae2b perf(protos): cache internode msgpack-only flag (#5191)
Cache the internode msgpack-only env gate after the first read so metadata RPC send paths avoid repeated environment parsing. Keep a reset hook for tests that intentionally change the env in-process.

Co-authored-by: heihutu <[email protected]>
2026-07-24 11:54:14 +00:00
d8426dc459 feat(ilm): gate durable manual transition mode (#5190)
Co-authored-by: heihutu <[email protected]>
2026-07-24 11:53:19 +00:00
cxymdsandGitHub f46ea6e14f fix(s3): enforce SSE-C copy key validation (#5185) 2026-07-24 19:11:36 +08:00
ea417b6a32 feat(ilm): trace manual transition runs (#5187)
Co-authored-by: heihutu <[email protected]>
2026-07-24 10:58:04 +00:00
4963412265 fix(internode): guard msgpack-only JSON fallback (#5180)
Keep internode JSON compatibility fields unless operators explicitly confirm fleet-wide msgpack-only readiness. This prevents a single legacy rollout flag from emptying JSON fields in mixed-version clusters where older peers may still read the legacy JSON payload.

Co-authored-by: heihutu <[email protected]>
2026-07-24 17:57:51 +08:00
GatewayJandGitHub 1c88aa43c1 fix(iam): virtualize OIDC service account parents (#5152)
* fix(iam): preserve OIDC service account policy boundary

* fix(iam): virtualize OIDC service account parents

* fix(iam): reject malformed OIDC policy boundaries

* test(iam): isolate federated policy regression

* fix(iam): keep OIDC replication envelope off claims
2026-07-24 17:41:52 +08:00
cxymdsandGitHub 9eaf5fc8e3 fix(s3): complete CopyObject checksum support (#5178) 2026-07-24 16:57:11 +08:00
3132637294 feat(ilm): bound manual transition duration (#5174)
* feat(ilm): bound manual transition duration

Add maxDurationSeconds handling for manual transition runs so operators can bound long scoped scans without changing the existing enqueue_only job contract.

Co-Authored-By: heihutu <[email protected]>

* docs(ilm): document manual transition run limits

Document the enqueue_only manual transition contract, secret-handling guidance, and best-effort duration budget while pinning the new duration flag in the e2e response model.

Co-Authored-By: heihutu <[email protected]>

---------

Co-authored-by: heihutu <[email protected]>
2026-07-24 07:54:52 +00:00
cxymdsandGitHub 358caa23cb fix(storage): expose truthful storage class capabilities (#5172) 2026-07-24 15:28:03 +08:00
6765aca3f9 feat(ilm): add manual transition run endpoint (#5171)
* feat(ilm): report manual transition backfill outcomes

Add scoped lifecycle transition backfill reporting for backlog #1478 and expose enqueue outcomes needed by #1479 without changing the existing scanner/compensation bool API.

Co-Authored-By: heihutu <[email protected]>

* feat(admin): add manual transition run endpoint

Add a bounded POST /rustfs/admin/v3/ilm/transition/run API for backlog #1477 and cover the route, policy, query parsing, and partial status contract needed by #1481. Console operations from #1480 are intentionally left for a later client integration.

Co-Authored-By: heihutu <[email protected]>

* feat(ilm): allow manual transition resume markers

Accept additive marker and versionMarker parameters on the bounded manual transition run API so clients can continue from a partial report without changing the existing default scan behavior.

Co-Authored-By: heihutu <[email protected]>

* fix(ilm): harden manual transition partial reports

Preserve the null-version cursor contract, stop manual scans on enqueue pressure without skipping the failed object, and keep raw resume markers out of admin JSON responses.

Co-Authored-By: heihutu <[email protected]>

* test(ilm): add manual transition e2e coverage

Co-Authored-By: heihutu <[email protected]>

* perf(ilm): keep transition enqueue hot path direct

Co-Authored-By: heihutu <[email protected]>

---------

Co-authored-by: heihutu <[email protected]>
2026-07-24 15:13:22 +08:00
4133fbe0fc fix(storage): cover inline reader fallback controls (#5169)
* test(ecstore): cover inline fast path boundaries

Co-Authored-By: heihutu <[email protected]>

* fix(storage): cover inline reader fallback controls

Co-Authored-By: heihutu <[email protected]>

* perf(tier): keep commit fanout concurrent

Co-Authored-By: heihutu <[email protected]>

---------

Co-authored-by: heihutu <[email protected]>
2026-07-24 06:08:17 +00:00
cxymdsandGitHub 6f6d8a4d3e fix(s3): persist multipart upload storage class (#5170)
Fixes rustfs/backlog#1464.
2026-07-24 13:47:25 +08:00
cxymdsandGitHub cb344a3c77 fix(s3): honor CopyObject replacement metadata (#5168)
Fixes rustfs/backlog#1463.
2026-07-24 12:33:33 +08:00
Zhengchao AnandGitHub 36e97aba26 fix(security): reject federated STS credentials that collide with the root principal (#5161)
A federated OIDC / AssumeRoleWithWebIdentity login derives parent_user from the external identity via session_identity() (username -> email -> sub -> oidc-user-unknown). At IAM request time a temporary credential whose parent_user equals the root access key is treated as owner (see rustfs/src/admin/auth.rs and the rustfs_iam owner resolution used by the policy engine and service-account paths). A federated identity whose display name happens to equal the root access key would therefore be silently granted full owner access on a string match alone.

Reject such issuance up front: immediately after parent_user is derived, and before credential generation, set_temp_user, and the site-replication hook, deny the binding when parent_user equals the root access key. Both federated flows funnel through the single DefaultFederatedSessionBinding::bind -> issue_credentials chokepoint, so this covers browser OIDC callback and AssumeRoleWithWebIdentity alike.

- rustfs/src/admin/service/federated_identity.rs: issue_credentials resolves the root access key via current_action_credentials() and rejects with FederatedSessionBindingError::InvalidRequest on collision. Added the pure helper parent_user_is_reserved(parent_user, root_access_key) so the collision decision is unit-testable without process globals; the comparison is exact and case-sensitive, mirroring the request-time owner comparison.

This is the federated-principal / root isolation pre-work (batch 0A, the immediate issuance-time block) from the OIDC review in rustfs/backlog#1437. Layer 2 (a signature-protected federated credential origin that forces is_owner=false at request time regardless of the parent_user string) is a separate follow-up. Already-issued colliding credentials continue on their existing TTL and should be revoked proactively. Legitimate federated identities are unaffected; only an exact collision with the configured root access key is denied.
2026-07-24 11:32:05 +08:00
cxymdsandGitHub a2fc6e15df fix(s3): honor CopyObject tagging directives (#5165)
Closes rustfs/backlog#1462.
2026-07-24 10:30:43 +08:00
0269c47bc6 fix(runtime): update codec and debug stack headroom (#5164)
* fix(runtime): raise debug worker stack headroom

Co-Authored-By: heihutu <[email protected]>

* chore(deps): update codec to 8.0.2

Co-Authored-By: heihutu <[email protected]>

---------

Co-authored-by: heihutu <[email protected]>
2026-07-24 09:27:49 +08:00
Zhengchao AnandGitHub d9e0a25174 fix(oidc): support separate discovery issuer (#5149) 2026-07-23 02:44:32 +00:00