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.

Least-privilege grant map for the mariabackup SST account A single SST account, sstuser at localhost, holds exactly five global privileges, each mapped to a mariabackup operation: RELOAD for FLUSH and backup-lock coordination, PROCESS to read running threads, LOCK TABLES for the brief backup-stage locks, BINLOG MONITOR to read the binary-log position on MariaDB 10.5 and later, and REPLICATION CLIENT for the same on MariaDB 10.4 and earlier. A callout notes that SUPER, ALL PRIVILEGES, and SELECT on application data are deliberately not granted, so the account cannot read table contents or bypass read_only. SST account sstuser@localhost least privilege no data SELECT RELOAD FLUSH & backup-lock coordination PROCESS read running threads / engine status LOCK TABLES brief backup-stage locks BINLOG MONITOR read binlog position (MariaDB 10.5+) REPLICATION CLIENT same, MariaDB 10.4 and earlier Deliberately NOT granted → SUPER · ALL PRIVILEGES · SELECT on application data The account cannot read table contents through SQL or bypass read_only.
Each privilege maps to one backup operation; nothing in the set lets the account read application data or act as an administrator.

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 MONITOR vs REPLICATION CLIENT breaks 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 localhost account still needs the socket, not TCP. If your SST invocation connects over 127.0.0.1, MariaDB treats it as a TCP login and the 'sstuser'@'localhost' grant does not match, producing Access denied despite a correct password. Ensure mariabackup uses 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.