Sign inSign up

andreidrang/postgreslite-tls

By andreidrang

•Updated 3 months ago

Image
0

905

andreidrang/postgreslite-tls repository overview

⁠PostgreSQL 18 with Auto-Generated TLS Certificates

A production-ready PostgreSQL 18.1 Docker image with automatic TLS certificate generation, optimized for secure connections from Cloudflare Hyperdrive, PgBouncer, and other clients requiring SSL.

⁠Features

  • PostgreSQL 18.1 - Latest stable release
  • Auto-generated TLS certificates - Self-signed CA and server certificates created on first startup
  • Cloudflare Hyperdrive ready - Supports require, verify-ca, and verify-full SSL modes
  • Secure defaults - scram-sha-256 authentication, TLS 1.2+ only, SSL-only remote connections
  • Performance optimized - Tuned for SSD storage, includes pg_stat_statements for query monitoring
  • Persistent certificates - Certificates survive container restarts when using volumes

⁠Quick Start

# Run with auto-generated TLS certificates
docker run -d \
  --name postgres-tls \
  -e POSTGRES_USER=myuser \
  -e POSTGRES_PASSWORD=mypassword \
  -e POSTGRES_DB=mydb \
  -p 5432:5432 \
  -v pgdata:/var/lib/postgresql/18/main \
  your-dockerhub-username/postgres-tls:18.1

⁠Environment Variables

VariableRequiredDefaultDescription
POSTGRES_USERYes-Database superuser name
POSTGRES_PASSWORDYes-Database superuser password
POSTGRES_DBNo$POSTGRES_USERDefault database name

⁠TLS Configuration

⁠Certificate Locations
FilePathDescription
CA Certificate/var/lib/postgresql/18/main/certs/ca.crtRoot CA for client verification
CA Private Key/var/lib/postgresql/18/main/certs/ca.keyCA signing key (keep secure)
Server Certificate/var/lib/postgresql/18/main/certs/server.crtServer identity certificate
Server Private Key/var/lib/postgresql/18/main/certs/server.keyServer private key

Note: Certificates are stored inside the data directory so they persist with a single volume mount.

⁠Certificate Validity
  • CA Certificate: 10 years
  • Server Certificate: 1 year
⁠SSL Settings
ssl = on
ssl_min_protocol_version = TLSv1.2
ssl_max_protocol_version = TLSv1.3
ssl_ciphers = HIGH:MEDIUM:+3DES:!aNULL
ssl_prefer_server_ciphers = on

⁠Extracting the CA Certificate

To connect with verify-ca or verify-full mode, clients need the CA certificate:

# Extract CA certificate from running container
docker cp postgres-tls:/var/lib/postgresql/18/main/certs/ca.crt ./ca.crt

# Or from a volume
docker run --rm -v pgdata:/data alpine cat /data/certs/ca.crt > ca.crt

⁠Connection Examples

⁠Basic SSL Connection (sslmode=require)
psql "postgresql://myuser:mypassword@localhost:5432/mydb?sslmode=require"
⁠Verified Connection (sslmode=verify-ca)
psql "host=localhost dbname=mydb user=myuser sslmode=verify-ca sslrootcert=./ca.crt"
⁠Full Verification (sslmode=verify-full)
psql "host=postgres-server dbname=mydb user=myuser sslmode=verify-full sslrootcert=./ca.crt"

Note: verify-full requires the hostname to match the certificate CN or SAN. Default CN is postgres-server.

⁠Application Integration

⁠Python (SQLAlchemy + asyncpg)
import ssl

# Create SSL context with CA verification
ssl_context = ssl.create_default_context(cafile="/path/to/ca.crt")
ssl_context.check_hostname = False  # verify-ca mode
ssl_context.verify_mode = ssl.CERT_REQUIRED

engine = create_async_engine(
    "postgresql+asyncpg://user:pass@host:5432/db",
    connect_args={"ssl": ssl_context}
)
⁠Node.js (node-postgres)
const { Client } = require('pg');
const fs = require('fs');

const client = new Client({
  host: 'localhost',
  database: 'mydb',
  user: 'myuser',
  password: 'mypassword',
  ssl: {
    rejectUnauthorized: true,
    ca: fs.readFileSync('/path/to/ca.crt').toString(),
  }
});
⁠Go (pgx)
import (
    "crypto/tls"
    "crypto/x509"
    "io/ioutil"
)

caCert, _ := ioutil.ReadFile("/path/to/ca.crt")
caCertPool := x509.NewCertPool()
caCertPool.AppendCertsFromPEM(caCert)

config, _ := pgx.ParseConfig("postgres://user:pass@host:5432/db")
config.TLSConfig = &tls.Config{
    RootCAs:            caCertPool,
    InsecureSkipVerify: false,
    ServerName:         "", // empty for verify-ca
}

