* fix(sse): resolve bucket default encryption per request PUT and the POST-object/extract path resolved a bucket's default encryption with a hard-coded "no explicit SSE-C" flag, so the default was layered onto a request that already carried an SSE-C header triple and then tripped that request's own mutual-exclusion check. Every bucket with default encryption refused SSE-C single PUTs with 400 InvalidArgument, while CreateMultipartUpload on the same bucket succeeded because it resolves SSE elsewhere. Both call sites now derive the flag from the request headers, as COPY already did. The bucket default's KMS key id was also inherited independently of the effective algorithm, so an explicit AES256 request against an aws:kms default bucket produced a self-contradictory algorithm/key-id pair and was rejected. The key id is now inherited only when the effective algorithm is aws:kms, matching the storage-layer resolver. Refs backlog#2368 B1, B2. * fix(sse): refuse SSE-KMS without a running KMS service A write requesting aws:kms on a node with no KMS service fell back to the node-local SSE-S3 provider: the data key was wrapped with RUSTFS_SSE_S3_MASTER_KEY while the object metadata still recorded aws:kms and the requested KMS key id. The stored object claimed a KMS protection it never had, under a key that was never consulted, and no signal distinguished it from a genuine SSE-KMS object. The managed-encryption path now asks the resolved DEK provider whether it wraps with a node-local master key and refuses SSE-KMS in that case: InvalidRequest when KMS was never configured, ServiceUnavailable when a configured service is not running. The check sits after the per-key authorization gate so an unauthorized caller still receives AccessDenied whatever the KMS runtime state is, and asks the provider rather than a parallel availability signal because the provider is what actually wraps the key. A missing master key no longer answers an SSE-KMS request with an SSE-S3-worded configuration error. The SSE-S3 local fallback is unchanged. Refs backlog#2368 B4. * fix(ecstore): restore and archive tiers in stored coordinates Multipart restore addressed the remote tier in plaintext coordinates while the copy-back reads the stored representation. Each part received a misaligned slice of the remote object whose length still satisfied the range, the hash reader and the completion size check, so the restore reported success and silently replaced the object's bytes. Encrypted and compressed multipart objects were both affected. Restore now accumulates stored part sizes, passes the stored length to the hash reader alongside the plaintext length, and validates against the stored size. The copy-back digests stored bytes, so its computed MD5 is not the object's public ETag. Restore now preserves the object ETag on both the single-part and multipart paths, and gives each restored part its own recorded part ETag rather than the object-level value. Transition also handed the tier the object's SSE headers and its RustFS-wrapped data key as request headers. Any S3 target rejected an SSE-C archive outright, an SSE-KMS archive asked the target to encrypt a second time under a key id it does not own, and the wrapped DEK left the cluster. The archive request now strips every SSE header and encryption marker with the predicate the replication path already uses; the local xl.meta keeps all of it, so read-through and restore are unaffected. Objects restored by an affected release are not detected or repaired retroactively and must be re-restored from the tier. Refs backlog#2368 B3, B5; backlog#2369 P7.1. * fix(rio): lock the v1 nonce layout within a segment Decrypting a v1 segment tried three historical nonce layouts per frame, independently for every frame. The last of them exists for streams written before 1.0.0-alpha.91, which reused a segment's part nonce for every block in it; because block zero's derived nonce equals that base nonce, a frame encrypted at index zero authenticated at any position. An attacker able to rewrite the underlying shards could replay it and have the forged plaintext returned with 200 and an unchanged length. Shard integrity uses a keyed-hash-free checksum, which such an attacker can recompute, so it is not a barrier. A segment now locks onto whichever layout decoded its first non-zero-index frame and rejects any later frame needing a different one. That leaves one residual shape: a stream built purely from repeats of frame zero has no later frame to disagree. New RUSTFS_ENCRYPTION_LEGACY_NONCE_FALLBACK (default true, so pre-alpha.91 objects keep decrypting) drops the third layout entirely when set to false, which closes it. Turning it off refuses pre-alpha.91 objects, so migrate them first by rewriting in place. Refs backlog#2369 P2. * fix(kms): reload a service that failed to start POST /rustfs/admin/v3/kms/reload short-circuited whenever the persisted configuration matched the in-memory one byte for byte. A node whose KMS failed to start keeps that configuration and sits in Error, so the documented recovery call returned "reloaded successfully" while leaving the node down. Peers reached the same path through the reload broadcast, so a cluster that lost Vault during a rolling restart had no working recovery route other than the node-local start endpoint. Reload now short-circuits only for a service that is actually running, and otherwise reconfigures, which starts a service that is not running. The AWS backend also advertised key-version enumeration through kms/status, which its own documentation says it cannot do; the capability and its golden snapshot now say false. Refs backlog#2369 P1, P7.3. * docs: record the SSE and KMS changes for 1.0.0 The Unreleased changelog section carried no entry for any encryption work merged since 1.0.0-rc.5, including three items with operational impact: the config-secret variable whose absence persists secrets in cleartext with only a warning, the v2 frame write switch and its rolling-upgrade constraint, and per-key authorization making a public bucket incompatible with SSE-KMS objects. Adds those plus this batch, including the SSE-KMS refusal as a breaking change with both routes out. Also corrects four places where documentation contradicted the code: the cleanup register still called encrypted range seek opt-in after its default flipped, the Helm README claimed vault_mount_path only applies to Transit while the template also feeds the KV2 mount, the disaster-recovery drill listed bundle contents for backends whose export is refused with 501, and the Chinese README capability table predated most of the feature set. Documents the SSE-S3 local master key as a first-class operational mode with its rotation dead end, and what the v1 frame layout does and does not authenticate. Refs backlog#2369 P5. * fix(kms): classify data-path KMS failures by what the caller can do Only "key not found" and a backend outage were classified; every other KMS failure that reached the S3 data path fell through to 500 InternalError with a generic message. A disabled or pending-deletion key, a denied KMS grant, an encryption-context mismatch, an unsupported algorithm, a credential or timeout failure, and a capability the configured backend does not have all looked identical to a server fault. SDKs therefore applied exponential backoff to configuration errors no retry can fix, and monitoring counted every one of them against the server's own error rate. Unusable-key and request-side failures now answer 400, a denied grant 403, transient backend failures 503, and a missing backend capability 501. Damaged, unreadable, or unknown-format key material keeps its 500: it is a server-side integrity fault, and existing tests pin it. The classifier is deliberately separate from the admin lifecycle mapping, which answers 404 for a missing key because there a key id is the resource being addressed; on the data path it arrives inside a request header or a bucket default. Messages either name what the caller asked for or stay generic, with deployment-side detail left on the error source the way the storage-IO mapping already does. Refs backlog#2368 B6. * fix(kms): track and renew static Vault tokens Token authentication hard-coded "this token carries no lease", so the renewal task never started, the remaining-TTL gauge was never published, and nothing looked wrong. `vault token create` grants a 768-hour TTL by default, so a cluster that had been healthy for a month turned every KMS call into a 403 and could not recover without a restart or a reconfigure. Production configuration validation only rejects the literal dev-token, so an ordinary expiring token reaches a whole cluster. The source now reads `auth/token/lookup-self` at login and adopts what Vault reports. A token with no expiry behaves exactly as before. An expiring renewable one is picked up by the existing renewal loop and renewed at half TTL like every other auth method. An expiring non-renewable one warns with its remaining lifetime and publishes the gauge, so the fail-closed window is visible before it arrives. The probe never fails the login: a policy that omits lookup-self, or a Vault that is briefly unreachable, warns and falls back to exactly the previous behaviour rather than taking down a deployment that works today. The scripted Vault test double answers the lookup out of band so existing scripts keep describing only the protocol under test. Refs backlog#2369 P3. * feat(sse): report SSE-C requests that arrive without TLS An SSE-C request carries the customer's AES key in a request header, so AWS S3 and MinIO both refuse one that did not arrive over TLS. RustFS accepted them on any transport: a plaintext hop hands the key to anyone on the path, and since the object cannot be read without that same key, the exposure lasts as long as the object does. Refusing outright is the correct end state but not a safe default to adopt inside a release window, because the project's own s3-tests and e2e lanes and most staging deployments speak plain HTTP. This release reports instead: each such request increments rustfs_ssec_plaintext_requests_total and logs one warning per process, so an operator can confirm nothing would break before the default flips. RUSTFS_SSE_C_REQUIRE_TLS=true opts into the AWS 400 now. The verdict is per connection rather than per deployment: the layer is built with whether this listener terminated TLS, and additionally accepts an https protocol forwarded by a proxy the trusted-proxy configuration already vetted. It sits beside the rate limiter, after the layer that makes a forwarded protocol trustworthy and after the request context, so a rejection can echo the request id. Refs backlog#2369 P7.2. * fix(kms): say what a node-local backend means for a cluster The Local backend keeps key material on each node's own disk and generates its Argon2id salt per node, so two nodes derive different keys from the same master_key and an object encrypted on one node cannot be decrypted on another. Behind a load balancer that surfaces as intermittent 500s on reads that succeeded moments earlier, with nothing tying the symptom to the cause: the only signal was a generic "development, testing and demos only" positioning warning that says nothing about what actually breaks. Configuring or reconfiguring Local while the deployment is distributed now logs a dedicated event and appends the consequence to the configure response, so the operator who made the change sees it. The product decision to warn rather than refuse is unchanged. Refs backlog#2369 P7.4. * docs: record the remaining SSE and KMS changes for 1.0.0 Adds changelog entries for the KMS data-path status classification, the Vault static-token lease probe, the SSE-C plaintext-transport report and its switch, and the node-local backend warning. Documents two things the backend security guide never stated: that SSE-C belongs on a secure transport, with the counter and switch to plan the change around, and that the Local backend cannot be shared by a multi-node deployment because each node derives different keys from the same master key. Refs backlog#2368 B6; backlog#2369 P3, P5, P7.2, P7.4. * fix(kms): report an unreadable key store as an outage on the S3 path A backend now distinguishes a key store it could not read from a key that is genuinely absent, but the S3 boundary collapsed the first one back onto 500 InternalError through the fallthrough for integrity faults. The distinction was therefore invisible to the client: a temporary key-directory outage looked exactly like a permanently damaged key record, and neither the status nor the metric said the request was worth retrying. An unreadable key store joins the retryable class and answers 503, next to a backend error and a credential failure. Damaged, unreadable or unknown-format key material keeps its 500. Refs backlog#2368 B6; builds on rustfs/rustfs#7470.
17 KiB
RustFS 是一个基于 Rust 构建的高性能分布式对象存储系统。
English | 简体中文 | Deutsch | Español | français | 日本語 | 한국어 | Portuguese | Русский
RustFS 是一个基于 Rust 构建的高性能分布式对象存储系统。Rust 是全球最受开发者喜爱的编程语言之一,RustFS 结合了 MinIO 的简洁性与 Rust 的内存安全及高性能优势。它为已支持功能提供广泛的 S3 API 兼容性,完全开源,并专为数据湖、人工智能(AI)和大数据负载进行了优化。
与其他存储系统不同,RustFS 采用更宽松、商业友好的 Apache 2.0 许可证,避免了 AGPL 协议的限制。以 Rust 为基石,RustFS 为下一代对象存储提供了更快的速度和更安全的分布式特性。
特征和功能状态
- 高性能:基于 Rust 构建,确保极致的速度和资源效率。
- 分布式架构:可扩展且容错的设计,适用于大规模部署。
- S3 兼容性:与常见 S3 兼容应用和工具集成,当前覆盖范围见 S3 兼容矩阵。
- 数据湖支持:专为高吞吐量的大数据和 AI 工作负载优化。
- 完全开源:采用 Apache 2.0 许可证,鼓励社区贡献和商业使用。
- 简单易用:设计简洁,易于部署和管理。
状态说明:✅ 可用 —— 已发布并有 CI 门禁覆盖;🧪 预览 —— 已发布但需显式开关,或兼容性承诺有边界。
| 功能 | 状态 | 功能 | 状态 |
|---|---|---|---|
| S3 核心功能 | ✅ 可用 | 分布式模式 | ✅ 可用 |
| 上传 / 下载 | ✅ 可用 | 单机模式 | ✅ 可用 |
| 版本控制 | ✅ 可用 | Bitrot (防数据腐烂) | ✅ 可用 |
| 对象锁定 (WORM) | ✅ 可用 | 修复与扫描器 | ✅ 可用 |
| 服务端加密 (SSE) | ✅ 可用 | 存储池扩容 / 下线 | ✅ 可用 |
| RustFS KMS | ✅ 可用 | 存储桶复制 | ✅ 可用 |
| 生命周期管理 (ILM) | ✅ 可用 | 站点复制 | ✅ 可用 |
| ILM 分层 (远端 S3) | ✅ 可用 | 存储桶配额 | ✅ 可用 |
| S3 Select | ✅ 可用 | 事件通知 | ✅ 可用 |
| S3 Tables (Iceberg REST) | 🧪 预览 | 审计日志 | ✅ 可用 |
| IAM / 策略 | ✅ 可用 | 日志与可观测性 | ✅ 可用 |
| OIDC / SSO | ✅ 可用 | Web 控制台 | ✅ 可用 |
| Keystone 认证 | ✅ 可用 | K8s Helm Chart | ✅ 可用 |
| Swift API | ✅ 可用 | FTPS / WebDAV | ✅ 可用 |
| 多租户 | ✅ 可用 | SFTP | ✅ 可用 |
| MinIO 磁盘格式兼容 | 🧪 预览 |
说明:
- 服务端加密:支持 SSE-C、SSE-S3 与 SSE-KMS。SSE-KMS 必须先配置 KMS 服务;未配置 KMS 时请求
aws:kms会被拒绝,不会降级到本地主密钥。 - RustFS KMS:生产环境支持 Vault(KV2 / Transit)与 AWS KMS 后端;
Local与Static后端仅供开发与测试使用,详见 KMS 后端安全属性。
RustFS vs MinIO 性能对比
压力测试环境参数:
| 类型 | 参数 | 备注 |
|---|---|---|
| CPU | 2 核 | Intel Xeon (Sapphire Rapids) Platinum 8475B , 2.7/3.2 GHz |
| 内存 | 4GB | |
| 网络 | 15Gbps | |
| 硬盘 | 40GB x 4 | IOPS 3800 / Drive |
https://github.com/user-attachments/assets/2e4979b5-260c-4f2c-ac12-c87fd558072a
RustFS vs 其他对象存储
| 特性 | RustFS | 其他对象存储 |
|---|---|---|
| 控制台体验 | 功能强大的控制台 提供全面的管理界面。 |
基础/简陋的控制台 通常功能过于简单或缺失关键特性。 |
| 语言与安全 | 基于 Rust 开发 天生的内存安全。 |
基于 Go 或 C 开发 存在内存 GC 停顿或内存泄漏的潜在风险。 |
| 数据主权 | 无遥测 / 完全合规 防止未经授权的数据跨境传输。完全符合 GDPR (欧盟/英国)、CCPA (美国) 和 APPI (日本) 等法规。 |
潜在风险 可能存在法律风险和隐蔽的数据遥测(Telemetry)。 |
| 开源协议 | 宽松的 Apache 2.0 商业友好,无“毒丸”条款。 |
受限的 AGPL v3 存在许可证陷阱和知识产权污染的风险。 |
| 兼容性 | S3 核心兼容 适配常见 S3 兼容客户端,覆盖范围以兼容矩阵为准。 |
兼容性不一 虽然支持 S3,但可能缺乏对本地云厂商或特定 API 的支持。 |
| 边缘与 IoT | 强大的边缘支持 非常适合安全、创新的边缘设备。 |
边缘支持较弱 对于边缘网关来说通常过于沉重。 |
| 成本 | 稳定且免费 免费社区支持,稳定的商业定价。 |
高昂成本 1PiB 的成本可能高达 250,000 美元。 |
| 风险控制 | 企业级风险规避 清晰的知识产权,商业使用安全无忧。 |
法律风险 知识产权归属模糊及使用限制风险。 |
保持领先
在 GitHub 上为 RustFS 点赞,即可第一时间收到新版本发布通知。
快速开始
Important
Pool 扩容 Notice:
- 单节点单盘(SNSD)部署仅支持使用本地路径独立运行,不支持原地扩容,也不能作为 Pool 加入集群。如需改为多盘拓扑,请创建新部署并通过 S3 迁移数据。
- 已有多盘 Pool 的端点和 Erasure Set 宽度应保持不变,扩容应追加新的 Pool。使用省略号表达式扩容时,每个 Pool 参数都必须包含省略号表达式,并展开为至少两个磁盘端点。
- 允许单节点多盘 Pool,也允许多节点、每节点一盘的 Pool,但必须满足 Erasure Set 布局和 EC 配置要求;配置合法不代表能够容忍整台主机故障。
这些拓扑规则与 MinIO 一致,但两者的默认 parity 选择方式存在差异。扩容前请阅读 Pool 布局兼容性与回归测试说明。
请按照以下步骤快速上手 RustFS:
1. 一键安装脚本 (选项 1)
curl -O https://rustfs.com/install_rustfs.sh && bash install_rustfs.sh
2. Docker 快速启动 (选项 2)
RustFS 容器以非 root 用户 rustfs (UID/GID 10001:10001) 运行。如果您通过 Docker 或 Compose 绑定挂载宿主机目录,请务必确保所有挂载路径都对该用户可写,否则启动时可能出现权限拒绝错误。这不仅适用于数据目录和日志目录,也适用于启用 RUSTFS_TLS_PATH 时挂载的 TLS 证书目录。
# 创建数据和日志目录
mkdir -p data logs
# 更改这两个目录的所有者
chown -R 10001:10001 data logs
# 使用最新版本运行
docker run -d -p 9000:9000 -p 9001:9001 -v $(pwd)/data:/data -v $(pwd)/logs:/logs rustfs/rustfs:latest
# 使用指定版本运行
docker run -d -p 9000:9000 -p 9001:9001 -v $(pwd)/data:/data -v $(pwd)/logs:/logs rustfs/rustfs:1.0.0-rc.5
如果您通过绑定挂载启用 TLS 证书目录,也请用同样方式准备该目录:
mkdir -p certs
chown -R 10001:10001 certs
您也可以使用 Docker Compose。使用根目录下的 docker-compose-simple.yml 文件:
docker compose -f docker-compose-simple.yml up -d
在使用 Compose 且包含宿主机绑定挂载时,请先确认:
- 所有挂载到容器内的宿主机路径都对
10001:10001可写。 - 如果启用了 TLS,挂载到
/opt/tls的证书目录也需要对10001:10001可读。 - 如果不方便调整宿主机目录归属,可以为
rustfs服务显式设置user: "<host-uid>:<host-gid>"。 docker-compose-simple.yml为命名 volume 提供了volume-permission-helper服务;docker-compose-simple.yml使用宿主机绑定挂载,因此需要您在启动前自行准备目录权限。
3. 源码编译 (选项 3) - 进阶用户
适用于希望从源码构建支持多架构 RustFS Docker 镜像的开发者:
# 在本地构建多架构镜像
./docker-buildx.sh --build-arg RELEASE=latest
# 构建并推送到仓库
./docker-buildx.sh --push
# 构建指定版本
./docker-buildx.sh --release v1.0.0 --push
# 构建并推送到自定义仓库
./docker-buildx.sh --registry your-registry.com --namespace yourname --push
docker-buildx.sh 脚本支持:
- 多架构构建:
linux/amd64,linux/arm64 - 自动版本检测: 使用 git tags 或 commit hash
- 灵活的仓库支持: 支持 Docker Hub, GitHub Container Registry 等
- 构建优化: 包含缓存和并行构建
为了方便起见,您也可以使用 Make 命令:
make docker-buildx # 本地构建
make docker-buildx-push # 构建并推送
make docker-buildx-version VERSION=v1.0.0 # 构建指定版本
make help-docker # 显示所有 Docker 相关命令
注意 (macOS 交叉编译): macOS 默认的
ulimit -n限制为 256,因此在使用cargo zigbuild或./build-rustfs.sh --platform ...交叉编译 Linux 版本时,可能会因ProcessFdQuotaExceeded失败。构建脚本会尝试自动提高限制,但如果您仍然看到警告,请在构建前在终端运行ulimit -n 4096(或更高)。
4. 使用 Helm Chart 安装 (选项 4) - 云原生环境
请按照 Helm Chart README 上的说明在 Kubernetes 集群上安装 RustFS。
5. Nix Flake (Option 5)
如果你已经 启用了 Nix Flakes 功能:
# 直接运行,无需安装
nix run github:rustfs/rustfs
# 编译二进制文件
nix build github:rustfs/rustfs
./result/bin/rustfs --help
# 或者从本地检出的代码库运行/编译
nix build
nix run
该 Flake 同时提供 NixOS 模块和 RustFS rc 客户端。将
inputs.rustfs.nixosModules.rustfs 加入 imports,并通过运行时密钥文件
(例如 sops-nix 或 agenix)配置 accessKeyFile 与 secretKeyFile,避免密钥
进入 Nix store。客户端包为
inputs.rustfs.packages.${pkgs.system}.rustfs-client,安装后的命令名为 rc。
6. X-CMD (Option 6)
如果你是 x-cmd 用户:
# 直接运行,无需安装
x rustfs
# 下载二进制文件并安装到全局环境中
x env use rustfs
rustfs --help
访问 RustFS
- 访问控制台: 打开浏览器并访问
http://localhost:9001进入 RustFS 控制台。- 默认账号/密码:
rustfsadmin/rustfsadmin
- 默认账号/密码:
- 创建存储桶: 使用控制台为您的对象创建一个新的存储桶 (Bucket)。
- 上传对象: 您可以直接通过控制台上传文件,或使用 S3 兼容的 API/客户端与您的 RustFS 实例进行交互。
注意: 如果您希望通过 https 访问 RustFS 实例,请参考 TLS 配置文档。
文档
有关详细文档,包括配置选项、API 参考和高级用法,请访问我们的 官方文档。
获取帮助
如果您有任何问题或需要帮助:
- 查看 FAQ 寻找常见问题和解决方案。
- 加入我们的 GitHub Discussions 提问并分享您的经验。
- 在我们的 GitHub Issues 页面提交 Bug 报告或功能请求。
链接
联系方式
- Bug 反馈: GitHub Issues
- 商务合作: hello@rustfs.com
- 工作机会: jobs@rustfs.com
- 一般讨论: GitHub Discussions
- 贡献指南: CONTRIBUTING.md
贡献者
RustFS 是一个社区驱动的项目,我们感谢所有的贡献。请查看 贡献者 页面,看看那些让 RustFS 变得更好的了不起的人们。
Star 历史
许可证
RustFS 是 RustFS, Inc. 的商标。所有其他商标均为其各自所有者的财产。