Skip to content

Commit d0f42ba

Browse files
committed
K8SPG-911: Add pg_tde support
This commit adds native pg_tde extension support into operator. **This commit only adds Vault KMS support for pg_tde. KMIP support will be added in future releases.** When pg_tde is enabled and Vault configuration is provided, the operator: - appends pg_tde into shared_preload_libraries, - mounts Vault token and CA secrets into database containers, - runs CREATE EXTENSION in all databases, - creates Vault provider by running pg_tde_add_global_key_provider_vault_v2, - create a global key by running pg_tde_create_key_using_global_key_provider, - sets the default key by running pg_tde_set_default_key_using_global_key_provider. -> Example configuration pg_tde: enabled: true vault: host: https://vault-service.vault-service.svc:8200 mountPath: tde tokenSecret: name: vault-secret key: token caSecret: name: vault-secret key: ca.crt Note that: - Mount path needs to be a KV v2 storage engine. - caSecret is optional and can be omitted if you want to use http. But in my testing I couldn't manage the make vault work without TLS. It responds with HTTP 405 if I disable TLS in vault. - tokenSecret and caSecret can be the same secret or different. Operator doesn't assume anything about the contents of the secrets since you'll need to set secret keys in cr.yaml yourself. - Using a non-root token requires more configuration. Check out pg_tde docs for that. But don't forget to add these in the Vault policy: ``` path "sys/internal/ui/mounts/*" { capabilities = ["read"] } path "sys/mounts/*" { capabilities = ["read"] } ``` -> API changes pg_tde requires more configuration options than other extensions operator supports. This required us make some changes in the extensions API. With these changes, 'spec.extensions.builtin' section is deprecated and all builtin extensions are moved to 'spec.extensions.<extension>' (i.e. 'spec.extensions.pg_stat_monitor'). Right now extensions can be enabled/disabled with the old and the new method. If two methods are used at the same time, 'spec.extensions.builtin' takes precedence. -> Status changes A hash will be calculated using pg_tde configuration provided by user. Operator uses this hash to understand if config is changed and it should reconfigure pg_tde. The hash can be found in status.pgTDERevision field of **PostgresCluster** object. This hash will be removed when pg_tde is disabled. Operator also communicates the status of pg_tde with conditions. The condition with type=PGTDEEnabled can be found in both PerconaPGCluster and PostgresCluster statuses. -> Disabling pg_tde Disabling pg_tde is more complex than other extensions: - First of all any encrypted objects must be dropped before disabling. Otherwise DROP EXTENSION will fail with a descriptive error message. **Operator won't drop anything, user needs to do this manually.** - The extension needs to be disabled in two steps: 1. First set pg_tde.enabled=false without removing the vault section. Operator will drop the extension and restart the pods. 2. Then you can remove pg_tde.vault. Database pods will be restarted again to remove secret mounts from containers. - It's recommended to run CHECKPOINT before removing pg_tde.vault. Even though extension is dropped, Postgres might still try to use encrypted objects during recovery after restart and it might try to access token secret. CHECKPOINT helps you prevent this failure case. -> Deleting and recreating clusters If cluster with pg_tde enabled is deleted but PVCs are retained, on recreation you'll see some errors about pg_tde in operator logs. They happen because the vault provider and/or global key already exists. Operator will handle these errors gracefully and configure pg_tde. Same thing applies when pg_tde is disabled and re-enabled. Since both vault provider and global key already exists, operator will handle "already exists" errors and configure pg_tde. The global key name is determined by cluster's .metadata.uid. For example 'global-master-key-ad19534a-d778-460e-ac87-ca38ef5e6755'. This means the key will be changed if cluster is deleted and recreated. As long as the old key and the new key is accessible to pg_tde, this won't cause any issues. pg_tde will handle it as it handles key rotation. -> Validations - You can't set pg_tde.enabled=true without setting pg_tde.vault. - If you already had pg_tde.enabled, you can't remove pg_tde section completely. - If you already had pg_tde.enabled, you can't remove pg_tde.vault section completely. --------- K8SPG-911: pg_tde improvements/fixes - add pg version validation - explicitly disable wal encryption - enable pg_tde in restore job - [e2e] read from all pods after restore - use pg_tde binaries in patroni - fix vault provider change All items except the last is straightforward. Fixing the vault provider change, required a lot of changes. The problem with changing the Vault token in pg_tde was that pg_tde requires both the new and the old token at the same time to perform the change. This is not trivial to achieve on K8s, since operator needs to mount the new secret to the pods and somehow needs the keep the old secret mounted. To achieve this, operator performs provider change in two phases: 1. In the first phase, operator keeps the old secret mounted in the pod and prevents restart. Then it fetches the new secret contents and stores them in temporary files in `/pgdata` directory. Then, operator runs pg_tde_change_global_key_provider_vault_v2. 2. In the second phase, operator mounts the new secret and restarts the pods. Then it runs pg_tde_change_global_key_provider_vault_v2 with standard credential paths. At the end of this phase, temporary files are cleaned up.
1 parent 1172a77 commit d0f42ba

