#7989·rustfs

GetObject without versionId on a versioned bucket is denied unless the policy also grants s3:GetObjectVersion (1.0.0; built-in readonly policy affected)

Author: thetidystandardCreated Sep 17, 2026Updated Sep 17, 2026
LabelsS-reproducing

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 via RUSTFS_ACCESS_KEY/RUSTFS_SECRET_KEY
  • Client: mc (quay.io/minio/mc sha256:a7fe349e…). Control: quay.io/minio/minio sha256:14cea493… run identically, where every step below succeeds.

Reproduction

bash
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: hello

Discriminating 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.sys IAM data could not be decrypted. This report is a fresh single-node install, users and policies created natively with mc 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.