Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

mTLS Authentication

Configure mutual TLS (client certificate) authentication for database connections.

Overview

mTLS provides:

  • Strong authentication via X.509 certificates
  • Encrypted connections (TLS)
  • No password management required
  • Certificate-based identity

Prerequisites

  • TLS-enabled database server
  • Client certificate and key
  • CA certificate (for verification)

Basic Configuration

from horizon_epoch import Config

config = Config(
    metadata_url="postgresql://localhost/horizon_epoch"
).add_postgres(
    name="production",
    host="db.example.com",
    database="production",
    username="epoch_user",
    sslmode="verify-full",
    ssl_cert="/path/to/client.crt",
    ssl_key="/path/to/client.key",
    ssl_rootcert="/path/to/ca.crt"
)

TLS Modes

ModeServer CertClient CertDescription
disableNoNoUnencrypted connection
requireNoNoEncrypted, no verification
verify-caYesOptionalVerify server cert against CA
verify-fullYesOptionalVerify cert + hostname

For mTLS, use verify-full with client certificates.

Certificate Files

Client Certificate

PEM-encoded X.509 certificate:

-----BEGIN CERTIFICATE-----
MIIDXTCCAkWgAwIBAgIJAJC1HiIAZAiU...
-----END CERTIFICATE-----

Client Key

PEM-encoded private key:

-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEA7Zq7k...
-----END RSA PRIVATE KEY-----

Or encrypted key:

-----BEGIN ENCRYPTED PRIVATE KEY-----
MIIFHzBJBgkqhkiG9w0BBQ0wPDA...
-----END ENCRYPTED PRIVATE KEY-----

CA Certificate

For verifying server certificate:

-----BEGIN CERTIFICATE-----
MIIDrzCCApegAwIBAgIQCDvg...
-----END CERTIFICATE-----

Configuration File

# epoch.toml
[storage.postgres.production]
host = "db.example.com"
database = "production"
username = "epoch_user"
sslmode = "verify-full"
ssl_cert = "/etc/epoch/certs/client.crt"
ssl_key = "/etc/epoch/certs/client.key"
ssl_rootcert = "/etc/epoch/certs/ca.crt"

Using Vault PKI

Dynamically issue certificates from Vault:

config = Config(
    vault_addr="https://vault.example.com:8200",
    vault_token="${VAULT_TOKEN}"
).add_postgres(
    name="production",
    host="db.example.com",
    database="production",
    username="epoch_user",
    sslmode="verify-full",
    vault_pki_role="pki/issue/epoch-client",
    ssl_rootcert="/etc/epoch/certs/ca.crt"
)

Certificates are automatically renewed before expiry.

PostgreSQL Server Setup

Enable SSL on PostgreSQL server:

# postgresql.conf
ssl = on
ssl_cert_file = '/var/lib/postgresql/server.crt'
ssl_key_file = '/var/lib/postgresql/server.key'
ssl_ca_file = '/var/lib/postgresql/ca.crt'

Configure client certificate authentication:

# pg_hba.conf
# TYPE  DATABASE  USER         ADDRESS        METHOD
hostssl all       epoch_user   0.0.0.0/0      cert clientcert=verify-full

Create user with certificate CN:

CREATE USER epoch_user;
-- The CN in the client cert must match the username

Encrypted Private Keys

If your key is password-protected:

config.add_postgres(
    name="production",
    host="db.example.com",
    ssl_key="/path/to/encrypted.key",
    ssl_key_password="${SSL_KEY_PASSWORD}"
)

Certificate Rotation

Manual Rotation

  1. Generate new certificate
  2. Update configuration
  3. Restart/reload application

Automatic with Vault

Certificates are automatically rotated:

config = Config(
    vault_pki_role="pki/issue/epoch-client",
    cert_renewal_threshold=0.7  # Renew when 70% of TTL elapsed
)

Troubleshooting

Certificate Expired

Error: SSL error: certificate has expired
  • Check certificate expiry: openssl x509 -enddate -noout -in client.crt
  • Renew certificate
  • If using Vault, check renewal is working

Certificate Verification Failed

Error: SSL error: certificate verify failed
  • Verify CA certificate is correct
  • Check certificate chain is complete
  • Verify server hostname matches certificate

Permission Denied on Key File

Error: could not load private key file
  • Check file permissions: chmod 600 client.key
  • Verify file is readable by process user

Key Doesn’t Match Certificate

Error: key values mismatch
  • Verify key matches certificate:
    openssl x509 -noout -modulus -in client.crt | md5sum
    openssl rsa -noout -modulus -in client.key | md5sum
    

Wrong Password for Encrypted Key

Error: bad decrypt
  • Verify password is correct
  • Check key file format (PKCS#8 vs traditional)

Security Best Practices

  1. Protect private keys - Use encrypted keys, restrict file permissions
  2. Use short-lived certificates - Rotate frequently
  3. Verify server certificates - Always use verify-full
  4. Audit certificate usage - Log certificate fingerprints
  5. Use separate certificates - Don’t share between environments

CLI Usage

# Using environment variables
export EPOCH_SSL_CERT="/path/to/client.crt"
export EPOCH_SSL_KEY="/path/to/client.key"
export EPOCH_SSL_ROOTCERT="/path/to/ca.crt"

epoch init my-repo \
    --metadata-url "postgresql://user@db.example.com/horizon_epoch?sslmode=verify-full"

Next Steps