Skip to content

Object Storage Access Control (ACLs and Bucket Policies)

This page explains how access control works in Infomaniak Ceph Object Storage with Keystone multi-tenancy, and how to share buckets across tenants.

Important Distinction: Infomaniak vs Ceph Native IAM

Infomaniak Public Cloud uses OpenStack Keystone for authentication and authorization. This is different from the native Ceph IAM Accounts system described in some Ceph documentation and blog posts.

Feature Ceph Native IAM Infomaniak (Keystone)
Identity system Ceph IAM Users & Accounts OpenStack Keystone (Users & Projects)
Cross-tenant access STS AssumeRole + IAM Roles Bucket policies only
User differentiation in policies Yes (per-user policies within account) No (all users in same tenant share same identity)

Note: IAM Accounts is a built-in RGW feature, not a separate module. However, Infomaniak Public Cloud uses Keystone authentication instead, so the native IAM features (STS, IAM Roles, per-user policies) are not exposed to customers. All information on this page applies specifically to the Infomaniak Keystone-based Object Storage setup.

How Access Control Works in Ceph Object Storage

Ceph Object Storage uses two mechanisms:

Mechanism Scope Use case
Bucket policies Entire bucket or specific objects Cross-tenant sharing, fine-grained rules
Object ACLs Individual object Simple per-object sharing

Bucket policies are the primary tool for cross-tenant access. Object ACLs work within the same tenant only.

Keystone Multi-Tenant Architecture

In Infomaniak Ceph Object Storage, buckets are owned by tenants (projects), not individual users.

Within the same tenant: All users share implicit ownership. No bucket policy is needed for them to access, read, write.

Across tenants: Complete isolation by default. A bucket in tenant A is invisible to users in tenant B unless a bucket policy explicitly grants access.

Access method

Cross-tenant access requires path-style URLs:

https://s3.gva2.infomaniak.cloud/<owner-tenant-id>:<bucket-name>/<object>

s3cmd supports this natively. AWS CLI and boto3 do not because they validate bucket names and reject the : character.

Sharing a Bucket with Another Tenant (Read-Only)

This was validated and the procedure below is fully tested.

Step 1: Identify the other tenant

Each tenant has a unique ID. Ask the other tenant for it, or extract it from their bucket owner info:

# List your own tenant ID from any bucket you own
aws s3api list-buckets --query 'Owner.ID'

Step 2: Create a read-only bucket policy

Save as cross-tenant-read-policy.json:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowCrossTenantReadOnly",
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::<other-tenant-id>:root"
      },
      "Action": [
        "s3:GetObject",
        "s3:ListBucket"
      ],
      "Resource": [
        "arn:aws:s3:::my-shared-bucket",
        "arn:aws:s3:::my-shared-bucket/*"
      ]
    }
  ]
}

Important: The Principal format arn:aws:iam::<tenant-id>:root matches the entire tenant, not a specific user. This is the only format that works reliably for cross-tenant access in the current Ceph release.

Apply it:

aws s3api put-bucket-policy \
  --bucket my-shared-bucket \
  --policy file://cross-tenant-read-policy.json

Verify it is set:

aws s3api get-bucket-policy --bucket my-shared-bucket

Step 3: The other tenant accesses your bucket

Using s3cmd (path-style):

# List your shared bucket
s3cmd -c ~/.s3cfg_other ls s3://<your-tenant-id>:my-shared-bucket/

# Download a file
s3cmd -c ~/.s3cfg_other get \
  s3://<your-tenant-id>:my-shared-bucket/report.pdf \
  /tmp/report.pdf

Important: The other tenant cannot use AWS CLI or virtual-hosted style URLs because bucket names contain : in the path-style format, which boto3 rejects.

What the other tenant can and cannot do

Operation Can they do it? Why
List objects Yes s3:ListBucket granted
Read (GET) objects Yes s3:GetObject granted
Upload new objects No s3:PutObject not granted
Delete objects No s3:DeleteObject not granted
Modify object ACLs No s3:PutObjectAcl not granted
Bypass Object Lock GOVERNANCE No s3:BypassGovernanceRetention not granted

Sharing with Write Access

