PMM-14979 Never replace an existing PMM encryption key - #921
Draft
ademidoff wants to merge 1 commit into
Draft
Conversation
PMM encrypts the credentials of monitored services, and those credentials can only be read back with the key they were encrypted with. Both charts could hand a replica a key that does not match the database. pmm-ha created the pg-encryption-key secret with `kubectl apply`, which overwrites it. The secret is not owned by the Helm release, so it outlives `helm uninstall`: reinstalling over retained pg-db storage replaced the key while the data it had encrypted remained, leaving every stored credential unreadable. The job now keeps an existing secret and only generates a key when there is none. pmm kept the key solely on the data volume at /srv/pmm-encryption.key. That survives restarts and upgrades, but not loss of the volume, which is recoverable data loss when PMM is pointed at an external PostgreSQL that outlived it. Two init containers now keep the volume and a secret in sync: whichever already holds a key wins, so an existing installation has its key adopted rather than replaced, and a rebuilt volume is restored from the secret instead of getting a freshly generated key. Reaching the API server is required, because continuing without it would let PMM generate a key of its own and leave the stored credentials unreadable. The extra permission is a namespaced Role granting get and create on secrets. The chart's ClusterRole already allows reading them cluster-wide, so nothing cluster-scoped is added.
This was referenced Aug 5, 2026
Contributor
There was a problem hiding this comment.
Pull request overview
This PR updates the pmm and pmm-ha Helm charts to ensure PMM encryption keys are never replaced unintentionally, adding “adopt/restore” behavior so existing encrypted credentials remain readable across reinstalls, upgrades, or volume loss scenarios.
Changes:
pmm-ha: stop overwritingpg-encryption-key; only create it when absent.pmm: add init-container based reconciliation to keep/srv/pmm-encryption.keyand a backup Secret in sync with “adopt-not-generate” semantics.- Add namespaced RBAC for Secret
get/createand bump chart versions.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| charts/pmm/values.yaml | Adds configurable encryptionKey settings for secret backup and kubectl image. |
| charts/pmm/templates/statefulset.yaml | Introduces init containers to generate/reconcile encryption key between volume and Secret. |
| charts/pmm/templates/encryption-key-rbac.yaml | Adds a Role/RoleBinding allowing Secret get/create in the release namespace. |
| charts/pmm/templates/_helpers.tpl | Adds helper to compute the encryption key Secret name. |
| charts/pmm/README.md | Documents encryption key backup/restore behavior and new values. |
| charts/pmm/Chart.yaml | Bumps chart version to 1.9.1. |
| charts/pmm-ha/templates/pmm-secret-init-job.yaml | Ensures existing pg-encryption-key is preserved; avoids apply/overwrite behavior. |
| charts/pmm-ha/Chart.yaml | Bumps chart version to 1.6.1. |
Suppressed comments (1)
charts/pmm/templates/statefulset.yaml:136
- Same permissions concern as above: installing the generated key sets mode 0644. Consider using 0600 so the key is not world-readable within the pod/volume.
chmod 644 "$KEY_FILE"
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| echo "Error: secret $SECRET holds no key" >&2 | ||
| exit 1 | ||
| fi | ||
| chmod 644 "$KEY_FILE" |
| The secret is not owned by the Helm release, so it outlives `helm uninstall`. Back it up together with the rest of your PMM configuration: | ||
|
|
||
| ```sh | ||
| kubectl get secret pmm-encryption-key -o yaml > pmm-encryption-key-backup.yaml |
Comment on lines
+105
to
+109
| if kubectl get secret "$SECRET" --namespace="$NAMESPACE" >/dev/null 2>&1; then | ||
| SECRET_EXISTS=yes | ||
| else | ||
| SECRET_EXISTS=no | ||
| fi |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Ticket: PMM-14979
Chart side of the same ticket as percona/pmm#5735, which carries the pmm-managed and documentation changes.
Problem
PMM encrypts the credentials of monitored services, and they can only be read back with the key they were encrypted with. Both charts could give a replica a key that does not match the database.
pmm-hacreated thepg-encryption-keysecret withkubectl create --dry-run | kubectl apply, which overwrites an existing one. The secret is created bykubectlrather than Helm, so it is not owned by the release and outliveshelm uninstall. Reinstalling over retainedpg-dbstorage therefore replaced the key while the data it had encrypted remained, making every stored credential unreadable — the same failure class as the forum report behind this ticket.pmmkept the key only on the data volume at/srv/pmm-encryption.key. That survives restarts and upgrades but not loss of the volume, which is recoverable data loss when PMM is pointed at an external PostgreSQL that outlived it.Changes
charts/pmm-ha/templates/pmm-secret-init-job.yamlkeeps an existing secret and only generates a key when there is none.charts/pmmgains two init containers that keep the volume and a secret in sync, with adopt-not-generate semantics:The third row is what makes this safe to upgrade into: an existing release has its key adopted, never replaced. Reaching the API server is required rather than best-effort, because continuing without it would let PMM generate a key of its own and leave the stored credentials unreadable.
The new permission is a namespaced Role granting
getandcreateon secrets (charts/pmm/templates/encryption-key-rbac.yaml). The chart's existing ClusterRole already allows reading secrets cluster-wide, so nothing cluster-scoped is added. It can be turned off withencryptionKey.backupToSecret=false.Versions bumped:
pmm1.9.0 -> 1.9.1,pmm-ha1.6.0 -> 1.6.1.Note for reviewers
Because
serviceAccount.createdefaults tofalse, the Role binds to the namespace'sdefaultServiceAccount, so other pods using it in that namespace also gain the permission. An init container cannot use a separate ServiceAccount, and a pre-install hook Job cannot read the ReadWriteOnce data volume, so this is inherent to the approach;serviceAccount.create=truescopes it to a PMM-only account.Flipping that default is not advisable as-is: in this chart it also creates a ClusterRole granting cluster-wide read on secrets, a static ServiceAccount token, and activates the currently inert
pmmEnv.ENABLE_CLUSTER_ROLE_ADMINpath that bindscluster-admin. Decoupling ServiceAccount creation from that cluster RBAC is worth a separate ticket.Testing
helm lintpasses for both charts;ct lintpasses forpmmup to itsyamllintstep, which could not run locally (not installed,pipblocked by PEP 668) — the added YAML follows the existing file conventions but CI has the last word.helm templateverified for both charts, includingencryptionKey.backupToSecret=falserendering nothing and user-suppliedinitContainersstill working.kubectlfor all four states above, confirming a pre-existing key is adopted rather than overwritten and a rebuilt volume is restored rather than regenerated.helm install/upgrade against a cluster. The kind-based install in CI coverspmm;charts/pmm-hachanges run through.github/workflows/pmm-ha-pr-checks.yaml.