mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-11-04 02:57:44 +01:00
s3vectors: put, get and delete vector bucket policies (#12201)
* util: do not bind outbound dials to a loopback -ip.bind `weed server -ip.bind=127.0.0.1` keeps every listener local, but the same address became the source of outbound connections, so the filer dialled its remote mount from 127.0.0.1 and failed with "connect: invalid argument". A loopback source cannot reach off the host; leave the source to the OS, as for a wildcard bind. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * s3vectors: put, get and delete vector bucket policies The AWS S3 Vectors policy operations had no handlers; the policy document is stored on the shared table bucket, so the handlers delegate to the catalog's PutTableBucketPolicy, GetTableBucketPolicy and DeleteTableBucketPolicy and keep its guard. * s3vectors: treat ICEBERG buckets as absent in policy handlers GetVectorBucketPolicy/PutVectorBucketPolicy/DeleteVectorBucketPolicy delegated straight to the table bucket, so a bucket created for ICEBERG tables — invisible to every other vectors operation — could still have its policy read and written through the vectors API. Gate all three on lanceBucket, the same format check DeleteVectorBucket and CreateIndex already apply. * test/vectors: cover vector bucket policy put/get/delete Round-trips a policy document through the new ops and asserts a get on the deleted policy surfaces NotFoundException, so the boto3 conformance run exercises the added handlers end to end. * s3vectors: validate the bucket ARN and policy document Bucket-level operations now reject an index ARN and an ARN minted for another service, and PutVectorBucketPolicy refuses a document carrying s3vectors actions — the evaluator only honors s3tables actions, so such a rule would be a dead grant. * s3vectors: validate the bucket ARN on the remaining bucket-level operations * s3vectors: cover bucket ARN and policy document validation TestBucketARNs pins the accepted forms and the index/foreign-service rejections; TestValidatePolicyDocument covers a valid document, a malformed one, and the dead s3vectors action namespace. * s3vectors test: use an enforceable action in the bucket policy test The catalog refuses s3vectors actions in a stored policy, so the put call rejected the document before the get and delete assertions ran. --------- Co-authored-by: Ólafur Bjarki Bogason <olafur@genkiinstruments.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
7 files changed
+261
-22
No files matched your search
@@ -60,6 +60,11 @@ func (h *S3TablesHandler) handlePutTableBucketPolicy(w http.ResponseWriter, r *h
|
||||
return fmt.Errorf("resourcePolicy is required")
|
||||
}
|
||||
|
||||
if err := validatePolicyDocument(req.ResourcePolicy); err != nil {
|
||||
h.writeError(w, http.StatusBadRequest, ErrCodeInvalidRequest, err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
bucketName, err := parseBucketNameFromARN(req.TableBucketARN)
|
||||
if err != nil {
|
||||
h.writeError(w, http.StatusBadRequest, ErrCodeInvalidRequest, err.Error())
|
||||
@@ -320,6 +325,11 @@ func (h *S3TablesHandler) handlePutTablePolicy(w http.ResponseWriter, r *http.Re
|
||||
return fmt.Errorf("resourcePolicy is required")
|
||||
}
|
||||
|
||||
if err := validatePolicyDocument(req.ResourcePolicy); err != nil {
|
||||
h.writeError(w, http.StatusBadRequest, ErrCodeInvalidRequest, err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
bucketName, err := parseBucketNameFromARN(req.TableBucketARN)
|
||||
if err != nil {
|
||||
h.writeError(w, http.StatusBadRequest, ErrCodeInvalidRequest, err.Error())
|
||||
|
||||
@@ -141,6 +141,41 @@ func CheckTableOrBucketPermission(operation, principal, resourceOwner, resourceP
|
||||
return resourceOK || bucketOK
|
||||
}
|
||||
|
||||
// validatePolicyDocument rejects a policy the evaluator could not honor: one
|
||||
// that does not parse, or whose actions live in a namespace that never
|
||||
// matches. An s3vectors action stored here would be a dead rule the writer
|
||||
// believes is enforced — vector requests authorize their s3tables action.
|
||||
func validatePolicyDocument(raw string) error {
|
||||
var doc PolicyDocument
|
||||
if err := json.Unmarshal([]byte(raw), &doc); err != nil {
|
||||
return fmt.Errorf("invalid policy document: %w", err)
|
||||
}
|
||||
for _, stmt := range doc.Statement {
|
||||
for _, action := range statementActions(stmt.Action) {
|
||||
if strings.HasPrefix(action, "s3vectors:") {
|
||||
return fmt.Errorf("action %q is not enforceable in a resource policy", action)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func statementActions(spec interface{}) []string {
|
||||
switch a := spec.(type) {
|
||||
case string:
|
||||
return []string{a}
|
||||
case []interface{}:
|
||||
var actions []string
|
||||
for _, item := range a {
|
||||
if str, ok := item.(string); ok {
|
||||
actions = append(actions, str)
|
||||
}
|
||||
}
|
||||
return actions
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// deniedByPolicy reports whether another applicable resource policy vetoes the
|
||||
// request. The admin account is exempt, matching CheckPermissionWithContext.
|
||||
func deniedByPolicy(operation, principal, resourcePolicy, resourceARN string, ctx *PolicyContext) bool {
|
||||
|
||||
@@ -428,3 +428,21 @@ func TestCheckPermissionLegacyPolicyPrincipalShapes(t *testing.T) {
|
||||
t.Error("legacy Allow with array principal must still match the scoped caller")
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidatePolicyDocument(t *testing.T) {
|
||||
valid := `{"Version":"2012-10-17","Statement":[{"Effect":"Deny","Principal":"*","Action":"s3tables:GetTable","Resource":"*"}]}`
|
||||
if err := validatePolicyDocument(valid); err != nil {
|
||||
t.Fatalf("valid policy rejected: %v", err)
|
||||
}
|
||||
if err := validatePolicyDocument("not-json"); err == nil {
|
||||
t.Fatal("malformed policy should be rejected")
|
||||
}
|
||||
dead := `{"Version":"2012-10-17","Statement":[{"Effect":"Deny","Principal":"*","Action":"s3vectors:PutVectors","Resource":"*"}]}`
|
||||
if err := validatePolicyDocument(dead); err == nil {
|
||||
t.Fatal("s3vectors action should be rejected")
|
||||
}
|
||||
deadList := `{"Version":"2012-10-17","Statement":[{"Effect":"Deny","Principal":"*","Action":["s3tables:*","s3vectors:*"],"Resource":"*"}]}`
|
||||
if err := validatePolicyDocument(deadList); err == nil {
|
||||
t.Fatal("action list containing s3vectors should be rejected")
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user