62 files changed

Lines changed: 3421 additions & 63 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

build/crd/crunchy/generated/postgres-operator.crunchydata.com_postgresclusters.yaml

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13594,6 +13594,53 @@ spec:
1359413594
type: boolean
1359513595
extensions:
1359613596
properties:
13597+
pg_tde:
13598+
properties:
13599+
enabled:
13600+
type: boolean
13601+
vault:
13602+
properties:
13603+
caSecret:
13604+
description: Name of the secret that contains the CA certificate
13605+
for SSL verification.
13606+
properties:
13607+
key:
13608+
type: string
13609+
name:
13610+
type: string
13611+
required:
13612+
- key
13613+
- name
13614+
type: object
13615+
host:
13616+
description: Host of Vault server.
13617+
type: string
13618+
mountPath:
13619+
default: secret/data
13620+
description: The mount point on the Vault server where
13621+
the key provider should store the keys.
13622+
type: string
13623+
tokenSecret:
13624+
description: Name of the secret that contains the access
13625+
token with read and write access to the mount path.
13626+
properties:
13627+
key:
13628+
type: string
13629+
name:
13630+
type: string
13631+
required:
13632+
- key
13633+
- name
13634+
type: object
13635+
required:
13636+
- host
13637+
- tokenSecret
13638+
type: object
13639+
type: object
13640+
x-kubernetes-validations:
13641+
- message: vault is required for enabling pg_tde
13642+
rule: '!has(self.enabled) || (has(self.enabled) && self.enabled
13643+
== false) || has(self.vault)'
1359713644
pgAudit:
1359813645
type: boolean
1359913646
pgRepack:
@@ -13605,6 +13652,11 @@ spec:
1360513652
pgvector:
1360613653
type: boolean
1360713654
type: object
13655+
x-kubernetes-validations:
13656+
- message: to disable pg_tde first set enabled=false without removing
13657+
vault and wait for pod restarts
13658+
rule: '!has(oldSelf.pg_tde) || !has(oldSelf.pg_tde.vault) || !has(oldSelf.pg_tde.enabled)
13659+
|| !oldSelf.pg_tde.enabled || has(self.pg_tde.vault)'
1360813660
image:
1360913661
description: |-
1361013662
The image name to use for PostgreSQL containers. When omitted, the value
@@ -30971,6 +31023,10 @@ spec:
3097131023
description: The PostgreSQL system identifier reported by Patroni.
3097231024
type: string
3097331025
type: object
31026+
pgTDERevision:
31027+
description: Identifies the pg_tde configuration that have been installed
31028+
into PostgreSQL.
31029+
type: string
3097431030
pgbackrest:
3097531031
description: Status information for pgBackRest
3097631032
properties:

