Skip to content

fix(auth): give each served subresource its own operation, by method - #102

Merged
alanshaw merged 2 commits into
ash/feat/encryption-config-permsfrom
ash/fix/fail-closed-classification
Oct 20, 2026
Merged

alanshaw merged 2 commits into
ash/feat/encryption-config-permsfrom
ash/fix/fail-closed-classification

Conversation

@alanshaw

@alanshaw alanshaw commented Oct 20, 2026 •

Copy link
Copy Markdown
Member

Stacked on #101. Pins libforge to its merge of fil-forge/libforge#86.

#101 refuses a query naming two subresources (ConflictingQuery) or an unsupported one, and classifies DeleteObjects. It classifies the remaining supported subresources as the plain bucket or object operation. Those include writes: PUT /b?versioning and ?object-lock run on a CreateBucket authorization, which skips the bucket scope check, so a key holding s3:CreateBucket can change any bucket's versioning or object lock. This PR gives each of them its own operation and permission.

What changes

Each supported subresource now selects its operation per method, as the gateway's router does (bucketRoutes, objectRoutes, alongside #101's subresources vocabulary):

Operation Permission
GetBucketLocation / Acl / Cors s3:GetBucketLocation / s3:GetBucketAcl / s3:GetBucketCORS
GetBucketPolicyStatus s3:GetBucketPolicyStatus
Get / Put / DeleteBucketTagging s3:GetBucketTagging / s3:PutBucketTagging (both writes)
Get / PutBucketVersioning s3:GetBucketVersioning / s3:PutBucketVersioning
Get / PutObjectLockConfiguration s3:GetBucketObjectLockConfiguration / s3:PutBucketObjectLockConfiguration
Get / Put / DeleteObjectTagging s3:GetObjectTagging / s3:PutObjectTagging / s3:DeleteObjectTagging
GetObjectAttributes s3:GetObjectAttributes

The bucket configuration writes and the policy status are service-key only, like the policy and encryption. The other new permissions are policy actions (fil-forge/libforge#86, merged). Object tagging writes are catalog writes; the bucket configuration lives in Ingot's registry and needs no Forge commands.

Routing follows the router per method:

  • A route the gateway has that Hilt does not authorize is refused with UnsupportedOperation: PUT/DELETE ?cors, PUT ?acl, object ACLs, ?uploads/?versions on an object, and uploadId on a PUT without a partNumber.
  • A POST naming no route is refused. On a bucket it is a browser form upload, authenticated by a signed policy in the body that Hilt doesn't verify; feat: classify and permit the bucket encryption operations #101 classifies it as CreateBucket.
  • A supported subresource the router doesn't route on for the method is the plain operation there, as in the router (PUT /b?uploads creates the bucket). A HEAD is HeadBucket or HeadObject whatever its subresource.
  • Other query parameters are ignored, as feat: classify and permit the bucket encryption operations #101 has it.

Behaviour changes to know about

  • Existing service keys don't hold the new permissions, so they lose these operations until they're re-issued with them. FilOne's console sets explicit permission lists on the keys it issues and will need the new names.
  • Principal policies that list actions (instead of s3:*) need the new reads added for principals to keep reading bucket configuration, tags and attributes.
  • POST /b/k?partNumber&uploadId is now CompleteMultipartUpload, as the router routes it (same permission as the PutObject it was before).

Tests

go test ./... and go vet -tags itest ./itest pass. TestClassifyRequest covers each new operation, the router's fall-through cases, and the refused routes. TestRoutesAddressExistingBuckets checks that every routed operation is scope-checked against its bucket and routes only on a supported subresource. I haven't run the IAM itest scenarios against the change. fil-forge/ingot#287 pins this branch and runs its route-permission scenario against it.

🤖 Generated with Claude Code

Loading
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants