joseluisll opened a new pull request, #8638:
URL: https://github.com/apache/hadoop/pull/8638

   ### Description of PR
   
   `dfs.block.access.token.lifetime` is read without validation. A non-positive
   value is accepted and produces block access tokens that are already expired 
at
   the instant they are created, so every write fails with an error that names
   neither the property nor the cause.
   
   `BlockManager.createBlockTokenSecretManager` converts the value with
   `lifetimeMin * 60 * 1000L` and passes it to `BlockTokenSecretManager`, which
   stamps every token with
   
   ```java
   identifier.setExpiryDate(timer.now() + tokenLifetime);   // 
BlockTokenSecretManager:490
   ```
   
   and evaluates expiry with a strict comparison
   
   ```java
   private static boolean isExpired(long expiryDate) {
     return Time.now() > expiryDate;                        // 
BlockTokenSecretManager:443
   }
   ```
   
   With a lifetime of `0` the expiry date equals the creation instant, so the
   token is invalid as soon as the clock advances by one millisecond — which it
   always has by the time the token has travelled NameNode → client → DataNode.
   `DataXceiver` answers `ERROR_ACCESS_TOKEN` and
   `DataStreamer.createBlockOutputStream` fails with 
`InvalidBlockTokenException`.
   Unlike `InvalidEncryptionKeyException`, the write path has no token-refetch
   retry, so each DataNode is marked bad and excluded in turn until the pipeline
   is exhausted.
   
   Reproduced on a MiniDFSCluster with `dfs.block.access.token.enable=true` and
   `dfs.block.access.token.lifetime=0`, before the fix:
   
   ```
   WARN hdfs.DataStreamer (DataStreamer.java:createBlockOutputStream(1960)) -
     Exception in createBlockOutputStream blk_1073741825_1001
   org.apache.hadoop.hdfs.security.token.block.InvalidBlockTokenException:
     Got access token error, status message , ack with firstBadLink as 
127.0.0.1:50639
       at 
DataTransferProtoUtil.checkBlockOpStatus(DataTransferProtoUtil.java:113)
       at DataStreamer.createBlockOutputStream(DataStreamer.java:1948)
       at DataStreamer.setupPipelineForCreate(DataStreamer.java:1843)
   WARN - Abandoning BP-47005896-192.168.1.76-1785266294724:blk_1073741825_1001
   WARN - Excluding datanode DatanodeInfoWithStorage[127.0.0.1:50639,...]
   WARN - DataStreamer Exception
   java.io.IOException: Unable to create new block.
   ```
   
   Two further consequences of the same arithmetic are worth recording:
   
   * `generateDataEncryptionKey()` also stamps `timer.now() + tokenLifetime`
     (`BlockTokenSecretManager:541`), so a non-positive lifetime additionally
     produces data-transfer encryption keys that are born expired.
   * The value propagates cluster-wide through `ExportedBlockKeys`: DataNodes
     (`DataNode:2132`) and the Balancer (`KeyManager:74`) build their own
     `BlockTokenSecretManager` from the value the NameNode advertises. 
Validating
     at the NameNode therefore covers the whole cluster, and no DataNode-side
     check is needed.
   
   #### The change
   
   `BlockManager.createBlockTokenSecretManager` now rejects a non-positive
   lifetime with `HadoopIllegalArgumentException`, naming the property and the
   constraint. The check runs only when `dfs.block.access.token.enable` is true,
   so clusters that do not use block tokens are unaffected. Because it sits in 
the
   `BlockManager` constructor it applies to every path that builds an
   `FSNamesystem` — the NameNode will neither format nor start with an invalid
   value, and `NameNode.main` reports it through the existing
   `terminate(1, e)` path.
   
   The bound is simply "greater than zero", not some larger floor. The property 
is
   expressed in minutes, so the smallest representable positive value (`1`, i.e.
   60,000 ms) already exceeds the mint-to-verify window by about four orders of
   magnitude; there is no positive value in this config's units that reproduces
   the failure. Choosing an arbitrary minimum would reject configurations that
   work correctly today and would invent policy this project has never defined.
   Short-but-positive lifetimes do have real costs — tolerance to 