build/crd/percona/generated/pgv2.percona.com_perconapgclusters.yaml

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13693,6 +13693,8 @@ spec:
1369313693
description: The specification of extensions.
1369413694
properties:
1369513695
builtin:
13696+
description: 'Deprecated: Use extensions.<extension> instead.
13697+
This field will be removed after 2.11.0.'
1369613698
properties:
1369713699
pg_audit:
1369813700
type: boolean
@@ -13722,6 +13724,78 @@ spec:
1372213724
description: PullPolicy describes a policy for if/when to pull
1372313725
a container image
1372413726
type: string
13727+
pg_audit:
13728+
properties:
13729+
enabled:
13730+
type: boolean
13731+
type: object
13732+
pg_repack:
13733+
properties:
13734+
enabled:
13735+
type: boolean
13736+
type: object
13737+
pg_stat_monitor:
13738+
properties:
13739+
enabled:
13740+
type: boolean
13741+
type: object
13742+
pg_stat_statements:
13743+
properties:
13744+
enabled:
13745+
type: boolean
13746+
type: object
13747+
pg_tde:
13748+
properties:
13749+
enabled:
13750+
type: boolean
13751+
vault:
13752+
properties:
13753+
caSecret:
13754+
description: Name of the secret that contains the CA certificate
13755+
for SSL verification.
13756+
properties:
13757+
key:
13758+
type: string
13759+
name:
13760+
type: string
13761+
required:
13762+
- key
13763+
- name
13764+
type: object
13765+
host:
13766+
description: Host of Vault server.
13767+
type: string
13768+
mountPath:
13769+
default: secret/data
13770+
description: The mount point on the Vault server where
13771+
the key provider should store the keys.
13772+
type: string
13773+
tokenSecret:
13774+
description: Name of the secret that contains the access
13775+
token with read and write access to the mount path.
13776+
properties:
13777+
key:
13778+
type: string
13779+
name:
13780+
type: string
13781+
required:
13782+
- key
13783+
- name
13784+
type: object
13785+
required:
13786+
- host
13787+
- tokenSecret
13788+
type: object
13789+
type: object
13790+
x-kubernetes-validations:
13791+
- message: vault is required for enabling pg_tde
13792+
rule: '!has(self.enabled) || (has(self.enabled) && self.enabled
13793+
== false) || has(self.vault)'
13794+
pgvector:
13795+
properties:
13796+
enabled:
13797+
type: boolean
13798+
type: object
1372513799
storage:
1372613800
properties:
1372713801
bucket:
@@ -13804,6 +13878,11 @@ spec:
1380413878
type: string
1380513879
type: object
1380613880
type: object
13881+
x-kubernetes-validations:
13882+
- message: to disable pg_tde first set enabled=false without removing
13883+
vault and wait for pod restarts
13884+
rule: '!has(oldSelf.pg_tde) || !has(oldSelf.pg_tde.vault) || !has(oldSelf.pg_tde.enabled)
13885+
|| !oldSelf.pg_tde.enabled || has(self.pg_tde.vault)'
1380713886
image:
1380813887
description: The image name to use for PostgreSQL containers.
1380913888
type: string
@@ -28778,6 +28857,9 @@ spec:
2877828857
- postgresVersion
2877928858
type: object
2878028859
x-kubernetes-validations:
28860+
- message: pg_tde is only supported for PG17 and above
28861+
rule: '!has(self.extensions) || !has(self.extensions.pg_tde) || !has(self.extensions.pg_tde.enabled)
28862+
|| !self.extensions.pg_tde.enabled || self.postgresVersion >= 17'
2878128863
- message: PostgresVersion must be >= 15 if grantPublicSchemaAccess exists
2878228864
and is true
2878328865
rule: '!has(self.users) || self.postgresVersion >= 15 || self.users.all(u,

config/crd/bases/pgv2.percona.com_perconapgclusters.yaml

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14332,6 +14332,8 @@ spec:
1433214332
description: The specification of extensions.
1433314333
properties:
1433414334
builtin:
14335+
description: 'Deprecated: Use extensions.<extension> instead.
14336+
This field will be removed after 2.11.0.'
1433514337
properties:
1433614338
pg_audit:
1433714339
type: boolean
@@ -14361,6 +14363,78 @@ spec:
1436114363
description: PullPolicy describes a policy for if/when to pull
1436214364
a container image
1436314365
type: string
14366+
pg_audit:
14367+
properties:
14368+
enabled:
14369+
type: boolean
14370+
type: object
14371+
pg_repack:
14372+
properties:
14373+
enabled:
14374+
type: boolean
14375+
type: object
14376+
pg_stat_monitor:
14377+
properties:
14378+
enabled:
14379+
type: boolean
14380+
type: object
14381+
pg_stat_statements:
14382+
properties:
14383+
enabled:
14384+
type: boolean
14385+
type: object
14386+
pg_tde:
14387+
properties:
14388+
enabled:
14389+
type: boolean
14390+
vault:
14391+
properties:
14392+
caSecret:
14393+
description: Name of the secret that contains the CA certificate
14394+
for SSL verification.
14395+
properties:
14396+
key:
14397+
type: string
14398+
name:
14399+
type: string
14400+
required:
14401+
- key
14402+
- name
14403+
type: object
14404+
host:
14405+
description: Host of Vault server.
14406+
type: string
14407+
mountPath:
14408+
default: secret/data
14409+
description: The mount point on the Vault server where
14410+
the key provider should store the keys.
14411+
type: string
14412+
tokenSecret:
14413+
description: Name of the secret that contains the access
14414+
token with read and write access to the mount path.
14415+
properties:
14416+
key:
14417+
type: string
14418+
name:
14419+
type: string
14420+
required:
14421+
- key
14422+
- name
14423+
type: object
14424+
required:
14425+
- host
14426+
- tokenSecret
14427+
type: object
14428+
type: object
14429+
x-kubernetes-validations:
14430+
- message: vault is required for enabling pg_tde
14431+
rule: '!has(self.enabled) || (has(self.enabled) && self.enabled
14432+
== false) || has(self.vault)'
14433+
pgvector:
14434+
properties:
14435+
enabled:
14436+
type: boolean
14437+
type: object
1436414438
storage:
1436514439
properties:
1436614440
bucket:
@@ -14443,6 +14517,11 @@ spec:
1444314517
type: string
1444414518
type: object
1444514519
type: object
14520+
x-kubernetes-validations:
14521+
- message: to disable pg_tde first set enabled=false without removing
14522+
vault and wait for pod restarts
14523+
rule: '!has(oldSelf.pg_tde) || !has(oldSelf.pg_tde.vault) || !has(oldSelf.pg_tde.enabled)
14524+
|| !oldSelf.pg_tde.enabled || has(self.pg_tde.vault)'
1444614525
image:
1444714526
description: The image name to use for PostgreSQL containers.
1444814527
type: string
@@ -29417,6 +29496,9 @@ spec:
2941729496
- postgresVersion
2941829497
type: object
2941929498
x-kubernetes-validations:
29499+
- message: pg_tde is only supported for PG17 and above
29500+
rule: '!has(self.extensions) || !has(self.extensions.pg_tde) || !has(self.extensions.pg_tde.enabled)
29501+
|| !self.extensions.pg_tde.enabled || self.postgresVersion >= 17'
2942029502
- message: PostgresVersion must be >= 15 if grantPublicSchemaAccess exists
2942129503
and is true
2942229504
rule: '!has(self.users) || self.postgresVersion >= 15 || self.users.all(u,

