From 334af951da96515e835464a1896f9848addc26ab Mon Sep 17 00:00:00 2001 From: Hiranmoy Das Chowdhury <49994927+HiranmoyChowdhury@users.noreply.github.com> Date: Tue, 4 Feb 2025 19:29:40 +0600 Subject: [PATCH] Add PGBouncer TLS & Reconfigure TLS (#736) Signed-off-by: Hiranmoy Das Chowdhury --- .../pgbouncer/reconfigure-tls/add-tls.yaml | 24 + .../reconfigure-tls/change-issuer.yaml | 14 + .../pgbouncer/reconfigure-tls/issuer.yaml | 8 + .../pgbouncer/reconfigure-tls/new-issuer.yaml | 8 + .../pgbouncer/reconfigure-tls/pb.yaml | 23 + .../pgbouncer/reconfigure-tls/remove-tls.yaml | 14 + .../pgbouncer/reconfigure-tls/rotate-tls.yaml | 11 + docs/examples/pgbouncer/tls/issuer.yaml | 8 + .../examples/pgbouncer/tls/pgbouncer-ssl.yaml | 38 ++ .../pgbouncer/reconfigure-tls/_index.md | 10 + .../pgbouncer/reconfigure-tls/overview.md | 54 ++ .../reconfigure-tls/reconfigure-tls.md | 628 ++++++++++++++++++ docs/guides/pgbouncer/tls/_index.md | 10 + docs/guides/pgbouncer/tls/configure_ssl.md | 324 +++++++++ docs/guides/pgbouncer/tls/overview.md | 71 ++ .../pgbouncer/pb-reconfigure-tls.png | Bin 0 -> 81555 bytes .../day-2-operation/pgbouncer/pb-tls.svg | 4 + 17 files changed, 1249 insertions(+) create mode 100644 docs/examples/pgbouncer/reconfigure-tls/add-tls.yaml create mode 100644 docs/examples/pgbouncer/reconfigure-tls/change-issuer.yaml create mode 100644 docs/examples/pgbouncer/reconfigure-tls/issuer.yaml create mode 100644 docs/examples/pgbouncer/reconfigure-tls/new-issuer.yaml create mode 100644 docs/examples/pgbouncer/reconfigure-tls/pb.yaml create mode 100644 docs/examples/pgbouncer/reconfigure-tls/remove-tls.yaml create mode 100644 docs/examples/pgbouncer/reconfigure-tls/rotate-tls.yaml create mode 100644 docs/examples/pgbouncer/tls/issuer.yaml create mode 100644 docs/examples/pgbouncer/tls/pgbouncer-ssl.yaml create mode 100644 docs/guides/pgbouncer/reconfigure-tls/_index.md create mode 100644 docs/guides/pgbouncer/reconfigure-tls/overview.md create mode 100644 docs/guides/pgbouncer/reconfigure-tls/reconfigure-tls.md create mode 100755 docs/guides/pgbouncer/tls/_index.md create mode 100644 docs/guides/pgbouncer/tls/configure_ssl.md create mode 100644 docs/guides/pgbouncer/tls/overview.md create mode 100644 docs/images/day-2-operation/pgbouncer/pb-reconfigure-tls.png create mode 100644 docs/images/day-2-operation/pgbouncer/pb-tls.svg diff --git a/docs/examples/pgbouncer/reconfigure-tls/add-tls.yaml b/docs/examples/pgbouncer/reconfigure-tls/add-tls.yaml new file mode 100644 index 000000000..63ea17675 --- /dev/null +++ b/docs/examples/pgbouncer/reconfigure-tls/add-tls.yaml @@ -0,0 +1,24 @@ +apiVersion: ops.kubedb.com/v1alpha1 +kind: PgBouncerOpsRequest +metadata: + name: add-tls + namespace: demo +spec: + type: ReconfigureTLS + databaseRef: + name: pb + tls: + sslMode: verify-full + clientAuthMode: md5 + issuerRef: + name: pb-issuer + kind: Issuer + apiGroup: "cert-manager.io" + certificates: + - alias: client + subject: + organizations: + - pgbouncer + organizationalUnits: + - client + apply: Always \ No newline at end of file diff --git a/docs/examples/pgbouncer/reconfigure-tls/change-issuer.yaml b/docs/examples/pgbouncer/reconfigure-tls/change-issuer.yaml new file mode 100644 index 000000000..f483e3b23 --- /dev/null +++ b/docs/examples/pgbouncer/reconfigure-tls/change-issuer.yaml @@ -0,0 +1,14 @@ +apiVersion: ops.kubedb.com/v1alpha1 +kind: PgBouncerOpsRequest +metadata: + name: change-issuer + namespace: demo +spec: + type: ReconfigureTLS + databaseRef: + name: pb + tls: + issuerRef: + name: pb-new-issuer + kind: Issuer + apiGroup: "cert-manager.io" \ No newline at end of file diff --git a/docs/examples/pgbouncer/reconfigure-tls/issuer.yaml b/docs/examples/pgbouncer/reconfigure-tls/issuer.yaml new file mode 100644 index 000000000..c1c587fd3 --- /dev/null +++ b/docs/examples/pgbouncer/reconfigure-tls/issuer.yaml @@ -0,0 +1,8 @@ +apiVersion: cert-manager.io/v1 +kind: Issuer +metadata: + name: pb-issuer + namespace: demo +spec: + ca: + secretName: pgbouncer-ca \ No newline at end of file diff --git a/docs/examples/pgbouncer/reconfigure-tls/new-issuer.yaml b/docs/examples/pgbouncer/reconfigure-tls/new-issuer.yaml new file mode 100644 index 000000000..8c2a8c6b8 --- /dev/null +++ b/docs/examples/pgbouncer/reconfigure-tls/new-issuer.yaml @@ -0,0 +1,8 @@ +apiVersion: cert-manager.io/v1 +kind: Issuer +metadata: + name: pb-new-issuer + namespace: demo +spec: + ca: + secretName: pgbouncer-new-ca \ No newline at end of file diff --git a/docs/examples/pgbouncer/reconfigure-tls/pb.yaml b/docs/examples/pgbouncer/reconfigure-tls/pb.yaml new file mode 100644 index 000000000..07a3d82a3 --- /dev/null +++ b/docs/examples/pgbouncer/reconfigure-tls/pb.yaml @@ -0,0 +1,23 @@ +apiVersion: kubedb.com/v1 +kind: PgBouncer +metadata: + name: pb + namespace: demo +spec: + replicas: 1 + version: "1.18.0" + database: + syncUsers: true + databaseName: "postgres" + databaseRef: + name: "ha-postgres" + namespace: demo + connectionPool: + poolMode: session + port: 5432 + reservePoolSize: 5 + maxClientConnections: 87 + defaultPoolSize: 2 + minPoolSize: 1 + authType: md5 + deletionPolicy: WipeOut \ No newline at end of file diff --git a/docs/examples/pgbouncer/reconfigure-tls/remove-tls.yaml b/docs/examples/pgbouncer/reconfigure-tls/remove-tls.yaml new file mode 100644 index 000000000..bdadcf158 --- /dev/null +++ b/docs/examples/pgbouncer/reconfigure-tls/remove-tls.yaml @@ -0,0 +1,14 @@ +apiVersion: ops.kubedb.com/v1alpha1 +kind: PgBouncerOpsRequest +metadata: + name: remove-tls + namespace: demo +spec: + type: ReconfigureTLS + databaseRef: + name: pb + tls: + clientAuthMode: md5 + remove: true + timeout: 5m + apply: IfReady \ No newline at end of file diff --git a/docs/examples/pgbouncer/reconfigure-tls/rotate-tls.yaml b/docs/examples/pgbouncer/reconfigure-tls/rotate-tls.yaml new file mode 100644 index 000000000..cd6bfc3db --- /dev/null +++ b/docs/examples/pgbouncer/reconfigure-tls/rotate-tls.yaml @@ -0,0 +1,11 @@ +apiVersion: ops.kubedb.com/v1alpha1 +kind: PgBouncerOpsRequest +metadata: + name: rotate-tls + namespace: demo +spec: + type: ReconfigureTLS + databaseRef: + name: pb + tls: + rotateCertificates: true \ No newline at end of file diff --git a/docs/examples/pgbouncer/tls/issuer.yaml b/docs/examples/pgbouncer/tls/issuer.yaml new file mode 100644 index 000000000..965f5e9a6 --- /dev/null +++ b/docs/examples/pgbouncer/tls/issuer.yaml @@ -0,0 +1,8 @@ +apiVersion: cert-manager.io/v1 +kind: Issuer +metadata: + name: pgbouncer-ca-issuer + namespace: demo +spec: + ca: + secretName: pgbouncer-ca \ No newline at end of file diff --git a/docs/examples/pgbouncer/tls/pgbouncer-ssl.yaml b/docs/examples/pgbouncer/tls/pgbouncer-ssl.yaml new file mode 100644 index 000000000..e35f61fcc --- /dev/null +++ b/docs/examples/pgbouncer/tls/pgbouncer-ssl.yaml @@ -0,0 +1,38 @@ +apiVersion: kubedb.com/v1 +kind: PgBouncer +metadata: + name: pb-tls + namespace: demo +spec: + replicas: 1 + version: "1.18.0" + database: + syncUsers: true + databaseName: "postgres" + databaseRef: + name: "pg" + namespace: demo + connectionPool: + poolMode: session + port: 5432 + reservePoolSize: 5 + maxClientConnections: 87 + defaultPoolSize: 2 + minPoolSize: 1 + authType: md5 + deletionPolicy: WipeOut + sslMode: verify-ca + tls: + issuerRef: + apiGroup: cert-manager.io + name: pb-ca-issuer + kind: Issuer + certificates: + - alias: server + subject: + organizations: + - kubedb:server + dnsNames: + - localhost + ipAddresses: + - "127.0.0.1" \ No newline at end of file diff --git a/docs/guides/pgbouncer/reconfigure-tls/_index.md b/docs/guides/pgbouncer/reconfigure-tls/_index.md new file mode 100644 index 000000000..ed86414ff --- /dev/null +++ b/docs/guides/pgbouncer/reconfigure-tls/_index.md @@ -0,0 +1,10 @@ +--- +title: Reconfigure PgBouncer TLS/SSL +menu: + docs_{{ .version }}: + identifier: pb-reconfigure-tls + name: Reconfigure TLS/SSL + parent: pb-pgbouncer-guides + weight: 46 +menu_name: docs_{{ .version }} +--- diff --git a/docs/guides/pgbouncer/reconfigure-tls/overview.md b/docs/guides/pgbouncer/reconfigure-tls/overview.md new file mode 100644 index 000000000..b7aced43f --- /dev/null +++ b/docs/guides/pgbouncer/reconfigure-tls/overview.md @@ -0,0 +1,54 @@ +--- +title: Reconfiguring TLS of PgBouncer +menu: + docs_{{ .version }}: + identifier: pb-reconfigure-tls-overview + name: Overview + parent: pb-reconfigure-tls + weight: 10 +menu_name: docs_{{ .version }} +section_menu_id: guides +--- + +> New to KubeDB? Please start [here](/docs/README.md). + +# Reconfiguring TLS of PgBouncer + +This guide will give an overview on how KubeDB Ops-manager operator reconfigures TLS configuration i.e. add TLS, remove TLS, update issuer/cluster issuer or Certificates and rotate the certificates of a `PgBouncer`. + +## Before You Begin + +- You should be familiar with the following `KubeDB` concepts: + - [PgBouncer](/docs/guides/pgbouncer/concepts/pgbouncer.md) + - [PgBouncerOpsRequest](/docs/guides/pgbouncer/concepts/opsrequest.md) + +## How Reconfiguring PgBouncer TLS Configuration Process Works + +The following diagram shows how KubeDB Ops-manager operator reconfigures TLS of a `PgBouncer`. Open the image in a new tab to see the enlarged version. + +
+  Reconfiguring TLS process of PgBouncer +
Fig: Reconfiguring TLS process of PgBouncer
+
+ +The Reconfiguring PgBouncer TLS process consists of the following steps: + +1. At first, a user creates a `PgBouncer` Custom Resource Object (CRO). + +2. `KubeDB` Provisioner operator watches the `PgBouncer` CRO. + +3. When the operator finds a `PgBouncer` CR, it creates `PetSet` and related necessary stuff like secrets, services, etc. + +4. Then, in order to reconfigure the TLS configuration of the `PgBouncer` the user creates a `PgBouncerOpsRequest` CR with desired information. + +5. `KubeDB` Ops-manager operator watches the `PgBouncerOpsRequest` CR. + +6. When it finds a `PgBouncerOpsRequest` CR, it pauses the `PgBouncer` object which is referred from the `PgBouncerOpsRequest`. So, the `KubeDB` Provisioner operator doesn't perform any operations on the `PgBouncer` object during the reconfiguring TLS process. + +7. Then the `KubeDB` Ops-manager operator will add, remove, update or rotate TLS configuration based on the Ops Request yaml. + +8. Then the `KubeDB` Ops-manager operator will restart all the Pods of the pgbouncer so that they restart with the new TLS configuration defined in the `PgBouncerOpsRequest` CR. + +9. After the successful reconfiguring of the `PgBouncer` TLS, the `KubeDB` Ops-manager operator resumes the `PgBouncer` object so that the `KubeDB` Provisioner operator resumes its usual operations. + +In the next docs, we are going to show a step-by-step guide on reconfiguring TLS configuration of a PgBouncer using `PgBouncerOpsRequest` CRD. \ No newline at end of file diff --git a/docs/guides/pgbouncer/reconfigure-tls/reconfigure-tls.md b/docs/guides/pgbouncer/reconfigure-tls/reconfigure-tls.md new file mode 100644 index 000000000..5842502f5 --- /dev/null +++ b/docs/guides/pgbouncer/reconfigure-tls/reconfigure-tls.md @@ -0,0 +1,628 @@ +--- +title: Reconfigure PgBouncer TLS/SSL Encryption +menu: + docs_{{ .version }}: + identifier: pb-reconfigure-tls-cluster + name: Reconfigure PgBouncer TLS/SSL Encryption + parent: pb-reconfigure-tls + weight: 10 +menu_name: docs_{{ .version }} +section_menu_id: guides +--- + +> New to KubeDB? Please start [here](/docs/README.md). + +# Reconfigure PgBouncer TLS/SSL (Transport Encryption) + +KubeDB supports reconfigure i.e. add, remove, update and rotation of TLS/SSL certificates, changing issuer for existing PgBouncer database via a PgBouncerOpsRequest. This tutorial will show you how to use KubeDB to reconfigure 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](https://kind.sigs.k8s.io/docs/user/quick-start/). + +- Install `cert-manger` v1.0.0 or later to your cluster to manage your SSL/TLS certificates from [here](https://cert-manager.io/docs/installation/). + +- Now, install KubeDB cli on your workstation and KubeDB operator in your cluster following the steps [here](/docs/setup/README.md). + +- To keep things isolated, this tutorial uses a separate namespace called `demo` throughout this tutorial. + + ```bash + $ kubectl create ns demo + namespace/demo created + ``` + +> Note: YAML files used in this tutorial are stored in [docs/examples/pgbouncer](https://github.com/kubedb/docs/tree/{{< param "info.version" >}}/docs/examples/pgbouncer) folder in GitHub repository [kubedb/docs](https://github.com/kubedb/docs). + +### Prepare Postgres +For a PgBouncer surely we will need a Postgres server so, prepare a KubeDB Postgres cluster using this [tutorial](/docs/guides/postgres/clustering/streaming_replication.md), or you can use any externally managed postgres but in that case you need to create an [appbinding](/docs/guides/pgbouncer/concepts/appbinding.md) yourself. In this tutorial we will use 3 node Postgres cluster named `ha-postgres`. + +Now, we are going to deploy a `PgBouncer` using a supported version by `KubeDB` operator. Then we are going to apply `PgBouncerOpsRequest` to reconfigure its configuration. + +### Prepare PgBouncer + +Now, we are going to deploy a `PgBouncer` with version `1.18.0`. + +## Add TLS to a PgBouncer database + +Here, We are going to create a PgBouncer database without TLS and then reconfigure the database to use TLS. + +### Deploy PgBouncer without TLS + +In this section, we are going to deploy a PgBouncer Replicaset database without TLS. In the next few sections we will reconfigure TLS using `PgBouncerOpsRequest` CRD. Below is the YAML of the `PgBouncer` CR that we are going to create, + +```yaml +apiVersion: kubedb.com/v1 +kind: PgBouncer +metadata: + name: pb + namespace: demo +spec: + replicas: 1 + version: "1.18.0" + database: + syncUsers: true + databaseName: "postgres" + databaseRef: + name: "ha-postgres" + namespace: demo + connectionPool: + poolMode: session + port: 5432 + reservePoolSize: 5 + maxClientConnections: 87 + defaultPoolSize: 2 + minPoolSize: 1 + authType: md5 + deletionPolicy: WipeOut +``` + +Let's create the `PgBouncer` CR we have shown above, + +```bash +$ kubectl create -f https://github.com/kubedb/docs/raw/{{< param "info.version" >}}/docs/examples/pgbouncer/reconfigure-tls/pb.yaml +pgbouncer.kubedb.com/pb created +``` + +Now, wait until `pb` has status `Ready`. i.e, + +```bash +$ kubectl get pb -n demo +NAME VERSION STATUS AGE +pb 1.18.0 Ready 131m + +$ kubectl dba describe pgbouncer pb -n demo +Name: pb +Namespace: demo +Labels: +Annotations: +API Version: kubedb.com/v1 +Kind: PgBouncer +Metadata: + Creation Timestamp: 2025-01-25T09:21:57Z + Finalizers: + kubedb.com + Generation: 2 + Resource Version: 157918 + UID: d3635680-b216-47db-8e4f-aac7e42f196c +Spec: + Auth Secret: + Name: pb-auth + Auto Ops: + Connection Pool: + Auth Type: md5 + Default Pool Size: 2 + Max Client Connections: 87 + Min Pool Size: 1 + Pool Mode: session + Port: 5432 + Reserve Pool Size: 5 + Database: + Database Name: postgres + Database Ref: + Name: ha-postgres + Namespace: demo + Sync Users: true + Deletion Policy: WipeOut + Health Checker: + Failure Threshold: 1 + Period Seconds: 10 + Timeout Seconds: 10 + Pod Template: + Controller: + Metadata: + Spec: + Containers: + Name: pgbouncer + Resources: + Limits: + Memory: 1Gi + Requests: + Cpu: 500m + Memory: 1Gi + Security Context: + Privileged: false + Run As Group: 70 + Run As User: 70 + Pod Placement Policy: + Name: default + Security Context: + Fs Group: 70 + Run As Group: 70 + Run As User: 70 + Service Account Name: pb + Replicas: 1 + Ssl Mode: disable + Version: 1.18.0 +Status: + Conditions: + Last Transition Time: 2025-01-25T09:22:17Z + Message: The KubeDB operator has started the provisioning of PgBouncer: demo/pb + Reason: DatabaseProvisioningStartedSuccessfully + Status: True + Type: ProvisioningStarted + Last Transition Time: 2025-01-25T09:22:29Z + Message: All desired replicas are ready. + Reason: AllReplicasReady + Status: True + Type: ReplicaReady + Last Transition Time: 2025-01-25T09:22:49Z + Message: pgBouncer demo/pb is accepting connection + Observed Generation: 2 + Reason: AcceptingConnection + Status: True + Type: AcceptingConnection + Last Transition Time: 2025-01-25T09:22:49Z + Message: pgBouncer demo/pb is ready + Observed Generation: 2 + Reason: AllReplicasReady + Status: True + Type: Ready + Last Transition Time: 2025-01-25T09:23:01Z + Message: The PgBouncer: demo/pb is successfully provisioned. + Observed Generation: 2 + Reason: DatabaseSuccessfullyProvisioned + Status: True + Type: Provisioned + Observed Generation: 2 + Phase: Ready +Events: +``` + +Now, we can verify that the TLS is disabled. + +$ kubectl exec -it -n demo pb-0 -- /bin/sh +cat /etc/config/pgbouncer.ini +[databases] +postgres= host=ha-postgres.demo.svc port=5432 dbname=postgres + +[pgbouncer] +max_client_conn = 87 +min_pool_size = 1 +reserve_pool_size = 5 +max_user_connections = 2 +listen_addr = * +admin_users = pgbouncer +pool_mode = session +reserve_pool_timeout = 5 +max_db_connections = 1 +logfile = /tmp/pgbouncer.log +auth_file = /var/run/pgbouncer/secret/userlist +listen_port = 5432 +default_pool_size = 2 +stats_period = 60 +pidfile = /tmp/pgbouncer.pid +auth_type = md5 +ignore_startup_parameters = extra_float_digits +``` +Here we can see `client_tls_sslmode` is not present. That means it is by default in `disable` mode. + +### Create Issuer/ ClusterIssuer + +Now, We are going to create an example `Issuer` that will be used to enable SSL/TLS in PgBouncer. Alternatively, you can follow this [cert-manager tutorial](https://cert-manager.io/docs/configuration/ca/) to create your own `Issuer`. + +- Start off by generating a ca certificates using openssl. + +```bash +$ openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout ./ca.key -out ./ca.crt -subj "/CN=pgbouncer/O=kubedb" +Generating a RSA private key +................+++++ +........................+++++ +writing new private key to './ca.key' +----- +``` + +- Now we are going to create a ca-secret using the certificate files that we have just generated. + +```bash +$ kubectl create secret tls pgbouncer-ca \ + --cert=ca.crt \ + --key=ca.key \ + --namespace=demo +secret/pgbouncer-ca created +``` + +Now, Let's create an `Issuer` using the `pgbouncer-ca` secret that we have just created. The `YAML` file looks like this: + +```yaml +apiVersion: cert-manager.io/v1 +kind: Issuer +metadata: + name: pb-issuer + namespace: demo +spec: + ca: + secretName: pgbouncer-ca +``` + +Let's apply the `YAML` file: + +```bash +$ kubectl create -f https://github.com/kubedb/docs/raw/{{< param "info.version" >}}/docs/examples/pgbouncer/reconfigure-tls/issuer.yaml +issuer.cert-manager.io/pb-issuer created +``` + +```bash +$ kubectl get issuer -n demo +NAME READY AGE +pb-issuer True 30s +``` +Issuer is ready(true). + +### Create PgBouncerOpsRequest + +In order to add TLS to the database, we have to create a `PgBouncerOpsRequest` CRO with our created issuer. Below is the YAML of the `PgBouncerOpsRequest` CRO that we are going to create, + +```yaml +apiVersion: ops.kubedb.com/v1alpha1 +kind: PgBouncerOpsRequest +metadata: + name: add-tls + namespace: demo +spec: + type: ReconfigureTLS + databaseRef: + name: pb + tls: + sslMode: verify-full + clientAuthMode: md5 + issuerRef: + name: pb-issuer + kind: Issuer + apiGroup: "cert-manager.io" + certificates: + - alias: client + subject: + organizations: + - pgbouncer + organizationalUnits: + - client + apply: Always +``` +Here, + +- `spec.databaseRef.name` specifies that we are performing reconfigure TLS operation on `pb` database. +- `spec.type` specifies that we are performing `ReconfigureTLS` on our database. +- `spec.tls.issuerRef` specifies the issuer name, kind and api group. +- `spec.tls.certificates` specifies the certificates. + +Let's create the `PgBouncerOpsRequest` CR we have shown above, + +```bash +$ kubectl apply -f https://github.com/kubedb/docs/raw/{{< param "info.version" >}}/docs/examples/pgbouncer/reconfigure-tls/add-tls.yaml +pgbounceropsrequest.ops.kubedb.com/add-tls created +``` + +#### Verify TLS Enabled Successfully + +Let's wait for `PgBouncerOpsRequest` to be `Successful`. Run the following command to watch `PgBouncerOpsRequest` CRO, + +```bash +$ kubectl get pbops -n demo add-tls +NAME TYPE STATUS AGE +add-tls ReconfigureTLS Successful 2m27s +``` + +We can see from the above output that the `PgBouncerOpsRequest` has succeeded. + +Now, Let's exec into a database primary pods to see if certificates are added there. +```bash +$ kubectl exec -it -n demo pb-0 -- /bin/sh +/ $ cat /etc/config/pgbouncer.ini +[databases] +postgres= host=ha-postgres.demo.svc port=5432 dbname=postgres + +[pgbouncer] +max_db_connections = 1 +max_user_connections = 2 +auth_type = md5 +ignore_startup_parameters = extra_float_digits +pidfile = /tmp/pgbouncer.pid +auth_file = /var/run/pgbouncer/secret/userlist +min_pool_size = 1 +stats_period = 60 +client_tls_cert_file = /var/run/pgbouncer/tls/serving/server/tls.crt +reserve_pool_timeout = 5 +pool_mode = session +max_client_conn = 87 +logfile = /tmp/pgbouncer.log +listen_addr = * +client_tls_sslmode = verify-full +admin_users = pgbouncer +listen_port = 5432 +reserve_pool_size = 5 +client_tls_ca_file = /var/run/pgbouncer/tls/serving/server/ca.crt +client_tls_key_file = /var/run/pgbouncer/tls/serving/server/tls.key +default_pool_size = 2 +``` +Here we can see the presence of `client_tls_sslmode`, `client_tls_cert_file`, `client_tls_ca_file` and `client_tls_key_file`. + +## Rotate Certificate + +Now we are going to rotate the certificate of this database. First let's check the current expiration date of the certificate. + +```bash +kubectl get secrets -n demo pb-client-cert -o jsonpath='{.data.ca\.crt}' | base64 -d | openssl x509 -noout -dates +notBefore=Jan 25 11:39:53 2025 GMT +notAfter=Jan 25 11:39:53 2026 GMT +``` + +So, the certificate will expire on this time `Jan 25 11:39:53 2026 GMT`. + +### Create PgBouncerOpsRequest + +Now we are going to increase it using a PgBouncerOpsRequest. Below is the yaml of the ops request that we are going to create, + +```yaml +apiVersion: ops.kubedb.com/v1alpha1 +kind: PgBouncerOpsRequest +metadata: + name: rotate-tls + namespace: demo +spec: + type: ReconfigureTLS + databaseRef: + name: pb + tls: + rotateCertificates: true + +``` + +Here, + +- `spec.databaseRef.name` specifies that we are performing reconfigure TLS operation on `pb` database. +- `spec.type` specifies that we are performing `ReconfigureTLS` on our database. +- `spec.tls.rotateCertificates` specifies that we want to rotate the certificate of this database. + +Let's create the `PgBouncerOpsRequest` CR we have shown above, + +```bash +$ kubectl apply -f https://github.com/kubedb/docs/raw/{{< param "info.version" >}}/docs/examples/pgbouncer/reconfigure-tls/rotate-tls.yaml +pgbounceropsrequest.ops.kubedb.com/rotate-tls created +``` + +#### Verify Certificate Rotated Successfully + +Let's wait for `PgBouncerOpsRequest` to be `Successful`. Run the following command to watch `PgBouncerOpsRequest` CRO, + +```bash +$ kubectl get pbops -n demo rotate-tls +NAME TYPE STATUS AGE +rotate-tls ReconfigureTLS Successful 109s +``` + +We can see from the above output that the `PgBouncerOpsRequest` has succeeded. And we can check that the tls.crt has been updated. +```bash +$ kubectl get secrets -n demo pb-client-cert -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -noout -dates +notBefore=Jan 25 11:53:14 2025 GMT +notAfter=Apr 25 11:53:14 2025 GMT + +$ kubectl get secrets -n demo pb-server-cert -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -noout -dates +notBefore=Jan 25 11:53:14 2025 GMT +notAfter=Apr 25 11:53:14 2025 GMT +``` + + +As we can see from the above output, the certificate has been rotated successfully. + +## Change Issuer/ClusterIssuer + +Now, we are going to change the issuer of this database. + +- Let's create a new ca certificate and key using a different subject `CN=ca-update,O=kubedb-updated`. + +```bash +$ openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout ./ca.key -out ./ca.crt -subj "/CN=pgbouncer/O=kubedb-updated" +........+....+.....+...+....+...+..+.+...+..+...+............+....+.........+..+.......+.....+..........+..+.+...+.....+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*.+..+.............+......+...........+....+.....+....+........+....+...+...+..+.......+......+..+.+.....+.+...+..+......+....+...........+.......+...+.........+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*.....+...........+.......+...+.....+....+..+.+..+............+.........+.+......+.........+......+.....+............+............+...+.+..+....+...+........+...+.+......+.........+........+.+..+...+.......+........+.+...........+...+....+..+...............+..........+...........+...+.+.....+.+...+............+...+...........+......+.......+...+...+..+............+..........+............+.........+.....+.+.....+....+...........+.+..+.+............+........+.......+........+......+..................+.......+........+...+...+....+..................+..+.......+...+........+....+.....+....+.........+...+...+......+...+..+..............................+...+......+......+.............+...+..+......+....+..+.........+............+....+...+........+...+.+........+.......+.....+...+......+..........+..+.......+.....+..........+...+........+....+..+.+..............+.............+...+..+..........+..+...................+..+...+.+...+...........+.+...+...........+................+..............+.........+......+....+..+..........+.....+.+..+...+....+.....+......+....+.........+..+...+....+......+..............+...+...+.+...........+...+.......+..+.+...........+...+.+.....+.+...+...+..............................+...+......+..............+.+........+.+....................+......+.........+.+...........+....+.....+......+.......+...+..+.+..+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +...........+.......+.....+...+....+..+...................+..+....+...+...+...+..+...+....+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*....+......+..+...+....+..+.+.........+...+......+..+.......+...+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*.............+.......+...............+...+...........+...+.+............+........+.........+.............+..+...+....+.....+................+...+..+...+.......+..+..........+.....+...+.............+..+...+.+..............+.+......+...+..+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +``` + +- Now we are going to create a new ca-secret using the certificate files that we have just generated. + +```bash +$ kubectl create secret tls pgbouncer-new-ca \ + --cert=ca.crt \ + --key=ca.key \ + --namespace=demo +secret/pgbouncer-new-ca created +``` + +Now, Let's create a new `Issuer` using the `pgbouncer-new-ca` secret that we have just created. The `YAML` file looks like this: + +```yaml +apiVersion: cert-manager.io/v1 +kind: Issuer +metadata: + name: pb-new-issuer + namespace: demo +spec: + ca: + secretName: pgbouncer-new-ca +``` + +Let's apply the `YAML` file: + +```bash +$ kubectl create -f https://github.com/kubedb/docs/raw/{{< param "info.version" >}}/docs/examples/pgbouncer/reconfigure-tls/new-issuer.yaml +issuer.cert-manager.io/pb-new-issuer created +``` + +### Create PgBouncerOpsRequest + +In order to use the new issuer to issue new certificates, we have to create a `PgBouncerOpsRequest` CRO with the newly created issuer. Below is the YAML of the `PgBouncerOpsRequest` CRO that we are going to create, + +```yaml +apiVersion: ops.kubedb.com/v1alpha1 +kind: PgBouncerOpsRequest +metadata: + name: change-issuer + namespace: demo +spec: + type: ReconfigureTLS + databaseRef: + name: pb + tls: + issuerRef: + name: pb-new-issuer + kind: Issuer + apiGroup: "cert-manager.io" +``` + +Here, + +- `spec.databaseRef.name` specifies that we are performing reconfigure TLS operation on `pb` database. +- `spec.type` specifies that we are performing `ReconfigureTLS` on our database. +- `spec.tls.issuerRef` specifies the issuer name, kind and api group. + +Let's create the `PgBouncerOpsRequest` CR we have shown above, + +```bash +$ kubectl apply -f https://github.com/kubedb/docs/raw/{{< param "info.version" >}}/docs/examples/pgbouncer/reconfigure-tls/change-issuer.yaml +pgbounceropsrequest.ops.kubedb.com/change-issuer created +``` + +#### Verify Issuer is changed successfully + +Let's wait for `PgBouncerOpsRequest` to be `Successful`. Run the following command to watch `PgBouncerOpsRequest` CRO, + +```bash +$ kubectl get pbops -n demo change-issuer +NAME TYPE STATUS AGE +change-issuer ReconfigureTLS Successful 104s +``` + +We can see from the above output that the `PgBouncerOpsRequest` has succeeded. + +Now, Let's exec into a database node and find out the ca subject to see if it matches the one we have provided. + +```bash +$ kubectl get secrets -n demo pb-client-cert -o jsonpath='{.data.ca\.crt}' | base64 -d | openssl x509 -noout -subject +subject=CN = pgbouncer, O = kubedb-updated + +$ kubectl get secrets -n demo pb-server-cert -o jsonpath='{.data.ca\.crt}' | base64 -d | openssl x509 -noout -subject +subject=CN = pgbouncer, O = kubedb-updated +``` +Now you can check [here](https://certlogik.com/decoder/). + +We can see from the above output that, the subject name matches the subject name of the new ca certificate that we have created. So, the issuer is changed successfully. + + +## Remove TLS from the Database + +Now, we are going to remove TLS from this database using a PgBouncerOpsRequest. + +### Create PgBouncerOpsRequest + +Below is the YAML of the `PgBouncerOpsRequest` CRO that we are going to create, + +```yaml +apiVersion: ops.kubedb.com/v1alpha1 +kind: PgBouncerOpsRequest +metadata: + name: remove-tls + namespace: demo +spec: + type: ReconfigureTLS + databaseRef: + name: pb + tls: + clientAuthMode: md5 + remove: true + timeout: 5m + apply: IfReady +``` + +Here, + +- `spec.databaseRef.name` specifies that we are performing reconfigure TLS operation on `pb` database. +- `spec.type` specifies that we are performing `ReconfigureTLS` on our database. +- `spec.tls.remove` specifies that we want to remove tls from this database. +- `spec.tls.clientAuthMode` defines clientAuthentication mode after removing tls. Possible values are `md5` `scram`. + + +Let's create the `PgBouncerOpsRequest` CR we have shown above, + +```bash +$ kubectl apply -f https://github.com/kubedb/docs/raw/{{< param "info.version" >}}/docs/examples/pgbouncer/reconfigure-tls/remove-tls.yaml +pgbounceropsrequest.ops.kubedb.com/remove-tls created +``` + +#### Verify TLS Removed Successfully + +Let's wait for `PgBouncerOpsRequest` to be `Successful`. Run the following command to watch `PgBouncerOpsRequest` CRO, + +```bash +$ kubectl get pbops -n demo remove-tls +NAME TYPE STATUS AGE +remove-tls ReconfigureTLS Successful 104s +``` + +Now first verify if this works in config. + +```bash +kubectl exec -it -n demo pb-0 -- /bin/sh +/ $ cat etc/config/pgbouncer.ini +[databases] +postgres= host=ha-postgres.demo.svc port=5432 dbname=postgres + +[pgbouncer] +max_db_connections = 1 +max_user_connections = 2 +pidfile = /tmp/pgbouncer.pid +logfile = /tmp/pgbouncer.log +listen_port = 5432 +pool_mode = session +max_client_conn = 87 +min_pool_size = 1 +default_pool_size = 2 +reserve_pool_size = 5 +admin_users = pgbouncer +listen_addr = * +auth_file = /var/run/pgbouncer/secret/userlist +reserve_pool_timeout = 5 +stats_period = 60 +auth_type = md5 +ignore_startup_parameters = extra_float_digits +``` + +SSL is off now. + +## Cleaning up + +To cleanup the Kubernetes resources created by this tutorial, run: + +```bash +kubectl delete pgbouncer -n demo pb +kubectl delete issuer -n demo pb-issuer pb-new-issuer +kubectl delete pgbounceropsrequest add-tls remove-tls rotate-tls change-issuer +kubectl delete ns demo +``` + +## Next Steps + +- Detail concepts of [PgBouncer object](/docs/guides/pgbouncer/concepts/pgbouncer.md). +- Monitor your PgBouncer database with KubeDB using [out-of-the-box Prometheus operator](/docs/guides/pgbouncer/monitoring/using-prometheus-operator.md). +- Monitor your PgBouncer database with KubeDB using [out-of-the-box builtin-Prometheus](/docs/guides/pgbouncer/monitoring/using-builtin-prometheus.md). +- Use [private Docker registry](/docs/guides/pgbouncer/private-registry/using-private-registry.md) to deploy PgBouncer with KubeDB. +- Use [kubedb cli](/docs/guides/pgbouncer/cli/cli.md) to manage databases like kubectl for Kubernetes. +- Detail concepts of [PgBouncer object](/docs/guides/pgbouncer/concepts/pgbouncer.md). +- Want to hack on KubeDB? Check our [contribution guidelines](/docs/CONTRIBUTING.md). diff --git a/docs/guides/pgbouncer/tls/_index.md b/docs/guides/pgbouncer/tls/_index.md new file mode 100755 index 000000000..a5aca0c89 --- /dev/null +++ b/docs/guides/pgbouncer/tls/_index.md @@ -0,0 +1,10 @@ +--- +title: Run PGBouncer with TLS +menu: + docs_{{ .version }}: + identifier: pb-tls + name: TLS/SSL Encryption + parent: pb-pgbouncer-guides + weight: 45 +menu_name: docs_{{ .version }} +--- diff --git a/docs/guides/pgbouncer/tls/configure_ssl.md b/docs/guides/pgbouncer/tls/configure_ssl.md new file mode 100644 index 000000000..d0ef41752 --- /dev/null +++ b/docs/guides/pgbouncer/tls/configure_ssl.md @@ -0,0 +1,324 @@ +--- +title: PgBouncer TLS/SSL Encryption +menu: + docs_{{ .version }}: + identifier: pb-tls-configure + name: PgBouncer_SSL + parent: pb-tls + weight: 20 +menu_name: docs_{{ .version }} +section_menu_id: guides +--- + +> New to KubeDB? Please start [here](/docs/README.md). + +# Run PgBouncer with TLS/SSL (Transport Encryption) + +KubeDB supports providing TLS/SSL encryption (via, `sslMode` and `connectionPool.authType`) for PgBouncer. This tutorial will show you how to use KubeDB to run a PgBouncer 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](https://kind.sigs.k8s.io/docs/user/quick-start/). + +- Install [`cert-manger`](https://cert-manager.io/docs/installation/) 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](/docs/setup/README.md). + +- To keep things isolated, this tutorial uses a separate namespace called `demo` throughout this tutorial. + + ```bash + $ kubectl create ns demo + namespace/demo created + ``` + +> Note: YAML files used in this tutorial are stored in [docs/examples/pgbouncer](https://github.com/kubedb/docs/tree/{{< param "info.version" >}}/docs/examples/pgbouncer) folder in GitHub repository [kubedb/docs](https://github.com/kubedb/docs). + +## Overview + +KubeDB uses following crd fields to enable SSL/TLS encryption in PgBouncer. + +- `spec:` + - `sslMode` + - `tls:` + - `issuerRef` + - `certificate` + - `connectionPool` + - `authType` + +Read about the fields in details in [pgbouncer concept](/docs/guides/pgbouncer/concepts/pgbouncer.md), + +`sslMode` enables TLS/SSL or mixed TLS/SSL used for all network connections. The value of `sslMode` field can be one of the following: + +| Value | Description | +|:-------------:|:--------------------------------------------------------------------------------------------------------------------------------------------| +| `disabled` | The server does not use TLS/SSL. | +| `allow` | If client requests TLS, it is used. If not, plain TCP is used. If the client presents a client certificate, it is not validated. | +| `prefer` | Same as allow. | +| `require` | Client must use TLS. If not, the client connection is rejected. If the client presents a client certificate, it is not validated. | +| `verify-ca` | Client must use TLS with valid client certificate. | +| `verify-full` | Same as verify-ca. | + +The specified ssl mode will be used by health checker and exporter of PgBouncer. + +The value of `connectionPool.authType` field can be one of the following: + +| Value | Description | +|:---------------:|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `scram-sha-256` | The server uses scram-sha-256 authentication method to authenticate the users. | +| `md5` | The server uses md5 authentication method to authenticate the users. | + +The `userlist.txt` of PgBouncer will have the configuration based on the specified AuthType. + +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 PgBouncer. Alternatively, you can follow this [cert-manager tutorial](https://cert-manager.io/docs/configuration/ca/) to create your own `Issuer`. + +- Start off by generating you ca certificates using openssl. + +```bash +openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout ./ca.key -out ./ca.crt -subj "/CN=pgbouncer/O=kubedb" +``` + +- Now create a ca-secret using the certificate files you have just generated. + +```bash +kubectl create secret tls pgbouncer-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: + +```yaml +apiVersion: cert-manager.io/v1 +kind: Issuer +metadata: + name: pgbouncer-ca-issuer + namespace: demo +spec: + ca: + secretName: pgbouncer-ca +``` + +Apply the `YAML` file: + +```bash +$ kubectl create -f https://github.com/kubedb/docs/raw/{{< param "info.version" >}}/docs/examples/pgbouncer/tls/issuer.yaml +issuer.cert-manager.io/pgbouncer-ca-issuer created +``` + +## Prepare Postgres +Prepare a KubeDB Postgres cluster using this [tutorial](/docs/guides/postgres/clustering/streaming_replication.md), or you can use any externally managed postgres but in that case you need to create an [appbinding](/docs/guides/pgbouncer/concepts/appbinding.md) yourself. In this tutorial we will use 3 node Postgres cluster named `ha-postgres`. + +## TLS/SSL encryption in PgBouncer + +Below is the YAML for PgBouncer with TLS enabled: + +```yaml +apiVersion: kubedb.com/v1 +kind: PgBouncer +metadata: + name: pb-tls + namespace: demo +spec: + replicas: 1 + version: "1.18.0" + database: + syncUsers: true + databaseName: "postgres" + databaseRef: + name: "pg" + namespace: demo + connectionPool: + poolMode: session + port: 5432 + reservePoolSize: 5 + maxClientConnections: 87 + defaultPoolSize: 2 + minPoolSize: 1 + authType: md5 + deletionPolicy: WipeOut + sslMode: verify-ca + tls: + issuerRef: + apiGroup: cert-manager.io + name: pb-ca-issuer + kind: Issuer + certificates: + - alias: server + subject: + organizations: + - kubedb:server + dnsNames: + - localhost + ipAddresses: + - "127.0.0.1" +``` + +### Deploy PgBouncer + +```bash +$ kubectl create -f https://github.com/kubedb/docs/raw/{{< param "info.version" >}}/docs/examples/pgbouncer/tls/pgbouncer-ssl.yaml +pgbouncer.kubedb.com/pb-tls created +``` + +Now, wait until `pb-tls created` has status `Ready`. i.e, + +```bash +$ watch kubectl get pb -n demo +Every 2.0s: kubectl get pgbouncer -n demo +NAME VERSION STATUS AGE +pb-tls 1.18.0 Ready 108s +``` + +### Verify TLS/SSL in PgBouncer + +Now, connect to this database through [psql](https://www.postgresql.org/docs/current/app-psql.html) and verify if `SSLMode` has been set up as intended (i.e, `require`). + +```bash +$ kubectl describe secret -n demo pb-tls-client-cert +Name: pb-tls-client-cert +Namespace: demo +Labels: app.kubernetes.io/component=connection-pooler + app.kubernetes.io/instance=pb-tls + app.kubernetes.io/managed-by=kubedb.com + app.kubernetes.io/name=pgbouncers.kubedb.com + controller.cert-manager.io/fao=true +Annotations: cert-manager.io/alt-names: + cert-manager.io/certificate-name: pb-tls-client-cert + cert-manager.io/common-name: pgbouncer + cert-manager.io/ip-sans: + cert-manager.io/issuer-group: cert-manager.io + cert-manager.io/issuer-kind: Issuer + cert-manager.io/issuer-name: pb-ca-issuer + cert-manager.io/uri-sans: + +Type: kubernetes.io/tls + +Data +==== +ca.crt: 1159 bytes +tls.crt: 1135 bytes +tls.key: 1679 bytes +``` + +Now, Lets save the client cert and key to two different files: + +```bash +$ kubectl get secrets -n demo pb-tls-client-cert -o jsonpath='{.data.tls\.crt}' | base64 -d > client.crt +$ cat client.crt +-----BEGIN CERTIFICATE----- +MIIDGTCCAgGgAwIBAgIQFzXjq6IExD5sjF7FW44NzTANBgkqhkiG9w0BAQsFADAl +MRIwEAYDVQQDDAlwZ2JvdW5jZXIxDzANBgNVBAoMBmt1YmVkYjAeFw0yNTAxMjMx +MDQ2MDBaFw0yNTA0MjMxMDQ2MDBaMBQxEjAQBgNVBAMTCXBnYm91bmNlcjCCASIw +DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANtr22zMM8A0k7tsPvXICpNWUAfW +1xqDrEv5dsHP04Pd8YwioCP6lrDSahV8jkFhI4jrLCy4RYYhC8nzf3QLTkYIPTEd +PfYaS9jTfNPgGHMD8hSKFfO+gXSidg+PzUW2x8/hA8SFq9rJwn3/b39DVL71E4aU +D8aJYPc51LsIr2JoiGb0qPNSPpud/4bma1GcqCgsChkMLzsn88vOg0B9a74RUSKd +W78I37N2xNUwS5M7mgNmpzKVIhBfs0h01F6vfTVzOwOl/C9as1uQGDCIRBx6ONyl +7r1SJCENuEEr4Q33iTmBLRBwy5HKGy+UHc58DZ1lLwBaJsQdujUcbEoRrbMCAwEA +AaNWMFQwDgYDVR0PAQH/BAQDAgWgMBMGA1UdJQQMMAoGCCsGAQUFBwMCMAwGA1Ud +EwEB/wQCMAAwHwYDVR0jBBgwFoAUfEEIxgLcuBXzCYzm48qnmkbxZvIwDQYJKoZI +hvcNAQELBQADggEBAAkFInhE2W8bVbuRM+PESMNDff3cfgH8bzi9A+iWDR0XmpBm +qLqq8zciebGmuqH8PLQr518U6dCI9g0iATfV/WQ6JlRFhxiO3h+7rAjwW77V49QM +06CkL2uSRk0GeO9a/VNXMmcNZGARgG+m7gYZJ/sOVnzlj5zEchfaH82FY5HnInRl +coSL5sY28QU1iS0bO3wHoFx6t8gzwluP/H040ImS60CE5t/b3njIgfWDHzhDOkKV +Rl66yC3j2YD8+Dvdl63Dp8r5KtWDvGAkiM8SVysASHnKAM/ipEqUoqyWBUT7gG/L +JbiZCRCTnewRU9/mzcn9FxxmAPt7yq9IEND1cMQ= +-----END CERTIFICATE----- +$ kubectl get secrets -n demo pb-tls-client-cert -o jsonpath='{.data.tls\.key}' | base64 -d > client.key +$ cat client.key +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEA22vbbMwzwDSTu2w+9cgKk1ZQB9bXGoOsS/l2wc/Tg93xjCKg +I/qWsNJqFXyOQWEjiOssLLhFhiELyfN/dAtORgg9MR099hpL2NN80+AYcwPyFIoV +876BdKJ2D4/NRbbHz+EDxIWr2snCff9vf0NUvvUThpQPxolg9znUuwivYmiIZvSo +81I+m53/huZrUZyoKCwKGQwvOyfzy86DQH1rvhFRIp1bvwjfs3bE1TBLkzuaA2an +MpUiEF+zSHTUXq99NXM7A6X8L1qzW5AYMIhEHHo43KXuvVIkIQ24QSvhDfeJOYEt +EHDLkcobL5QdznwNnWUvAFomxB26NRxsShGtswIDAQABAoIBAQCXOLNmPSnhapry +TbzqiS54ssC/VlqzJFJnngsxsbjVpe2mJer2QOr//FQucMRd3MOvxlyQiYMo2LeW +PGH3qR8N9vmtUrj0VtU1HzRllYlkIzEA5NYSQZZYuurg+LuBM2JsK2j8VR/Gzsxj +J9tA+zd5z8/gLUTeEKoqWMn7CRZOm/OhorKM2PnduniazZjF9w9PZwSUlIjJnBNi +rx21RvVUw7UGCw/5jVvsDENSAkt/RHAQySu3Zzbk+gbpyhq2VIa/SADhKO9BgjxG +EQxWNQbi8anmVtSneGngfeY/OOnlyahsdzuQ9l53Iz/o511897TePDgvz6mmGxhS +4ht1QWk5AoGBAPThrun/G26f/GUxQN5QTywj926UBqpmfiCHaIoP/UM2M7Kck8Um +bgmZu2M9FvSErmmvi/KYHUJlY9yGRHX+8TqP8RVxHi0MxaPjCq0Jv/IRcSM15qVl +IoIbGPtAQrXNe1crLTeJboQ5mY20ekzkj1q9KYWWC/0Jc/Xj5kcIdO1vAoGBAOVi +PkTanAN7lMfBVEDk2dpcZTzf17WM//LGsZ1G/KTFjHq7hNMHakhla2CbPEkCOt9l +HgUOKqROsFf8lyWNIUnllEhyHfoBFRweplub2Zh3Y/JkQONA3MohKbkO28ZvDJDg +5AZB/eaTB36URqEr6hHdI037MwACZxOSKxjRp+n9AoGAZceTcrBUT4NxXQG+q2gH +sBn20l/18UcOLyj4m0GQCypxDFCl3nBdleHuj42ph9HJyCVtblQo/Rq1CchIlh5z +VtrS4g2U9DZ1wusv2cHOpKb5NiBGEAJb+GWY2XzY/UU9eXp5nbaiV5S1LL+RgXoR +1y3+HwbBTtdp+g5R/L4YE0MCgYBqxBeHpNkJJfRSJcI5kkt0P50/gFC+yCo5rhHt +yqS9bNW+KpngP4tQtyQLizW8JbWRVVdrsvRWFeouifswF0hvRNSIA9XAD9DrjbiQ +2zGkra1vnQo2vHIIAveQk0HoUrfel06LOxwavkS2vf1B91azieJs4YcTcgrYKSi2 +HJ+zYQKBgQCKfewbwVLuexdW6yLrxwXuMAZljtHUQWe7Txx3k+bw+kAF46NEBlN2 +bZc0zaz8cEn8d7GWVGGGulZA7XxZM+Tr3uD1t/8AkiS/GwRKcXBOjzQZS08bnTVJ +BwIhO4g2OiLojS6dQxrXtj/miB3pTZbVed7QhYOBUGEFs3lUV+KEVQ== +``` + +Now, if you see the common name of the client.crt you can see, +```bash +$ openssl x509 -in client.crt -inform PEM -subject -nameopt RFC2253 -noout +subject=CN=pgbouncer +``` +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, `pgbouncer`. So, we will use pgbouncer user to connect with PgBouncer. + +Now, we can connect using `subject=CN=pgbouncer` to connect to the psql, + +```bash +$ psql "sslmode=require port=9999 host=localhost dbname=pgbouncer user=pgbouncer 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. + +pgbouncer=# +``` + +We are connected to the pgbouncer database. Let's run some command to verify the sslMode and the user, + +```bash +pgbouncer=# SHOW SERVERS; +type | user | database | state | addr | port | local_addr | local_port | connect_time | request_time | wait | wait_us | close_needed | ptr | link | remote_pid | tls | application_name +------+----------+----------+-------+---------------+------+------------+------------+-------------------------+-------------------------+------+---------+--------------+----------------+------+------------+-----+------------------ + S | postgres | postgres | idle | 10.96.125.227 | 5432 | 10.244.0.6 | 57524 | 2025-01-24 05:17:47 UTC | 2025-01-24 06:06:07 UTC | 0 | 0 | 0 | 0x70872bea80a0 | | 476 | | +(1 row) +~ +~ +(END) +pgbouncer=# exit +⏎ +``` + +## Changing the SSLMode & ClusterAuthMode + +User can update `sslMode` & `connectionPool.authType` if needed. Some changes may be invalid from pgbouncer end, like using `sslMode: disabled` with `connectionPool.authType: cert`. + +The good thing is, **KubeDB operator will throw error for invalid SSL specs while creating/updating the PgBouncer object.** i.e., + +```bash +$ kubectl patch -n demo pb/pb-tls -p '{"spec":{"sslMode": "disabled"}}' --type="merge" +The PgBouncer "pb-tls" is invalid: spec.sslMode: Unsupported value: "disabled": supported values: "disable", "allow", "prefer", "require", "verify-ca", "verify-full" +``` + +> Note: There is no official support from kubedb for PgBouncer to connect wit cert mode`. + +## Cleaning up + +To clean up the Kubernetes resources created by this tutorial, run: + +```bash +kubectl delete pgbouncer -n demo pb-tls +kubectl delete issuer -n demo pb-ca-issuer +kubectl delete ns demo +``` + +## Next Steps + +- Detail concepts of [PgBouncer object](/docs/guides/pgbouncer/concepts/pgbouncer.md). +- Detail concepts of [PgBouncerVersion object](/docs/guides/pgbouncer/concepts/catalog.md). +- Monitor your PgBouncer database with KubeDB using [out-of-the-box Prometheus operator](/docs/guides/pgbouncer/monitoring/using-prometheus-operator.md). +- Monitor your PgBouncer database with KubeDB using [out-of-the-box builtin-Prometheus](/docs/guides/pgbouncer/monitoring/using-builtin-prometheus.md). +- Want to hack on KubeDB? Check our [contribution guidelines](/docs/CONTRIBUTING.md). diff --git a/docs/guides/pgbouncer/tls/overview.md b/docs/guides/pgbouncer/tls/overview.md new file mode 100644 index 000000000..844ba9b66 --- /dev/null +++ b/docs/guides/pgbouncer/tls/overview.md @@ -0,0 +1,71 @@ +--- +title: PgBouncer TLS/SSL Encryption Overview +menu: + docs_{{ .version }}: + identifier: pb-tls-overview + name: Overview + parent: pb-tls + weight: 10 +menu_name: docs_{{ .version }} +section_menu_id: guides +--- + +> New to KubeDB? Please start [here](/docs/README.md). + +# PgBouncer TLS/SSL Encryption + +**Prerequisite :** To configure TLS/SSL in `PgBouncer`, `KubeDB` uses `cert-manager` to issue certificates. So first you have to make sure that the cluster has `cert-manager` installed. To install `cert-manager` in your cluster following steps [here](https://cert-manager.io/docs/installation/kubernetes/). + +To issue a certificate, the following crd of `cert-manager` is used: + +- `Issuer/ClusterIssuer`: Issuers, and ClusterIssuers represent certificate authorities (CAs) that are able to generate signed certificates by honoring certificate signing requests. All cert-manager certificates require a referenced issuer that is in a ready condition to attempt to honor the request. You can learn more details [here](https://cert-manager.io/docs/concepts/issuer/). + +- `Certificate`: `cert-manager` has the concept of Certificates that define a desired x509 certificate which will be renewed and kept up to date. You can learn more details [here](https://cert-manager.io/docs/concepts/certificate/). + +**PgBouncer CRD Specification :** + +KubeDB uses following crd fields to enable SSL/TLS encryption in `PgBouncer`. + +- `spec:` + - `sslMode` + - `tls:` + - `issuerRef` + - `certificates` + - `connectionPool` + - `authType` +Read about the fields in details from [pgbouncer concept](/docs/guides/pgbouncer/concepts/pgbouncer.md), + +When, `sslMode` is set to `require`, the 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 using `Issuer/ClusterIssuers` specification. These certificates secrets including `ca.crt`, `tls.crt` and `tls.key` etc. are used to configure `PgBouncer` server, exporter etc. respectively. + +## How TLS/SSL configures in PgBouncer + +The following figure shows how `KubeDB` enterprise used to configure TLS/SSL in PgBouncer. Open the image in a new tab to see the enlarged version. + +
+Deploy PgBouncer with TLS/SSL +
Fig: Deploy PgBouncer with TLS/SSL
+
+ +Deploying PgBouncer with TLS/SSL configuration process consists of the following steps: + +1. At first, a user creates a `Issuer/ClusterIssuer` cr. + +2. Then the user creates a `PgBouncer` cr which refers to the `Issuer/ClusterIssuer` cr that the user created in the previous step. + +3. `KubeDB` Provisioner operator watches for the `PgBouncer` cr. + +4. When it finds one, it creates `Secret`, `Service`, etc. for the `PgBouncer` database. + +5. `KubeDB` Ops-manager operator watches for `PgBouncer`, `Issuer/ClusterIssuer`, `Secret` and `Service`. + +6. When it finds all the resources(`PgBouncer`, `Issuer/ClusterIssuer`, `Secret`, `Service`), it creates `Certificates` by using `tls.issuerRef` and `tls.certificates` field specification from `PgBouncer` cr. + +7. `cert-manager` watches for certificates. + +8. When it finds one, it creates certificate secrets `tls-secrets`(server, client, exporter secrets etc.) that holds the actual certificate signed by the CA. + +9. `KubeDB` Provisioner operator watches for the Certificate secrets `tls-secrets`. + +10. When it finds all the tls-secret, it creates the related `Petset` so that PgBouncer database can be configured with TLS/SSL. + +In the next doc, we are going to show a step-by-step guide on how to configure a `PgBouncer` database with TLS/SSL. \ No newline at end of file diff --git a/docs/images/day-2-operation/pgbouncer/pb-reconfigure-tls.png b/docs/images/day-2-operation/pgbouncer/pb-reconfigure-tls.png new file mode 100644 index 0000000000000000000000000000000000000000..b9c95acd8bbe9b5cda590444fa4568397d519005 GIT binary patch literal 81555 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 %;}^zYDK?OZ^!=H;`21Ut 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 literal 0 HcmV?d00001 diff --git a/docs/images/day-2-operation/pgbouncer/pb-tls.svg b/docs/images/day-2-operation/pgbouncer/pb-tls.svg new file mode 100644 index 000000000..618a27a63 --- /dev/null +++ b/docs/images/day-2-operation/pgbouncer/pb-tls.svg @@ -0,0 +1,4 @@ + + + +            Enterprise            Operator              Community            Operator
service
se...
secret
se...
tls-secret
tls-secret
Cert- manager
Cert- ma...
PetSet
Statef...
Issuer/Cluster Issuer
Issuer...
PgBouncer
PgBouncer
Certificates
Certif...
User
User
2.Create
2.Create
1.Create
1.Create
5a.Watch
5a.Watch
3.Watch
3.Watch
4.Create
4.Create
5c.Watch
5c.Watch
6.Create
6.Create
7.Watch
7.Watch
uses
uses
8.Create
8.Create
9.Watch
9.Watch
10.Create
10.Create
5b.Watch
5b.Watch
refers to
refers to
Text is not SVG - cannot display
\ No newline at end of file