From a553e6e386ec69431f322c5fdb26a2680de58a53 Mon Sep 17 00:00:00 2001 From: "Nilson.Henao" Date: Sun, 20 Sep 2026 15:16:27 +0000 Subject: [PATCH] fix: bump a volume mount's version when the secret behind it changes PATCH /api/v3/secrets/ updates the secret and marks change tracking for the linked nodes, but nothing bumps the version of the volume mounts that carry it. An agent only re-materialises a mount whose version is greater than the one it holds -- "Skipping update - new version 1 not greater than current version 1" in its journal -- so every linked node keeps the old content indefinitely. Any rotation of publisher keys, credentials or TLS material looks applied in the controller and is not applied on the fleet. Measured on Controller 3.8.2 with Edgelet v1.0.3-rc.1, on a node linked to a secret-backed volume mount: adding a key through the secrets API alone changed nothing on the node, and only a PATCH of the volume mount (which does bump the version) made it arrive. With this change the same API call moved the version and the node had the new file about 30 s later; removing the key propagated the same way. updateSecretEndpoint now bumps every volume mount that references the secret, in the same transaction that writes it. Unconditional rather than "only when the data differs", because the stored copy lives in the vault and comparing it with the request body is not a hash of the request body. No API change. Signed-off-by: Nilson.Henao Co-Authored-By: Claude Opus 5 (1M context) --- src/services/secret-service.js | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/services/secret-service.js b/src/services/secret-service.js index 5b5d2dcc..0bc9696f 100644 --- a/src/services/secret-service.js +++ b/src/services/secret-service.js @@ -97,6 +97,7 @@ async function updateSecretEndpoint (secretName, secretData, transaction) { validateSecretData(nextType, secretData.data) const secret = await SecretManager.updateSecret(secretName, nextType, secretData.data, transaction) + await _bumpVolumeMountsUsingSecret(secretName, transaction) await _updateChangeTrackingForFogs(secretName, transaction) await _updateMicroservicesUsingSecret(secretName, transaction) return { @@ -208,6 +209,26 @@ async function deleteSecretEndpoint (secretName, transaction) { return {} } +// A node only re-materialises a volume mount whose version is greater than the +// one it holds ("Skipping update - new version 1 not greater than current +// version 1" in the agent's journal), and nothing bumped that version when the +// secret behind the mount changed: every linked node kept the old content, so no +// rotation of keys, credentials or TLS material ever arrived. +// +// Bumped on every update rather than only when the data differs: a PATCH is an +// explicit instruction to set this secret, and the stored copy lives in the +// vault, so comparing stored with posted is not a hash of the request body. +async function _bumpVolumeMountsUsingSecret (secretName, transaction) { + const volumeMounts = await VolumeMountingManager.findAll({ secretName }, transaction) + for (const volumeMount of volumeMounts) { + await VolumeMountingManager.update( + { name: volumeMount.name }, + { version: volumeMount.version + 1 }, + transaction + ) + } +} + async function _deleteVolumeMountsUsingSecret (secretName, transaction) { const volumeMounts = await VolumeMountingManager.findAll({ secretName }, transaction) if (volumeMounts.length > 0) {