ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

RustFS 预签名上传大小限制:用 SigV4 查询参数为浏览器直传 PutObject 与 Multipart 设置容量上限

RustFS 预签名上传大小限制:用 SigV4 查询参数为浏览器直传 PutObject 与 Multipart 设置容量上限 RustFS 预签名上传大小限制用 SigV4 查询参数为浏览器直传 PutObject 与 Multipart 设置容量上限【免费下载链接】rustfs2.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.项目地址: https://gitcode.com/GitHub_Trending/rus/rustfs导读RustFS 是开源、兼容 S3 的对象存储系统。当你的后端需要向浏览器签发 SigV4 预签名presigned上传 URL但又必须限制单个 URL 允许上传的数据量时可以通过两个 RustFS 专有的查询参数来达成x-rustfs-max-content-length单请求PutObject上限与x-rustfs-max-total-object-size单个 Multipart 上传的整体对象上限。读完本文你将掌握这两个参数的签名规则、适用边界、超限行为以及它们在 RustFS 源码中的解析与执行路径从而安全地把它集成进自己的直传方案。本文依据 docs/operations/presigned-size-limits.md 展开并结合仓库源码与测试用例进行纵深说明。适用场景为什么需要在预签名 URL 上带大小限制常规的对象存储权限模型只决定谁能写不决定一次能写多少。在典型的浏览器直传架构中后端持有长期凭证浏览器不接触密钥后端为浏览器生成一次性预签名 PUT URL浏览器直接向存储服务上传。问题在于一旦 URL 被签发上传体积就不再受后端控制。恶意或出错的客户端可能用同一个 URL 上传远超预期的数据产生存储与带宽消耗。RustFS 给出的答案是把本次上传允许的最大字节数作为受签名保护的查询参数一起签进 URL后端在签名前设置预算浏览器拿到 URL 后无法篡改存储端在数据落地前强制执行。何时使用本文方案后端向浏览器签发 SigV4 预签名上传 URL且需要按请求PutObject或按 Multipart 上传限制单个 URL 的容量上限。两个限制参数总览两个参数都是 RustFS 专有的查询参数携带在 SigV4 canonical query 中。下面这张对照表来自 docs/operations/presigned-size-limits.md概括了两者的全部差异V1 按请求限制V2 按上传限制查询参数x-rustfs-max-content-lengthu64x-rustfs-max-total-object-sizeu64接受于仅 SigV4 预签名PutObject仅CreateMultipartUpload签名或预签名 SigV4 均可携带在其它操作上InvalidRequestCopyObject、Multipart、GET、HEAD、DELETE、bucket 操作InvalidRequestUploadPart、CompleteMultipartUpload、AbortMultipartUpload、列举、复制——这些操作改为读取持久化的会话状态度量对象该请求解码后的请求体字节数对象逻辑字节数actual_size跨上传的所有 part 求和不含纠删码、加密或压缩字节限制存储位置仅存在于该请求Multipart 会话元数据SUFFIX_MAX_TOTAL_OBJECT_SIZE创建时写入超限结果EntityTooLarge对象不会被发布违规的UploadPart返回EntityTooLargeCompleteMultipartUpload时若记录的 parts 合计超限同样返回EntityTooLarge两条链路各自的源码落点查询参数常量与解析入口集中在 rustfs/src/auth.rsRUSTFS_MAX_CONTENT_LENGTH_QUERY、RUSTFS_MAX_TOTAL_OBJECT_SIZE_QUERY、parse_presigned_put_max_content_length、parse_presigned_multipart_max_total_object_size。V1 的执行点在 rustfs/src/app/object/put.rsMaxContentLengthStream。V2 的执行点分别在 rustfs/src/app/multipart_usecase.rsmultipart_max_total_object_size与 crates/ecstore/src/set_disk/ops/multipart.rsmultipart_size_limit_from_metadata、admitted_multipart_size。共享签名规则四条必须遵守的约束无论使用哪个参数都必须满足以下四条规则这是整个方案安全性的根基签名前追加参数后端在计算 SigV4 预签名签名之前把参数追加到请求 URI 上。它属于 canonical query 的一部分因此签名之后再添加、删除或修改该参数都会导致签名失效浏览器拿到 URL 后无法改动它。认证通过后才解析RustFS 只在请求被判定为 SigV4 签名请求VerifiedPresignedRequest/VerifiedSigV4Request请求标记之后才解析该参数。同一个查询串出现在未签名请求上会被拒绝返回InvalidRequest。严格的无符号 64 位整数参数值必须是 u64。重复出现、大小写变体、格式错误、负值或溢出超过u64::MAX一律返回InvalidRequest。不携带参数则行为不变普通签名或匿名上传等未携带该参数的请求保持原有行为不受影响。从源码看第 2、3 条由解析函数强制执行。以 V1 为例rustfs/src/auth.rs 中的parse_presigned_put_max_content_length会用form_urlencoded遍历整个查询串要求参数恰好出现一次重复出现或大小写不一致直接报错校验verified_presigned标记、完整的 SigV4 查询串is_complete_sigv4_query以及AuthType::Presigned最后才把字符串解析为u64失败即返回InvalidRequest。对应的单元测试覆盖了这些拒绝路径例如 rustfs/src/auth.rs 中presigned_put_max_content_length_rejects_unsigned_or_invalid_values对未签名请求、负值、18446744073709551616溢出的断言。操作感知的边界拒绝仅仅在解析函数里校验还不够RustFS 还在访问边界上做了操作感知的二次拦截rustfs/src/storage/access.rs 会在请求进入具体 S3 操作之前检查若请求携带x-rustfs-max-content-length但当前操作不是PutObject返回InvalidRequest若请求携带x-rustfs-max-total-object-size但当前操作不是CreateMultipartUpload同样返回InvalidRequest。这样做的目的源码注释里写得很清楚是让不支持的GET/HEAD/DELETE/bucket 路由无法静默忽略一个已签名的能力参数从根上堵住把 PUT URL 复用成其它操作的尝试。V1x-rustfs-max-content-length单请求上传上限生成预签名 URLuri /photos/avatar.png uri ?x-rustfs-max-content-length10485760 presigned_url sigv4_presign(PUT, uri, credentials) # 把 presigned_url 返回给浏览器。之后绝不能再追加该参数。行为细则声明长度超限先拒绝请求声明的Content-Length高于上限时在进入存储之前就被拒绝。流式超限即截断如果请求体实际流出的字节数超过上限会被EntityTooLarge截断且不会有任何对象被发布。不与归档自动解压组合不能与x-amz-meta-snowball-auto-extractarchive 自动解压同时使用否则返回InvalidRequest对应 rustfs/src/app/object/put.rs 中的检查。不覆盖未知长度/流式 chunk 上传未知长度和 SigV4 streaming-chunked 上传原本就不在现有 PutObject 准入契约内本参数不会顺带启用它们。仅限单请求它不是多次 PUT 之间的累计上限也不是 multipart 限制。源码级执行原理在 PutObject 写路径上rustfs/src/app/object/put.rs 解析出max_content_length后会把请求体包装成MaxContentLengthStreamlet body match max_content_length { Some(limit) StreamingBlob::new(MaxContentLengthStream { inner: body, limit, received: 0, exceeded: false, }), None body, };MaxContentLengthStreamrustfs/src/app/object/put.rs实现Stream逐块累计received一旦超过limit就置exceeded并停止继续产出数据同时它实现ByteStream通过remaining_length把剩余量暴露给上层从而保留底层 S3 请求体的流式行为——限制是边读边限流而不是把整个 body 缓冲进来再判断。超限的传播路径也值得注意截断产生的错误会携带 rustfs/src/error.rs 中的UploadLimitExceeded标记该标记设计为必须穿越 body 读取层与存储层让客户端收到EntityTooLarge而不是泛化的内部错误。单元测试 rustfs/src/error.rs 验证了该标记在跨 IO 边界转换后仍映射为EntityTooLarge。此外在包装 body 之前rustfs/src/app/object/put.rs 还会调用resolve_put_object_authoritative_size解析权威的解码后对象长度如果声明的Content-Length本身就超过限制直接返回EntityTooLarge连请求体都不必读取。V2x-rustfs-max-total-object-sizeMultipart 整体上限生成预签名 URLuri /photos/archive.zip?uploads uri x-rustfs-max-total-object-size104857600 presigned_url sigv4_presign(POST, uri, credentials) # 把 presigned_url 返回给浏览器。之后绝不能再追加该参数。注意这里的方法和路径POST到对象 key 并带上?uploads即 S3 的CreateMultipartUpload。完整工作流程创建时固化限制RustFS 校验 SigV4 请求后把限制值随 upload ID 一起持久化。浏览器用 upload ID 上传 parts后续 part 请求不携带任何自定义参数。逐 part 准入每个UploadPart以及写入该上传的UploadPartCopy只有在上传当前逻辑总量 本 part 大小不超过预算时才被准入。替换语义若重传已存在的 part 编号先释放旧 part 的大小再准入新 part 的大小。完成时复核CompleteMultipartUpload会重新汇总记录的 parts若合计超过限制则拒绝完成。受限上传的附加性质未知长度/负长度 part 直接拒绝返回UnexpectedContent而不是无边界地缓冲。写锁与暂存许可受限 part 在创建临时 shard 之前先在上传级写锁下完成准入并持有一个 per-upload 的暂存许可staging permit来约束本地在途数据。锁在读取 body 期间释放在最终检查和重命名时重新获取——因此Complete和Abort不会被慢速上传阻塞。当客户端停止发送时请求体停顿超时request-body stall timeout会释放暂存许可。未携带参数的上传保持无限制创建时未带该参数则整个上传不受限。滚动升级注意事项执行发生在所有节点的 multipart 数据面。滚动升级期间只把受限上传路由到已实现 V2 的节点未实现的节点把内部元数据视为未知无法执行限制。源码级执行原理持久化发生在 rustfs/src/app/multipart_usecase.rsexecute_create_multipart_upload解析出限制后把它写入内部元数据键SUFFIX_MAX_TOTAL_OBJECT_SIZE。该常量定义在 crates/utils/src/http/metadata_compat.rs是真正的 max-total-object-size。数据面准入在 crates/ecstore/src/set_disk/ops/multipart.rsmultipart_size_limit_from_metadataL145-L166从元数据读取限制缺失返回None值冲突或非法则fail-closed报错admitted_multipart_sizeL168-L180用checked_add防溢出计算总量超过限制返回EntityTooLarge(total, limit)上传级写锁与 per-upload 暂存信号量由acquire_multipart_upload_write_lock与CappedMultipartStagingGuardL107-L129基于CAPPED_MULTIPART_STAGING这个OnceLockMutexHashMap...管理实现准入守卫的 Drop 实现会先释放 permit 再清理 map 条目避免与并发的 Abort/Complete 清理竞争。Complete 复核位于同一文件L2485-L2504汇总所有 part 的actual_size逻辑大小而非纠删/加密/压缩后的大小超限返回EntityTooLarge成功完成后才把SUFFIX_MAX_TOTAL_OBJECT_SIZE从元数据中移除——限制只在会话生命周期内有效。Replace 语义体现在execute_upload_part中先计算current_multipart_logical_size会排除被替换的旧 part 大小再对新 part 调用admitted_multipart_size准入L1532-L1536。测试证据crates/ecstore/src/set_disk/ops/multipart.rsmultipart_size_limit_metadata_is_dual_key_and_fail_closed验证双键兼容写入x-minio-internal-*冲突键会报错与非法值 fail-closed。crates/ecstore/src/set_disk/ops/multipart.rsmultipart_size_admission_handles_boundaries_and_overflow验证边界值正好等于限制通过、超 1 字节拒绝与u64溢出防护。rustfs/src/app/multipart_usecase.rsmultipart_max_total_object_size_reads_compatible_internal_metadata验证会话元数据读取逻辑。rustfs/src/auth.rsmultipart_max_total_object_size_requires_signed_create_request与multipart_max_total_object_size_rejects_tampering_and_invalid_values覆盖签名要求、大小写、重复、负值与溢出拒绝。端到端验证仓库中的预签名测试资产仓库在 crates/e2e_test/src/presigned_negative_test.rs 中维护了一套完整的预签名 URL 回归套件backlog#1151可作为理解签名边界的参考。其核心断言包括有效预签名 GET / PUT 必须成功正向对照防止服务器拒绝一切预签名请求导致的假阳性已过期预签名 GET 必须返回 403AccessDenied篡改X-Amz-Signature必须返回 403SignatureDoesNotMatch且被拒的 PUT不得落盘随后 HEAD 必须 404用错误 secret 生成的 URL 必须返回 403篡改签名时覆盖的对象 key 必须被拒预签名 PUT 不允许事后追加未签名的x-amz-*头如x-amz-tagging、SSE-C、x-amz-copy-source而普通非x-amz头如Content-Type仍可接受。这些测试与你部署的x-rustfs-max-*限制共享同一个底层保证签名覆盖什么就只允许什么。在接入本文两个参数后建议参照这套套件补充篡改限制参数值在其它操作上复用参数的负向用例。集成检查清单把本文方案落地到自己的直传服务时请逐条核对参数在sigv4_presign之前拼进 URI之后不要在任何环节改写查询串V1 只用于PUT单对象上传V2 只用于POST ?uploads创建 multipart不要把两者混用或复用到GET/HEAD/DELETE/copy/列举操作上值为十进制 u64不要带符号、小数或前后空白不要重复出现保持全小写参数名明确限制的度量口径V1 是单请求解码后的请求体字节数V2 是跨 part 的逻辑对象字节数不含纠删、加密、压缩开销后端对EntityTooLargeV1 的截断、V2 的 part 拒绝与 complete 拒绝要有面向浏览器的友好提示多节点滚动升级时先把所有节点升级到带 V2 实现的版本再放行受限上传限制是会话级/请求级的V1 不跨 PUT 累计V2 只约束该 upload ID 的生命周期未带参数的请求完全不受影响。总结x-rustfs-max-content-length与x-rustfs-max-total-object-size是 RustFS 提供的两个受 SigV4 签名保护的容量预算机制前者在 PutObject 请求体读取层以流式方式截断超限数据后者把预算写入 multipart 会话元数据并在每个UploadPart/UploadPartCopy准入时与CompleteMultipartUpload复核时强制执行。它们共同解决了浏览器直传场景下后端无法控制单次上传体积的问题且安全性依赖签名覆盖——参数一旦随签名签发客户端便无法篡改。完整的解析逻辑、操作边界拦截与数据面准入实现可分别在 rustfs/src/auth.rs、rustfs/src/storage/access.rs、rustfs/src/app/object/put.rs、rustfs/src/app/multipart_usecase.rs 与 crates/ecstore/src/set_disk/ops/multipart.rs 中继续深入研读。【免费下载链接】rustfs2.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.项目地址: https://gitcode.com/GitHub_Trending/rus/rustfs创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表