* feat(iam): expose stored bucket tags to OPA * fix(ci): lint bucket tag fixtures and register new e2e tests * fix(ecstore): restore strict Clippy compatibility on Rust 1.99 Use try_update without changing atomic ordering or overflow behavior. Keep recursive storage futures boxed once at each frame and remove the redundant async-recursion macro, including its non-recursive SQL planner use. Remove needless closure borrows and orphaned dependency entries. Co-Authored-By: heihutu <heihutu@gmail.com> Co-Authored-By: zhi22915 <qiuzgang@gmail.com> (cherry picked from commit537c15277a) * fix(deps): replace yanked yoke-derive release (cherry picked from commitb54b32b8a9) * perf(iam): borrow unchanged OPA bucket tag conditions Reuse clean conditions for bucketless and untagged OPA decisions. Keep the existing clone-and-sanitize path for tagged requests, reserve enrichment capacity, and preserve authoritative lookup errors. Cover borrowed input, reserved namespaces, immutable request state, and both paths in the OPA input regression. Co-Authored-By: heihutu <heihutu@gmail.com> Co-Authored-By: zhi22915 <qiuzgang@gmail.com> * test(iam): clarify OPA bucket policy fallback semantics Document that an OPA tag mismatch remains an implicit IAM denial which a bucket-policy Allow can supplement. Extend the existing S3 contract test to contrast a normal OPA denial with corrupt bucket metadata, whose lookup error must abort the same Allow fallback. Co-Authored-By: heihutu <heihutu@gmail.com> Co-Authored-By: zhi22915 <qiuzgang@gmail.com> * fix(ci): isolate durable progress fixtures from competing IO Reserve the nextest execution budget for schedule, streaming lock, retirement, MRF checkpoint/crash, and native scanner progress scenarios. Preserve their internal concurrency, existing deadlines, typed failure assertions, and zero-retry policy in default and CI profiles. Co-Authored-By: heihutu <heihutu@gmail.com> Co-Authored-By: zhi22915 <qiuzgang@gmail.com> --------- Co-authored-by: rjregenold <214054+rjregenold@users.noreply.github.com> Co-authored-by: Hauser <housemecn@gmail.com> Co-authored-by: heihutu <heihutu@gmail.com> Co-authored-by: zhi22915 <qiuzgang@gmail.com> Co-authored-by: overtrue <anzhengchao@gmail.com>
RustFS Policy - Policy Engine
Advanced policy engine and access control system for RustFS distributed object storage
📖 Documentation
· 🐛 Bug Reports
· 💬 Discussions
📖 Overview
RustFS Policy provides advanced policy engine and access control capabilities for the RustFS distributed object storage system. For the complete RustFS experience, please visit the main RustFS repository.
✨ Features
- AWS-compatible bucket policy engine
- Fine-grained resource-based access control
- Condition-based policy evaluation
- Policy validation and syntax checking
- Role-based access control integration
- Dynamic policy evaluation with context
Stored bucket tags in OPA input
When the OPA authorization plugin is enabled, IAM resolves the addressed bucket's stored tags before sending its decision request. Tags appear in the existing conditions map; no additional S3 calls or credentials are needed by OPA:
{
"input": {
"resource": { "bucket": "financial-reports", "object": "annual/report.parquet" },
"action": "s3:GetObject",
"context": {
"conditions": {
"ExistingBucketTag/department": ["finance"],
"ExistingBucketTag/environment": ["production"]
}
}
}
}
The example omits unchanged identity and context fields. Tag names and values
are case-sensitive. Values use single-element string arrays, including [""]
for an empty tag value. A simple resource predicate is:
package rustfs.example
import rego.v1
finance_bucket if {
input.context.conditions["ExistingBucketTag/department"] == ["finance"]
}
Combine this predicate with the policy's identity and action checks; tags do not
establish caller roles or grant access by themselves. Restrict PutBucketTagging
and DeleteBucketTagging separately when tags control access.
- Only server-resolved, stored tags populate
ExistingBucketTag/<key>. Incoming conditions in that namespace (including case variants and thes3:alias) are discarded before enrichment. Headers, claims, requested replacement tags and object tags cannot override it. - A confirmed missing bucket, or a bucket with no stored tags, contributes no bucket-tag conditions. New bucket creation therefore has no existing tags; a create request naming an existing bucket uses its stored tags. Bucketless requests, including STS and the global ListBuckets check, have no bucket tags.
- Bucket metadata lookup failures, unreadable tag XML, and ambiguous/incomplete tags fail closed, even if a separate bucket policy would allow the request. This applies to requests evaluated by OPA even if the policy does not inspect tags. Boolean IAM callers deny on a lookup error; S3 access checks propagate it.
- Object reads/writes, versions, listing and multipart checks use their addressed bucket. CopyObject and UploadPartCopy authorize source and destination with each bucket's own tags. Tag replacement/removal uses the existing stored state.
- ListBuckets retains its existing contract: a global
s3:ListAllMyBucketsallow lists all buckets. Otherwise, per-buckets3:ListBucket/s3:GetBucketLocationchecks carry each candidate's tags and can filter the result. - Resolution reuses the IAM storage instance's authoritative metadata reader and existing metadata cache/reload machinery, not a separate OPA cache. Local tag updates/removal update the local cache, and tag mutations wait for healthy-peer reload attempts before responding. Failed peer notification can leave that peer's cached tags stale until a later reload or distributed metadata refresh (normally every 15 minutes). This adds no stronger consistency barrier or partition-time revocation guarantee, and does not revoke in-flight requests.
This is an OPA input extension, not a new native IAM condition key. Native policy
evaluation, owner/anonymous handling, and existing authorization combination rules
remain unchanged. For S3 authorization, an OPA false decision is an implicit IAM
denial that an applicable bucket-policy Allow may supplement. A tag mismatch alone
therefore does not revoke access granted by such a bucket policy; policy authors
must account for those grants when relying on tags to restrict access. ListBuckets
filtering remains IAM-only. Lookup errors on the OPA path abort authorization and
never fall through to a bucket-policy Allow. Custom IAM Store implementations
must implement load_bucket_tags to support bucket-scoped OPA requests; the default
fails closed.
📚 Documentation
For comprehensive documentation, examples, and usage guides, please visit the main RustFS repository.
📄 License
This project is licensed under the Apache License 2.0 - see the LICENSE file for details.
