S3 Vector Bucket Security: tag conditions in vector bucket policies

Chris Lu committed 2026-11-02 12:41:21 -08:00
1 parent dbde7c3ebe
commit a2f59f1ee9
2 files changed
+57

No files matched your search

+56
@@ -93,6 +93,58 @@ An identity with no vector grants of its own can then call `QueryVectors` on `id
`GetVectorBucketPolicy` returns the document as it was stored; with no policy it returns `NotFoundException`. `DeleteVectorBucketPolicy` removes it, and with it every access the policy granted.
## Tag conditions
Tag conditions need a build that includes [#12351](https://github.com/seaweedfs/seaweedfs/pull/12351).
A statement in a vector bucket policy can have a `Condition` on the [[S3 Vectors API#tags|tags]] of the bucket or index, or on the tags a request sets:
| Condition key | Compared with |
|---------------|---------------|
| `aws:ResourceTag/<key>` | The bucket's tag on a bucket request. On a request that names an index, the index's own tag. |
| `s3vectors:VectorBucketTag/<key>` | The bucket's tag, on bucket and index requests alike. |
| `aws:RequestTag/<key>` | The tag that `TagResource` or `CreateIndex` sets. |
| `aws:TagKeys` | The keys that `TagResource` or `CreateIndex` sets, or that `UntagResource` removes. |
Example, on bucket `team-a` tagged `env=prod`, whose index `idx` is tagged `stage=live`:
```json
{
"Version": "2012-10-17",
"Statement": [
{"Effect": "Allow", "Principal": {"AWS": "reader"}, "Action": "s3vectors:GetIndex",
"Resource": "arn:aws:s3vectors:us-east-1:000000000000:bucket/team-a/index/*",
"Condition": {"StringEquals": {"aws:ResourceTag/stage": "live"}}},
{"Effect": "Allow", "Principal": {"AWS": "reader"}, "Action": "s3vectors:QueryVectors",
"Resource": "arn:aws:s3vectors:us-east-1:000000000000:bucket/team-a/index/*",
"Condition": {"StringEquals": {"s3vectors:VectorBucketTag/env": "prod"}}},
{"Effect": "Allow", "Principal": {"AWS": "writer"}, "Action": "s3vectors:TagResource",
"Resource": "arn:aws:s3vectors:us-east-1:000000000000:bucket/team-a/index/*",
"Condition": {"StringEquals": {"aws:RequestTag/team": "search"},
"ForAllValues:StringEquals": {"aws:TagKeys": ["team"]}}}
]
}
```
* `reader` can call `GetIndex` on `idx`. On an index without `stage=live` it gets `AccessDeniedException`.
* `reader` can query every index in the bucket. Once the bucket is retagged `env=dev`, its next query gets `AccessDeniedException`.
* `writer` can tag an index with `{"team": "search"}`. Tagging with `{"team": "other"}`, or with `{"team": "search", "env": "dev"}`, gets `AccessDeniedException`.
A `Deny` with a tag condition protects the resources carrying the tag. This statement keeps every index tagged `protected=true` from being deleted, by any identity except the admin described above:
```json
{"Effect": "Deny", "Principal": "*", "Action": "s3vectors:DeleteIndex",
"Resource": "arn:aws:s3vectors:us-east-1:000000000000:bucket/team-a/index/*",
"Condition": {"StringEquals": {"aws:ResourceTag/protected": "true"}}}
```
* Conditions apply in bucket policies only. The actions of an identity in the S3 configuration have no conditions.
* `CreateVectorBucket` cannot be limited by tags, because a new bucket has no policy yet.
* A tag that is not set matches no value. A statement that allows `CreateIndex` only with `aws:RequestTag/team` = `search` also refuses a `CreateIndex` without tags.
* A tag change applies from the next request.
* The operators are the ones in [[S3 Policy Conditions#supported-condition-operators]], including the `ForAllValues:` and `ForAnyValue:` forms. The `IfExists` forms are rejected.
* Condition keys are case-sensitive: `aws:resourcetag/env` is rejected.
## The engine's identity
`weed-vector` reads and writes every index's files with the identity passed in its `--storage-option` flags, whoever called the API. Authorization happens in the gateway before the engine is called. In our tests the engine used an identity with the `Admin` action. On a SeaweedFS without identities it needs none.
@@ -107,3 +159,7 @@ An identity with no vector grants of its own can then call `QueryVectors` on `id
| A resource ARN of another service, e.g. `arn:aws:s3tables:...` | `resource "arn:aws:s3tables:..." is not enforceable in a vector bucket policy` |
| `NotAction` | `NotAction is not supported` |
| `NotResource` | `NotResource is not supported` |
| A condition on a key other than those under [Tag conditions](#tag-conditions), e.g. `aws:SourceIp` | `condition key "aws:SourceIp" is not supported in a vector bucket policy` |
| An unsupported condition operator, e.g. `StringEqualsIfExists` | `condition operator "StringEqualsIfExists" is not supported` |
| A condition value that is `null`, an object or an empty list | `condition StringEquals on "aws:ResourceTag/env" needs a string, number or boolean, or a non-empty list of them` |
| A condition field not spelled `Condition`, e.g. `condition` | `condition must be spelled Condition` |
+1
@@ -53,6 +53,7 @@ s3v.list_tags_for_resource(resourceArn=bucket_arn)["tags"] # {"team": "search"}
* Keys are 1 to 128 characters and values up to 256, made of letters, digits, spaces and `_ . : / = + - @`.
* `TagResource` overwrites the value of a key that is already set. `UntagResource` ignores keys that are not set.
* A missing bucket or index gets `NotFoundException`.
* A vector bucket policy can allow or deny requests by these tags. See [[S3 Vector Bucket Security#tag-conditions]].
## Distance metrics