GetObject without versionId on a versioned bucket is denied unless the policy also grants s3:GetObjectVersion (1.0.0; built-in readonly policy affected)
Summary
On a versioned bucket, a user whose policy grants s3:GetObject (but not s3:GetObjectVersion) is denied a plain GetObject — a request with no versionId. The identical policy is honoured on an unversioned bucket, and adding s3:GetObjectVersion to the grant makes the versioned-bucket GET succeed. So RustFS appears to evaluate a plain GET against a versioned bucket as s3:GetObjectVersion.
AWS S3 (and MinIO) consult s3:GetObjectVersion only when the request names a versionId; a plain GET of the current version needs only s3:GetObject. Policies written to that contract — including, notably, RustFS's own built-in readonly policy — cannot read a versioned bucket on RustFS.
Every negative case denies correctly (PutObject to a bucket the policy does not name, GetObject on a bucket it does not name), so this fails closed; it is over-restriction, not a leak.
Environment
rustfs/rustfs:1.0.0(sha256:8cc9801755448b71a786705ce76692c77e14936cccd87cf2fc31842e58f4d1ff),rustfs 1.0.0, build 2026-09-16 07:46:40 UTC — the newest image on Docker Hub as of 2026-09-17- Single node,
RUSTFS_VOLUMES=/data, root credentials viaRUSTFS_ACCESS_KEY/RUSTFS_SECRET_KEY - Client:
mc(quay.io/minio/mcsha256:a7fe349e…). Control:quay.io/minio/miniosha256:14cea493…run identically, where every step below succeeds.
Reproduction
mc alias set rfs http://127.0.0.1:9000 <root> <root-secret>
# versioned bucket + one object
mc mb rfs/vbucket && mc version enable rfs/vbucket
printf hello > /tmp/o && mc cp /tmp/o rfs/vbucket/o.txt
# user with s3:GetObject ONLY on the bucket's objects
cat > /tmp/p.json <<'JSON'
{"Version":"2012-10-17","Statement":[
{"Effect":"Allow","Action":["s3:GetObject"],"Resource":["arn:aws:s3:::vbucket/*"]}
]}
JSON
mc admin user add rfs reader readersecret000
mc admin policy create rfs readerpol /tmp/p.json
mc admin policy attach rfs readerpol --user reader
mc alias set reader http://127.0.0.1:9000 reader readersecret000
mc cat reader/vbucket/o.txt # RustFS: Access Denied MinIO: helloDiscriminating variants, same server, fresh user each time:
| policy / bucket | GetObject (mc cat, no versionId) |
|---|---|
s3:GetObject on arn:aws:s3:::vbucket/* — versioned bucket |
AccessDenied |
s3:GetObject + s3:GetObjectVersion on arn:aws:s3:::vbucket/* — versioned |
OK |
s3:GetObject on arn:aws:s3:::* — versioned |
AccessDenied |
s3:* on arn:aws:s3:::vbucket/* and arn:aws:s3:::vbucket — versioned |
OK |
s3:GetObject on arn:aws:s3:::ubucket/* — unversioned bucket |
OK |
built-in readonly policy attached — versioned |
AccessDenied |
Two things point at the same mechanism: the policy that fails on the versioned bucket passes unchanged on an unversioned one, and the only addition that turns the versioned-bucket GET on is s3:GetObjectVersion. Not resource matching (the arn:aws:s3:::* variant still fails), not statement ordering (tested both orders), not a missing ListBucket (adding it changes HeadObject, see below, but not GetObject).
Re-confirmed on a second, fresh container with a policy naming both buckets at once (arn:aws:s3:::vbucket/* and arn:aws:s3:::ubucket/*), same user:
mc cat reader/vbucket/o.txt # Access Denied (versioned)
mc cat reader/ubucket/o.txt # hello (unversioned, same policy, same user)
mc cat reader2/vbucket/o.txt # hello (versioned, policy = s3:GetObject + s3:GetObjectVersion)With RUSTFS_OBS_LOGGER_LEVEL=info no denial event reached the container's stdout for the refused GET, so I could not capture the reason code from #5761 for you; happy to re-run at whatever level surfaces it.
Expected
A GetObject request without versionId on a versioned bucket is authorised by s3:GetObject alone, as on AWS S3 and MinIO. s3:GetObjectVersion should be required only when a versionId is present.
Related, smaller
HeadObject (mc stat) on the same versioned bucket is denied under s3:GetObject alone and allowed once the policy also grants s3:ListBucket on the bucket ARN. S3 authorises HeadObject with s3:GetObject. Mentioned because it looks like the same evaluation path; happy to open it separately if you prefer.
Impact / workaround
Any deployment that keeps versioning on and scopes service identities with least-privilege policies hits this on every read; the built-in readonly policy is affected too. Workaround on our side: grant s3:GetObjectVersion alongside every s3:GetObject, which is portable (it is a no-op on MinIO). That widens each identity to reading noncurrent versions by id, which is not what s3:GetObject was meant to convey, so a server-side fix would be preferable.
Not a duplicate of the closed ones, I think
- #5740 (GetObject AccessDenied after a MinIO → RustFS migration) was closed on the IAM-migration track: the reporter had started RustFS on an existing MinIO data directory with new root credentials, so
.minio.sysIAM data could not be decrypted. This report is a fresh single-node install, users and policies created natively withmc admin; no MinIO data directory involved. It may well be what that reporter was also hitting — a MinIO → RustFS migration carries versioned buckets and MinIO-shaped policies — but the mechanism here is reproducible without any migration. - #1423 was traced to an STS session policy (
is_allowed_sts … is_allowed_sp=false). No STS here; a plain IAM user with an attached policy.
Source: rustfs/rustfs