All articles

Passbolt with MariaDB Galera Cluster using Mutual TLS (mTLS) authentication

13 min. read

Gareth

Gareth

20 November, 2025

Passbolt with MariaDB Galera Cluster using Mutual TLS (mTLS) authentication

  • Virtually synchronous replication
  • Active-active multi-primary topology
  • Read and write to any cluster node
  • Automatic membership control: failed nodes drop from the cluster
  • Automatic node joining
  • True parallel replication, on row level
  • Direct client connections, native MariaDB look & feel
  • No replica lag
  • No lost transactions
  • Read scalability
  • Smaller client latencies
  • No maintenance window flexibility: Database updates require downtime
  • Single point of failure: Hardware failure takes the entire service offline
  • Replication lag concerns: Async replication means the DR site is always behind
  • Network security complexity: Firewall rules get unwieldy when nodes span multiple locations
  • Certificates authenticate peers, not IP addresses: You can move nodes between subnets without rewriting firewall rules
  • Network topology becomes flexible: Nodes can be in different racks, buildings, or data centres
  • Security scales with infrastructure: Adding a node means issuing a certificate, not managing complex firewall rules
  • Distribute nodes across data centres for disaster recovery
  • Perform rolling maintenance without service interruption
  • Scale the database tier without network reconfiguration
  • Three MariaDB 11.8 nodes in a Galera cluster with TLS-encrypted replication
  • Mutual TLS authentication between passbolt and the database
  • Valkey (Redis-compatible) for session storage and caching
  • Production-ready configurations that work on physical servers
  • Creates a trust chain: all nodes trust certificates signed by this CA
  • Valid for 10 years (3650 days)
  • Self-signed (it signs itself)
  • Mounted on all Galera nodes as /etc/mysql/ssl/ca.pem to verify peer certificates
  • Mounted on passbolt as /etc/passbolt/db-ca.crt to verify database server certificates
  1. Galera replication (node-to-node mTLS)
  2. Client connections (passbolt-to-database TLS)
  • Common Name (CN): The .local alias (e.g., galera1.local)
  • Subject Alternative Names (SANs):
    • DNS.1 = galera1 (short hostname)
    • DNS.2 = galera1.local (DNS alias)
  • Extended Key Usage: serverAuth, clientAuth (can act as both server and client)
wsrep_provider_options="socket.ssl_cert=/etc/mysql/ssl/server-cert.pem;\
socket.ssl_key=/etc/mysql/ssl/server-key.pem;\
socket.ssl_ca=/etc/mysql/ssl/ca.pem"
  • Each node presents its own certificate when connecting to peers
  • Each node verifies peer certificates using the CA
  • This is mutual TLS: both sides authenticate each other
