2026-03-12 21:01:38
This commit is contained in:
305
postgresql/postgres_TLS_01.md
Normal file
305
postgresql/postgres_TLS_01.md
Normal file
@@ -0,0 +1,305 @@
|
||||
## Generate PostgreSQL server certificate
|
||||
|
||||
PostgreSQL server generate **certificate private key** and a **certificate request** for the `CN = PostgreSQL server`
|
||||
|
||||
```bash
|
||||
openssl genrsa -out postgres_server.key 4096
|
||||
openssl req -new -key postgres_server.key -out postgres_server.csr
|
||||
```
|
||||
|
||||
The CA root put the **certificate request** in a temporary location (ex. `generated` directory), generate a **signed certificate** and optionally create a **certificate full chain**.
|
||||
|
||||
Configuration file used by root CA:
|
||||
|
||||
```
|
||||
[ req ]
|
||||
default_bits = 4096
|
||||
prompt = no
|
||||
default_md = sha256
|
||||
distinguished_name = dn
|
||||
req_extensions = req_ext
|
||||
|
||||
[ dn ]
|
||||
CN = PostgreSQL server
|
||||
|
||||
[ req_ext ]
|
||||
subjectAltName = @alt_names
|
||||
|
||||
[ alt_names ]
|
||||
DNS.1 = raxus.swgalaxy
|
||||
DNS.2 = mobus.swgalaxy
|
||||
DNS.3 = pgsql.swgalaxy
|
||||
```
|
||||
|
||||
> **_NOTE:_** `CN` in CA configuration file can be diffrent than the `CN` of CSR. The CA does not replace it unless explicitly configured to do so.
|
||||
|
||||
```bash
|
||||
openssl x509 -req \
|
||||
-in generated/postgres_server.csr \
|
||||
-CA rootCA.pem -CAkey rootCA.key -CAserial rootCA.srl \
|
||||
-out generated/postgres_server.crt \
|
||||
-days 3650 \
|
||||
-sha256 \
|
||||
-extensions req_ext -extfile generated/postgres_server.cnf
|
||||
|
||||
cat generated/postgres_server.crt rootCA.pem > generated/postgres_server.fullchain.crt
|
||||
```
|
||||
|
||||
To inspect a certificate in Linux command line:
|
||||
|
||||
```bash
|
||||
openssl x509 -in rootCA.pem -text -noout
|
||||
openssl x509 -in generated/postgres_server.crt -text -noout
|
||||
```
|
||||
|
||||
## Generate PostgreSQL client certificate(s)
|
||||
|
||||
We will generate 2 certificates for:
|
||||
- `CN=PostgreSQL client1`
|
||||
- `CN=PostgreSQL client2`
|
||||
|
||||
```bash
|
||||
openssl genrsa -out postgres_client1.key 4096
|
||||
openssl req -new -key postgres_client1.key -out postgres_client1.csr
|
||||
|
||||
openssl genrsa -out postgres_client2.key 4096
|
||||
openssl req -new -key postgres_client2.key -out postgres_client2.csr
|
||||
```
|
||||
|
||||
Configuration file used by root CA will be the same for both certificates:
|
||||
|
||||
```
|
||||
[ req ]
|
||||
default_bits = 4096
|
||||
prompt = no
|
||||
default_md = sha256
|
||||
distinguished_name = dn
|
||||
req_extensions = req_ext
|
||||
|
||||
[ dn ]
|
||||
CN = Generic Client PostgreSQL
|
||||
|
||||
[ req_ext ]
|
||||
subjectAltName = @alt_names
|
||||
|
||||
[ alt_names ]
|
||||
DNS.1 = anyhost.anydomain
|
||||
```
|
||||
|
||||
```bash
|
||||
openssl x509 -req \
|
||||
-in generated/postgres_client1.csr \
|
||||
-CA rootCA.pem -CAkey rootCA.key -CAserial rootCA.srl \
|
||||
-out generated/postgres_client1.crt \
|
||||
-days 3650 \
|
||||
-sha256 \
|
||||
-extensions req_ext -extfile generated/postgres_client.cnf
|
||||
|
||||
cat generated/postgres_client1.crt rootCA.pem > generated/postgres_client1.fullchain.crt
|
||||
|
||||
openssl x509 -req \
|
||||
-in generated/postgres_client2.csr \
|
||||
-CA rootCA.pem -CAkey rootCA.key -CAserial rootCA.srl \
|
||||
-out generated/postgres_client2.crt \
|
||||
-days 3650 \
|
||||
-sha256 \
|
||||
-extensions req_ext -extfile generated/postgres_client.cnf
|
||||
|
||||
cat generated/postgres_client2.crt rootCA.pem > generated/postgres_client2.fullchain.crt
|
||||
```
|
||||
|
||||
|
||||
## On PostgreSQL server, add the root CA certificate as trusted certificate.
|
||||
|
||||
Put root CA certificate (`.crt` ou `.pem`)under `/etc/pki/ca-trust/source/anchors`, then update the system trust store.
|
||||
|
||||
```bash
|
||||
cd /etc/pki/ca-trust/source/anchors
|
||||
chmod 644 rootCA.pem
|
||||
update-ca-trust extract
|
||||
```
|
||||
|
||||
```bash
|
||||
openssl verify -CAfile /etc/pki/tls/certs/ca-bundle.crt <path_to>/postgres_server.crt
|
||||
# Inspect the root CA certificate to get the exact CN
|
||||
grep -R "<CN_name>" /etc/pki/ca-trust/extracted/
|
||||
```
|
||||
|
||||
## Set up SSL on PostgreSQL server
|
||||
|
||||
Place server certificate, server private key and root CA certificate (optional but recommended) in the PostgreSQL data directory.
|
||||
Default paths:
|
||||
- `$PGDATA/server.key`
|
||||
- `$PGDATA/server.crt`
|
||||
|
||||
Or override with:
|
||||
|
||||
```
|
||||
ssl_cert_file = '/path/to/server.crt'
|
||||
ssl_key_file = '/path/to/server.key'
|
||||
ssl_ca_file = '/path/to/root.crt'
|
||||
```
|
||||
|
||||
Set file pemisions:
|
||||
|
||||
```bash
|
||||
chmod 640 $PGDATA/postgres_server.crt
|
||||
chmod 600 $PGDATA/postgres_server.key
|
||||
chmod 640 $PGDATA/rootCA.pem
|
||||
```
|
||||
|
||||
> root CA certificate is required **only for mTLS** mode when the server will validate the client certificate authenticity
|
||||
> Add (concatebate) all CA intermediate (if any) in `ssl_cert_file`
|
||||
|
||||
Enable TLS in `$PGDATA/postgresql.conf`.
|
||||
|
||||
```
|
||||
ssl_cert_file = 'postgres_server.crt'
|
||||
ssl_key_file = 'postgres_server.key'
|
||||
ssl_ca_file = 'rootCA.pem'
|
||||
|
||||
ssl = on
|
||||
ssl_ciphers = 'HIGH:!aNULL:!MD5'
|
||||
ssl_prefer_server_ciphers = on
|
||||
ssl_min_protocol_version = 'TLSv1.2'
|
||||
ssl_max_protocol_version = 'TLSv1.3'
|
||||
```
|
||||
PostgreSQL will now listen for both encrypted and unencrypted connections on the **same port**.
|
||||
|
||||
## Server side SSL modes
|
||||
|
||||
### Request TLS mode
|
||||
|
||||
Config in `$PGDATA/pg_hba.conf`:
|
||||
|
||||
```
|
||||
host all all all md5
|
||||
```
|
||||
|
||||
### Require TLS mode
|
||||
|
||||
Config in `$PGDATA/pg_hba.conf`:
|
||||
```
|
||||
hostssl all all 0.0.0.0/0 md5
|
||||
```
|
||||
|
||||
### Require TLS mode + client certificate
|
||||
|
||||
Config in `$PGDATA/pg_hba.conf`:
|
||||
```
|
||||
hostssl all all 0.0.0.0/0 cert
|
||||
```
|
||||
|
||||
## Client side SSL modes
|
||||
|
||||
| Mode | Encrypts | Validates CA | Validates Hostname | Typical Use |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `require` | Yes | No | No | Basic encryption |
|
||||
| `verify-ca` | Yes | Yes | No | Internal/IP-based |
|
||||
| `verify-full` | Yes | Yes | Yes | Production |
|
||||
|
||||
Examples:
|
||||
|
||||
```bash
|
||||
# mode: require
|
||||
psql "host=raxus.swgalaxy port=5501 user=vplesnila dbname=postgres sslmode=require"
|
||||
|
||||
# mode: verify-ca
|
||||
psql "host=raxus.swgalaxy port=5501 user=vplesnila dbname=postgres sslmode=verify-ca sslrootcert=rootCA.pem"
|
||||
```
|
||||
|
||||
For `verify-full` mode the client need client certificate, client private key and root CA certificate on client side.
|
||||
In our example we will use previously generated certificats for:
|
||||
- `CN=PostgreSQL client1`
|
||||
- `CN=PostgreSQL client2`
|
||||
|
||||
Set file pemisions:
|
||||
|
||||
```bash
|
||||
chmod 640 postgres_client1.crt
|
||||
chmod 600 postgres_client1.key
|
||||
|
||||
chmod 640 postgres_client2.crt
|
||||
chmod 600 postgres_client2.key
|
||||
|
||||
chmod 640 rootCA.pem
|
||||
```
|
||||
|
||||
In `verify-full` mode we can get ride of client password by mapping the `CN` in the certificate to a **local PostgreSQL role** (aka **local user**).
|
||||
Create local roles (with no password) in PostgreSQL instance:
|
||||
```sql
|
||||
CREATE ROLE app1 LOGIN;
|
||||
CREATE ROLE app2 LOGIN;
|
||||
```
|
||||
|
||||
Add in `$PGDATA/pg_ident.conf`:
|
||||
|
||||
```
|
||||
# MAPNAME SYSTEM-IDENTITY PG-USERNAME
|
||||
certmap_app1 "PostgreSQL client1" app1
|
||||
certmap_app2 "PostgreSQL client2" app2
|
||||
```
|
||||
|
||||
Add in `$PGDATA/pg_hba.conf`:
|
||||
|
||||
```
|
||||
hostssl all app1 0.0.0.0/0 cert map=certmap_app1
|
||||
hostssl all app2 0.0.0.0/0 cert map=certmap_app2
|
||||
```
|
||||
|
||||
> Restart PostgreSQL instance after modifying `$PGDATA/pg_ident.conf` and `$PGDATA/pg_hba.conf`.
|
||||
|
||||
Connect in `verify-full` mode using certificate for authentification:
|
||||
|
||||
```bash
|
||||
# mode: verify-full
|
||||
psql "host=raxus.swgalaxy port=5501 user=app1 dbname=postgres sslmode=verify-full sslrootcert=rootCA.pem sslcert=postgres_client1.crt sslkey=postgres_client1.key"
|
||||
|
||||
psql "host=raxus.swgalaxy port=5501 user=app2 dbname=postgres sslmode=verify-full sslrootcert=rootCA.pem sslcert=postgres_client2.crt sslkey=postgres_client2.key"
|
||||
```
|
||||
|
||||
As `SAN` **(Subject Alternative Name)** of the **server** certificate match to:
|
||||
- `raxus.swgalaxy`
|
||||
- `mobus.swgalaxy`
|
||||
- `pgsql.swgalaxy`
|
||||
|
||||
it is possible to connect to all theses DNS enteries using the same **server** certificate.
|
||||
Example:
|
||||
|
||||
```bash
|
||||
psql "host=pgsql.swgalaxy port=5501 user=app1 dbname=postgres sslmode=verify-full sslrootcert=rootCA.pem sslcert=postgres_client1.crt sslkey=postgres_client1.key"
|
||||
```
|
||||
|
||||
> ⭐ Server certificates → client checks SAN/CN for hostname
|
||||
> ⭐ Client certificates → server checks only CN for user identity
|
||||
|
||||
> **IMPORTANT**: we can mix TLS authentification by certificate and password.
|
||||
PostgreSQL processes pg_hba.conf top‑to‑bottom, and the first matching rule wins.
|
||||
Once a connection matches a line (based on type, database, user, address, etc.), PostgreSQL stops and applies that rule’s authentication method.
|
||||
|
||||
The folowing `$PGDATA/pg_hba.conf` allows authentification using certificates for PostgreSQL users app1 and app2 and authentification using password for all other users.
|
||||
|
||||
```
|
||||
hostssl all app1 0.0.0.0/0 cert map=certmap_app1
|
||||
hostssl all app2 0.0.0.0/0 cert map=certmap_app2
|
||||
hostssl all all 0.0.0.0/0 md5
|
||||
```
|
||||
|
||||
Check if connections are using TLS:
|
||||
|
||||
```sql
|
||||
SELECT * FROM pg_stat_ssl;
|
||||
```
|
||||
|
||||
Check for connection username:
|
||||
```sql
|
||||
select pid as process_id,
|
||||
usename as username,
|
||||
datname as database_name,
|
||||
client_addr as client_address,
|
||||
application_name,
|
||||
backend_start,
|
||||
state,
|
||||
state_change
|
||||
from pg_stat_activity;
|
||||
```
|
||||
Reference in New Issue
Block a user