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 controlkeystone:userid— exposes the Keystone user UUID for user-specific policiesWhen available, this will allow policies such as:
Currently, this feature is not yet included in any stable release (not in v20.2.1 or v20.3.0)."Condition": { "StringEquals": { "keystone:userid": "63b2536d1563467f9a621cd78d95f4a0" } }
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-readpublic-read-writeauthenticated-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.
Related Documentation
- Object Lock — How WORM lock interact with cross-tenant bucket policies.
- Ceph Object Storage Overview
- Quick Start Guide
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 |