New to KubeDB? Please start here.

Run Pgpool with TLS/SSL (Transport Encryption)

KubeDB supports providing TLS/SSL encryption (via, sslMode and clientAuthMode) for Pgpool. This tutorial will show you how to use KubeDB to run a Pgpool database with TLS/SSL encryption.

Before You Begin

  • At first, you need to have a Kubernetes cluster, and the kubectl command-line tool must be configured to communicate with your cluster. If you do not already have a cluster, you can create one by using kind.

  • Install cert-manger v1.0.0 or later to your cluster to manage your SSL/TLS certificates.

  • Now, install KubeDB cli on your workstation and KubeDB operator in your cluster following the steps here.

  • To keep things isolated, this tutorial uses a separate namespace called demo throughout this tutorial.

    $ kubectl create ns demo
    namespace/demo created
    

Note: YAML files used in this tutorial are stored in docs/examples/pgpool folder in GitHub repository kubedb/docs.

Overview

KubeDB uses following crd fields to enable SSL/TLS encryption in Mongodb.

  • spec:
    • sslMode
    • tls:
      • issuerRef
      • certificate
    • clientAuthMode

Read about the fields in details in pgpool concept,

sslMode enables TLS/SSL or mixed TLS/SSL used for all network connections. The value of sslMode field can be one of the following:

ValueDescription
disabledThe server does not use TLS/SSL.
requireThe server uses and accepts only TLS/SSL encrypted connections.
verify-caThe server uses and accepts only TLS/SSL encrypted connections and client want to be sure that client connect to a server that client trust.
verify-fullThe server uses and accepts only TLS/SSL encrypted connections and client want to be sure that client connect to a server client trust, and that it’s the one client specify.

The specified ssl mode will be used by health checker and exporter of Pgpool.

The value of clientAuthMode field can be one of the following:

ValueDescription
scramThe server uses scram-sha-256 authentication method to authenticate the users.
md5The server uses md5 authentication method to authenticate the users.
certThe server uses tls certificates to authenticate the users and for this sslMode must not be disabled

The pool_hba.conf of Pgpool will have the configuration based on the specified clientAuthMode.

When, SSLMode is anything other than disabled, users must specify the tls.issuerRef field. KubeDB uses the issuer or clusterIssuer referenced in the tls.issuerRef field, and the certificate specs provided in tls.certificate to generate certificate secrets. These certificate secrets are then used to generate required certificates including ca.pem, tls.crt and tls.key.

Create Issuer/ ClusterIssuer

We are going to create an example Issuer that will be used throughout the duration of this tutorial to enable SSL/TLS in Pgpool. Alternatively, you can follow this cert-manager tutorial to create your own Issuer.

  • Start off by generating you ca certificates using openssl.
openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout ./ca.key -out ./ca.crt -subj "/CN=pgpool/O=kubedb"
  • Now create a ca-secret using the certificate files you have just generated.
kubectl create secret tls pgpool-ca \
     --cert=ca.crt \
     --key=ca.key \
     --namespace=demo

Now, create an Issuer using the ca-secret you have just created. The YAML file looks like this:

apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
  name: pgpool-ca-issuer
  namespace: demo
spec:
  ca:
    secretName: pgpool-ca

Apply the YAML file:

$ kubectl create -f https://github.com/kubedb/docs/raw/v2024.8.21/docs/examples/pgpool/tls/issuer.yaml
issuer.cert-manager.io/pgpool-ca-issuer created

Prepare Postgres

Prepare a KubeDB Postgres cluster using this tutorial, or you can use any externally managed postgres but in that case you need to create an appbinding yourself. In this tutorial we will use 3 node Postgres cluster named ha-postgres.

TLS/SSL encryption in Pgpool

Below is the YAML for Pgpool with TLS enabled:

apiVersion: kubedb.com/v1alpha2
kind: Pgpool
metadata:
  name: pp-tls
  namespace: demo
spec:
  version: "4.5.0"
  replicas: 1
  postgresRef:
    name: ha-postgres
    namespace: demo
  sslMode: require
  clientAuthMode: cert
  tls:
    issuerRef:
      apiGroup: cert-manager.io
      name: pgpool-ca-issuer
      kind: Issuer
    certificates:
      - alias: server
        subject:
          organizations:
            - kubedb:server
        dnsNames:
          - localhost
        ipAddresses:
          - "127.0.0.1"
  syncUsers: true
  deletionPolicy: WipeOut

Deploy Pgpool

$ kubectl create -f https://github.com/kubedb/docs/raw/v2024.8.21/docs/examples/pgpool/tls/pgpool-ssl.yaml
pgpool.kubedb.com/pp-tls created

Now, wait until pp-tls created has status Ready. i.e,

$ watch kubectl get pp -n demo
Every 2.0s: kubectl get pgpool -n demo
NAME     TYPE                  VERSION   STATUS   AGE
pp-tls   kubedb.com/v1alpha2   4.5.0     Ready    60s

Verify TLS/SSL in Pgpool

Now, connect to this database through psql and verify if SSLMode has been set up as intended (i.e, require).