⁠Cloudflare Hyperdrive Setup

⁠1. Extract and Upload CA Certificate
# Extract CA certificate
docker cp postgres-tls:/var/lib/postgresql/certs/ca.crt ./ca.crt

# Upload to Cloudflare
npx wrangler cert upload certificate-authority \
  --ca-cert ./ca.crt \
  --name postgres-ca
⁠2. Create Hyperdrive Configuration
npx wrangler hyperdrive create my-database \
  --connection-string="postgres://user:pass@your-host:5432/dbname" \
  --ca-certificate-id <CERT_ID_FROM_STEP_1> \
  --sslmode verify-ca
⁠3. Use in Cloudflare Worker
export default {
  async fetch(request: Request, env: Env) {
    const client = new Client(env.HYPERDRIVE.connectionString);
    await client.connect();

    const result = await client.query('SELECT NOW()');

    await client.end();
    return Response.json(result.rows);
  }
};

⁠PgBouncer Configuration

To use PgBouncer as a connection pooler with this PostgreSQL instance:

pgbouncer.ini:

[databases]
mydb = host=postgres-host port=5432 dbname=mydb

[pgbouncer]
listen_addr = 0.0.0.0
listen_port = 6432
auth_type = scram-sha-256
auth_file = /etc/pgbouncer/userlist.txt
pool_mode = transaction

# Backend TLS to PostgreSQL
server_tls_sslmode = verify-ca
server_tls_ca_file = /etc/pgbouncer/ca.crt

⁠Volume Persistence

For production, use a volume to persist data and certificates:

docker run -d \
  --name postgres-tls \
  -e POSTGRES_USER=myuser \
  -e POSTGRES_PASSWORD=mypassword \
  -v pgdata:/var/lib/postgresql/18/main \
  your-dockerhub-username/postgres-tls:18.1
Volume MountContains
/var/lib/postgresql/18/mainDatabase files + TLS certificates

Important: Certificates are stored in /var/lib/postgresql/18/main/certs/ inside the data directory. If you delete the volume, new certificates will be generated and you'll need to update clients with the new CA certificate.

⁠Host-Based Authentication (pg_hba.conf)

Default authentication rules:

TypeDatabaseUserAddressMethod
localallall-trust
hostsslallall0.0.0.0/0scram-sha-256
hostsslallall::/0scram-sha-256
  • Local connections: Unix socket, trusted (no password)
  • Remote connections: SSL required, password authentication

⁠Security Considerations

⁠What's Public (safe to share)
  • ca.crt - CA certificate (needed by clients to verify server)
  • server.crt - Server certificate (sent during TLS handshake)
⁠What's Secret (never share)
  • ca.key - CA private key (can sign fake certificates)
  • server.key - Server private key (can impersonate server)
  • Database passwords
⁠Recommendations
  1. Use named volumes for certificate persistence
  2. Extract CA cert once and distribute to clients
  3. Rotate server certificate annually (regenerate by clearing certs volume)
  4. Use strong passwords with scram-sha-256
  5. Network isolation - Run PostgreSQL in a private network when possible

⁠Performance Tuning

Pre-configured optimizations:

SettingValueDescription
shared_buffers512MBShared memory for caching
work_mem32MBPer-operation memory
effective_cache_size1.5GBPlanner's cache assumption
random_page_cost1.1Optimized for SSD
effective_io_concurrency300Parallel I/O for SSD
max_connections100Maximum concurrent connections

⁠Included Extensions

  • pg_stat_statements - Query performance monitoring (pre-loaded)
  • pg_trgm - Trigram text similarity (initialized)

⁠Troubleshooting

⁠"SSL certificate verify failed"

Ensure you're using the correct CA certificate extracted from the container.

# Verify certificate chain
openssl verify -CAfile ca.crt server.crt
⁠"Permission denied" on config files

Config files should be readable:

docker exec postgres-tls ls -la /etc/postgresql.conf
# Should show: -rw-r--r-- postgres postgres
⁠Check SSL is enabled
docker exec postgres-tls psql -U myuser -d mydb -c "SHOW ssl;"
# Should return: on
⁠View certificate details
openssl x509 -in ca.crt -text -noout

⁠Building from Source

git clone <repository>
cd postgresql

docker build -f Dockerfile.tls -t postgres-tls:18.1 .

⁠License

Based on the official PostgreSQL Docker image. PostgreSQL is released under the PostgreSQL License.

Tag summary

Content type

Image

Digest

sha256:fff04ae7e…

Size

155.5 MB

Last updated

3 months ago

docker pull andreidrang/postgreslite-tls:slave-18.4