Skip to content

Access policies

Access policies define what authenticated users can do in S3 Lens — which providers, buckets, prefixes, and actions they may use. Policies compile at startup; changes require a restart.

See Authentication for OIDC setup and identity mapping.

When policy.enabled is true (and auth.enabled is true):

  1. Every API request must include a valid session
  2. S3 Lens resolves the user’s roles and attached policies from their IdP groups or direct bindings
  3. The requested operation is checked against allow and deny rules
  4. Explicit deny beats allow; if nothing matches, access is denied

When policy.enabled is false, authenticated users can perform any action. When auth.enabled is false, all requests are allowed (local development only).

Roles bundle one or more policies. Users receive roles through auth.bindings or auth.local_users.

policy:
enabled: true
use_defaults: true
policies:
uploads-only:
allow:
- actions: [read, write]
resource: { provider: "*", bucket: "*", prefix: "uploads/" }
roles:
uploader:
policies: [default-viewer, uploads-only]
auth:
bindings:
- groups: ["team-uploaders"]
role: uploader

When use_defaults: true, S3 Lens provides starter templates:

Template Grants
default-viewer Read providers, buckets, and objects
default-write Upload, copy, rename, and create buckets
default-admin All actions

Reference these in your roles, or define entirely custom policies under policy.policies.

Each API operation maps to an action. Use these in policy rules:

Action What it allows
providers:read List and view configured providers
buckets:read List buckets
buckets:create Create buckets
buckets:delete Delete buckets
objects:read List, download, and bulk download objects
objects:write Upload, copy, and rename objects
objects:delete Delete and bulk-delete objects
objects:presign Generate presigned URLs

In native YAML policies, you can use shorthand aliases:

Alias Expands to
read providers:read, buckets:read, objects:read
write objects:write, buckets:create
delete objects:delete, buckets:delete
admin All actions

Rules target resources using three dimensions:

resource:
provider: "my-garage" # provider name from config, or "*"
bucket: "logs" # exact name, wildcard, or "*"
prefix: "2024/" # object key prefix, or "*"

Examples:

  • Read everything on one provider: { provider: "garage", bucket: "*", prefix: "*" }
  • Write only under uploads/: { provider: "*", bucket: "*", prefix: "uploads/" }
  • Full access everywhere: { provider: "*", bucket: "*", prefix: "*" }

The provider dimension matches the provider name in your config. It applies to native YAML policies; S3/IAM JSON policies use bucket and prefix from ARNs (see below).

Define allow and deny rules under policy.policies:

policy:
enabled: true
use_defaults: true
policies:
garage-readonly:
allow:
- actions: [read]
resource: { provider: "garage-local", bucket: "*", prefix: "*" }
deny-presign:
deny:
- actions: ["objects:presign"]
resource: { provider: "*", bucket: "*", prefix: "*" }
custom-readonly:
allow:
- actions: [read]
resource: { provider: "*", bucket: "*", prefix: "*" }

Attach policies to roles:

roles:
garage-reader:
policies: [garage-readonly]
viewer-no-presign:
policies: [default-viewer, deny-presign]

Reuse existing AWS-style policy documents:

policy:
policies:
bucket-readonly:
s3:
file: policies/read-only-bucket.json

Or inline:

policy:
policies:
bucket-readonly:
s3:
inline: |
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": ["s3:GetObject", "s3:ListBucket"],
"Resource": [
"arn:aws:s3:::my-bucket",
"arn:aws:s3:::my-bucket/*"
]
}]
}

Notes for IAM JSON in S3 Lens:

  • Principal is ignored — identity comes from OIDC bindings, not the policy document
  • Standard Action and Resource fields are supported
  • Condition, NotAction, and NotResource are not supported and will be rejected at startup
  • s3:ListBucket matches the bucket ARN (arn:aws:s3:::bucket); object actions match arn:aws:s3:::bucket/*
  1. If auth.enabled is false → allow all
  2. Resolve the user from their session (subject + groups)
  3. Collect policies from role and direct bindings
  4. Match the request against allow and deny rules
  5. Explicit Deny wins over Allow
  6. No matching allow → deny
roles:
viewer:
policies: [default-viewer]
roles:
editor:
policies: [default-viewer, default-write]

Limit a team to one backend:

policy:
policies:
seaweed-only:
allow:
- actions: [read, write, delete]
resource: { provider: "seaweed-local", bucket: "*", prefix: "*" }
roles:
seaweed-editor:
policies: [default-viewer, seaweed-only]

The default-viewer policy lets users see the provider list; seaweed-only restricts mutations to SeaweedFS.

policy:
policies:
uploads-prefix:
allow:
- actions: [read, write]
resource: { provider: "*", bucket: "*", prefix: "uploads/" }

Users can list and upload under uploads/ but not modify objects elsewhere.

policy:
policies:
deny-presign:
deny:
- actions: ["objects:presign"]
resource: { provider: "*", bucket: "*", prefix: "*" }
roles:
viewer-no-presign:
policies: [default-viewer, deny-presign]

See s3lens.yaml.example for a complete working example with multiple custom policies and roles.