Skip to content

Commit 7a830c3

Browse files
feat: enable SSL/TLS for PgBouncer client connections (#49)
* feat: enable SSL/TLS for PgBouncer client connections PgBouncer was rejecting SSL connections on port 5432 because client-side TLS was disabled. This change enables SSL for all client connections to PgBouncer with sslmode=require as the default. Changes: - Add client TLS configuration to PgBouncer entrypoint script - Auto-generate self-signed certificates if not provided - Add openssl package to PgBouncer Dockerfile for cert generation - Add PGBOUNCER_CLIENT_TLS_* environment variables to docker-compose - Update .env.example with new TLS configuration options - Remove obsolete pgbouncer.ini.tpl (config generated dynamically) The default sslmode is 'require', enforcing SSL for all connections. Users can set PGBOUNCER_CLIENT_TLS_SSLMODE=disable to opt out. * Update pgbouncer/entrypoint.sh Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Patrick Audley <paudley@blackcat.ca> --------- Signed-off-by: Patrick Audley <paudley@blackcat.ca> Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
1 parent c3ae86f commit 7a830c3

5 files changed

Lines changed: 60 additions & 27 deletions

File tree

.env.example

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,15 @@ PGBOUNCER_STATS_PASSWORD_FILE=./secrets/pgbouncer_stats_password
145145
PGBOUNCER_ADMIN_USERS=postgres
146146
PGBOUNCER_STATS_USERS=pgbouncer_stats
147147

148+
# PgBouncer TLS configuration (client-side SSL for connections to PgBouncer)
149+
# Self-signed certificates are generated automatically if files are absent
150+
# sslmode options: disable, allow, prefer, require, verify-ca, verify-full
151+
PGBOUNCER_CLIENT_TLS_SSLMODE=require
152+
PGBOUNCER_CLIENT_TLS_CERT_FILE=/tmp/pgbouncer/tls/server.crt
153+
PGBOUNCER_CLIENT_TLS_KEY_FILE=/tmp/pgbouncer/tls/server.key
154+
PGBOUNCER_CLIENT_TLS_SELF_SIGNED_SUBJECT=/CN=core_data_pgbouncer
155+
PGBOUNCER_CLIENT_TLS_SELF_SIGNED_DAYS=730
156+
148157
# Memcached hot object cache
149158
MEMCACHED_PORT=11211
150159
MEMCACHED_MEMORY_MB=128

docker-compose.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,12 @@ services:
363363
PGBOUNCER_STATS_USER: ${PGBOUNCER_STATS_USER}
364364
PGBOUNCER_AUTH_USER: ${PGBOUNCER_AUTH_USER}
365365
PGBOUNCER_AUTH_PASSWORD_FILE: /run/secrets/pgbouncer_auth_password
366+
# Client TLS configuration (SSL for connections TO PgBouncer)
367+
PGBOUNCER_CLIENT_TLS_SSLMODE: ${PGBOUNCER_CLIENT_TLS_SSLMODE:-require}
368+
PGBOUNCER_CLIENT_TLS_CERT_FILE: ${PGBOUNCER_CLIENT_TLS_CERT_FILE:-/tmp/pgbouncer/tls/server.crt}
369+
PGBOUNCER_CLIENT_TLS_KEY_FILE: ${PGBOUNCER_CLIENT_TLS_KEY_FILE:-/tmp/pgbouncer/tls/server.key}
370+
PGBOUNCER_CLIENT_TLS_SELF_SIGNED_SUBJECT: ${PGBOUNCER_CLIENT_TLS_SELF_SIGNED_SUBJECT:-/CN=core_data_pgbouncer}
371+
PGBOUNCER_CLIENT_TLS_SELF_SIGNED_DAYS: ${PGBOUNCER_CLIENT_TLS_SELF_SIGNED_DAYS:-730}
366372
entrypoint: ["/opt/core_data/bin/pgbouncer-entrypoint.sh"]
367373
volumes:
368374
- ./secrets/pgbouncer_auth_password:/run/secrets/pgbouncer_auth_password:ro

docker/pgbouncer/Dockerfile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ RUN apt-get update && \
1313
netcat-openbsd \
1414
procps \
1515
postgresql-client \
16-
ca-certificates && \
16+
ca-certificates \
17+
openssl && \
1718
rm -rf /var/lib/apt/lists/* && \
1819
mkdir -p /opt/core_data/bin /opt/core_data/docs && chmod 755 /opt/core_data
1920
COPY README.md SECURITY.md /opt/core_data/docs/

pgbouncer/entrypoint.sh

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,13 @@ export POSTGRES_PORT=${POSTGRES_PORT:-5433}
1919
export PGBOUNCER_AUTH_USER=${PGBOUNCER_AUTH_USER:-pgbouncer_auth}
2020
export POSTGRES_DB=${POSTGRES_DB:-postgres}
2121

22+
# Client TLS configuration (connections from clients to PgBouncer)
23+
export PGBOUNCER_CLIENT_TLS_SSLMODE=${PGBOUNCER_CLIENT_TLS_SSLMODE:-require}
24+
export PGBOUNCER_CLIENT_TLS_CERT_FILE=${PGBOUNCER_CLIENT_TLS_CERT_FILE:-/tmp/pgbouncer/tls/server.crt}
25+
export PGBOUNCER_CLIENT_TLS_KEY_FILE=${PGBOUNCER_CLIENT_TLS_KEY_FILE:-/tmp/pgbouncer/tls/server.key}
26+
export PGBOUNCER_CLIENT_TLS_SELF_SIGNED_SUBJECT=${PGBOUNCER_CLIENT_TLS_SELF_SIGNED_SUBJECT:-/CN=core_data_pgbouncer}
27+
export PGBOUNCER_CLIENT_TLS_SELF_SIGNED_DAYS=${PGBOUNCER_CLIENT_TLS_SELF_SIGNED_DAYS:-730}
28+
2229
wait_for_backend() {
2330
local attempts=${PGBOUNCER_BACKEND_WAIT_ATTEMPTS:-120}
2431
local delay=2
@@ -87,6 +94,32 @@ fi
8794
mkdir -p "${log_dir}" "${run_dir}" "$(dirname "${config_path}")" "$(dirname "${userlist_path}")" "$(dirname "${hba_path}")"
8895
umask 077
8996

97+
# Generate self-signed TLS certificate for client connections if not provided
98+
if [[ "${PGBOUNCER_CLIENT_TLS_SSLMODE}" != "disable" ]]; then
99+
tls_cert_dir=$(dirname "${PGBOUNCER_CLIENT_TLS_CERT_FILE}")
100+
tls_key_dir=$(dirname "${PGBOUNCER_CLIENT_TLS_KEY_FILE}")
101+
mkdir -p "${tls_cert_dir}" "${tls_key_dir}"
102+
if [[ ! -f "${PGBOUNCER_CLIENT_TLS_CERT_FILE}" || ! -f "${PGBOUNCER_CLIENT_TLS_KEY_FILE}" ]]; then
103+
echo "[pgbouncer] Generating self-signed TLS certificate for client connections." >&2
104+
if ! command -v openssl >/dev/null 2>&1; then
105+
echo "[pgbouncer] ERROR: openssl not available; cannot create TLS assets." >&2
106+
exit 1
107+
fi
108+
if ! openssl_output=$(openssl req -x509 -nodes -newkey rsa:4096 \
109+
-keyout "${PGBOUNCER_CLIENT_TLS_KEY_FILE}" \
110+
-out "${PGBOUNCER_CLIENT_TLS_CERT_FILE}" \
111+
-days "${PGBOUNCER_CLIENT_TLS_SELF_SIGNED_DAYS}" \
112+
-subj "${PGBOUNCER_CLIENT_TLS_SELF_SIGNED_SUBJECT}" 2>&1); then
113+
echo "[pgbouncer] ERROR: Failed to generate self-signed TLS certificate:" >&2
114+
echo "${openssl_output}" >&2
115+
exit 1
116+
fi
117+
chmod 600 "${PGBOUNCER_CLIENT_TLS_KEY_FILE}"
118+
chmod 644 "${PGBOUNCER_CLIENT_TLS_CERT_FILE}"
119+
echo "[pgbouncer] TLS certificate generated successfully." >&2
120+
fi
121+
fi
122+
90123
wait_for_backend
91124
unset PGUSER PGDATABASE PGPASSWORD
92125

@@ -106,6 +139,15 @@ if [[ -r "${NETWORK_ALLOW_FILE}" ]]; then
106139
else
107140
echo "[pgbouncer] WARNING: ${NETWORK_ALLOW_FILE} not found; defaulting to internal allow rules only." >&2
108141
fi
142+
# Build client TLS configuration block
143+
client_tls_config=""
144+
if [[ "${PGBOUNCER_CLIENT_TLS_SSLMODE}" != "disable" ]]; then
145+
client_tls_config="client_tls_sslmode = ${PGBOUNCER_CLIENT_TLS_SSLMODE}
146+
client_tls_cert_file = ${PGBOUNCER_CLIENT_TLS_CERT_FILE}
147+
client_tls_key_file = ${PGBOUNCER_CLIENT_TLS_KEY_FILE}"
148+
echo "[pgbouncer] Client TLS enabled with sslmode=${PGBOUNCER_CLIENT_TLS_SSLMODE}" >&2
149+
fi
150+
109151
cat >"${config_path}" <<EOF
110152
[databases]
111153
* = host=${POSTGRES_HOST} port=${POSTGRES_PORT} auth_user=${PGBOUNCER_AUTH_USER}
@@ -131,6 +173,7 @@ stats_users = ${PGBOUNCER_STATS_USERS}
131173
logfile = ${log_dir}/pgbouncer.log
132174
pidfile = ${run_dir}/pgbouncer.pid
133175
server_tls_sslmode = ${PGBOUNCER_SERVER_TLS_MODE:-require}
176+
${client_tls_config}
134177
EOF
135178

136179
cat >"${userlist_path}" <<EOF

pgbouncer/pgbouncer.ini.tpl

Lines changed: 0 additions & 26 deletions
This file was deleted.

0 commit comments

Comments
 (0)