NameNode/DataNode
   clock skew, and read-path token refetch churn (cf. HDFS-16332) — but those 
are
   tuning concerns rather than the defect reported here.
   
   `dfs.block.access.key.update.interval` was examined as a possible sibling of
   this bug and deliberately left alone: with an interval of `0` the retiring 
key
   still receives an expiry of `now + keyUpdateInterval + tokenLifetime`, so 
tokens
   remain verifiable. Key rotation becomes wasteful, not broken, and it is out 
of
   scope here.
   
   #### Compatibility
   
   This is technically an incompatible change: a cluster configured with a
   non-positive `dfs.block.access.token.lifetime` starts today and will now 
refuse
   to start. It cannot regress a working deployment, because such a cluster is
   already unable to complete any write, but it is called out here explicitly
   rather than left to be discovered in review.
   
   Contains content generated by Anthropic Claude Code.
   
   ### How was this patch tested?
   
   New tests, both added by this patch:
   
   * `TestBlockTokenZeroLifetime` (6 tests, no cluster) pins the underlying
     behaviour at the secret-manager level in both legacy and protobuf token
     formats: with a lifetime of `0` the minted token's expiry date falls inside
     the creation instant, and the DataNode-side (worker) manager then rejects 
it
     with `InvalidToken ... is expired`; a 600-minute control verifies cleanly.
     The expiry assertion waits for one clock tick past the recorded expiry 
rather
     than sleeping, so it is deterministic rather than racing the clock.
   * `TestBlockTokenZeroLifetimeWithDFS` (5 tests, MiniDFSCluster) covers format
     rejecting `0` and `-1`, an already-formatted NameNode refusing to restart
     after the value is changed to `0`, a 600-minute control that writes and
     verifies a file, and — importantly — that the value is still ignored when
     `dfs.block.access.token.enable` is false.
   
   Full runs on the branch (JDK 17, `mvn test -pl 
hadoop-hdfs-project/hadoop-hdfs`):
   
   ```
   TestBlockTokenZeroLifetime          6 tests   0 failures
   TestBlockTokenZeroLifetimeWithDFS   5 tests   0 failures
   TestBlockToken                     25 tests   0 failures  (2 skipped: 
Linux-only /proc/self/fd)
   TestBlockTokenWithDFS               4 tests   0 failures
   TestFailoverWithBlockTokensEnabled  4 tests   0 failures
   TestEncryptedTransfer              32 tests   0 failures
   TestHdfsConfigFields                4 tests   0 failures
   ```
   
   The pre-fix reproduction quoted above was produced by the same
   `TestBlockTokenZeroLifetimeWithDFS` fixture with the validation reverted; the
   committed version of that test asserts the post-fix behaviour.
   
   No existing test or configuration file in the tree sets
   `dfs.block.access.token.lifetime`, so the new validation changes no existing
   test's behaviour.
   
   ### For code changes:
   
   - [x] Does the title or this PR starts with the corresponding JIRA issue id 
(e.g. 'HADOOP-17799. Your PR title ...')?
   - [ ] Object storage: have the integration tests been executed and the 
endpoint declared according to the connector-specific documentation? *(N/A — no 
object-storage changes)*
   - [ ] If adding new dependencies to the code, are these dependencies 
licensed in a way that is compatible for inclusion under [ASF 
2.0](http://www.apache.org/legal/resolved.html#category-a)? *(N/A — no new 
dependencies)*
   - [ ] If applicable, have you updated the `LICENSE`, `LICENSE-binary`, 
`NOTICE-binary` files? *(N/A)*
   
   ### AI Tooling
   
   This contribution was prepared with the assistance of Anthropic Claude Code:
   the root-cause analysis, the validation change, the tests and this 
description
   were drafted with its assistance, and were reviewed, executed and verified
   against the source by the contributor.
   
   - [x] The PR includes the phrase "Contains content generated by <tool>"
         where <tool> is the name of the AI tool used.
   - [x] My use of AI contributions follows the ASF legal policy
         https://www.apache.org/legal/generative-tooling.html
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to