config/crd/bases/postgres-operator.crunchydata.com_postgresclusters.yaml

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13554,6 +13554,53 @@ spec:
1355413554
type: boolean
1355513555
extensions:
1355613556
properties:
13557+
pg_tde:
13558+
properties:
13559+
enabled:
13560+
type: boolean
13561+
vault:
13562+
properties:
13563+
caSecret:
13564+
description: Name of the secret that contains the CA certificate
13565+
for SSL verification.
13566+
properties:
13567+
key:
13568+
type: string
13569+
name:
13570+
type: string
13571+
required:
13572+
- key
13573+
- name
13574+
type: object
13575+
host:
13576+
description: Host of Vault server.
13577+
type: string
13578+
mountPath:
13579+
default: secret/data
13580+
description: The mount point on the Vault server where
13581+
the key provider should store the keys.
13582+
type: string
13583+
tokenSecret:
13584+
description: Name of the secret that contains the access
13585+
token with read and write access to the mount path.
13586+
properties:
13587+
key:
13588+
type: string
13589+
name:
13590+
type: string
13591+
required:
13592+
- key
13593+
- name
13594+
type: object
13595+
required:
13596+
- host
13597+
- tokenSecret
13598+
type: object
13599+
type: object
13600+
x-kubernetes-validations:
13601+
- message: vault is required for enabling pg_tde
13602+
rule: '!has(self.enabled) || (has(self.enabled) && self.enabled
13603+
== false) || has(self.vault)'
1355713604
pgAudit:
1355813605
type: boolean
1355913606
pgRepack:
@@ -13565,6 +13612,11 @@ spec:
1356513612
pgvector:
1356613613
type: boolean
1356713614
type: object
13615+
x-kubernetes-validations:
13616+
- message: to disable pg_tde first set enabled=false without removing
13617+
vault and wait for pod restarts
13618+
rule: '!has(oldSelf.pg_tde) || !has(oldSelf.pg_tde.vault) || !has(oldSelf.pg_tde.enabled)
13619+
|| !oldSelf.pg_tde.enabled || has(self.pg_tde.vault)'
1356813620
image:
1356913621
description: |-
1357013622
The image name to use for PostgreSQL containers. When omitted, the value
@@ -30869,6 +30921,10 @@ spec:
3086930921
description: The PostgreSQL system identifier reported by Patroni.
3087030922
type: string
3087130923
type: object
30924+
pgTDERevision:
30925+
description: Identifies the pg_tde configuration that have been installed
30926+
into PostgreSQL.
30927+
type: string
3087230928
pgbackrest:
3087330929
description: Status information for pgBackRest
3087430930
properties:

0 commit comments

Comments
 (0)