houseme bf81a9bab0 fix(experiments): import io_uring cancel-safety spike and apply backlog#1051 audit remediation (#4625)
* chore(experiments): import io_uring cancel-safety spike as audit baseline (backlog#894)

Import the Spike 0 io_uring cancel-safety prototype from the closed PR #4381
branch (houseme/p2-spike0-uring-cancel-safety) as the baseline for the
backlog#1051 audit remediation. This crate is a standalone workspace and is
deliberately kept out of the main Cargo.lock/build graph (NOT production code).

Subsequent commits apply the fixes tracked in backlog#1051 sub-issues, one
commit per issue.

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

* fix(experiments/uring): drain probe SQE to its CQE before releasing buffer (rustfs/backlog#1053)

The probe path had no pending-table backstop: after pushing the read SQE,
any early return (`submit_and_wait` error, missing CQE) dropped the probe
buffer and file while the read could still be in flight in io-wq, and the
caller dropped/unmapped the ring on the error path. If the kernel then wrote
the 512-byte result into that freed heap block, it was a use-after-free — the
exact bug class this spike exists to prevent, living in its own probe path.

Fix: once the SQE is pushed, drain to its CQE via `drain_probe_cqe`, retrying
the WAIT on EINTR without re-pushing (the kernel consumed the SQE atomically
before the wait). A bounded attempt count prevents a probe against a hung
device from blocking forever; on any drain failure the buffer (and file) are
`mem::forget`-ed ("leak over UAF") so the kernel can never write into freed
memory. Unmapping the ring on its own is safe; only the user buffer must
survive.

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

* fix(experiments/uring): split probe/runtime/transient errno classes, guard offset (rustfs/backlog#1059)

`is_expected_restriction` folded EINVAL into the "environment restricted"
class, but at runtime EINVAL is triple-meaning — offset > i64::MAX (signed
loff_t), O_DIRECT misalignment, and setup entries over the cap. Implementing
the rustfs/backlog#1048 permanent-degradation latch literally against this
class would fault a healthy disk off io_uring on one alignment retry or
offset-arithmetic bug.

Document that the class is probe-time ONLY and that P2 must split errnos into
probe-restriction / runtime-parameter-error / transient (EINTR/EAGAIN). Add a
concrete guard: `submit` rejects offset > i64::MAX with an InvalidInput error
instead of letting it reach the kernel as a runtime EINVAL. The probe EINTR
half of this issue is already handled by the drain loop from rustfs/backlog#1053
(retry the wait, never re-push).

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

* fix(experiments/uring): abort on driver-thread panic instead of freeing in-flight buffers (rustfs/backlog#1054)

The ownership model's "CQE is the only reclamation point" invariant held only
while the driver thread never unwound. On a panic inside drive(), Rust drop
order freed the `pending` table (every in-flight buffer) before the ring,
while the kernel could still be writing into those buffers → mass UAF.
`catch_unwind` cannot fix this: the destructors run during the unwind, before
the catch boundary.

Move ring + pending + backlog into a `DriverState` whose `Drop` checks
`thread::panicking()` and calls `process::abort()` BEFORE any field destructor
runs — leaving the ring mapped and the buffers allocated (leak over UAF). The
capacity-overflow panic that made this reachable (caller-controlled `len`) is
closed at the source in the len-guard commit (rustfs/backlog#1057).

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

* fix(experiments/uring): reject reads above MAX_RW_COUNT to stop u32 truncation (rustfs/backlog#1057)

The SQE length field is `len as u32`, so len == 4 GiB became a 0-byte read the
kernel answered with res=0 → an Ok(empty) the caller decodes as a false EOF
(and len > 4 GiB read only the low 32 bits). Silent truncation (CWE-197),
forbidden by the repo's rust-code-quality rules.

`submit` now rejects len > MAX_RW_COUNT (2 GiB - 4 KiB) with InvalidInput; the
`len as u32` cast in the driver is consequently lossless. This also closes the
caller-controlled capacity-overflow panic feeding rustfs/backlog#1054. P2 must
chunk reads larger than the cap.

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

* fix(experiments/uring): resubmit short reads to satisfy the whole-range contract (rustfs/backlog#1058)

CQE res >= 0 was truncated and delivered as final with no resubmit loop, and
Pending did not even store the requested length. io_uring can legally
short-read a regular file (io-wq signal interruption, NOWAIT partial page
cache, O_DIRECT tail blocks), while LocalIoBackend::pread_bytes is a whole-
range contract — a short shard fed to EC bitrot verification surfaces as
intermittent, hard-to-attribute integrity/quorum errors.

Track offset/nread in Pending and drive a resubmit loop: a short non-EOF read
re-queues the remainder into buf[nread..], keeping the entry (and its buffer,
and in_flight) until the FINAL CQE of the logical read; res == 0 is treated as
a real EOF. The resubmitted SQE reuses the op's user_data, so a late
ASYNC_CANCEL from a dropped future still cancels the logical read cleanly.

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

* fix(experiments/uring): bound the shutdown drain and record cancel outcomes (rustfs/backlog#1055)

Shutdown made "drain to in_flight == 0" a hard precondition for unmapping the
ring, but ASYNC_CANCEL is best-effort: it cannot interrupt a regular-file read
already executing in io-wq, so on a D-state/NFS-hung disk the CQE may never
arrive and drain-to-zero never terminates — the driver loops forever and
shutdown()/Drop join blocks the caller (and any tokio worker) permanently.
This is an internal contradiction (safe unmap needs drain; a bad disk makes
drain unbounded) in the very environment io_uring exists to handle.

Add a bounded-drain escape hatch: after DRAIN_TIMEOUT with ops still in flight,
leak the whole DriverState (ring stays mapped, buffers stay allocated — leak
over UAF) and exit so shutdown() returns. Soften shutdown()'s hard assert to a
warning for that degraded path; clean-drain tests still assert in_flight == 0
themselves. Also record the ASYNC_CANCEL three-state result
(succeeded/not-found/already-executing) so the hung-disk signal is observable
instead of discarded.

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

* fix(experiments/uring): assert NODROP, monitor CQ overflow, handle EBUSY (rustfs/backlog#1056)

In-flight had no upper bound and could exceed CQ capacity (entries=64 → CQ=128)
with zero overflow handling: no NODROP check, no overflow read, no EBUSY
handling. A lost CQE means its pending entry is never reclaimed, drain never
completes and shutdown hangs — and the spike only avoided this by accidental
reliance on the io-uring crate's auto-flush + NODROP kernel + poll cadence,
all of which P2's eventfd/AsyncFd reaping removes.

Assert the NODROP feature at probe (degrade via ENOSYS otherwise), monitor the
kernel CQ-overflow counter each turn and surface a non-zero value as fatal, and
handle submit() EBUSY as CQ-overflow backpressure (keep the backlog, reap this
turn) instead of swallowing it. The hard in-flight bound (permits ≤ CQ capacity)
lands with the backpressure work in rustfs/backlog#1060.

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

* fix(experiments/uring): add backpressure with permits released at the CQE (rustfs/backlog#1060)

Submission was unbounded (unbounded mpsc + uncapped pending/backlog), so a
concurrent large-object read storm had no memory ceiling. The subtler trap:
the planned SQ-depth semaphore, implemented the natural RAII way (permit held
by the ReadHandle/future), would release permits at future drop while orphan
buffers stay resident in the pending table awaiting slow-disk CQEs —
decoupling the permit count from resident memory and reopening the DoS surface
exactly in the EC quorum-drop hot path.

Add a `Backpressure` semaphore sized to the SQ depth (entries < CQ capacity, so
CQ overflow is structurally unreachable). `submit` acquires before handing the
op to the driver; the driver releases the permit at the CQE (pending-table
removal), NOT at future drop, tying the in-flight/memory bound to actual kernel
residency. Permits are balanced on the shutting-down reject and send-failure
paths, and the driver wakes all waiters on exit.

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

* fix(experiments/uring): open the probe file via O_TMPFILE instead of a predictable path (rustfs/backlog#1061)

The probe wrote a predictable temp path (uring-spike-probe-{pid}-{seq}) with
std::fs::write (O_CREAT|O_TRUNC, no O_EXCL/O_NOFOLLOW): a local attacker could
pre-plant a symlink there and have the process — often root — truncate and
overwrite an arbitrary target (CWE-59/377), with a TOCTOU window between write
and open (CWE-367). This probe is the direct blueprint for P2's per-disk
startup probe, so copied verbatim it becomes a production vulnerability.

Open via O_TMPFILE (anonymous inode, no name → nothing to plant a symlink at,
no TOCTOU, no leftover), falling back to O_CREAT|O_EXCL|O_NOFOLLOW + 0600 +
per-process nonce + immediate unlink on filesystems without O_TMPFILE. P2's
per-disk probe should create inside the tested data-disk directory the same
way, which also validates that disk's filesystem + io_uring combination.

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

* docs(experiments/uring): correct invariant 2 mechanism, add invariants 6/7/8 (rustfs/backlog#1063)

Invariant 2 (the spike's flagship finding) mis-described the fd-reuse hazard:
it claimed the danger window is submission→CQE and that the kernel would
"write into someone else's file". Both are wrong — a submitted op holds a
struct file reference and is immune to fd close/reuse; the real window is SQE
construction (as_raw_fd) → io_uring_enter (backlog residency), and for a READ
the consequence is reading the WRONG file, not writing. A P2 optimization
reasoning from the false premise (drop Arc<File> after submit / registered-file
table) would step straight into it.

Correct the mechanism and add the invariants this audit hardened: driver-thread
unwind safety (6), backpressure permit released at the CQE (7), reused-buffer
content hygiene (8, detailed in rustfs/backlog#1062), plus the errno three-class
contract, bounded-drain escape hatch, and short-read resubmit responsibility.
Mark the now-remediated items in the leftover list.

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

* docs(experiments/uring): pin the reused-buffer content-hygiene invariant for P3 (rustfs/backlog#1062)

The spike leaks nothing today (fresh zeroed buffer per op + truncate to res),
but rustfs/backlog#1048's P3 constraint mandates a driver-owned aligned slab
whose buffers are reused across requests as dirty memory. Both the SPIKE
invariants and the #1048 constraint address only buffer LIFETIME (UAF), not
content hygiene: once buffers are reused, any path that forgets to bound the
caller-visible bytes to cqe.res (O_DIRECT full-block read sliced upstream,
error path returning the whole buffer) discloses a previous tenant's object
data (CWE-226) in an S3 store.

Pin invariant 8: reused-buffer bytes visible to the caller must be strictly
⊆ [0, cqe.res). Documented in SPIKE.md and marked at the delivery point in the
driver so P3 preserves it; needs a dirty-buffer + short-read regression test.

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

* test(experiments/uring): pin fd ownership and orphan-integrity directly (rustfs/backlog#1064)

The memory-safety assertions were all counter proxies, and invariant 2 (fd
owned by the pending table) had zero coverage — deleting Pending.file compiled
and left every test green because each test kept its own Arc<File> alive.

Add two direct observations: pending_table_owns_fd_after_caller_drop drops the
caller's Arc while the op is in flight and asserts F_GETFD still succeeds (only
the pending table's clone keeps the fd open; removing that field would close it
→ EBADF). orphan_in_flight_does_not_corrupt_delivered_reads keeps an orphaned
buffer in flight while 64 delivered reads must return byte-exact, asserting the
orphan buffer is not reclaimed early and its kernel writes corrupt nothing. A
driver-level poison/canary leg is noted as a P2 acceptance-matrix item (ASAN
cannot see a kernel write into a freed buffer).

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

* test(experiments/uring): cover CQ-overflow safety and read boundaries (rustfs/backlog#1065)

The suite never approached CQ capacity and never touched EOF/len boundaries.
Add no_cq_overflow_under_load (300 ops through a CQ of 128 with backpressure
capping in-flight at 64, asserting cq_overflow stays 0 and all deliver),
boundary_reads (len==0, read at EOF, a cross-EOF short read delivered to a
live receiver exercising the positioned resubmit path, and the rejected
huge-len/huge-offset guards), and pipe_half_close_reads_eof (a closed write
end surfaces res==0 EOF).

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

* test(experiments/uring): cover Drop-without-shutdown and de-flake cancel_stress (rustfs/backlog#1066)

All tests ended via explicit shutdown(), so the UringDriver Drop impl's
live-thread branch (send Shutdown before join) was never exercised; add
drop_without_shutdown_drains_and_cancels which drops the driver with ops in
flight and asserts the held futures resolve to ECANCELED (a join-first
regression or unbounded hang makes it hang).

Also de-flake cancel_stress: the exact assert delivered == OPS/2 raced the
driver — an even-i read can complete between read_at returning and drop(handle),
delivering to the still-live receiver and flipping the split. Relax to the
deterministic conservation identity plus delivered >= OPS/2.

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

* test(experiments/uring): make run-docker.sh assert each leg's real path (rustfs/backlog#1067)

Both legs ran the identical cargo test and checked only the exit code, and a
skip is indistinguishable from a real pass at that level: leg 1 depended on the
host Docker's default seccomp "usually" blocking io_uring, and leg 2 printed
"both legs passed" even if every test skipped (vacuous pass — zero real
io_uring coverage).

Add an explicit seccomp profile (seccomp-block-uring.json) that returns EPERM
for io_uring_setup/enter/register so leg 1 deterministically hits the
graceful-degradation path regardless of host defaults, and assert leg 1
actually degraded (SKIP lines present) while leg 2 did NOT skip a single test
(io_uring really ran). Either violation now fails the harness.

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

* style(experiments/uring): apply repo rustfmt (max_width=130) to the audit changes

Normalize the formatting of the remediation code to the repo rustfmt.toml.
Pure formatting; no behavior change. clippy --all-targets -D warnings is clean.

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

---------

Co-authored-by: heihutu <heihutu@gmail.com>
2026-07-09 23:00:57 +08:00
2025-12-18 20:13:24 +08:00
2025-07-08 09:04:37 +08:00
2025-08-07 22:37:05 +08:00
2025-06-30 21:27:45 +08:00

RustFS

RustFS is a high-performance, distributed object storage system built in Rust.

CI Build and Push Docker Images GitHub commit activity Github Last Commit Discord Featured|HelloGitHub

rustfs%2Frustfs | Trendshift ROSS Index - Fastest Growing Open-Source Startups in Q4 2025 | Runa Capital

Getting Started · Docs · Bug reports · Discussions

English | 简体中文 | Deutsch | Español | français | 日本語 | 한국어 | Portuguese | Русский

RustFS is a high-performance, distributed object storage system built in Rust—one of the most loved programming languages worldwide. RustFS combines the simplicity of MinIO with the memory safety and raw performance of Rust. It offers broad S3 API compatibility for supported features, is completely open-source, and is optimized for data lakes, AI, and big data workloads.

Unlike other storage systems, RustFS is released under the permissible Apache 2.0 license, avoiding the restrictions of AGPL. With Rust as its foundation, RustFS delivers superior speed and secure distributed features for next-generation object storage.

Feature & Status

  • High Performance: Built with Rust to ensure maximum speed and resource efficiency.
  • Distributed Architecture: Scalable and fault-tolerant design suitable for large-scale deployments.
  • S3 Compatibility: Seamless integration with common S3-compatible applications and tools; current coverage is tracked in the S3 compatibility matrix.
  • OpenStack Swift API: Native support for Swift protocol with Keystone authentication.
  • OpenStack Keystone Integration: Native support for OpenStack Keystone authentication with X-Auth-Token headers.
  • Data Lake Support: Optimized for high-throughput big data and AI workloads.
  • Open Source: Licensed under Apache 2.0, encouraging unrestricted community contributions and commercial usage.
  • User-Friendly: Designed with simplicity in mind for easy deployment and management.
Feature Status Feature Status
S3 Core Features Available Bitrot Protection Available
Upload / Download Available Single Node Mode Available
Versioning Available Bucket Replication Available
Logging Available Lifecycle Management 🚧 Under Testing
Event Notifications Available Distributed Mode 🚧 Under Testing
K8s Helm Charts Available RustFS KMS 🚧 Under Testing
Keystone Auth Available Multi-Tenancy Available
Swift API Available Swift Metadata Ops 🚧 Partial

RustFS vs MinIO Performance

Stress Test Environment:

Type Parameter Remark
CPU 2 Core Intel Xeon (Sapphire Rapids) Platinum 8475B, 2.7/3.2 GHz
Memory 4GB
Network 15Gbps
Drive 40GB x 4 IOPS 3800 / Drive

https://github.com/user-attachments/assets/2e4979b5-260c-4f2c-ac12-c87fd558072a

RustFS vs Other Object Storage

Feature RustFS Other Object Storage
Console Experience Powerful Console
Comprehensive management interface.
Basic / Limited Console
Often overly simple or lacking critical features.
Language & Safety Rust-based
Memory safety by design.
Go or C-based
Potential for memory GC pauses or leaks.
Data Sovereignty No Telemetry / Full Compliance
Guards against unauthorized cross-border data egress. Compliant with GDPR (EU/UK), CCPA (US), and APPI (Japan).
Potential Risk
Possible legal exposure and unwanted data telemetry.
Licensing Permissive Apache 2.0
Business-friendly, no "poison pill" clauses.
Restrictive AGPL v3
Risk of license traps and intellectual property pollution.
Compatibility S3-Compatible Core
Works with common S3-compatible clients, with coverage tracked in the compatibility matrix.
Variable Compatibility
May lack support for local cloud vendors or specific APIs.
Edge & IoT Strong Edge Support
Ideal for secure, innovative edge devices.
Weak Edge Support
Often too heavy for edge gateways.
Risk Profile Enterprise Risk Mitigation
Clear IP rights and safe for commercial use.
Legal Risks
Intellectual property ambiguity and usage restrictions.

Staying ahead

Star RustFS on GitHub and be instantly notified of new releases.

Quickstart

To get started with RustFS, follow these steps:

1. One-click Installation (Option 1)

curl -O https://rustfs.com/install_rustfs.sh && bash install_rustfs.sh

2. Docker Quick Start (Option 2)

The RustFS container runs as a non-root user rustfs (UID/GID 10001:10001). If you bind-mount host directories with Docker or Compose, every mounted path must be writable by that user, otherwise startup may fail with permission denied errors. This applies to data directories, log directories, and TLS certificate directories when RUSTFS_TLS_PATH is enabled.

# Create data and logs directories
mkdir -p data logs

# Change the owner of these directories
chown -R 10001:10001 data logs

# Using latest version
docker run -d -p 9000:9000 -p 9001:9001 -v $(pwd)/data:/data -v $(pwd)/logs:/logs rustfs/rustfs:latest

# Using specific version
docker run -d -p 9000:9000 -p 9001:9001 -v $(pwd)/data:/data -v $(pwd)/logs:/logs rustfs/rustfs:1.0.0-beta.8

If you use podman instead of docker, you can install the RustFS with the below command

# Create data and logs directories
mkdir -p data logs

# Run the container (podman will automatically set the folders ownership)
podman run -d -p 9000:9000 -p 9001:9001 -v $(pwd)/data:/data:Z,U -v $(pwd)/logs:/logs:Z,U rustfs/rustfs:latest

If you enable TLS with a bind-mounted certificate directory, prepare that mount the same way:

mkdir -p certs
chown -R 10001:10001 certs

You can also use Docker Compose. Using the docker-compose-simple.yml file in the root directory:

docker compose -f docker-compose-simple.yml up -d

Before running Compose with host bind mounts:

  • Ensure every mounted host path is writable by 10001:10001.
  • If you enable TLS, ensure the certificate mount for /opt/tls is also readable by 10001:10001.
  • If matching host ownership is not practical, run the rustfs service with user: "<host-uid>:<host-gid>" instead.
  • docker-compose-simple.yml includes a volume-permission-helper service for named volumes. docker-compose-simple.yml relies on you to prepare bind-mounted host paths in advance.

Similarly, you can run the command with podman

podman compose -f docker-compose-simple.yml up -d

Webhook notification quick start (Docker):

docker run -d --name rustfs -p 9000:9000 \
  -e RUSTFS_NOTIFY_ENABLE=true \
  -e RUSTFS_NOTIFY_WEBHOOK_ENABLE_PRIMARY=on \
  -e RUSTFS_NOTIFY_WEBHOOK_ENDPOINT_PRIMARY=http://<host-ip>:3020/webhook \
  -e RUSTFS_NOTIFY_WEBHOOK_QUEUE_DIR_PRIMARY=/tmp/rustfs-events \
  rustfs/rustfs:latest

Notes:

  • RUSTFS_NOTIFY_ENABLE=true enables the global notify module switch.
  • For ARN arn:rustfs:sqs::primary:webhook, use instance-scoped env vars with _PRIMARY.
  • If queue dir is omitted, default is /opt/rustfs/events; ensure it is writable by the container runtime user.
  • RUSTFS_NOTIFY_WEBHOOK_SKIP_TLS_VERIFY_PRIMARY defaults to false; enabling it skips webhook TLS certificate verification, allows MITM attacks, and emits a startup warning. Prefer RUSTFS_NOTIFY_WEBHOOK_CLIENT_CA_PRIMARY for private CAs.

NOTE: We recommend reviewing the docker-compose.yml file before running. It defines several services including Grafana, Prometheus, and Jaeger, which are helpful for RustFS observability. If you wish to start Redis or Nginx containers, you can specify the corresponding profiles.

3. Build from Source (Option 3) - Advanced Users

For developers who want to build RustFS Docker images from source with multi-architecture support:

# Build multi-architecture images locally
./docker-buildx.sh --build-arg RELEASE=latest

# Build and push to registry
./docker-buildx.sh --push

# Build specific version
./docker-buildx.sh --release v1.0.0 --push

# Build for custom registry
./docker-buildx.sh --registry your-registry.com --namespace yourname --push

The docker-buildx.sh script supports:

  • Multi-architecture builds: linux/amd64, linux/arm64
  • Automatic version detection: Uses git tags or commit hashes
  • Registry flexibility: Supports Docker Hub, GitHub Container Registry, etc.
  • Build optimization: Includes caching and parallel builds

You can also use Make targets for convenience:

make docker-buildx                    # Build locally
make docker-buildx-push               # Build and push
make docker-buildx-version VERSION=v1.0.0  # Build specific version
make help-docker                      # Show all Docker-related commands

Heads-up (macOS cross-compilation): macOS keeps the default ulimit -n at 256, so cargo zigbuild or ./build-rustfs.sh --platform ... may fail with ProcessFdQuotaExceeded when targeting Linux. The build script attempts to raise the limit automatically, but if you still see the warning, run ulimit -n 4096 (or higher) in your shell before building.

4. Build with Helm Chart (Option 4) - Cloud Native

Follow the instructions in the Helm Chart README to install RustFS on a Kubernetes cluster.

For scanner pacing, cycle budgets, bitrot cadence, lifecycle transition status, and single-node single-disk idle CPU tuning, see Scanner Runtime Controls. For repeatable scanner-pressure validation, see Scanner Benchmark Runbook.

5. Nix Flake (Option 5)

If you have Nix with flakes enabled:

# Run directly without installing
nix run github:rustfs/rustfs

# Build the binary
nix build github:rustfs/rustfs
./result/bin/rustfs --help

# Or from a local checkout
nix build
nix run

6. X-CMD (Option 6)

If you are an x-cmd user:

# Run directly without installing
x rustfs

# Download the binary and install it to the global environment
x env use rustfs
rustfs --help

Accessing RustFS

  1. Access the Console: Open your web browser and navigate to http://localhost:9001 to access the RustFS console.
    • Default credentials: rustfsadmin / rustfsadmin
  2. Create a Bucket: Use the console to create a new bucket for your objects.
  3. Upload Objects: You can upload files directly through the console or use S3-compatible APIs/clients to interact with your RustFS instance.

NOTE: To access the RustFS instance via https, please refer to the TLS Configuration Docs.

OIDC Roles Claim (Microsoft Entra ID)

RustFS supports mapping an OIDC claim containing role values into the existing authorization pipeline. The roles_claim setting is optional: when unset or empty, only the groups claim contributes to authorization (same as older RustFS releases). For Microsoft Entra ID app roles, set roles_claim=roles so both console admin checks and bucket IAM policies can evaluate those roles.

Example environment configuration (opt-in roles claim):

RUSTFS_IDENTITY_OPENID_ENABLE=on
RUSTFS_IDENTITY_OPENID_CONFIG_URL="https://login.microsoftonline.com/<tenant-id>/v2.0/.well-known/openid-configuration"
RUSTFS_IDENTITY_OPENID_CLIENT_ID="<client-id>"
RUSTFS_IDENTITY_OPENID_CLIENT_SECRET="<client-secret>"
RUSTFS_IDENTITY_OPENID_SCOPES="openid,profile,email"
RUSTFS_IDENTITY_OPENID_GROUPS_CLAIM="groups"
RUSTFS_IDENTITY_OPENID_ROLES_CLAIM="roles"

Policy condition example (evaluate app roles directly with jwt:roles; when roles_claim is configured, RustFS also merges those values into jwt:groups for backward compatibility with older policies):

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": ["admin:*"],
      "Resource": ["arn:aws:s3:::*"],
      "Condition": {
        "ForAnyValue:StringEquals": {
          "jwt:roles": ["RustFS.ConsoleAdmin"]
        }
      }
    }
  ]
}

Documentation

For detailed documentation, including configuration options, API references, and advanced usage, please visit our Documentation.

Getting Help

If you have any questions or need assistance:

  • Check the FAQ for common issues and solutions.
  • Join our GitHub Discussions to ask questions and share your experiences.
  • Open an issue on our GitHub Issues page for bug reports or feature requests.

Contact

Contributors

RustFS is a community-driven project, and we appreciate all contributions. Check out the Contributors page to see the amazing people who have helped make RustFS better.

Contributors

Star History

Star History Chart

License

Apache 2.0

RustFS is a trademark of RustFS, Inc. All other trademarks are the property of their respective owners.

S
Description
2.3x faster than MinIO for 4KB object payloads. RustFS is an open-source, S3-compatible high-performance object storage system supporting migration and coexistence with other S3-compatible platforms such as MinIO and Ceph.
Readme Apache-2.0 108 MiB
Languages
Rust 94.9%
Shell 3.9%
Python 1%