Repository navigation
TLS PostgreSQL
The CDP4-COMET WebServices can encrypt the connection to PostgreSQL with TLS and verify the identity of the database server. This page explains how to configure both sides of the connection, how to run it with Docker, and how to read the log messages when something is wrong.
TLS for the database connection is available in the CDP4-COMET WebServices releases, after 11.0.0-RC3.
| Deployment | Recommendation |
|---|---|
| WebServices and PostgreSQL in the same Docker Compose project | The traffic stays on the private Docker network of the host. TLS is optional (defence in depth). |
| PostgreSQL on another machine | Use TLS with SslMode=VerifyFull. Without it, the database password and all model data travel in plain text. |
| Managed PostgreSQL (Azure, AWS, ...) | These services usually require TLS. Use SslMode=VerifyFull, see Managed PostgreSQL. |
Without any TLS setting, the WebServices use SslMode=Prefer:
- when the server has TLS enabled, the connection is encrypted, but the server certificate is not verified, so it is not protected against a man-in-the-middle;
- when the server has TLS disabled (the default of the PostgreSQL Docker images), the connection silently falls back to plain text.
The startup log tells you which of the two applies, see Log messages.
The TLS settings are part of the Backtier section, see Configuration. Like any setting, they can be overridden by environment variables (Backtier__SslMode=VerifyFull).
"Backtier": {
"HostName": "comet-db",
"Port": 5432,
"UserName": "cdp4",
"Password": "cdp4",
"Database": "cdp4server",
"DatabaseRestore": "cdp4serverrestore",
"DatabaseManage": "cdp4manage",
"StatementTimeout": 180,
"IsDbSeedEnabled": false,
"IsDbRestoreEnabled": false,
"IsDbImportEnabled": false,
"SslMode": "VerifyFull",
"RootCertificate": "/app/certs/db-ca.crt",
"ClientCertificate": "",
"ClientCertificateKey": "",
"ClientCertificatePassword": "",
"CheckCertificateRevocation": false
}
| Setting | Default | Description |
|---|---|---|
SslMode |
Prefer |
Disable, Allow, Prefer, Require, VerifyCA or VerifyFull, see the table below. A misspelled value stops the server at startup and lists the valid values. |
RootCertificate |
empty | Path to the CA certificate (PEM or DER) used to verify the PostgreSQL server. See Which CA is used. |
ClientCertificate |
empty | Optional client certificate, only needed when PostgreSQL authenticates with certificates (cert in pg_hba.conf). PEM, or PFX containing the key. |
ClientCertificateKey |
empty | Private key (PEM) of the ClientCertificate. Not needed for a PFX file, or when the key is in the same PEM file. |
ClientCertificatePassword |
empty | Password of an encrypted private key or of a PFX file. |
CheckCertificateRevocation |
false |
Checks whether the server certificate has been revoked. The CRL/OCSP endpoints of the CA must be reachable from the WebServices. Only true or false are accepted. |
SslMode |
Encrypted | Server identity verified | Use it for |
|---|---|---|---|
Disable |
no | no | never in production |
Allow |
only if the server insists | no | not recommended |
Prefer (default) |
if the server supports it | no | backwards compatibility |
Require |
always | no | only on a network you trust |
VerifyCA |
always | the certificate chain | when the host name cannot match the certificate |
VerifyFull |
always | the certificate chain and the host name | recommended |
With VerifyFull, the certificate of the PostgreSQL server must be valid for the name in Backtier:HostName. In Docker Compose that is the service or host name (for example comet-db), not localhost and not an IP address, unless that address is in the certificate.
To verify the server certificate, the WebServices look for a CA certificate in this order, the same order Npgsql uses:
- the file in
Backtier:RootCertificate; - the file in the
PGSSLROOTCERTenvironment variable; - the default
root.crtof the user running the WebServices, when it exists (~/.postgresql/root.crton Linux,%APPDATA%\postgresql\root.crton Windows); - the trust store of the operating system. The WebServices Docker image contains the public CA bundle, so certificates from a public CA are trusted without any setting.
The startup log shows which one is used: Backtier-RootCertificate: /app/certs/db-ca.crt (Backtier:RootCertificate).
Before connecting, the WebServices check the configured certificate files: they must exist and be readable, the CA file must contain a certificate, and the client certificate must load together with its key and password. When a check fails, the server logs the problem and the setting to change, and stops with exit code 1.
The client settings alone cannot prevent a fallback to plain text: the server has to refuse unencrypted connections.
- Enable TLS, in
postgresql.confor as-carguments of thepostgrescommand:
ssl = on
ssl_cert_file = '/etc/postgresql-tls/server.crt'
ssl_key_file = '/etc/postgresql-tls/server.key'
ssl_min_protocol_version = 'TLSv1.2'
- Only accept encrypted network connections: use
hostsslinstead ofhostinpg_hba.conf. Thelocalline is used by the initialisation scripts of the Docker image:
local all all trust
hostssl all all all scram-sha-256
- The private key must be owned by the user PostgreSQL runs as, and must not be readable by others (
chmod 600). Otherwise PostgreSQL does not start. In thestariongroup/cdp4-database-community-editionandstariongroup/cdp4-test-database-community-editionimages (Alpine) that user ispostgreswith uid 70; in Debian-based PostgreSQL images it is uid 999.
For a test installation, a CA and a server certificate can be created with OpenSSL. The server certificate must be valid for the host name the WebServices connect to (comet-db in the examples). For production, use certificates from your organisation's CA.
mkdir certs && cd certs
openssl req -x509 -newkey rsa:2048 -nodes -days 365 -subj "/CN=COMET Database CA" -keyout ca.key -out ca.crt
openssl req -newkey rsa:2048 -nodes -subj "/CN=comet-db" -keyout server.key -out server.csr
printf "subjectAltName=DNS:comet-db\n" > san.cnf
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -days 365 -out server.crt -extfile san.cnf
printf "local all all trust\nhostssl all all all scram-sha-256\n" > pg_hba.conf
Keep ca.key out of the deployment: only ca.crt goes to the WebServices, and only server.crt, server.key and pg_hba.conf go to PostgreSQL.
The example below runs PostgreSQL with TLS only, and WebServices that connect with VerifyFull. The certs folder is the one created above.
services:
comet_db:
image: stariongroup/cdp4-database-community-edition:4.2.0
container_name: comet-db
hostname: comet-db
shm_size: 2gb
restart: always
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=change-me
# copy the certificate files into the container with the ownership PostgreSQL requires;
# this works on Linux, Windows and macOS hosts alike
entrypoint: >
sh -c 'mkdir -p /etc/postgresql-tls &&
cp /certs/server.crt /certs/server.key /certs/pg_hba.conf /etc/postgresql-tls/ &&
chown -R postgres:postgres /etc/postgresql-tls && chmod 600 /etc/postgresql-tls/server.key &&
exec docker-entrypoint.sh "$$@"' --
command: >
postgres -c max_locks_per_transaction=1024
-c ssl=on
-c ssl_cert_file=/etc/postgresql-tls/server.crt
-c ssl_key_file=/etc/postgresql-tls/server.key
-c ssl_min_protocol_version=TLSv1.2
-c hba_file=/etc/postgresql-tls/pg_hba.conf
volumes:
- comet-db-data:/var/lib/postgresql/data
- ./certs:/certs:ro
networks:
- comet
comet_webservices:
image: stariongroup/comet-webservices-community-edition:<version with TLS support>
container_name: comet-webservices
restart: always
depends_on:
- comet_db
environment:
- Backtier__HostName=comet-db
- Backtier__SslMode=VerifyFull
- Backtier__RootCertificate=/app/certs/db-ca.crt
volumes:
- ./certs/ca.crt:/app/certs/db-ca.crt:ro
- comet-ws-logs:/app/logs
- comet-ws-storage:/app/storage
- comet-ws-upload:/app/upload
ports:
- "5000:5000"
networks:
- comet
networks:
comet:
volumes:
comet-db-data:
comet-ws-logs:
comet-ws-storage:
comet-ws-upload:
Notes:
- On a Linux host you can leave out the
entrypointand mount the files directly at/etc/postgresql-tls, after giving them the right ownership on the host:sudo chown 70:70 certs/server.key certs/server.crt certs/pg_hba.conf && sudo chmod 600 certs/server.key. - The database port is not published. The WebServices reach PostgreSQL over the
cometnetwork. If you need access from the host, for example for pgAdmin, publish it on the loopback address only:'127.0.0.1:5432:5432'. - The WebServices image runs as a non-root user (
$APP_UID). The mounted CA file must be readable by that user, for examplechmod 644 certs/ca.crt. The CA certificate is not secret. - The continuous integration of the WebServices runs the Integration Test Suite against a PostgreSQL configured this way, see the steps "Create TLS certificates for PostgreSQL" and "Start PostgreSQL Container (TLS only)" in CodeQuality.yml.
Managed services such as Azure Database for PostgreSQL and Amazon RDS for PostgreSQL support TLS and often require it.
- Use
SslMode=VerifyFulland setBacktier:HostNameto the host name the provider gives you; the provider's certificate is issued for that name. - Use the CA certificate published by the provider (for Amazon RDS, the regional or global certificate bundle) in
Backtier:RootCertificate. When the provider's certificate chains to a public root CA, the CA bundle of the WebServices image already trusts it andRootCertificatecan stay empty. - Check the provider's documentation for CA rotations: when the provider changes its CA, the file in
RootCertificatemust be updated.
Instead of a password, PostgreSQL can authenticate the WebServices with a client certificate:
- On the server: set
ssl_ca_fileto the CA that issues the client certificates, and usecertinpg_hba.conf:hostssl all cdp4 all cert. The common name (CN) of the client certificate must equal the database user name (cdp4). - In the WebServices: set
Backtier:ClientCertificate(andClientCertificateKeyandClientCertificatePasswordas needed).
The Docker examples in this wiki and in the repository use default passwords. Change them before you put a server into production:
| Account | Default | Where it is set |
|---|---|---|
PostgreSQL superuser postgres
|
pass (in docker-compose.yml) |
POSTGRES_PASSWORD of the database container. It is only applied when the database volume is created for the first time; afterwards change it with ALTER ROLE postgres WITH PASSWORD '...';
|
COMET database user cdp4
|
cdp4 |
Created by the database image. Change it with ALTER ROLE cdp4 WITH PASSWORD '...'; and set the same value in Backtier:Password (Backtier__Password) |
CDP4-COMET administrator admin
|
pass |
See Restore default admin password |
| Persons seeded from an Annex C.3 file | pass |
Defaults:PersonPassword, see Configuration
|
At startup the WebServices log the TLS configuration and the result of the first connection. This table lists the messages, what they mean and what to do.
| Log message | Meaning | What to do |
|---|---|---|
INF The connection to the data store at comet-db:5432 is encrypted using TLSv1.3 TLS_AES_256_GCM_SHA384 (Backtier:SslMode VerifyFull) |
Encrypted and verified | Nothing |
WRN The connection to the data store ... is NOT encrypted (Backtier:SslMode Prefer). Credentials and data are sent in plain text |
The server does not support TLS, or SslMode is Disable
|
Enable TLS on the server and set SslMode to VerifyFull
|
WRN The identity of the data store server is not fully verified (Backtier:SslMode Require) |
Encrypted, but the server certificate is not (fully) checked | Set SslMode to VerifyFull and configure the CA |
FTL Invalid TLS setting for the data-store connection: The file '...' configured in Backtier:RootCertificate does not exist |
A configured certificate file is missing, unreadable or invalid | Fix the path, the mount or the file permissions |
FTL ... The client certificate '...' could not be loaded. Check the files and Backtier:ClientCertificatePassword |
Client certificate, key or password do not match | Check the files and the password |
FTL ... failed: The TLS handshake failed or the server certificate was rejected followed by TLS diagnostics: lines |
The server certificate was rejected. The PROBLEM: lines explain why |
See the next rows |
TLS diagnostics: PROBLEM: the server certificate issued by '...' is not trusted by Backtier:RootCertificate '...' containing ['...'] |
The configured CA did not issue the server certificate | Configure the CA that issued the server certificate, or add missing intermediate certificates to ssl_cert_file on the server |
TLS diagnostics: PROBLEM: the server certificate is not valid for the host name '...' ... It is valid for [...] |
Backtier:HostName is not in the certificate |
Connect with one of the listed names, or issue a certificate for the host name |
TLS diagnostics: PROBLEM: the server certificate expired on ... / the CA certificate '...' ... is not valid now
|
Expired (or not yet valid) server or CA certificate | Renew the certificate; check the clock of both machines |
FTL ... Backtier:SslMode is VerifyFull, which requires TLS, but the PostgreSQL server does not accept TLS connections |
TLS is disabled on the server | Enable TLS on the server (ssl = on) |
FTL ... The PostgreSQL server only accepts encrypted connections, but Backtier:SslMode is Disable |
The server only allows hostssl
|
Set SslMode to VerifyFull
|
FTL ... The PostgreSQL server rejected the credentials of user 'cdp4' |
Wrong database user or password | Check Backtier:UserName and Backtier:Password
|
INF Waiting for the data store ... Reason: 3D000: database "cdp4server" does not exist. The database 'cdp4server' is created when an Annex C.3 file is posted to /Data/Exchange |
A new installation that has not been seeded yet | Seed the server, see [[REST API - ICD |
Configuration and TLS errors stop the server at once, with exit code 1, instead of waiting for Midtier:BacktierWaitTime. Temporary problems, such as a database that is still starting, are retried until BacktierWaitTime has passed.
copyright @ Starion Group S.A.