[
https://issues.apache.org/jira/browse/HDDS-16959?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
]
Siyao Meng updated HDDS-16959:
------------------------------
Attachment:
CR-4-keep-persisted-delegation-tokens-when-secret-key-fetch-fails.patch
> OM drops persisted delegation tokens from memory when the secret key fetch
> from SCM fails during token load
> -----------------------------------------------------------------------------------------------------------
>
> Key: HDDS-16959
> URL: https://issues.apache.org/jira/browse/HDDS-16959
> Project: Apache Ozone
> Issue Type: Bug
> Reporter: Siyao Meng
> Priority: Major
> Attachments:
> CR-4-keep-persisted-delegation-tokens-when-secret-key-fetch-fails.patch,
> TestBugCR4OmRestartDuringScmKeyInit.java
>
>
> h3. Mechanism
> The OM DB stores only the identifier and the renew date of a delegation
> token. When {{OzoneDelegationTokenSecretManager}} is created,
> {{loadTokenSecretState}} calls {{addPersistedDelegationToken}} for every
> stored token, which fetches the token's secret key from SCM with
> {{SecretKeyClient.getSecretKey}} and recomputes the password.
> {{loadTokenSecretState}} catches every exception from that call, logs
> "exception while loading delegation token from DB... ignored to continue
> startup" and goes on to the next token.
> HDDS-13234 added that catch for a secret key that SCM has already removed, in
> which case {{getSecretKey}} returns null and the token can never be verified
> again. A fetch that fails ({{SCMSecurityException}} "Error fetching secret
> key ...") ends in the same catch. Before HDDS-13234 the same failure aborted
> the OM start. That change is in 2.1.0 and later releases (its fix versions
> are 2.0.1 and 2.1.0). The token is then missing from {{currentTokens}}
> although it is unexpired, still in the DB, and its key still exists on SCM.
> Nothing loads it later, so the OM rejects it until the OM process is
> restarted.
> Two things make the failed fetch reachable during a normal start:
> * The token load runs in the {{OzoneManager}} constructor
> ({{instantiateServices}}), before {{startSecretManager}} starts the signer
> client. Only the signer client's initial fetch waits for SCM to leave
> {{SECRET_KEY_NOT_INITIALIZED}}.
> * {{SecretKeyProtocolClientSideTranslatorPB.handleError}} turns the error
> status into an exception after the RPC has returned, so the SCM failover
> retry policy does not retry it.
> h3. Trigger
> # Secure cluster, at least one delegation token issued.
> # An SCM is restarted and becomes leader. Until {{SecretKeyManagerService}}
> has run {{SecretKeyManager.checkAndInitialize}} on it, the secret key RPC
> answers {{SECRET_KEY_NOT_INITIALIZED}}.
> # An OM starts in that window. The window is normally short: SCM initializes
> the keys as soon as it is leader ready, and if that attempt fails the next
> one comes with the next rotation check
> ({{hdds.secret.key.rotate.check.duration}}, default 10m). Its natural width
> was not measured.
> # The token is renewed or used for authentication on that OM.
> Any other failure of the fetch at load time has the same effect, for example
> SCM not reachable within the client retries (same catch, not run).
> {{OzoneManager.reloadOMState}} (Ratis snapshot installation on a follower)
> and {{OzoneManager.restart}} build the secret manager the same way, so they
> are exposed too (read from the code, not run).
> h3. Impact
> * Every token loaded while the fetch fails is rejected by that OM: renewal
> fails with "Delegation token renewal failed" and the OM logs "token ... can't
> be found in cache", {{retrievePassword}} throws {{InvalidToken}} with the
> same message, so token authentication fails. Jobs that hold delegation tokens
> fail although their tokens are valid.
> * The state lasts until that OM is restarted while SCM can serve the key. The
> only trace at load time is one ERROR line per token.
> * In OM HA every OM loads its tokens on its own, and tokens are only checked
> on the leader. An OM that lost its tokens as a follower shows it when it
> becomes leader (read from the code, the reproduction uses one OM).
> h3. Reproduction
> PASS in about one minute, unmodified source.
> [^TestBugCR4OmRestartDuringScmKeyInit.java]
> ({{hadoop-ozone/integration-test}}) runs a Kerberos mini cluster with three
> SCMs and one OM. A client gets a delegation token, a follower SCM is
> restarted, leadership is transferred to it and the OM is restarted before
> that SCM has initialized its secret keys. The test holds the monitor of the
> new leader's {{SecretKeyManager}} during the OM restart, which only delays
> the synchronized {{checkAndInitialize}} so that the window is hit on every
> run. Afterwards the token is unexpired, present in the OM DB and its key is
> served by SCM, but {{renewDelegationToken}} fails and {{retrievePassword}}
> throws {{InvalidToken}}. After a second OM restart the same token renews. A
> passing test means the defect is present.
> h3. Patch
> [^CR-4-keep-persisted-delegation-tokens-when-secret-key-fetch-fails.patch],
> against 025266b52a6dcaf39508607cad736799c40907e8. It also applies to master
> at a65a32e7c4b.
> {{addPersistedDelegationToken}} now tells the two cases apart. If the fetch
> throws {{SCMSecurityException}}, the token is kept in {{currentTokens}} as an
> entry of its own type ({{TokenInfoWithoutPassword}}) and one WARN line
> without a stack trace is logged. {{validateToken}} computes the password of
> such an entry from its secret key when the token is used, and throws
> {{InvalidToken}} if the key still cannot be fetched or SCM reports it as
> gone. Only the load creates such an entry, and every later update of the
> token replaces it with a regular one. The value {{validateToken}} computes is
> the one the existing signature check requires the stored password to have, so
> the accepted tokens are the same as after a successful load. The HDDS-13234
> behaviour for a key that SCM no longer has (null result) is unchanged, and OM
> startup does not wait for SCM any longer than today.
> Retrying the fetch during the load was considered. It would hold the OM
> constructor (and a snapshot installation) for as long as SCM does not answer,
> and would still need a decision on what to do when the retries run out.
> Covered by {{testReloadTokenWhenSecretKeyFetchFails}} in the existing
> {{TestOzoneDelegationTokenSecretManager}}: the fetch fails during the reload
> and on first use, then SCM reports the key as missing, then serves it.
> Without the change the test fails because the token "can't be found in
> cache". With the change {{TestOzoneDelegationTokenSecretManager}} (17 tests,
> including {{testExpiredSecretKey}} from HDDS-13234), the token identifier,
> delegation token request and response and double buffer unit tests (19
> tests), {{TestDelegationToken}} (2 tests), {{TestSecureOzoneCluster}} (9
> tests) and {{TestSecretKeySnapshot}} pass and checkstyle is clean. With the
> patch the reproduction fails at the assertion that expects the renewal to
> fail, because the renewal succeeds.
> Found by code review of the SCM secret key rotation and distribution paths,
> as part of the TLA+ verification effort under HDDS-15926, on commit
> 025266b52a6dcaf39508607cad736799c40907e8. Checked against HDDS issues and
> apache/ozone pull requests for duplicates before filing. The attached patch
> is a proposal for review. Generated with Specula (Claude Opus 5.5).
--
This message was sent by Atlassian Jira
(v8.20.10#820010)
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]