Skip to content

TLS / SSL Setup

Configure end-to-end TLS encryption for clients, proxy connection pooling, and cluster backends.

Updated View as Markdown

PgVisor supports full end-to-end TLS encryption, protecting database traffic against interception, eavesdropping, and man-in-the-middle (MITM) attacks.

TLS can be enabled both on the client-facing proxy listener and on the backend connections between the proxy and PostgreSQL cluster nodes.


TLS Encryption Architecture

flowchart LR
    Client["Client App / psql"] -->|"TLS 1.2 / 1.3 (SSLRequest)"| Proxy["pgvisor-proxy (Port 5432)"]

    subgraph Cluster ["Encrypted PostgreSQL Cluster"]
        Proxy -->|"TLS Backend (sslmode=require)"| Node1["Node 1 (Leader)"]
        Proxy -->|"TLS Backend (sslmode=require)"| Node2["Node 2 (Standby)"]
        Proxy -->|"TLS Backend (sslmode=require)"| Node3["Node 3 (Standby)"]

        Node1 -.->|"Encrypted Replication"| Node2
        Node1 -.->|"Encrypted Replication"| Node3
    end
  • Client-to-Proxy: Clients negotiate TLS via standard PostgreSQL SSLRequest packets before transmitting authentication credentials or SQL queries.
  • Proxy-to-Node: The proxy initiates encrypted connections to database backends using sslmode=require or verified certificates.
  • Enforcement Mode: Unencrypted plaintext connections can be optionally forbidden by setting PGVISOR_TLS_REQUIRED=true.

Generating Development Certificates

PgVisor includes a development certificate generator script (dev-generate-ssl.sh) that provisions a self-signed Root CA, proxy certificates with Subject Alternative Names (SANs), cluster node certificates, and client certificates with secure 0600 private key permissions:

Run the Certificate Generator

Execute the generator script from the repository root:

./dev-generate-ssl.sh --dir ./certs

Inspect Generated Certificates

The script generates the following files inside ./certs:

File Purpose
ca.crt / ca.key Development Root CA certificate and private key
proxy.crt / proxy.key Proxy TLS server certificate (CN: pgvisor-proxy, SAN: localhost, 127.0.0.1)
server.crt / server.key Shared cluster node certificate
node1/ .. node3/ Individual node-specific certificates
client.crt / client.key Client certificate for user postgres (mTLS authentication)

Environment Variables Reference

Configure TLS by defining these environment variables across your services:

Variable Target Default Description
PGVISOR_TLS_ENABLED Proxy / Node false Enables TLS listener on port 5432 and sidecars
PGVISOR_TLS_REQUIRED Proxy false When true, strictly rejects unencrypted plaintext client connections
PGVISOR_BACKEND_TLS Proxy Matches TLS_ENABLED Enables TLS encryption when proxy connects to PostgreSQL nodes (sslmode=require)
PGVISOR_TLS_CERT_DIR Proxy / Node /var/lib/postgresql/tls Directory scanned for server.crt and server.key (or proxy.crt/proxy.key)
PGVISOR_TLS_CERT_FILE Proxy / Node — Explicit path to TLS certificate file
PGVISOR_TLS_KEY_FILE Proxy / Node — Explicit path to TLS private key file

Docker Compose Configuration Example

Enable TLS across your cluster by mounting certificates and setting the environment variables:

services:
  pgvisor-proxy:
    image: dreamoutbox/pgvisor:latest
    container_name: pgvisor-proxy
    environment:
      - PGVISOR_TLS_ENABLED=true
      - PGVISOR_TLS_REQUIRED=true
      - PGVISOR_BACKEND_TLS=true
      - PGVISOR_TLS_CERT_FILE=/etc/pgvisor/tls/proxy.crt
      - PGVISOR_TLS_KEY_FILE=/etc/pgvisor/tls/proxy.key
    volumes:
      - ./certs/proxy.crt:/etc/pgvisor/tls/proxy.crt:ro
      - ./certs/proxy.key:/etc/pgvisor/tls/proxy.key:ro
    ports:
      - "5432:5432"
      - "8080:8080"

  pgvisor-node1:
    image: dreamoutbox/pgvisor:latest
    container_name: pgvisor-node1
    environment:
      - PGVISOR_NODE_ID=1
      - PGVISOR_ROLE=leader
      - PGVISOR_TLS_ENABLED=true
      - PGVISOR_TLS_CERT_FILE=/etc/pgvisor/tls/server.crt
      - PGVISOR_TLS_KEY_FILE=/etc/pgvisor/tls/server.key
    volumes:
      - ./certs/node1/server.crt:/etc/pgvisor/tls/server.crt:ro
      - ./certs/node1/server.key:/etc/pgvisor/tls/server.key:ro
      - node1_data:/var/lib/postgresql/data

Connecting with TLS

To connect securely with full certificate and hostname verification:

psql "host=localhost port=5432 user=postgres dbname=postgres sslmode=verify-full sslrootcert=./certs/ca.crt"

To encrypt the connection without verifying the certificate chain (useful for local self-signed dev certs):

psql "host=localhost port=5432 user=postgres dbname=postgres sslmode=require"

If client certificate authentication is enforced:

psql "host=localhost port=5432 user=postgres dbname=postgres sslmode=verify-full sslrootcert=./certs/ca.crt sslcert=./certs/client.crt sslkey=./certs/client.key"

Pass standard PostgreSQL SSL parameters in your connection string:

postgresql://postgres:postgres@localhost:5432/postgres?sslmode=verify-full&sslrootcert=./certs/ca.crt

Verifying Active Encryption

When connected via psql, the simplest way to check client-to-proxy TLS encryption is the native \conninfo meta-command:

postgres=> \conninfo
You are connected to database "postgres" as user "postgres" on host "localhost" (address "127.0.0.1") at port "5432" with SSL encryption (Cipher: TLS_AES_256_GCM_SHA384, Bits: 256).

2. Native PostgreSQL Built-in View (pg_stat_ssl)

To check connection encryption via SQL without installing any extensions, query PostgreSQL’s core pg_stat_ssl catalog view for the current backend PID:

SELECT pid, ssl, version, cipher, bits, client_dn
FROM pg_stat_ssl
WHERE pid = pg_backend_pid();

Output:

  pid  | ssl | version |         cipher          | bits | client_dn
-------+-----+---------+-------------------------+------+-----------
 14321 | t   | TLSv1.3 | TLS_AES_256_GCM_SHA384  |  256 |
(1 row)
Navigation

Type to search…

↑↓ navigate↵ selectEsc close