design/ceph/object/rgw-user-accounts.md
The Ceph Object Gateway added support for user accounts in Squid (v19.0.0) release. This as an optional feature to enable the self-service management of Users, Groups and Roles, similar to those in AWS Identity and Access Management (IAM), which helps enhancing Ceph Multitenancy.
Now instead of each S3 user operating independently, an account groups users and roles under shared ownership. Resources like buckets and objects are owned by the account, not individual users. This brings major advantages:
apiVersion: ceph.rook.io/v1
kind: CephObjectStoreAccount
metadata:
# resource name will be used as unique account name while creating the IAM account
name: my-account
namespace: rook-ceph
spec:
# [Required] The name of the object store to create the account in
store: my-store
# [Optional]: The desired name of the account if different from the CephObjectStoreAccount CR name.
name: my-account
# [Optional] Uniquely identifies an account and resource ownership. Format should be RGW followed by 17 digits (e.g.,
# RGW00889737169837717). If not specified, then ceph will auto generate the account ID.
accountID: RGW33567154695143645
# [Optional] Email address associated with the account
email: [email protected]
# [Optional] Root user for the account. The root user is created by default and has default
# permissions across all account resources. It can manage IAM users, roles, and policies.
rootUser:
# [Optional] If set to true, the root user will not be created for this account. This can be
# useful if the user wants to manually manage the root user outside of Rook. Default: false.
skipCreate: false
# [Optional] Display name for the root user
displayName: "Root User for Rook Account <namespace>/<name>"
status:
phase: Ready/Failure
# accountID of the IAM account. Adding account id to the status will help to get a quick reference to the account in case the user does not provide the account ID in the spec.
accountID: RGW33567154695143645
# Reference to the Kubernetes secret containing the root user's access credentials
rootAccountSecretName: rook-ceph-object-user-my-store-my-account
spec.name is provided, then it will be used to create the account. Otherwise, metadata.name will be used.spec.accountID is provided, then it will be used to create the account.spec.email is provided, it will be included in the account creation.spec.rootUser.skipCreate is set to true.metadata.uid (Kubernetes-generated UUID) of the CephObjectStoreAccount CR. This ensures global uniqueness across multi-cluster and multisite environments, avoiding conflicts when the same namespace/name combination exists on different clusters.The controller will use the RGW admin ops API to create the account and root user. This ensures a single implementation that works for both internal and external RGW deployments.
spec.rootUser.skipCreate is not true, create the root user via the admin ops API with the UID (metadata.uid), display name, account ID, and the account root flag. Access key and secret key will be auto-generated.If the root user is created, its access credentials (access key and secret key) will be stored in a Kubernetes secret, similar to how CephObjectStoreUser credentials are managed.
The controller will reconcile any changes made to the CephObjectStoreAccount resource and apply them to the underlying RGW account.
spec.accountID) is immutable and cannot be updated after account creation.Users can update the following attributes of the account in the spec:
When a CephObjectStoreAccount resource is updated, the controller will:
Account Metadata Updates: Update account name or email if modified (subject to RGW capabilities)
Root User Updates: Update root user display name if modified via the admin ops API.
Validation: The controller will validate updates and report errors in the status if:
CephObjectStoreAccount resource will trigger the operator reconcile to delete the account and its root user.spec.rootUser.skipCreate is not true), the controller will first delete the root user via the admin ops API.An account in Ceph Object Gateway is managed by a designated "account root user." This administrator-created entity serves as the primary manager for all resources within that account, including users, groups, and roles.
Root account users have default permissions across all account resources. Their access credentials (keys) enable management through the IAM API to create additional IAM users and roles for use with the Ceph Object Gateway S3 API, as well as to manage their associated access keys and policies.
The root user has a 1:1 relationship with the account and is created automatically by default as part of the CephObjectStoreAccount CR lifecycle. Users can opt out of automatic root user creation by setting spec.rootUser.skipCreate to true.
The root user is defined within the CephObjectStoreAccount spec:
spec:
rootUser:
# [Optional] If set to true, the root user will not be created for this account. Default: false.
skipCreate: false
# [Optional] Display name for the root user
displayName: "Root User for Rook Account <namespace>/<name>"
Root account users have the following privileges:
apiVersion: ceph.rook.io/v1
kind: CephObjectStoreAccount
metadata:
name: my-account
namespace: rook-ceph
spec:
store: my-store
accountID: RGW33567154695143645
email: [email protected]
rootUser:
displayName: "Root User for Rook Account rook-ceph/my-account"
This single CR will:
status.rootAccountSecretNameIn addition to the root user (managed by the CephObjectStoreAccount CR), accounts can have regular users associated with them. These account users differ from standalone RGW users in following ways:
[\w+=,.@-]+ for IAM compatibility.The existing CephObjectStoreUser CR is extended with an optional accountRef field to associate a user with an account.
The CephObjectStoreUser spec gains a new accountRef field:
apiVersion: ceph.rook.io/v1
kind: CephObjectStoreUser
metadata:
name: my-user
namespace: rook-ceph
spec:
# [Required] The name of the object store to create the user in
store: my-store
# [Required] Display name for the user. Must match [\w+=,.@-]+ when accountRef is set.
displayName: "my-user"
# [Optional] Reference to the CephObjectStoreAccount to associate this user with.
# The account must be in the same namespace as the user.
accountRef:
# [Required] Name of the CephObjectStoreAccount CR
name: my-account
The accountRef is a reference to a CephObjectStoreAccount CR, not a raw account ID. The referenced account must be in the same namespace as the user. Users deploy the account and user CRs simultaneously without needing to wait for the account to be provisioned first.
When a CephObjectStoreUser with accountRef is created, the user controller will:
Resolve the account reference: Look up the CephObjectStoreAccount CR by the name specified in accountRef, in the same namespace as the CephObjectStoreUser CR.
Validate the object store: Ensure the store field on the user matches the store field on the referenced account. If they differ, the controller sets the user status to Failed with an appropriate error message.
Wait for the account to be ready: If the referenced CephObjectStoreAccount does not exist or is not in Ready phase, the controller will requeue the reconciliation rather than failing. This supports workflows where the account and user CRs are deployed simultaneously.
Validate the display name: When accountRef is set, the controller validates that displayName matches the pattern [\w+=,.@-]+. This is required because account users are referenced by their display name in IAM policy ARNs (e.g., arn:aws:iam::RGW33567154695143645:user/my-user), and names with spaces or unsupported characters will break IAM policy resolution. If the display name is invalid, the controller sets the user status to Failed with an appropriate error message. This validation is performed in the controller code rather than at the CRD level, because adding a CRD-level pattern constraint on displayName would break existing standalone CephObjectStoreUser CRs that have display names with spaces or special characters.
Create the user with the account ID: Once the account is ready, the controller extracts the accountID from the account's status.accountID and passes it to the RGW admin ops API when creating or modifying the user.
accountRef is immutable once set. Moving a user between accounts changes resource ownership in Ceph and is a destructive operation that must be done manually.accountID and store:
// +kubebuilder:validation:XValidation:message="accountRef is immutable",rule="self == oldSelf"
accountRef before it reaches the controller. This includes changing the name, adding accountRef to an existing user, or removing it.When a CephObjectStoreUser with accountRef is deleted:
# 1. Create the account
apiVersion: ceph.rook.io/v1
kind: CephObjectStoreAccount
metadata:
name: my-account
namespace: rook-ceph
spec:
store: my-store
rootUser:
displayName: "Root User"
---
# 2. Create a user associated with the account
apiVersion: ceph.rook.io/v1
kind: CephObjectStoreUser
metadata:
name: my-user
namespace: rook-ceph
spec:
store: my-store
displayName: "my-user"
accountRef:
name: my-account
Both CRs can be applied simultaneously. The user controller will wait for the account to become ready before creating the user in RGW.
For external RGW clusters, the accountRef approach works as long as the account is managed via a CephObjectStoreAccount CR — the account controller uses the RGW admin ops API (HTTP), which works for both internal and external RGW deployments.
Associating users with pre-existing accounts that were created outside of Rook (e.g., directly via radosgw-admin) is not supported in this iteration. Future work may add external binding support to CephObjectStoreAccount, allowing a CR to adopt an existing account by its account ID. Once bound, the accountRef mechanism works the same way — the user controller always resolves the account ID from the referenced CR's status.
accountRef to be added to existing standalone CephObjectStoreUser CRs as a day-2 operation. The CEL immutability rule would need to be relaxed from self == oldSelf to !has(oldSelf) || self == oldSelf to permit the transition from unset to set while still preventing changes or removal once set. Additionally, the current CephObjectStoreUser CRD has no validation on displayName — standalone users can have display names with spaces or special characters. However, account users are referenced by display name in IAM policy ARNs and must match [\w+=,.@-]+. Since adding a CRD-level pattern constraint would break existing standalone users, the display name validation must be enforced in the controller only when accountRef is set. Users migrating into an account would need to update their displayName to be IAM-compatible before or at the same time as setting accountRef.CephObjectStoreUser must be in the same namespace as the referenced CephObjectStoreAccount. In the future, cross-namespace references could be supported by adding a namespace field to accountRef along with an opt-in mechanism on the CephObjectStoreAccount (e.g., allowUsersFromNamespaces list) to let the account owner control which namespaces can create users for their account. This prevents unauthorized cross-namespace association where any user could claim to belong to an account they don't own.CephObjectStoreAccount to adopt pre-existing accounts in external RGW clusters by referencing a raw account ID.