$ kubectl describe secret -n demo pp-tls-client-cert
Name:         pp-tls-client-cert
Namespace:    demo
Labels:       app.kubernetes.io/component=connection-pooler
              app.kubernetes.io/instance=pp-tls
              app.kubernetes.io/managed-by=kubedb.com
              app.kubernetes.io/name=pgpools.kubedb.com
              controller.cert-manager.io/fao=true
Annotations:  cert-manager.io/alt-names: 
              cert-manager.io/certificate-name: pp-tls-client-cert
              cert-manager.io/common-name: postgres
              cert-manager.io/ip-sans: 
              cert-manager.io/issuer-group: cert-manager.io
              cert-manager.io/issuer-kind: Issuer
              cert-manager.io/issuer-name: pgpool-ca-issuer
              cert-manager.io/uri-sans: 

Type:  kubernetes.io/tls

Data
====
tls.key:  1675 bytes
ca.crt:   1151 bytes
tls.crt:  1131 bytes

Now, Lets save the client cert and key to two different files:

$ kubectl get secrets -n demo pp-tls-client-cert -o jsonpath='{.data.tls\.crt}' | base64 -d > client.crt
$ cat client.crt
-----BEGIN CERTIFICATE-----
MIIDFjCCAf6gAwIBAgIRAO9tAQn/9lqHN4Pfi+UCe2IwDQYJKoZIhvcNAQELBQAw
IjEPMA0GA1UEAwwGcGdwb29sMQ8wDQYDVQQKDAZrdWJlZGIwHhcNMjQwNzE2MTAz
NzEzWhcNMjQxMDE0MTAzNzEzWjATMREwDwYDVQQDEwhwb3N0Z3JlczCCASIwDQYJ
KoZIhvcNAQEBBQADggEPADCCAQoCggEBAMo7Wikoc8XMAYorLO3lRJbcebiO+8ij
cI96UZ0SMzf7edyBbO1vVrlrBean9toCcW8Wf43o+Q+jRnvZJzzVnfV2C7gLeabC
o1I/g0JUmHdTxnIOLl6C4pvyoYZt4qB/cDorj89u6NIWnzs/fjFhYwCePQfPo7vM
eIeb8Mjngf77Cj5XENFpxR7+2Uy7SrpkeBoxuoPxrnStcwOIE06MbEHsLzGS59Kg
8qVLSyZAdguo8hBVV96gaRzW41uuf6MtBrrvwbv5IwtY9iHhlS9I3Uk+abnEnO6X
5aPUIQMcZixV2NXyEWZpbkoalvsywNcwNO/ESfN1DNA1oXknTknKeLkCAwEAAaNW
MFQwDgYDVR0PAQH/BAQDAgWgMBMGA1UdJQQMMAoGCCsGAQUFBwMCMAwGA1UdEwEB
/wQCMAAwHwYDVR0jBBgwFoAUXmZGbZwUM7ojagZqJW24+Z+c2PMwDQYJKoZIhvcN
AQELBQADggEBAG0+bEOLTAXeYUIeyA5+aqCs4WTxT4KJo0jY8vWjNH2que4fydDI
860ZP/RWogO+dpVKWYmvmYVkCcNalYs5HnGJDhs+4IyiKcARcKWimuYXy3FtrLWu
UzKACXptOpnLKzD9v1vv1HfyfeB1hXyaEJLUuBPDGb05YSNFehcipbGFBHSWFv13
rMQCOKGt8R0JJUXR0fcuDEGKv+jpz5P+n5dBtPQ40CrE34mhpa3m00Y64X4PVDI6
RusaLKyNGkaU+15WErg44/zM3LayvMImRnnoIttO7NkOe/9ige8C3hgEjZoivZKM
0Jc7koXlrnszBH2K/MOst9kHRTPk0VVmxBo=
-----END CERTIFICATE-----
$ kubectl get secrets -n demo pp-tls-client-cert -o jsonpath='{.data.tls\.key}' | base64 -d > client.key
$ cat client.key
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAyjtaKShzxcwBiiss7eVEltx5uI77yKNwj3pRnRIzN/t53IFs
7W9WuWsF5qf22gJxbxZ/jej5D6NGe9knPNWd9XYLuAt5psKjUj+DQlSYd1PGcg4u
XoLim/Khhm3ioH9wOiuPz27o0hafOz9+MWFjAJ49B8+ju8x4h5vwyOeB/vsKPlcQ
0WnFHv7ZTLtKumR4GjG6g/GudK1zA4gTToxsQewvMZLn0qDypUtLJkB2C6jyEFVX
3qBpHNbjW65/oy0Guu/Bu/kjC1j2IeGVL0jdST5pucSc7pflo9QhAxxmLFXY1fIR
ZmluShqW+zLA1zA078RJ83UM0DWheSdOScp4uQIDAQABAoIBAQC7PPSfQsreCaIr
UQpKQImevAcer5PDEj/3N6M0sFMns/gCBvrZYsqC5eoSwtS0yKpJ1iTHOTrQFbX+
mPHRS17ykxcKkeVSVsdsMU3QLg7z/Gax1xtrefdht+WBV2AKhbNcyFRgFCoPyc4n
xwOJqMdHHTsYblEEYa3+sIzhFiev8Qv/eORQGcA/RFWier4sIZMGlhz/2aRmWXqr
9g+3g1VdMqor/0QX/MzAmuffoeGK/lSDZZLZ0fjZHxTXT56hMknzHkOoz3XK/BXj
AnOAosdg/MXCZEiP13aLMYcZ13Lx9txdcgBIU2tZmssNiN9XiGq+tcaYwnbqF7YS
mwyyS4NZAoGBANO3qMoRP3OOR5butEy03K0nLv8oMEgFy4nkFUBsCnr9+ABgZrUn
c/i8pt1kuwTElkV2MMNgCrNIZK6EAWoeuVqzpEwkI/F5FDvyOcXq76okpd21IoqE
PWtn7CR0VQqRfn6XWhU3lle8VrujE+w4OJqWE7a9x1AfxqLqqY2aAgfLAoGBAPSH
ynOi8+aM7LxCTGrROXVE8E4YWaMegLNkYj6WygQYxsww7eQ3Jk6cm+vVgwqIdZyW
hcsDzNutlMBuLreQwCP+asciHBhw8F58Z+6TZO/cBShTq+gXCWMG2zXP5pmxqrKD
UcyQ5F7WcNl+sv9zUYrPry+Jt2zdDzbPsfpyigkLAoGAIaH8a1VAGjBRCRYUiFb2
837VBW2x9c8N3XLhOWGwbIdp3U1zI3YIA0ycyXDWENTV9mTnLDJWoNJwRBTuUJhe
45zEDeBz4UlVwIwjR2CiAApgWw8KVKzbQPO6XLQqSkqAqMWMZvB0rq1ZrecjJBRu
UYhjy1Tsk7roiDr1Amyjw+8CgYABt2JIZYBowdx3hc+bgFRy6kT1h145suEcYTv/
THemh7X9gOpqi6iNLLQ7d4gv7r1EmBngTuqFMDa3Ew7o4u82UXbWZvrjgQdu4lio
aAhxVo4CtnOicWbzdvza59aqhYC5OAq+8NVphP/NxwHioSCVZNfJ8aGD9hlBPTv2
kg89+QKBgAQDWZkq2mPZMmb+ltW1TZO2HqmEXBP9plgYGfrSjpofTjsBzykoaHnA
J/ocHs2cNkW8arrhiZQzDyokZRc1j5+PIYLfXZ1gSK7WfOe6HO/667eCNuoEcfDv
w8MtuCJgbYP8J0BXun982+EnLkuyDAoyX9GvEqyGQagme1ENiwFm
-----END RSA PRIVATE KEY-----

