shashank created CAMEL-25518:
--------------------------------
Summary: camel-hashicorp-vault - secret refresh never detects
updates of secrets outside the "secret" engine, nor updates made before its
first check
Key: CAMEL-25518
URL: https://issues.apache.org/jira/browse/CAMEL-25518
Project: Camel
Issue Type: Bug
Components: camel-hashicorp-vault
Reporter: shashank
The secret refresh added by CAMEL-22676
({{camel.vault.hashicorp.refreshEnabled=true}}) polls
{{<engine>/metadata/<secret>}} for every tracked secret. Besides the names
listed in {{camel.vault.hashicorp.secrets}}, it tracks every secret resolved
with the {{hashicorp:}} properties function; when that option is not set, the
documentation says the task takes into account all the properties with a
{{hashicorp:}} prefix.
1. {{HashicorpVaultPropertiesFunction.getSecretFromSource}} records only the
key: {{secrets.add(key)}} (line 214 at main c578a42a776d), so
{{hashicorp:kv:db#password}} is recorded as {{db}}.
{{HashicorpVaultReloadTriggerTask.buildMetadataPath}} (lines 272-297) reads a
name without an engine from the {{secret}} engine, so it reads
{{secret/metadata/db}}, which does not exist; {{VaultTemplate.read}} returns
null and the task skips it without a log. For a secret in any engine other than
{{secret}} (for example {{kv}}), an update never triggers a reload unless the
user also lists it as {{kv:db}} in {{camel.vault.hashicorp.secrets}}.
2. The first run of the task only records the version it reads (lines 212-215:
"First time seeing this secret, just record the version"). The first run
happens one refresh period after startup (60 s by default). If the secret is
updated in that window, the new version becomes the baseline and the
application keeps the value it resolved at startup until the secret changes
again.
h3. Reproduction
New {{HashicorpVaultReloadTriggerTaskTest}} with a minimal in-process fake of
the Vault KV v2 HTTP API (JDK {{HttpServer}}, no container): resolve
{{hashicorp:kv:db#password}} (version 1), run the task, set version 2, run the
task; and resolve {{hashicorp:secret:api#password}} (version 1), set version 2,
run the task. On main:
{noformat}
rotationInNonDefaultEngineIsDetected: the refresh task must read the metadata
of the engine the secret was resolved from, but read [secret/metadata/db,
secret/metadata/db] ==> expected: <true> but was: <false>
rotationBeforeFirstCheckIsDetected: the application resolved version 1 but
version 2 was not detected, updates: {} ==> expected: <false> but was: <true>
secretsAreTrackedWithTheirEngine: expected: <[api, kv:db]> but was: <[api, db]>
{noformat}
h3. Proposed fix
The properties function records the secret in the format of
{{camel.vault.hashicorp.secrets}}: {{engine:secret}}, or only {{secret}} for
the default {{secret}} engine (so the names of secrets in the {{secret}} engine
do not change, and a resolved secret has the same name as when it is listed in
the option, so it is not checked twice). It also remembers the version it read
({{data.metadata.version}} of the KV v2 response; not when a specific
{{@version}} was requested). For a secret the task has not seen before, it
starts from that version, so a newer version in Vault triggers a reload at the
first check. {{getSecrets()}} and the task's updates now name secrets of other
engines {{engine:secret}}; component docs and a 4.23 upgrade guide note
updated. The recorded set and the new version map are concurrent collections,
as the refresh task reads them from its own thread. camel-hashicorp-vault unit
tests: 6, 0 failures (the ITs need a Vault container and were not run).
Found with a TLA+ model of startup resolution, rotations and the check runs
(metadata read, version compare, reload): "after a complete check run with no
update since it started, the application uses the current version of every
secret" is violated for a {{kv}} secret relying on auto-detection (12 steps:
resolve, two check runs around one update, no reload) and for an update before
the first check (8 steps); it holds on main for the {{secret}} engine with
updates after the first check, and with
{{camel.vault.hashicorp.secrets=kv:db}}. With the fix it holds for two secrets
in different engines with two updates at any time. Then confirmed with the real
classes as above.
Workaround on affected versions: list every secret of a non-default engine as
{{engine:secret}} in {{camel.vault.hashicorp.secrets}} (fixes 1, not 2).
Affected: 4.17.0 and later (camel-4.18.x and camel-4.22.x have the same code;
camel-4.14.x has no secret refresh).
Duplicate check (2026-10-09): JIRA component camel-hashicorp-vault (7 issues,
none about the refresh), text "HashicorpVaultReloadTriggerTask" (none),
"hashicorp" with "refresh"/"reload"/"engine" (9; CAMEL-22676/22677 added the
feature, CAMEL-24636 is the re-authentication SPI); GitHub pull requests
"hashicorp refresh", "HashicorpVaultReloadTriggerTask": none; no open PR
touches camel-hashicorp-vault.
_Filed with Claude Code on behalf of allthingssecurity._
--
This message was sent by Atlassian Jira
(v8.20.10#820010)