ssl-cert=/etc/mysql/ssl/server-cert.pem
ssl-key=/etc/mysql/ssl/server-key.pem
ssl-ca=/etc/mysql/ssl/ca.pem
  • MariaDB presents this certificate to clients (like passbolt)
  • Clients verify it using the CA certificate
  • Common Name (CN): passbolt-db-client.local (just an identifier, doesn't need to resolve to a hostname)
  • Subject Alternative Names: Same as CN
  • Extended Key Usage: serverAuth, clientAuth
DATASOURCES_DEFAULT_SSL_CA: /etc/passbolt/db-ca.crt
DATASOURCES_DEFAULT_SSL_CERT: /etc/passbolt/db-client.crt
DATASOURCES_DEFAULT_SSL_KEY: /etc/passbolt/db-client.key
  1. passbolt connects to MariaDB
  2. MariaDB presents its server certificate (e.g., galera1.crt)
  3. passbolt verifies it using the CA (db-ca.crt)
  4. passbolt presents its client certificate (db-client.crt)
  5. MariaDB verifies it using the CA and checks the user requires SSL:
    ALTER USER 'passbolt'@'%' REQUIRE SSL;
galera1                          galera2
   |                               |
   |---[presents galera1.crt]----->|
   |<--[presents galera2.crt]------|
   |                               |
   |---[verifies with CA]--------->|
   |<--[verifies with CA]----------|
  • Present their server certificates
  • Verify each other using the CA
  • Encrypt replication traffic
passbolt                         galera1
   |                                |
   |---[connects]------------------>|
   |<--[presents galera1.crt]-------|
   |---[verifies with CA]---------->|
   |---[presents db-client.crt]---->|
   |<--[verifies with CA]-----------|
  • MariaDB presents its server certificate
  • passbolt verifies it with the CA
  • passbolt presents its client certificate
  • MariaDB verifies it with the CA
  1. Single CA: One root CA signs everything, simplifying trust management
  2. Dual-purpose server certs: Each node's certificate works for both replication and client connections
  3. SANs: Certificates support multiple hostname formats (short name and DNS alias)
  4. Mutual authentication: Both replication and application connections use mTLS, not just encryption
  1. git clone https://github.com/gyaresu/gareth-galera-lab
    cd gareth-galera-lab
    cp env.example .env
    # Edit .env with your passwords and passbolt admin details
    source .env
  2. bash ./scripts/generate-certs.sh
  3. ./scripts/start-lab.sh --reset
    https://passbolt.local/setup/start/07bc558e-c697-43d5-b5ff-df670a46e684/d5762e80-09cf-40aa-a017-f40c4ba69559
# All nodes should show Primary status
docker compose exec galera1 mariadb -uroot -p"$MARIADB_ROOT_PASSWORD" \
  -e "SHOW STATUS WHERE Variable_name IN ('wsrep_cluster_status', 'wsrep_cluster_size', 'wsrep_incoming_addresses');"
Variable_nameValue
wsrep_incoming_addressesgalera1.local:0,galera2.local:0,galera3.local:0
wsrep_cluster_size3
wsrep_cluster_statusPrimary
# Write to node 1
docker compose exec galera1 mariadb \
  -u"$DATASOURCES_DEFAULT_USERNAME" -p"$DATASOURCES_DEFAULT_PASSWORD" \
  "$DATASOURCES_DEFAULT_DATABASE" \
  -e "CREATE TABLE IF NOT EXISTS test_replication (id INT PRIMARY KEY AUTO_INCREMENT, note VARCHAR(255)); \
      INSERT INTO test_replication (note) VALUES ('test from galera1');"

# Read from node 2
docker compose exec galera2 mariadb \
  -u"$DATASOURCES_DEFAULT_USERNAME" -p"$DATASOURCES_DEFAULT_PASSWORD" \
  "$DATASOURCES_DEFAULT_DATABASE" \
  -e "SELECT * FROM test_replication;"
idnote
1test from galera1
# Check TLS cipher on client connections
docker compose exec galera1 mariadb \
  -u"$DATASOURCES_DEFAULT_USERNAME" -p"$DATASOURCES_DEFAULT_PASSWORD" \
  -e "SHOW STATUS LIKE 'Ssl_cipher';"
Variable_nameValue
Ssl_cipherTLS_AES_256_GCM_SHA384
docker compose exec passbolt \
  su -s /bin/bash -c "source /etc/environment >/dev/null 2>&1 || true; /usr/share/php/passbolt/bin/cake passbolt healthcheck" www-data
# TLS for Galera replication traffic (port 4567)
wsrep_provider_options="socket.ssl_cert=/etc/mysql/ssl/server-cert.pem;\
socket.ssl_key=/etc/mysql/ssl/server-key.pem;\
socket.ssl_ca=/etc/mysql/ssl/ca.pem"
# TLS for client connections (port 3306)
ssl-ca=/etc/mysql/ssl/ca.pem
ssl-cert=/etc/mysql/ssl/server-cert.pem
ssl-key=/etc/mysql/ssl/server-key.pem
wsrep_cluster_address=gcomm://galera1.local,galera2.local,galera3.local
wsrep_node_address=galera1.local:4567
  • 4567: Galera replication traffic (write-set replication between nodes) - uses mTLS via wsrep_provider_options socket.ssl settings
  • 3306: Standard MariaDB port for client connections - uses mTLS with client certificates
  • 4444: State Snapshot Transfer (SST) - used for catastrophic recovery scenarios. SST encryption is out of scope for this lab
  1. Resolve peer hostnames via DNS (Docker network aliases in the lab, DNS in production)
  2. Present their server certificates
  3. Verify peer certificates using the CA
  • IST (Incremental State Transfer): The preferred method. If the node was offline briefly and Galera's GCache still contains the missing transactions, it uses IST to replay only the missing write-sets (fast, incremental). This is the normal case for nodes that were briefly offline. IST uses the same replication traffic (port 4567) that's already encrypted with mTLS. Larger GCache size (gcache.size in wsrep_provider_options) allows nodes to be offline longer before falling back to SST—this lab sets 512M, but production deployments should size based on write rate and desired offline tolerance.
  • SST (State Snapshot Transfer): A full database copy (slower, resource-intensive). SST happens when adding new nodes for the first time, or during catastrophic recovery scenarios when a node has been offline so long that GCache no longer has the missing transactions. SST encryption is out of scope for this lab, which focuses on demonstrating mTLS for replication and client connections. For production, you can configure rsync SST to use SSH for encryption, though be aware that for very large databases (hundreds of GB or more), SST transfers can take hours even over fast networks.
DATASOURCES_DEFAULT_SSL_CA: /etc/passbolt/db-ca.crt
DATASOURCES_DEFAULT_SSL_CERT: /etc/passbolt/db-client.crt
DATASOURCES_DEFAULT_SSL_KEY: /etc/passbolt/db-client.key
  • Galera configuration files (config/mariadb/node*/galera.cnf) → /etc/mysql/conf.d/ on servers
  • Certificate generation logic → run on your CA host
  • MariaDB initialization SQL → apply identically
  • Monitoring commands → work on bare metal
  • Network: Replace .local hostnames with FQDNs (e.g., galera1.example.com). Ensure DNS resolution works across all network segments. Use short TTLs (60 seconds) if using DNS round-robin for passbolt frontends.
  • Certificates: Copy to standard locations (/etc/mysql/ssl/ for server certs, /etc/passbolt/db-*.crt for client certs). Ensure certificate SANs match the FQDNs used in wsrep_cluster_address. Plan for certificate renewal before expiration.
  • Firewall: Open ports 3306 (MariaDB client connections), 4567 (Galera replication), 4444 (SST) between database nodes. Restrict access to trusted networks only.
  • SST encryption: The lab uses rsync for SST without encryption. For production, you can configure rsync SST to use SSH for encrypted transfers, though be aware that for very large databases (hundreds of GB or more), SST transfers can take hours even over fast networks. Alternatively, consider using mariabackup SST method with TLS (encrypt=3) if you need encrypted SST transfers.
  • Service management: Use systemd instead of Docker health checks. Configure proper service dependencies and restart policies.
  • Resource allocation: Ensure adequate RAM for each MariaDB node (Galera keeps the entire dataset in memory during replication). Plan for network bandwidth between data centres if doing WAN clustering.
  • passbolt configuration sync: If running multiple passbolt frontends, sync /etc/passbolt/passbolt.php, /etc/passbolt/gpg/, and /etc/passbolt/jwt/ across all passbolt servers. See Louis's HA guide for details.
  • Zero data loss: Synchronous replication means no replication lag
  • Rolling maintenance: Upgrade MariaDB versions or apply patches without downtime by upgrading nodes one at a time
  • Disaster recovery: DR site stays in sync, not behind
  • High availability: Cluster tolerates single node failures
  • 3306: MariaDB client connections - uses mTLS
  • 4567: Galera replication traffic - uses mTLS
  • 4444: State Snapshot Transfer (SST) - out of scope for this lab
  • 443: passbolt HTTPS
  • config/mariadb/node*/galera.cnf: Galera cluster configuration
  • docker-compose.yaml: Container definitions and network aliases
  • scripts/generate-certs.sh: Certificate generation script
  • certs/: Generated certificates (CA, server certs, client certs)
  • wsrep_cluster_address: List of all cluster members (e.g., gcomm://galera1.local,galera2.local,galera3.local)
  • wsrep_node_address: This node's advertised address (e.g., galera1.local:4567)
  • wsrep_provider_options: TLS certificate paths for replication mTLS
  • Server certs: /etc/mysql/ssl/server-cert.pem, /etc/mysql/ssl/server-key.pem, /etc/mysql/ssl/ca.pem
  • Client certs: /etc/passbolt/db-client.crt, /etc/passbolt/db-client.key, /etc/passbolt/db-ca.crt
# Cluster status
SHOW STATUS LIKE 'wsrep_cluster_status';
SHOW STATUS LIKE 'wsrep_cluster_size';
SHOW STATUS LIKE 'wsrep_incoming_addresses';

# TLS verification
SHOW STATUS LIKE 'Ssl_cipher';
SHOW GLOBAL VARIABLES LIKE 'have_ssl';
  • Check DNS resolution: docker compose exec galera1 getent hosts galera2.local (or check /etc/hosts if using host networking)
  • Verify certificates exist: docker compose exec galera1 ls -la /etc/mysql/ssl/
  • Check cluster address matches DNS names used in wsrep_cluster_address
  • Review logs: docker compose logs galera1
  • Ensure certificate SANs include all hostnames used in wsrep_cluster_address
  • Verify certificate files have correct permissions (readable by mysql user)
  • Check certificate expiration: openssl x509 -in certs/galera1.crt -noout -dates
  • Regenerate certificates if SANs don't match: bash ./scripts/generate-certs.sh
  • Check if node can resolve other cluster members: docker compose exec galera2 getent hosts galera1.local
  • Verify wsrep_cluster_address lists all members correctly
  • Check for network partitions or firewall issues
  • Review node logs: docker compose logs galera2
  • Verify require_secure_transport=ON in galera.cnf
  • Check certificate paths in wsrep_provider_options are correct
  • Ensure MariaDB has SSL support: SHOW GLOBAL VARIABLES LIKE 'have_ssl'; should return YES
  • Verify client certificates are mounted correctly in docker-compose.yaml
  • Check passbolt can resolve database hostname: docker compose exec passbolt getent hosts galera1.local
  • Verify client certificates are mounted: docker compose exec passbolt ls -la /etc/passbolt/db-*.crt
  • Check database user requires SSL: SHOW GRANTS FOR 'passbolt'@'%'; should show REQUIRE SSL
  • Review passbolt logs: docker compose logs passbolt
  • ProxySQL with native Galera support for connection pooling and load balancing (open source, database-aware proxy)
  • HAProxy for TCP load balancing (open source, typically uses TCP health checks or custom scripts)
  • MaxScale for database proxy and query routing (commercial, recommended by MariaDB for Galera)
  • Cloud load balancers: AWS (ELB/NLB), Google Cloud, and Azure offer native load balancing services for Galera clusters
  • Galera Manager for cluster monitoring and management (commercial)

Continue reading

Passbolt 5.7: Secret history, user-group management, and import reports

4 min. read

Passbolt 5.7: Secret history, user-group management, and import reports

Expand secret history, improved user and group management and a detailed import summary report.

AT

Akanksha Thakar

19 November, 2025

So you want to play with the passbolt API? Let’s talk about JWTs.

5 min. read

So you want to play with the passbolt API? Let’s talk about JWTs.

Practical tour of Passbolt JWT authentication: create an OpenPGP challenge, exchange encrypted messages, and obtain access and refresh tokens without passwords.

Antony Bartolomucci

Antony Bartolomucci

5 November, 2025

Flag of European UnionMade in Europe. Privacy by default.