If you need to allow uploads, add s3:PutObject and optionally s3:PutObjectAcl:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowCrossTenantWrite",
      "Effect": "Allow",
      "Principal": {"AWS": "*"},
      "Action": [
        "s3:GetObject",
        "s3:ListBucket",
        "s3:PutObject",
        "s3:PutObjectAcl"
      ],
      "Resource": [
        "arn:aws:s3:::my-shared-bucket",
        "arn:aws:s3:::my-shared-bucket/*"
      ]
    }
  ]
}

Caution: The other tenant can now upload files to your bucket. They can also overwrite existing objects (creating new versions if versioning is enabled).

Sharing with Differentiated Permissions (Read-Only vs Read+Write)

You can grant different permissions to different tenants on the same bucket.

Example: One tenant reads, another tenant reads and writes

Scenario: - Tenant A (owner): bucket shared-data-project-a - Tenant B: needs read-only access - Tenant C: needs read+write access

Create cross-tenant-differentiated-policy.json:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowTenantBReadOnly",
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::<tenant-b-id>:root"
      },
      "Action": [
        "s3:GetObject",
        "s3:ListBucket"
      ],
      "Resource": [
        "arn:aws:s3:::shared-data-project-a",
        "arn:aws:s3:::shared-data-project-a/*"
      ]
    },
    {
      "Sid": "AllowTenantCReadWrite",
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::<tenant-c-id>:root"
      },
      "Action": [
        "s3:GetObject",
        "s3:ListBucket",
        "s3:PutObject"
      ],
      "Resource": [
        "arn:aws:s3:::shared-data-project-a",
        "arn:aws:s3:::shared-data-project-a/*"
      ]
    }
  ]
}

Test results verified on cluster:

Operation Tenant B (read-only) Tenant C (read+write)
List objects ✅ Allowed ✅ Allowed
Read (GET) ✅ Allowed ✅ Allowed
Upload (PUT) ❌ 403 AccessDenied ✅ Allowed
Delete ❌ Not tested ❌ Not tested

How it works:

The arn:aws:iam::<tenant-id>:root format creates an Account Principal that matches the entire tenant. When a user from Tenant B authenticates, Keystone maps them to tenant=<tenant-b-id>. The bucket policy then checks:

if (p.is_account()) {
    return p.get_account() == tenant;
}

This allows you to target specific tenants for differentiated access control.

Access from other tenants:

Tenant B uses s3cmd (path-style):

# List
s3cmd -c ~/.s3cfg_tenantb ls s3://<owner-tenant-id>:shared-data-project-a/

# Download
s3cmd -c ~/.s3cfg_tenantb get \
  s3://<owner-tenant-id>:shared-data-project-a/report.pdf \
  /tmp/report.pdf

# Upload (will FAIL for Tenant B, but succeed for Tenant C)
s3cmd -c ~/.s3cfg_tenantb put /tmp/file.txt \
  s3://<owner-tenant-id>:shared-data-project-a/file.txt
# ERROR: S3 error: 403 (AccessDenied)

Tenant C can perform all operations listed in the policy.

Important Considerations

1. CanonicalUser principals are not supported

Ceph RGW multi-tenant rejects Principal: {"CanonicalUser": "..."} with InvalidArgument. Only AWS principals work.

2. Tenant ARNs work, user ARNs do not match cross-tenant

  • ✅ arn:aws:iam::<tenant-id>:root — Works. Matches the entire tenant (Account Principal). All users in that tenant inherit the permissions.
  • ❌ arn:aws:iam::<tenant-id>:user/<username> — Does not work. The user ID portion is not populated in Keystone multi-tenant mode, so it never matches.
  • ✅ Bare tenant ID string (e.g., "40d2486d0bb44ecebc419ecafc3fd29a") — Works as a shorthand Account Principal.

Recommendation: Always use arn:aws:iam::<tenant-id>:root for cross-tenant policies.

3. {"AWS": "*"} permits anonymous requests

Principal: {"AWS": "*"} allows any authenticated or anonymous request. If you want to restrict access to specific tenants, use the explicit tenant ARN format instead:

"Principal": {
  "AWS": "arn:aws:iam::40d2486d0bb44ecebc419ecafc3fd29a:root"
}

