mirror of
https://github.com/versity/versitygw.git
synced 2026-09-23 16:34:18 +00:00
Bucket policy `Principal` named callers by access key id. Under the standalone IAM service it now names them by AWS-style ARN, as real S3 does: a user ARN, a role ARN (covering every session of that role), an assumed-role ARN (covering one session), the account root ARN or bare account id, or `*`. Every other IAM backend has no ARNs to name anything by and keeps access-key principals unchanged, gated on a new `auth.PrincipalResolver` capability interface that only the standalone client implements. `auth.Account` carries `Arn` and `RoleArn`, filled at authentication time, so a session can be matched against both its own ARN and its role's. Principals are validated at PutBucketPolicy time through a new `/private/resolve-principals` endpoint, which rejects anything that does not name a live identity with `MalformedPolicy: Invalid principal in policy`. An `Allow` naming the account root ARN or bare account id delegates to the account's own IAM rather than granting on its own, while a `Deny` naming it denies every principal in the account outright. Denial messages now name the caller by ARN wherever one exists. Also fixes `aws:PrincipalArn` for assumed-role sessions, which reported the session ARN where AWS reports the role's, and stops an unreachable IAM service being reported as a malformed policy.
68 lines
2.8 KiB
Go
68 lines
2.8 KiB
Go
// Copyright 2026 Versity Software
|
|
// This file is licensed under the Apache License, Version 2.0
|
|
// (the "License"); you may not use this file except in compliance
|
|
// with the License. You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package auth
|
|
|
|
// FixedBucketOwner is implemented by IAM backends that give every bucket the
|
|
// same owner instead of the account that created it — currently only the
|
|
// standalone IAM service client, which has no per-user ownership to express:
|
|
// every account is a plain RoleUser, they cannot be enumerated, and access is
|
|
// decided by IAM policy rather than by ACL.
|
|
//
|
|
// Backends that do not implement it keep per-creator ownership as before.
|
|
type FixedBucketOwner interface {
|
|
BucketOwner() Account
|
|
}
|
|
|
|
// ResolveFixedBucketOwner reports the account that owns every bucket when iam
|
|
// fixes ownership, and false when ownership follows the creator instead.
|
|
func ResolveFixedBucketOwner(iam IAMService) (Account, bool) {
|
|
fbo, ok := iam.(FixedBucketOwner)
|
|
if !ok {
|
|
return Account{}, false
|
|
}
|
|
|
|
return fbo.BucketOwner(), true
|
|
}
|
|
|
|
// rootIdentity returns the account a storage backend should see for a request
|
|
// signed with the gateway's root credentials. The S3 request path knows root
|
|
// only by its access key and secret, so root would otherwise reach the
|
|
// backend with the zero uid/gid — which the posix backend's --chuid/--chgid
|
|
// then tries to chown to, an operation an unprivileged gateway can never
|
|
// perform.
|
|
//
|
|
// An IAM backend that fixes bucket ownership to root also defines the POSIX
|
|
// identity root owns those buckets with, so take it from there: root's own
|
|
// object writes then land with the same ownership as the buckets root owns.
|
|
// Backends that do not fix ownership resolve a real per-account uid/gid for
|
|
// every other account and keep root exactly as it was.
|
|
//
|
|
// The same backend is also the one that knows root's principal ARN, which
|
|
// the S3 request path likewise cannot derive: root is the only identity
|
|
// resolved locally rather than through the IAM service, so without this it
|
|
// would reach bucket-policy matching unnamed and a statement naming the
|
|
// account root ARN would miss it.
|
|
func rootIdentity(iam IAMService, root Account) Account {
|
|
owner, fixed := ResolveFixedBucketOwner(iam)
|
|
if !fixed || owner.Access != root.Access {
|
|
return root
|
|
}
|
|
|
|
root.UserID = owner.UserID
|
|
root.GroupID = owner.GroupID
|
|
root.ProjectID = owner.ProjectID
|
|
root.Arn = owner.Arn
|
|
return root
|
|
}
|