Client TLS/SSL connections
See also
See Certificates
for more details on how CloudNativePG supports TLS certificates.
The CloudNativePG operator was designed to work with TLS/SSL for both encryption in transit and authentication on the server and client sides. Clusters created using the CNPG operator come with a certification authority (CA) to create and sign TLS client certificates. Using the cnpg plugin for kubectl, you can issue a new TLS client certificate for authenticating a user instead of using passwords.
These instructions for authenticating using TLS/SSL certificates assume you installed a cluster using the
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
name: cluster-example
spec:
instances: 3
postgresql:
pg_hba:
- hostssl app all all cert
storage:
size: 1Gi
manifest. According to the convention-over-configuration paradigm, that
file creates an app database that’s owned by a user called app. (You
can change this convention by way of the initdb configuration in the
bootstrap section.)
Issuing a new certificate
See also
content for details on how to use the plugin for kubectl.
You can create a certificate for the app user in the cluster-example
PostgreSQL cluster as follows:
kubectl cnpg certificate cluster-app \
--cnpg-cluster cluster-example \
--cnpg-user app
You can now verify the certificate:
kubectl get secret cluster-app \
-o jsonpath="{.data[tls\.crt]}" \
| base64 -d | openssl x509 -text -noout \
| head -n 11
Output:
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
5d:e1:72:8a:39:9f:ce:51:19:9d:21:ff:1e:4b:24:5d
Signature Algorithm: ecdsa-with-SHA256
Issuer: OU = default, CN = cluster-example
Validity
Not Before: Mar 22 10:22:14 2021 GMT
Not After : Mar 22 10:22:14 2022 GMT
Subject: CN = app
As you can see, TLS client certificates by default are created with 90
days of validity and with a simple CN that corresponds to the username
in PostgreSQL. This is necessary to leverage the cert authentication
method for hostssl entries in pg_hba.conf .
Testing the connection via a TLS certificate
Next, test this client certificate by configuring a demo client application that connects to your CloudNativePG cluster.
The following manifest, called cert-test.yaml , creates a demo pod
with a test application in the same namespace where your database
cluster is running:
apiVersion: apps/v1
kind: Deployment
metadata:
name: cert-test
spec:
replicas: 1
selector:
matchLabels:
app: webtest
template:
metadata:
labels:
app: webtest
spec:
containers:
- image: ghcr.io/cloudnative-pg/webtest:1.6.0
name: cert-test
volumeMounts:
- name: secret-volume-root-ca
mountPath: /etc/secrets/ca
- name: secret-volume-app
mountPath: /etc/secrets/app
ports:
- containerPort: 8080
env:
- name: DATABASE_URL
value: >
sslkey=/etc/secrets/app/tls.key
sslcert=/etc/secrets/app/tls.crt
sslrootcert=/etc/secrets/ca/ca.crt
host=cluster-example-rw.default.svc
dbname=app
user=app
sslmode=verify-full
- name: SQL_QUERY
value: SELECT 1
readinessProbe:
httpGet:
port: 8080
path: /tx
volumes:
- name: secret-volume-root-ca
secret:
secretName: cluster-example-ca
defaultMode: 0600
- name: secret-volume-app
secret:
secretName: cluster-app
defaultMode: 0600
This pod mounts secrets managed by the CloudNativePG operator, including:
sslcert– The TLS client public certificate.sslkey– The TLS client certificate private key.sslrootcert– The TLS CA certificate that signed the certificate on the server to use to verify the identity of the instances.
They’re used to create the default resources that psql (and other libpq-based applications, like pgbench) requires to establish a TLS-encrypted connection to the Postgres database.
By default, psql searches for certificates in the ~/.postgresql
directory of the current user, but you can use the sslkey ,
sslcert , and sslrootcert options to point libpq to the actual
location of the cryptographic material. The content of these files is
gathered from the secrets that were previously created by using the cnpg
plugin for kubectl.
Deploy the application:
kubectl create -f cert-test.yaml
Then use the created pod as the PostgreSQL client to validate the SSL connection and authentication using the TLS certificates you just created.
A readiness probe was configured to ensure that the application is ready when the database server can be reached.
You can verify that the connection works. To do so, execute an
interactive bash inside the pod’s container to run psql using the
necessary options. The PostgreSQL server is exposed through the
read-write Kubernetes service. Point the psql command to connect to this
service:
kubectl exec -it cert-test -- bash -c "psql
sslkey=/etc/secrets/app/tls.key sslcert=/etc/secrets/app/tls.crt
sslrootcert=/etc/secrets/ca/ca.crt host=cluster-example-rw.default.svc dbname=app
user=app sslmode=verify-full -c select version();"
Output:
version
- -------------------------------------------------------------------------------------
- -----------------
PostgreSQL 16.1 on x86_64-pc-linux-gnu, compiled by gcc (GCC) 8.3.1 20191121 (Red Hat
8. 3.1-5), 64-bit
(1 row)
About TLS protocol versions
By default, the operator sets both ssl_min_protocol_version
to TLSv1.3 .
Important
In PostgreSQL 11, these two GUCs don't exist. Hence, in these specific versions these values aren't set, and the default values are used.
This assumes that the PostgreSQL operand images include an OpenSSL
library that supports the TLSv1.3 version. If not, or if your client
applications need a lower version number, you need to manually configure
it in the PostgreSQL configuration as any other Postgres GUC.