This only allows users from that specific tenant. However, if you need to temporarily allow all authenticated users (e.g., for testing), {"AWS": "*"} is an option. Be aware that in the tested Ceph release, this also permits anonymous requests. Enforce anonymous blocking at your ingress layer if needed.

4. AWS CLI and boto3 cannot access cross-tenant buckets

Because the bucket name contains : when accessed path-style, boto3's client-side validation rejects it. Only tools that do not validate bucket names (like s3cmd, curl with manual signatures, or custom scripts) work.

5. Bucket policies cannot restrict users within the same tenant

In Ceph Object Storage with Keystone authentication, all users within the same tenant (project) share the same implicit ownership of the tenant's buckets. This means bucket policies cannot be used to restrict access between users who belong to the same tenant.

Note: This limitation is expected to be resolved in a future Ceph release. A merged contribution (PR #67752) introduces support for Keystone condition keys in bucket policies:

  • keystone:role — exposes Keystone role names for role-based access control
  • keystone:userid — exposes the Keystone user UUID for user-specific policies

When available, this will allow policies such as:

"Condition": {
  "StringEquals": {
    "keystone:userid": "63b2536d1563467f9a621cd78d95f4a0"
  }
}
Currently, this feature is not yet included in any stable release (not in v20.2.1 or v20.3.0).

However, bucket policies CAN differentiate between different tenants. You can give Tenant A read-only access while giving Tenant B read+write access on the same bucket. See the Sharing with Differentiated Permissions section above.

What this means in practice:

Scenario Does it work? Explanation
Allow user3, deny user4 in the same tenant No All users in the same tenant share the same identity for bucket policy matching
Deny a specific user in the same tenant No Bucket policies are not evaluated at the individual user level within a tenant

Solution:

To restrict access between different users, place them in different tenants (projects) within Keystone. Then use bucket policies to control cross-tenant access:

  • Tenant A: Production users who own the bucket
  • Tenant B: Development users who need limited access

Bucket policies can then grant or deny access between these tenants. Without a cross-tenant bucket policy, users in Tenant B have zero access to Tenant A's buckets.

Ceph documentation reference:

The official Ceph multi-tenancy documentation describes how buckets and users are segregated under tenants. Cross-tenant access requires path-style URLs (tenant:bucket format) and bucket policies. See Ceph RGW Multi-tenancy for details.

Object ACLs vs Bucket Policies

Feature Bucket policy Object ACL
Scope Bucket-level Per-object
Cross-tenant Yes No (only same tenant)
Grant anonymous access Yes Yes (but limited)
Fine-grained actions Yes (many S3 actions) Limited (read/write/full)
Use case Sharing entire buckets Quick per-object sharing within tenant

Object ACL Quick Reference

Apply a read ACL to a single object:

aws s3api put-object-acl \
  --bucket my-bucket \
  --key report.pdf \
  --acl public-read

This makes the object readable by anyone with the URL. Valid ACLs:

  • private (default)
  • public-read
  • public-read-write
  • authenticated-read

Note: public-read works for anonymous access via path-style URLs. Virtual-hosted style requires DNS and is tenant-scoped.

Revoking Access

To remove a bucket policy entirely:

aws s3api delete-bucket-policy --bucket my-shared-bucket

To modify permissions, update the policy JSON and re-apply:

aws s3api put-bucket-policy \
  --bucket my-shared-bucket \
  --policy file://updated-policy.json

Common Use Cases

  • Data sharing between projects: Tenant A generates reports, Tenant B reads them.
  • Backup across tenants: Tenant A writes backups to Tenant B's bucket.
  • Public datasets: Make a bucket readable by all authenticated users (or anonymous, with caution).
  • Multi-tenant applications: Each tenant has its own buckets, but shares specific folders read-only with partners.

Comparison with Legacy ACLs

Feature Ceph Object Storage Bucket Policies Legacy Swift ACLs
Cross-tenant sharing Yes (via bucket policy) Limited
Fine-grained actions Yes (S3 actions) No (read/write only)
Anonymous access Yes Yes (via public URLs)
Object-level ACLs Yes Yes
Multi-tenant scope Project-level Project-level