diff --git a/weed/s3api/s3api_tables.go b/weed/s3api/s3api_tables.go index cd2143fc31..2a9303e772 100644 --- a/weed/s3api/s3api_tables.go +++ b/weed/s3api/s3api_tables.go @@ -2,6 +2,7 @@ package s3api import ( "bytes" + "context" "encoding/json" "fmt" "io" @@ -63,6 +64,12 @@ func (st *S3TablesApiServer) SetEncryptionChecker(fn func() bool) { st.handler.SetEncryptionChecker(fn) } +// SetTrustedIssuerSource injects the configured OIDC issuer set for S3 Tables +// legacy-owner checks of federated STS sessions. +func (st *S3TablesApiServer) SetTrustedIssuerSource(fn func(context.Context) []string) { + st.handler.SetTrustedIssuerSource(fn) +} + // S3TablesHandler handles S3 Tables API requests func (st *S3TablesApiServer) S3TablesHandler(w http.ResponseWriter, r *http.Request) { st.handler.HandleRequest(w, r, st) @@ -85,6 +92,7 @@ func (s3a *S3ApiServer) registerS3TablesRoutes(router *mux.Router) { s3TablesApi.SetDefaultAllow(s3a.iam.iamIntegration.DefaultAllow()) if s3Integration, ok := s3a.iam.iamIntegration.(*S3IAMIntegration); ok && s3Integration.iamManager != nil { s3TablesApi.SetIAMAuthorizer(s3Integration.iamManager) + s3TablesApi.SetTrustedIssuerSource(s3Integration.trustedOIDCIssuers) } } else { // If IAM is not configured, allow all access by default diff --git a/weed/s3api/s3tables/handler.go b/weed/s3api/s3tables/handler.go index 84565439a5..4092e6fa2e 100644 --- a/weed/s3api/s3tables/handler.go +++ b/weed/s3api/s3tables/handler.go @@ -11,6 +11,7 @@ import ( "strings" "github.com/seaweedfs/seaweedfs/weed/glog" + "github.com/seaweedfs/seaweedfs/weed/iam/sts" "github.com/seaweedfs/seaweedfs/weed/pb/filer_pb" "github.com/seaweedfs/seaweedfs/weed/s3api/s3_constants" ) @@ -79,6 +80,7 @@ type S3TablesHandler struct { trusted bool // Trusted local tooling (shell/admin) bypasses authorization iamAuthorizer IAMAuthorizer localAccountIDs func(string) bool + oidcIssuers func(context.Context) []string canEncrypt func() bool } @@ -123,6 +125,12 @@ func (h *S3TablesHandler) SetLocalAccountChecker(fn func(string) bool) { h.localAccountIDs = fn } +// SetTrustedIssuerSource injects the configured OIDC issuer set. A federated +// STS session carries no asserted issuers, so its legacy owner checks read it. +func (h *S3TablesHandler) SetTrustedIssuerSource(fn func(context.Context) []string) { + h.oidcIssuers = fn +} + // SetEncryptionChecker injects the predicate that tells whether the gateway // holds an SSE-S3 key, so a bucket whose declared AES256 the write path // applies is not created where every write would then fail. @@ -390,8 +398,9 @@ func claimString(claims map[string]interface{}, key string) string { return str } -// trustedIssuers returns the OIDC issuers the auth layer asserted into the -// identity's claims; they re-anchor legacy owner checks in checkPermission. +// trustedIssuers returns the OIDC issuers that re-anchor legacy owner checks in +// checkPermission: those the auth layer asserted into a bearer token's claims, +// else, for a federated STS session, the configured set. func (h *S3TablesHandler) trustedIssuers(r *http.Request) []string { identityRaw := s3_constants.GetIdentityFromContext(r) if identityRaw == nil { @@ -404,7 +413,14 @@ func (h *S3TablesHandler) trustedIssuers(r *http.Request) []string { if val.Kind() != reflect.Struct { return nil } - return strings.Fields(claimString(identityClaimsMap(val), "seaweed:oidc-issuers")) + claims := identityClaimsMap(val) + if _, asserted := claims["seaweed:oidc-issuers"]; asserted { + return strings.Fields(claimString(claims, "seaweed:oidc-issuers")) + } + if h.oidcIssuers != nil && sts.IsParentUserID(claimString(claims, "aws:userid")) { + return h.oidcIssuers(r.Context()) + } + return nil } // normalizePrincipalID collapses ARN and identity strings to a key that is stable within a single account. diff --git a/weed/s3api/s3tables/manager.go b/weed/s3api/s3tables/manager.go index c89b0349a3..59a5820601 100644 --- a/weed/s3api/s3tables/manager.go +++ b/weed/s3api/s3tables/manager.go @@ -47,6 +47,16 @@ func (m *Manager) SetTrusted(trusted bool) { m.handler.SetTrusted(trusted) } +// SetLocalAccountChecker sets the handler's configured local account predicate. +func (m *Manager) SetLocalAccountChecker(fn func(string) bool) { + m.handler.SetLocalAccountChecker(fn) +} + +// SetTrustedIssuerSource sets the handler's configured OIDC issuer source. +func (m *Manager) SetTrustedIssuerSource(fn func(context.Context) []string) { + m.handler.SetTrustedIssuerSource(fn) +} + // SetEncryptionChecker sets the handler's SSE-S3 availability predicate. func (m *Manager) SetEncryptionChecker(fn func() bool) { m.handler.SetEncryptionChecker(fn)