Now, if you see the common name of the client.crt you can see,

$ openssl x509 -in client.crt -inform PEM -subject -nameopt RFC2253 -noout
subject=CN=postgres

Here common name of the client certificate is important if you want to connect with the client certificate, the username must match the common name of the certificate. Here, we can see the common name(CN) is, postgres. So, we will use postgres user to connect with Pgpool.

Now, we can connect using subject=CN=postgres to connect to the psql,

$ psql "sslmode=require port=9999 host=localhost dbname=postgres user=postgres sslrootcert=ca.crt sslcert=client.crt sslkey=client.key"
psql (16.3 (Ubuntu 16.3-1.pgdg22.04+1), server 16.1)
SSL connection (protocol: TLSv1.3, cipher: TLS_AES_256_GCM_SHA384, compression: off)
Type "help" for help.

postgres=# 

We are connected to the postgres database. Let’s run some command to verify the sslMode and the user,

postgres=# SELECT
    usename,
    ssl
FROM
    pg_stat_ssl
JOIN
    pg_stat_activity
ON
    pg_stat_ssl.pid = pg_stat_activity.pid;
 usename  | ssl 
----------+-----
 postgres | t
 postgres | t
 postgres | t
(3 rows)

postgres=# \q

You can see here that, postgres user with ssl status as t or true.

Changing the SSLMode & ClusterAuthMode

User can update sslMode & clientAuthMode if needed. Some changes may be invalid from pgpool end, like using sslMode: disabled with clientAuthMode: cert.

The good thing is, KubeDB operator will throw error for invalid SSL specs while creating/updating the Pgpool object. i.e.,

$ kubectl patch -n demo pp/pp-tls -p '{"spec":{"sslMode": "disabled","clientAuthMode": "cert"}}' --type="merge"
The Pgpool "pp-tls" is invalid: spec.sslMode: Unsupported value: "disabled": supported values: "disable", "allow", "prefer", "require", "verify-ca", "verify-full"

Note: There is no official support for Pgpool with the Postgres cluster having clientAuthMode as cert. Check here.

Cleaning up

To clean up the Kubernetes resources created by this tutorial, run:

kubectl delete pgpool -n demo pp-tls
kubectl delete issuer -n demo pp-ca-issuer
kubectl delete ns demo

Next Steps