Securing mariabackup SST Credentials & Privileges
This guide narrows mariabackup Streaming SST & Backups to one focused question: how do you give a mariabackup State Snapshot Transfer exactly the access it needs and nothing more? The SST account named in wsrep_sst_auth is a standing credential that lives in a configuration file and is used by a donor to read its entire data directory — a tempting target and an easy over-grant. Administrators reflexively hand it broad rights, store the password in a file checked into version control, and leave the transfer unencrypted. This page fixes all three: the precise least-privilege grant set mariabackup actually requires, how to keep the secret out of VCS and off disk in clear text, and how to encrypt both the donor login and the SST stream on the wire.
Context: Why the SST Credential Is a Real Attack Surface
The wsrep_sst_auth account is unlike an application user. It exists solely so a donor’s mariabackup process can authenticate to the local server, take a hot physical backup, and stream it to a joiner. That means the credential sits in plaintext in a config file on every node, is invoked automatically without a human present, and — if over-privileged — grants far more than the read access a backup needs. A wsrep_sst_auth set to a SUPER-privileged or ALL PRIVILEGES account turns a file any operator can read into full control of the database, and because the same string must be present on every node, one leaked config file compromises the whole cluster.
The correct posture is least privilege plus secret hygiene. mariabackup needs a small, specific set of global privileges to coordinate a consistent backup — nothing that lets it read table contents through SQL, and nothing administrative. Combined with keeping the secret in a manager rather than VCS and encrypting the transfer, this shrinks the SST credential from a group-wide liability to a scoped, auditable one.
Solution: Grant Least Privilege, Then Lock Down the Secret
Create the SST account with exactly the privileges mariabackup uses to coordinate a consistent hot backup — and no more. Run this once on any node; the grant replicates to the rest:
CREATE USER 'sstuser'@'localhost' IDENTIFIED BY 'REPLACE_AT_DEPLOY_FROM_VAULT';
GRANT RELOAD, PROCESS, LOCK TABLES, BINLOG MONITOR
ON *.* TO 'sstuser'@'localhost';
-- MariaDB 10.4 and earlier: REPLICATION CLIENT instead of BINLOG MONITOR
FLUSH PRIVILEGES;
Each privilege earns its place: RELOAD lets mariabackup issue the FLUSH and backup-lock statements that fix a consistent point; PROCESS lets it inspect running threads and engine status; LOCK TABLES covers the brief backup-stage locks on non-transactional metadata; and BINLOG MONITOR (called REPLICATION CLIENT before MariaDB 10.5) lets it record the binary-log position that a later point-in-time recovery replays from. Notably absent is any SELECT on application schemas — mariabackup reads InnoDB pages from disk, not table rows through SQL, so the account never needs to see your data.
Bind the account to localhost only. Because the donor runs mariabackup against its own server, the account should authenticate over the local Unix socket, not TCP. A 'sstuser'@'localhost' grant means a stolen credential is useless from any other host; a 'sstuser'@'%' grant turns the same leaked config into a remote login. Prefer the socket path in the SST invocation so authentication never crosses the network at all.
Keep the secret out of version control
wsrep_sst_auth must not live in a config file that is committed to a repository. Template the value at deploy time from a secrets manager and render it into a root-owned drop-in that your VCS ignores:
[mysqld]
# Rendered at deploy time from the secrets manager — this file is chmod 600,
# root-owned, and excluded from version control.
wsrep_sst_auth = sstuser:{{ vault_sst_password }}
# Lock the rendered file down so only the server user can read the secret
sudo chown root:mysql /etc/mysql/wsrep-sst-auth.cnf
sudo chmod 640 /etc/mysql/wsrep-sst-auth.cnf
# Ensure it is never committed
echo "wsrep-sst-auth.cnf" >> .gitignore
Rendering the secret follows the same idempotent flow used for the rest of the configuration in Automating Node Provisioning with Ansible; the discipline is that the plaintext exists only on the node, only in a mode-restricted file, and only for as long as the node runs.
Encrypt the SST stream on the wire
Least privilege protects the login; TLS protects the payload. Without it, a mariabackup SST streams your entire dataset across the network in clear text. Enable socat TLS with encrypt=4 and named certificates:
[sst]
encrypt = 4
tca = /etc/mysql/ssl/ca.pem
tcert = /etc/mysql/ssl/server-cert.pem
tkey = /etc/mysql/ssl/server-key.pem
Reuse the same certificate authority you deploy for group communication in Setting Up Secure TLS for Galera Cluster Communication, so certificate rotation covers replication and SST together rather than as two separate lifecycles.
Socket authentication versus TCP
The donor always runs mariabackup against its own local server, so there is no operational reason for the SST login to traverse the network — and every reason to prevent it from being able to. A 'sstuser'@'localhost' account authenticates only over the local Unix socket; the same username presented from another host, or even from 127.0.0.1 (which MariaDB classifies as a TCP connection, not localhost), does not match the grant and is rejected. This is a deliberate defence: even if the plaintext wsrep_sst_auth string leaks from a config file, it is unusable from anywhere but a shell already on the node. Keep the invocation on the socket path and resist the temptation to widen the host to % when a connection is refused — that single change converts a locally-scoped credential into a remotely-exploitable one across every node in the Galera cluster.
For deployments that must expose the account over TCP — for instance a donor whose mariabackup connects through a proxy — pin the grant to the specific replication subnet ('sstuser'@'10.0.1.%') rather than a wildcard, and require TLS on that login with REQUIRE SSL so the credential is never presented over a clear-text connection. The narrower the host pattern, the smaller the blast radius of a leaked secret.
Audit the account like any other privileged identity
Because the SST account is created once and then runs unattended, it is easy to forget — and forgotten accounts drift. Fold it into the same review that covers your human administrators: periodically assert that SHOW GRANTS returns exactly the intended four privileges and no more, that the host scope is still localhost, and that the password in the database matches the rendered wsrep_sst_auth. A drift-detection job that diffs live grants against the declared least-privilege set catches an accidental GRANT ALL before it becomes the path an attacker uses, the same way configuration drift detection catches an unexpected wsrep_provider_options change in the wsrep.cnf Configuration Deep Dive.
Parameter & Privilege Reference
| Item | Where | Value | Why |
|---|---|---|---|
RELOAD |
grant | required | FLUSH and backup-lock coordination |
PROCESS |
grant | required | Inspect running threads and engine status |
LOCK TABLES |
grant | required | Brief backup-stage locks on metadata |
BINLOG MONITOR |
grant (10.5+) | required | Read binary-log position for consistency |
REPLICATION CLIENT |
grant (≤10.4) | required | Pre-10.5 name for the binlog-position privilege |
| host scope | account | localhost |
Restrict to local socket; a leaked secret is useless remotely |
wsrep_sst_auth |
[mysqld] |
user:secret |
Rendered from a secrets manager; file mode 640, root-owned |
encrypt |
[sst] |
4 |
socat TLS so the stream is encrypted in transit |
Verification
Confirm the account has exactly the intended privileges and nothing broader:
SHOW GRANTS FOR 'sstuser'@'localhost';
-- Expect only: GRANT RELOAD, PROCESS, LOCK TABLES, BINLOG MONITOR ON *.* ...
-- Any SUPER, ALL PRIVILEGES, or schema-level SELECT is an over-grant to remove
Prove the account can actually drive a backup with just those rights — if the grant set were insufficient this fails with Access denied:
mariabackup --backup --user=sstuser --password="$SST_PASS" \
--stream=xbstream --target-dir=/tmp/sst-check > /dev/null
# "completed OK!" on stderr means the least-privilege set is sufficient
Finally, confirm the rendered secret file is not world-readable and is git-ignored:
stat -c '%a %U:%G' /etc/mysql/wsrep-sst-auth.cnf # expect 640 root:mysql
git check-ignore -v /etc/mysql/wsrep-sst-auth.cnf # must print a matching rule
Edge Cases & Gotchas
BINLOG MONITORvsREPLICATION CLIENTbreaks across versions. On MariaDB 10.5+ the privilege was renamed, and a grant script written for 10.4 fails on 11.x with an unknown-privilege error, while a 10.5 script fails on 10.4. During a rolling upgrade, grant the name matching each node’s version rather than assuming one script works fleet-wide.- A
localhostaccount still needs the socket, not TCP. If your SST invocation connects over127.0.0.1, MariaDB treats it as a TCP login and the'sstuser'@'localhost'grant does not match, producingAccess denieddespite a correct password. Ensuremariabackupuses the Unix socket, or add the matching host grant deliberately — do not widen it to%to work around this. - Rotating the SST password is a two-place change. The password lives in both the database account and
wsrep_sst_auth; updating one without the other silently breaks the next SST. Rotate them together in one automated step, and force a controlled rejoin afterward to prove the new credential works before you actually need it in an incident.
Related
- mariabackup Streaming SST & Backups — the parent guide to the non-blocking streaming transfer
- mariabackup vs rsync SST: Choosing a State-Transfer Method — why rsync needs no SST account and mariabackup does
- Setting Up Secure TLS for Galera Cluster Communication — the certificate authority to reuse for the encrypted SST stream
- Backup & Restore Sync — the recovery model these credentials protect