Reference
IAM permissions and conditions
Reference for the ABAC permission model: rule shape, actions, resource patterns, identity templates, condition operators and keys, LIST scoping, and group resolution.
Permission shape#
Each IAM user carries one or more permission rules, evaluated per request after SigV4 verification:
{
"effect": "Allow",
"actions": ["read", "write", "list"],
"resources": ["releases/firmware/*"],
"conditions": {
"IpAddress": { "aws:SourceIp": "203.0.113.0/24" }
}
}
effect, actions, and resources are required; conditions is optional.
Effect#
| Value | Meaning |
|---|---|
Allow | Grants access when actions, resources, and conditions all match |
Deny | Blocks access when it matches — overrides every Allow, whether the Deny comes from the user's direct rules or an inherited group |
A request with no matching Allow is implicitly denied.
Actions#
| Action | S3 operations |
|---|---|
read | GetObject, HeadObject |
write | PutObject, CopyObject, CreateMultipartUpload, UploadPart, CompleteMultipartUpload |
delete | DeleteObject, DeleteObjects |
list | ListBuckets, ListObjectsV2, ListMultipartUploads, ListParts |
admin | CreateBucket, DeleteBucket |
* | All actions |
A user is an admin (admin GUI access, config changes) when at least one Allow rule has actions containing * or admin AND resources containing *.
Resources#
Glob patterns matched against bucket/key:
| Pattern | Matches |
|---|---|
* | Every bucket and key |
releases | Bucket-level operations only (list, create) |
releases/* | Every object in releases |
releases/firmware/* | Objects under the firmware/ prefix |
releases/firmware/fw-2.* | Glob on the object key |
Bucket-level operations match the bare bucket name (no /*); object operations match bucket/*. Full access to one bucket therefore requires both rules:
[
{ "effect": "Allow", "actions": ["*"], "resources": ["releases"] },
{ "effect": "Allow", "actions": ["*"], "resources": ["releases/*"] }
]
Permission templates#
Resource strings and string condition values accept identity templates:
| Template | Expands to |
|---|---|
${iam:username} | The authenticated user's name |
${iam:access_key_id} | The authenticated user's access key ID |
Template facts:
- The
iam:prefix is mandatory; a bare${username}is not substituted. The prefix distinguishes request-time identity substitution from the${env:NAME}load-time config expansion. A stale bare${username}leaves a literal, unmatchable resource pattern — so the rule matches nothing and the user is silently denied. The save-time config advisories flag this; see Config advisories. - Templates are stored raw in the DB/YAML and expanded when the in-memory IAM index is built, after group permissions are merged into each member user.
- Identity values are percent-encoded before substitution: a username
dana/team*becomesdana%2Fteam%2A, so it cannot inject path separators or wildcards. - Unknown templates are rejected by user/group API validation and by declarative IAM apply.
Example — a per-user home prefix in db-archive, shared via the Engineering group:
{
"effect": "Allow",
"actions": ["read", "write", "list"],
"resources": ["db-archive/home/${iam:username}/*"]
}
For dana this expands to db-archive/home/dana/*.
Conditions#
Conditions within a single rule are ANDed — all must match for the rule to apply. Multiple values for the same key are ORed.
Condition operators#
| Operator | Description | Example |
|---|---|---|
StringEquals | Exact string match | s3:prefix = "firmware/" |
StringNotEquals | Exact string non-match | s3:prefix != "internal/" |
StringLike | Glob pattern match | s3:prefix LIKE "home/dana/*" |
StringNotLike | Glob pattern non-match | s3:prefix NOT LIKE ".*" |
IpAddress | CIDR range match | aws:SourceIp in 203.0.113.0/24 |
NotIpAddress | CIDR range non-match | aws:SourceIp NOT in 203.0.113.0/24 |
Condition keys#
| Key | Type | Available on | Value |
|---|---|---|---|
aws:SourceIp | IP address (CIDR) | All requests | Client IP — from the direct connection, or from X-Forwarded-For / X-Real-IP when DGP_TRUST_PROXY_HEADERS=true |
s3:prefix | String | LIST requests | The prefix query parameter |
With DGP_TRUST_PROXY_HEADERS=true on a proxy exposed directly to the internet, clients can spoof aws:SourceIp via a forged X-Forwarded-For header.
s3:prefix string values accept the identity templates above, with the same storage, expansion, and percent-encoding rules as resource patterns.
JSON format#
{
"IpAddress": {
"aws:SourceIp": ["203.0.113.0/24"]
},
"StringNotLike": {
"s3:prefix": "internal/*"
}
}
ListBucket prefix scoping#
When a user with prefix-scoped permissions (for example { "resources": ["db-archive/home/dana/*"] }) issues a LIST with an empty prefix, or a prefix wider than the policy covers, the proxy admits the request and post-filters the result:
- Each returned key and CommonPrefix is checked against the user's policy; only keys with
readorlistpermission are returned. - Filtered pages carry the response header
x-amz-meta-dg-list-filtered: true. is_truncatedand the continuation token reflect the engine-level cursor, not the filtered count; the client'smax_keysacts as a server-side inspection cap, so a returned page may be smaller than requested.- Users whose policy covers the full requested scope receive the engine page unchanged, with no filtering cost.
Workflow-bypass prevention#
A PUT to a non-existent bucket returns 404 NoSuchBucket on every backend — including the filesystem backend, where the underlying FS could create the parent directory. Bucket creation requires the admin action; it cannot occur as a side effect of a write.
Canned policy templates#
The admin GUI (Access → Users → Apply template) offers four starting-point policies. Generated permissions are editable before saving.
| Template | Permissions |
|---|---|
| Read-only | read, list on every resource |
| Developer | read, write, list on every resource |
| Admin | All actions on every resource |
| Bucket owner | All actions on one named bucket (selected on apply) |
Group resolution#
- A user's effective permissions are the union of their direct rules and the rules of every group they belong to (for example,
dana's direct rules plus theEngineeringgroup's rules). - Group permissions are merged into each member at IAM index build time; identity templates expand after this merge.
- Deny precedence applies across the union: a Deny in any source — direct or inherited — overrides Allows from all sources.
- OAuth group mapping rules add group memberships on each login; memberships are merged, never replaced, so manual assignments persist.
- The built-in Administrators group carries
{ "effect": "Allow", "actions": ["*"], "resources": ["*"] }.