Guides: access & security
How to create IAM users and groups
Give every client its own scoped credential — here, a CI pipeline (ci-uploader) that can write firmware builds and nothing else.
1. Enable bootstrap SigV4#
IAM users require authentication to be on. If the proxy still runs open, set the bootstrap credential pair and restart:
# validate
access:
access_key_id: acme-bootstrap-key
secret_access_key: a-long-random-secret-of-40-plus-characters
Or via env vars: DGP_ACCESS_KEY_ID + DGP_SECRET_ACCESS_KEY. For the guided zero-to-secured walkthrough, see the Secure your proxy tutorial.
2. Create the user#
Go to Settings → Access → Users → + Add user.

- Name:
ci-uploader. - Leave Access key ID and Secret access key blank to auto-generate them.
- Add permissions (next step) or pick a group.
- Save. The secret is shown once, on the "Created" toast — copy it now. If it's lost, rotate the keys (step 4).
If you script it instead, POST /_/api/admin/users needs name; access_key_id and secret_access_key are auto-generated when omitted, enabled defaults to true, and permissions defaults to empty. Group membership is a separate call: POST /_/api/admin/groups/:id/members. All admin routes require a session cookie from POST /_/api/admin/login — see the admin API reference.
3. Write a permission#
Each permission is an ABAC rule: effect, actions, resources, optional conditions. The shapes you'll write most often:
Write-only CI — ci-uploader may upload firmware builds and list its bucket, nothing else:
[
{ "effect": "Allow", "actions": ["write"], "resources": ["releases/firmware/*"] },
{ "effect": "Allow", "actions": ["list"], "resources": ["releases"] }
]
Read-only prefix — download access to one folder:
{ "effect": "Allow", "actions": ["read", "list"], "resources": ["downloads/public/*"] }
Full access to one bucket — note the two rules: bucket-level operations match the bare bucket name, object operations match bucket/*:
[
{ "effect": "Allow", "actions": ["*"], "resources": ["db-archive"] },
{ "effect": "Allow", "actions": ["*"], "resources": ["db-archive/*"] }
]
Deny is absolute: if any Deny rule matches — direct or group-inherited — the request fails regardless of Allows. For the full grammar (action-to-S3-operation mapping, glob rules, ${iam:username} identity templates) and the canned starting-point templates in Settings → Access → Users → Apply template, see the IAM permissions reference.
4. Rotate keys without downtime#
POST /_/api/admin/users/:id/rotate-keys swaps the credential atomically — the old key stops working the moment the call returns. If a brief 403 window on the client is acceptable, rotate and redeploy.
To rotate with zero downtime, overlap two credentials:
- Clone the user: per-user row menu → Clone, or
POST /_/api/admin/users/:id/clone. The clone gets fresh keys and a copy of the permissions. - Roll the new credentials out to every client.
- Delete the original user once nothing signs with it (check the audit log at
/_/admin/diagnostics/audit).
If you only need to suspend a user, untick Enabled instead of deleting — the row, groups, and permissions survive.
5. Put shared policy in a group#
Don't copy the same rules onto ten users. Acme's Engineering group carries read access to releases and downloads:
- Settings → Access → Groups → + Add group, name it
Engineering. - Add permissions on the group (same JSON schema as user permissions):
[
{ "effect": "Allow", "actions": ["read", "list"], "resources": ["releases", "releases/*"] },
{ "effect": "Allow", "actions": ["read", "list"], "resources": ["downloads", "downloads/*"] }
]
- Add members: Settings → Access → Users → edit
dana→ Groups → tickEngineering.
Members get the group's rules in addition to their direct rules; a Deny from either source wins. If you use SSO, mapping rules can add people to Engineering automatically on login — see How to set up OAuth/OIDC single sign-on.
6. Switch an AWS client over#
No client-side code changes — only the endpoint and the credentials:
export AWS_ACCESS_KEY_ID=ci-uploader-key
export AWS_SECRET_ACCESS_KEY=ci-uploader-secret
export AWS_ENDPOINT_URL=https://s3.acme.example
aws s3 cp firmware-v2.4.0.bin s3://releases/firmware/firmware-v2.4.0.bin
Or as a profile in ~/.aws/credentials + ~/.aws/config:
# ~/.aws/credentials
[acme-proxy]
aws_access_key_id = ci-uploader-key
aws_secret_access_key = ci-uploader-secret
# ~/.aws/config
[profile acme-proxy]
endpoint_url = https://s3.acme.example
region = us-east-1
boto3, Terraform, rclone, and the rest of the SigV4 ecosystem work the same way — endpoint override plus proxy credentials. See client configuration.
7. Presigned URLs#
aws s3 presign works out of the box; URLs expire after at most 7 days and carry the signing user's permissions — Deny rules apply to presigned requests too.
Verify#
Test the allow and the deny — a policy that's never seen a 403 isn't verified:
# Allowed: write inside the granted prefix
aws --profile acme-proxy s3 cp build.bin s3://releases/firmware/build.bin
# upload: ./build.bin to s3://releases/firmware/build.bin
# Denied: read outside the grant
aws --profile acme-proxy s3 cp s3://db-archive/nightly/dump.sql.gz .
# fatal error: An error occurred (AccessDenied)
Every denial lands in the audit log (Settings → Observability → Audit) with user, action, bucket, and path.
Related#
- IAM permissions reference — full grammar, identity templates, canned templates.
- How to restrict access by IP and prefix — add conditions to these rules.
- How to set up OAuth/OIDC single sign-on — auto-provision humans into groups.
- About authentication and access control — why the layers stack the way they do.