Proxmox VEVIII · CephCeph security
Ceph authentication: cephx, keyrings and capabilities
What you'll learn
- Explain what cephx authenticates, and what it deliberately does not protect
- Read a capability string and predict what a client holding it can do
- Create a least-privilege client for one pool and verify the restriction actually holds
- Recover from a capability edit that locked a client out
Prerequisites
Verified against Proxmox VE 9.2.4 · Proxmox Backup Server 4.2.5 · Ceph Squid / Tentacle · Debian 13 (Trixie) · Linux kernel 7.0 (PVE 9.2 default) · 2026-08-12
pveceph init turns cephx on. It has been on in every Proxmox cluster you
have ever built, and unless you went looking, every one of those clusters
authenticates every client as client.admin — a user whose capability
string is allow * on the monitors, the OSDs and the managers.
That is not a misconfiguration. It is the default, and for a hyper-converged cluster where the only clients are the Proxmox nodes themselves it is a defensible one. It stops being defensible the moment something that is not a Proxmox node needs a pool: a Kubernetes CSI driver, a second cluster, a backup appliance, a colleague who wants to mount CephFS from a workstation. At that point the question “what exactly can this key do” has an answer, and the answer is usually “everything”.
This lesson makes that question answerable.
What cephx is, and the thing it is not
cephx is mutual authentication using shared secrets. Every entity —
each daemon and each client — has a name (client.admin, osd.4,
mgr.pve1) and a secret key. The monitors hold a copy of every key. A
client proves it knows its key, receives a time-limited ticket, and
presents that ticket to the OSDs it wants to talk to.
Three settings control whether any of this is required, and all three
default to cephx:
| Setting | Default | What it requires |
|---|---|---|
auth_cluster_required | cephx | Daemons authenticate to each other |
auth_service_required | cephx | Clients authenticate to the cluster |
auth_client_required | cephx | The cluster authenticates to the client |
The alternative value is none, and the third row is the one people
forget: cephx is mutual, so a client can also detect that it is talking
to an impostor cluster.
Reading a capability string
A cephx user is a name plus a set of capabilities, one per daemon type.
The grammar is small enough to learn in one sitting, and learning it is
what turns allow rwx pool=vmdata from noise into a sentence.
| Token | Grants |
|---|---|
r | Read |
w | Write to objects |
x | Execute class methods, and use auth operations |
* (or all) | Read, write, execute, plus administrative commands |
class-read | Read-only class methods (a subset of x) |
class-write | Write-only class methods (a subset of x) |
Two restrictions narrow the scope: pool=<name> limits a capability to
one pool, and namespace=<name> limits it further to one namespace
inside a pool. Written as shell, that is pool=vmdata — a literal, not
a placeholder.
On top of the letters, Ceph ships profiles: named bundles that
express an intent rather than a permission set. profile rbd is the
right capability for an RBD client and is far harder to get subtly
wrong than assembling class-read object_prefix rbd_children, rwx by
hand. profile rbd-read-only is its read-only counterpart, and
profile rbd-mirror is what the mirroring daemon needs.
ceph auth ls$ ceph auth get client.admin[client.admin]
key = REDACTED
caps mds = "allow *"
caps mgr = "allow *"
caps mon = "allow *"
caps osd = "allow *"Illustrative output
Read that as: this key can create and destroy pools, read and write every object in all of them, remove OSDs from the CRUSH map, and edit the authentication database — including its own capabilities. There is no Ceph operation it cannot perform.
The least-privilege client, end to end
Suppose an application team wants RBD images in a pool of their own, from hosts that are not Proxmox nodes. The whole exchange is four commands.
POOL=app-images
CLIENT=app-team
pveceph pool create "$POOL" --size 3 --min_size 2 --application rbd
ceph auth get-or-create "client.$CLIENT" \
mon "profile rbd" \
osd "profile rbd pool=$POOL" \
mgr "profile rbd pool=$POOL"$ ceph auth get-or-create client.app-team mon 'profile rbd' osd 'profile rbd pool=app-images'[client.app-team]
key = REDACTEDIllustrative output
Then hand it over — and hand over only the key, not ceph auth ls:
CLIENT=app-team
ceph auth get "client.$CLIENT" -o "/root/ceph.client.$CLIENT.keyring"
# just the secret, for tooling that wants the bare string
ceph auth print-key "client.$CLIENT"Verification that can fail
This is the part people skip, and it is the only part that proves anything. A capability string that looks restrictive and is not is worse than no restriction, because it is documented as a control.
POOL=app-images
OTHER=vmdata
CLIENT=app-team
KEYRING="/root/ceph.client.$CLIENT.keyring"
# must succeed
rbd --id "$CLIENT" --keyring "$KEYRING" ls "$POOL"
# must fail: operation not permitted
rbd --id "$CLIENT" --keyring "$KEYRING" ls "$OTHER"
# must fail: no administrative capability
ceph --id "$CLIENT" --keyring "$KEYRING" osd pool ls$ rbd --id app-team --keyring /root/ceph.client.app-team.keyring ls vmdatarbd: error opening pool 'vmdata': (1) Operation not permittedIllustrative output
Note the shape of the error. Ceph reports a scope violation as
(1) Operation not permitted, not as “pool not found” — so a
capability that is too narrow and a pool name that is misspelled produce
different errors. That distinction saves ten minutes every time.
Where Proxmox keeps the keys
pveceph init writes the cluster configuration to /etc/pve/ceph.conf
and, in the documentation’s words, “also creates a symbolic link at
/etc/ceph/ceph.conf, which points to that file”. The consequence is
worth stating: the Ceph configuration is in pmxcfs, so it is
cluster-replicated, and it stops being writable when the cluster loses
quorum.
The keyrings follow the same pattern. On a pveceph-managed cluster the
admin keyring lives under /etc/pve/priv/, and each storage entry
that talks to a Ceph cluster Proxmox does not manage gets its own
keyring at /etc/pve/priv/ceph/<STORAGE_ID>.keyring. That file name is
the storage ID from /etc/pve/storage.cfg, not the Ceph user name — a
detail that makes external-cluster storage fail in a confusing way when
somebody renames the storage.
ls -l /etc/ceph/
ls -l /etc/pve/priv/ 2>/dev/null | grep -i ceph
ls -l /etc/pve/priv/ceph/ 2>/dev/nullTurning cephx off is not a troubleshooting step
pveceph init accepts --disable_cephx, and it exists for closed lab
networks. It is the wrong response to an authentication problem, for
three reasons worth being specific about.
It is not a per-client switch. Authentication is a cluster property. Turning it off removes authentication for every client, every daemon and every pool at once.
Everything on the storage network becomes trusted. Without cephx, any host that can route to the monitors can read and write every pool. There is no second control behind it — Ceph has no pool-level ACL that survives the loss of authentication.
It is not a clean toggle on a running cluster. Flipping the auth_*
settings needs a restart of every daemon, and the window in between has
daemons on both sides of the change failing to talk to each other. A
cluster that was struggling is now down.
If the authentication is genuinely broken, the fix is in the keyring or the clock. Both are in the diagnostic order above.
Common mistakes
- Handing out
client.adminbecause it is what is already in/etc/pve/priv. It is the easiest key to copy and the worst one to give away. Five seconds ofceph auth get-or-createproduces a scoped one. - Assuming cephx encrypts. It authenticates. Encryption is msgr2
securemode, and the default negotiation prefers the unencryptedcrcmode. - Using
ceph auth capsto add a capability. It replaces the whole set. Read the current caps first, edit, then apply all of them. - Building the capability by hand when a profile exists.
profile rbdis maintained upstream and changes when RBD needs new object prefixes. A hand-built equivalent silently stops working after an upgrade. - Scoping the
osdcapability to a pool and leavingmonatallow *. The data is protected and the cluster is not. Both lines need scoping. - Never testing the restriction. A capability is a claim until a denied command proves it.
- Leaving a delivered keyring in
/etc/pve/priv. It replicates to every node and lands in every configuration backup.
Key takeaways
- cephx is on by default in Proxmox and authenticates every client;
the default client is
client.adminwithallow *everywhere. - cephx does not encrypt. msgr2
securemode does, and the defaultcrc secureordering means a stock cluster is unencrypted on the wire. - Capabilities are per daemon type. Use
profile rbdand scope it withpool=; scope themoncapability too, not justosd. ceph auth capsoverwrites. Back up the existing caps before you run it, and keep a break-glass admin off the cluster.- Proxmox keeps per-storage keyrings at
/etc/pve/priv/ceph/<STORAGE_ID>.keyring, named after the storage ID. - Prove a restriction with a command that must be denied. Untested least privilege is a comment, not a control.
Knowledge check
Knowledge check · 4 questions
Q1. A security review asks whether Ceph replication traffic between OSDs is protected from an attacker with a span port on the storage switch. The cluster is a default pveceph install with cephx enabled. What is the accurate answer?
Q2. You run: ceph auth caps client.backup osd "profile rbd pool=backups". The user previously had both a mon and an osd capability and worked correctly. It now cannot connect at all. Why?
Q3. You have created client.app-team with mon "profile rbd" and osd "profile rbd pool=app-images". Which checks would actually demonstrate that the restriction holds? Select all that apply.
Q4. When authentication starts failing for clients on one node while the rest of the cluster is healthy, the keyring is the first thing to check.
Passing score: 75%. Answers are checked in this browser.