# S3 access keys

DanubeData access keys are S3-compatible credentials your applications use to connect to your buckets. This guide explains the two scope types — team-wide and per-bucket — and how to create, rotate, and revoke keys.

## Overview

Every S3 request to DanubeData is authenticated by an **access key ID** and a **secret access key**. Access keys belong to a team and inherit that team's bucket ownership.

There are two scope types:

- **Team-wide** — Full access to every bucket the team owns. This is the default and matches the behavior of existing keys.
- **Specific buckets** — Restricted to a chosen list of buckets, with an independent permission level per bucket.

Scoped keys are enforced at the storage backend itself via bucket policies — not just inside the dashboard. The restriction holds even when the key is used outside our tools.

## Team-wide vs scoped keys

| Property | Team-wide | Specific buckets |
|----------|-----------|------------------|
| Buckets reachable | All current and future buckets owned by the team | Only the buckets explicitly granted |
| Permission model | Full read/write/delete on all buckets | Chosen per bucket (read-only, read-write, or full) |
| `aws s3 ls` (no bucket) | Lists all team buckets | Returns empty (see [Known behavior](#known-behavior-aws-s3-ls-and-scoped-keys)) |
| Recommended for | Admins, backup tooling, shared CI runners | Application credentials, per-environment isolation, third-party integrations |

If a key with a leaked secret is restricted to a single bucket, only that bucket is exposed — the rest of your storage remains isolated.

## Permission levels

When you create a scoped key you pick a level for each bucket:

| Level | Includes | Typical use |
|-------|----------|-------------|
| **Read-only** | `GetObject`, `ListBucket`, `HeadObject`, `GetObjectVersion`, `GetBucketLocation` | Analytics readers, CDN origins, backup verifiers |
| **Read-write** | All read-only actions plus `PutObject`, `AbortMultipartUpload`, `ListMultipartUploadParts`, `ListBucketMultipartUploads` | Application uploads, sync jobs, image processors |
| **Full** | All read-write actions plus `DeleteObject`, `DeleteObjectVersion`, `GetBucketVersioning`, `PutBucketCors`, `PutBucketLifecycle`, `PutBucketVersioning` | Bucket maintenance, migrations, lifecycle automation |

Different buckets attached to the same key can have different levels — for example, read-only on `analytics-prod` and full on `analytics-staging`.

## Creating an access key

### From the Security page (any scope)

1. Open **Account → Security → S3 Credentials**.
2. Click **Create access key**.
3. Enter a short name (up to 32 characters).
4. Choose **Access scope**:
   - **Team-wide** — Skip to step 6.
   - **Specific buckets** — Add one row per bucket. For each row pick the bucket and a level (Read-only, Read-write, or Full). You can add up to 50 buckets per key.
5. (Optional) Set an expiry date — the key will be auto-revoked after this date.
6. Click **Create access key**.
7. **Copy the secret immediately.** The secret is shown once at creation time and cannot be retrieved later. The access key ID is always visible.

### From a bucket detail page (always scoped to that bucket)

1. Open the bucket you want to scope the key to.
2. Scroll to the **Scoped access keys** section.
3. Click **Create access key for this bucket**.
4. Enter a name and pick the permission level for this bucket.
5. Click **Create access key** and copy the secret immediately.

This entry point creates a key scoped to that single bucket. To extend an existing scoped key to additional buckets, manage it from **Security → S3 Credentials** instead.

## Listing and inspecting keys

The **Security → S3 Credentials** table shows:

- Access key ID (the secret is never displayed again)
- Name and creation time
- **Scope** column — a `Team-wide` pill or a `Specific buckets` pill with a tooltip listing each bucket and level
- Status (Active, Expired, Revoked, Error)
- Last-used timestamp

On a bucket detail page, the **Scoped access keys** section only shows keys that include that bucket, along with the level granted to that specific bucket.

## Rotating a key

DanubeData supports the standard two-key rotation pattern. Scope is immutable on an existing key, so to rotate:

1. **Create a new key** with the same scope and the same per-bucket levels as the existing key.
2. **Deploy the new credentials** to your applications.
3. **Confirm traffic** — Watch the new key's last-used timestamp climb while the old key's stays still.
4. **Revoke the old key.**

There is no "rotate in place" operation — this is deliberate so you control the cutover window.

## Revoking a key

1. Open **Security → S3 Credentials** (or the bucket's **Scoped access keys** section).
2. Click **Revoke** on the row you want to disable.
3. Confirm the prompt.

Revocation invalidates the credentials at the storage backend immediately. For scoped keys, the bucket policy is rebuilt without that key's principal, so access stops within seconds. Revoked keys remain in the table for audit purposes but cannot be reactivated.

## Changing the buckets a scoped key can access

You can edit a scoped key's **per-bucket permissions** at any time:

- Add a new bucket to the key (pick a level)
- Change the level on an existing bucket
- Remove a bucket from the key

These changes apply within seconds — the backend bucket policy is rebuilt and pushed to the storage layer automatically.

You **cannot** change a key's scope type after creation (for example, you cannot convert a team-wide key into a scoped key, or vice versa). If you need a different scope type, create a new key and revoke the old one.

## Using your key with the S3 API

Use the access key ID and secret as standard S3 credentials, pointing at the DanubeData endpoint.

### Environment variables

```bash
export AWS_ACCESS_KEY_ID="YOUR_ACCESS_KEY_ID"
export AWS_SECRET_ACCESS_KEY="YOUR_SECRET_ACCESS_KEY"
export AWS_ENDPOINT_URL="https://s3.danubedata.ro"
export AWS_REGION="fsn1"
```

### `aws` CLI example

```bash
# Upload an object to a bucket the key has access to
aws --endpoint-url https://s3.danubedata.ro s3 cp report.pdf s3://my-bucket/

# List objects in a bucket
aws --endpoint-url https://s3.danubedata.ro s3 ls s3://my-bucket/
```

### Python (boto3) example

```python
import boto3

s3 = boto3.client(
    's3',
    endpoint_url='https://s3.danubedata.ro',
    aws_access_key_id='YOUR_ACCESS_KEY_ID',
    aws_secret_access_key='YOUR_SECRET_ACCESS_KEY',
    region_name='fsn1',
)

s3.put_object(Bucket='my-bucket', Key='hello.txt', Body=b'hello')
```

## Known behavior: `aws s3 ls` and scoped keys

`aws s3 ls` (without a bucket name) calls the S3 `ListAllMyBuckets` operation, which only returns buckets **owned** by the requesting credentials. Scoped keys don't own any buckets — they're granted access through bucket policies — so `aws s3 ls` returns an empty list.

To list objects in a bucket the scoped key can access, include the bucket name:

```bash
aws --endpoint-url https://s3.danubedata.ro s3 ls s3://your-bucket-name/
```

This calls `ListBucket`, which the bucket policy grants for all three levels. Team-wide keys do see the full bucket list with `aws s3 ls`.

## Best practices

1. **Use least privilege** — Prefer scoped keys with the lowest level that works (read-only over read-write, read-write over full).
2. **One key per application** — Don't share keys across services; this makes rotation safer and audit easier.
3. **Rotate periodically** — Use the two-key rotation pattern at a cadence that fits your compliance needs.
4. **Set expiry dates** — For temporary integrations, set an expiry so the key is auto-revoked.
5. **Never commit secrets** — Inject credentials via environment variables, mounted files, or a secrets manager.
6. **Monitor last-used timestamps** — Keys that haven't been used in a long time are good candidates for cleanup.
7. **Separate buckets by sensitivity** — A scoped key on a sensitive bucket can't be misused to read unrelated data.

## Troubleshooting

### "Access Denied" on a scoped key

1. **Confirm the bucket is in the key's scope** — Open the key in Security → S3 Credentials and check the bucket list in the tooltip.
2. **Check the level** — A read-only level can't perform `PutObject`; a read-write level can't perform `DeleteObject`. Bump the level or use a different key.
3. **Check key status** — Expired, Revoked, or Error keys all reject requests. Active is the only working status.
4. **Wait a few seconds after editing** — Bucket policy changes are reconciled in the backend; brief propagation can take a few seconds after an edit.

### `aws s3 ls` returns an empty list

Expected behavior for scoped keys. See [Known behavior](#known-behavior-aws-s3-ls-and-scoped-keys) above. Use `aws s3 ls s3://bucket-name/` with the bucket name to list objects.

### Status is "Error" in the dashboard

The key's bucket policy could not be applied at the backend. This is usually transient and resolves on the next reconcile sweep (runs hourly). If the status persists, contact support with the access key ID.

## Next steps

- [Object Storage security](https://docs.danubedata.ro/object-storage-security) — Encryption, public access, CORS, presigned URLs
- [Object Storage quick start](https://docs.danubedata.ro/object-storage-quickstart) — Create your first bucket
- [S3 API supported actions](https://docs.danubedata.ro/object-storage-supported-actions) — Full list of supported S3 operations

---

**Questions?** Contact support at support@danubedata.ro
