[
https://issues.apache.org/jira/browse/CAMEL-25518?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
]
shashank updated CAMEL-25518:
-----------------------------
Description:
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. A further test checks that
a secret read at a pinned version ({{@1}}) does not start a reload.
camel-hashicorp-vault unit tests: 7, 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._
was:
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._
> 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
> Assignee: shashank
> Priority: Major
>
> 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. A further
> test checks that a secret read at a pinned version ({{@1}}) does not start a
> reload. camel-hashicorp-vault unit tests: 7, 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)