Skip to content

HDFS-17947. dfs.block.access.token.lifetime=0 can cause DataStreamer createBlockOutputStream failure. - #8638

Open
joseluisll wants to merge 3 commits into
apache:trunkfrom
joseluisll:HDFS-17947-block-token-lifetime
Open

HDFS-17947. dfs.block.access.token.lifetime=0 can cause DataStreamer createBlockOutputStream failure.#8638
joseluisll wants to merge 3 commits into
apache:trunkfrom
joseluisll:HDFS-17947-block-token-lifetime

Conversation

@joseluisll

Copy link
Copy Markdown

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

identifier.setExpiryDate(timer.now() + tokenLifetime);   // BlockTokenSecretManager:490

and evaluates expiry with a strict comparison

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:

  • 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? (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.

…createBlockOutputStream failure.

When block access tokens are enabled and dfs.block.access.token.lifetime is
set to 0, BlockTokenSecretManager stamps every token with an expiry date equal
to its creation instant (timer.now() + tokenLifetime). Because expiry is
evaluated with a strict comparison (Time.now() > expiryDate), the token is
already invalid by the time a DataNode verifies it, so DataXceiver answers
ERROR_ACCESS_TOKEN and DataStreamer.createBlockOutputStream fails with
InvalidBlockTokenException. Unlike InvalidEncryptionKeyException, the write
path does not refetch the token, so every DataNode in the pipeline is excluded
in turn and the write fails with "Unable to create new block".

The same arithmetic is used by generateDataEncryptionKey(), so a non-positive
lifetime also produces data transfer encryption keys that are born expired.
The value additionally propagates to DataNodes and the Balancer through
ExportedBlockKeys, so validating it at the NameNode covers the whole cluster.

Reject a non-positive dfs.block.access.token.lifetime in
BlockManager.createBlockTokenSecretManager, so the misconfiguration surfaces
as a clear error instead of an unexplained write failure, and document the
constraint in hdfs-default.xml. The check sits in the BlockManager
constructor, so the NameNode will neither format nor start with an invalid
value.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@hadoop-yetus

Copy link
Copy Markdown

🎊 +1 overall

Vote Subsystem Runtime Logfile Comment
+0 🆗 reexec 0m 58s Docker mode activated.
_ Prechecks _
+1 💚 dupname 0m 0s No case conflicting files found.
+0 🆗 codespell 0m 1s codespell was not available.
+0 🆗 detsecrets 0m 1s detect-secrets was not available.
+0 🆗 xmllint 0m 1s xmllint was not available.
+1 💚 @author 0m 0s The patch does not contain any @author tags.
+1 💚 test4tests 0m 0s The patch appears to include 2 new or modified test files.
_ trunk Compile Tests _
+1 💚 mvninstall 41m 9s trunk passed
+1 💚 compile 1m 45s trunk passed with JDK Ubuntu-21.0.11+10-1-24.04.2-Ubuntu
+1 💚 compile 1m 47s trunk passed with JDK Ubuntu-17.0.19+10-1-24.04.2-Ubuntu
+1 💚 checkstyle 1m 48s trunk passed
+1 💚 mvnsite 1m 55s trunk passed
+1 💚 javadoc 1m 29s trunk passed with JDK Ubuntu-21.0.11+10-1-24.04.2-Ubuntu
+1 💚 javadoc 1m 28s trunk passed with JDK Ubuntu-17.0.19+10-1-24.04.2-Ubuntu
+1 💚 spotbugs 4m 7s trunk passed
+1 💚 shadedclient 31m 35s branch has no errors when building and testing our client artifacts.
_ Patch Compile Tests _
+1 💚 mvninstall 1m 20s the patch passed
+1 💚 compile 1m 14s the patch passed with JDK Ubuntu-21.0.11+10-1-24.04.2-Ubuntu
+1 💚 javac 1m 14s the patch passed
+1 💚 compile 1m 15s the patch passed with JDK Ubuntu-17.0.19+10-1-24.04.2-Ubuntu
+1 💚 javac 1m 15s the patch passed
+1 💚 blanks 0m 0s The patch has no blanks issues.
+1 💚 checkstyle 1m 16s the patch passed
+1 💚 mvnsite 1m 26s the patch passed
+1 💚 javadoc 0m 56s the patch passed with JDK Ubuntu-21.0.11+10-1-24.04.2-Ubuntu
+1 💚 javadoc 1m 0s the patch passed with JDK Ubuntu-17.0.19+10-1-24.04.2-Ubuntu
+1 💚 spotbugs 3m 45s the patch passed
+1 💚 shadedclient 30m 31s patch has no errors when building and testing our client artifacts.
_ Other Tests _
+1 💚 unit 273m 14s hadoop-hdfs in the patch passed.
+1 💚 asflicense 0m 46s The patch does not generate ASF License warnings.
403m 2s
Subsystem Report/Notes
Docker ClientAPI=1.55 ServerAPI=1.55 base: https://ci-hadoop.apache.org/job/hadoop-multibranch/job/PR-8638/1/artifact/out/Dockerfile
GITHUB PR #8638
Optional Tests dupname asflicense compile javac javadoc mvninstall mvnsite unit shadedclient spotbugs checkstyle codespell detsecrets xmllint
uname Linux 3dcb1751fb89 5.15.0-181-generic #191-Ubuntu SMP Fri May 22 19:09:02 UTC 2026 x86_64 x86_64 x86_64 GNU/Linux
Build tool maven
Personality dev-support/bin/hadoop.sh
git revision trunk / 150cd8d
Default Java Ubuntu-17.0.19+10-1-24.04.2-Ubuntu
Multi-JDK versions /usr/lib/jvm/java-21-openjdk-amd64:Ubuntu-21.0.11+10-1-24.04.2-Ubuntu /usr/lib/jvm/java-17-openjdk-amd64:Ubuntu-17.0.19+10-1-24.04.2-Ubuntu
Test Results https://ci-hadoop.apache.org/job/hadoop-multibranch/job/PR-8638/1/testReport/
Max. process+thread count 3397 (vs. ulimit of 10000)
modules C: hadoop-hdfs-project/hadoop-hdfs U: hadoop-hdfs-project/hadoop-hdfs
Console output https://ci-hadoop.apache.org/job/hadoop-multibranch/job/PR-8638/1/console
versions git=2.43.0 maven=3.9.15 spotbugs=4.9.7
Powered by Apache Yetus 0.14.1 https://yetus.apache.org

This message was automatically generated.

Comment thread hadoop-hdfs-project/hadoop-hdfs/src/main/resources/hdfs-default.xml Outdated
- Shorten the validation message to state only that the value is invalid
  and should be positive, matching the ensurePositiveInt idiom already in
  BlockManager.
- Revert the hdfs-default.xml description change.
- Drop the two new test classes and cover the validation with a single
  test in the existing TestBlockTokenWithDFS instead.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

@ayushtkn ayushtkn left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

minor comment, rest changes LGTM

- Fold the message check into assertThrows.
- Drop the test javadoc, matching the rest of the class.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@hadoop-yetus

Copy link
Copy Markdown

💔 -1 overall

Vote Subsystem Runtime Logfile Comment
+0 🆗 reexec 0m 56s Docker mode activated.
_ Prechecks _
+1 💚 dupname 0m 0s No case conflicting files found.
+0 🆗 codespell 0m 0s codespell was not available.
+0 🆗 detsecrets 0m 0s detect-secrets was not available.
+0 🆗 xmllint 0m 0s xmllint was not available.
+1 💚 @author 0m 0s The patch does not contain any @author tags.
+1 💚 test4tests 0m 0s The patch appears to include 3 new or modified test files.
_ trunk Compile Tests _
+1 💚 mvninstall 41m 30s trunk passed
+1 💚 compile 1m 43s trunk passed with JDK Ubuntu-21.0.11+10-1-24.04.2-Ubuntu
+1 💚 compile 1m 44s trunk passed with JDK Ubuntu-17.0.19+10-1-24.04.2-Ubuntu
+1 💚 checkstyle 1m 50s trunk passed
+1 💚 mvnsite 1m 54s trunk passed
+1 💚 javadoc 1m 29s trunk passed with JDK Ubuntu-21.0.11+10-1-24.04.2-Ubuntu
+1 💚 javadoc 1m 29s trunk passed with JDK Ubuntu-17.0.19+10-1-24.04.2-Ubuntu
+1 💚 spotbugs 4m 7s trunk passed
+1 💚 shadedclient 31m 28s branch has no errors when building and testing our client artifacts.
_ Patch Compile Tests _
+1 💚 mvninstall 1m 19s the patch passed
+1 💚 compile 1m 13s the patch passed with JDK Ubuntu-21.0.11+10-1-24.04.2-Ubuntu
+1 💚 javac 1m 13s the patch passed
+1 💚 compile 1m 17s the patch passed with JDK Ubuntu-17.0.19+10-1-24.04.2-Ubuntu
+1 💚 javac 1m 17s the patch passed
+1 💚 blanks 0m 0s The patch has no blanks issues.
+1 💚 checkstyle 1m 14s the patch passed
+1 💚 mvnsite 1m 24s the patch passed
+1 💚 javadoc 0m 59s the patch passed with JDK Ubuntu-21.0.11+10-1-24.04.2-Ubuntu
+1 💚 javadoc 0m 59s the patch passed with JDK Ubuntu-17.0.19+10-1-24.04.2-Ubuntu
+1 💚 spotbugs 3m 46s the patch passed
+1 💚 shadedclient 30m 41s patch has no errors when building and testing our client artifacts.
_ Other Tests _
-1 ❌ unit 273m 30s /patch-unit-hadoop-hdfs-project_hadoop-hdfs.txt hadoop-hdfs in the patch passed.
+1 💚 asflicense 0m 48s The patch does not generate ASF License warnings.
403m 41s
Reason Tests
Failed junit tests hadoop.hdfs.TestBlockTokenZeroLifetimeWithDFS
Subsystem Report/Notes
Docker ClientAPI=1.55 ServerAPI=1.55 base: https://ci-hadoop.apache.org/job/hadoop-multibranch/job/PR-8638/2/artifact/out/Dockerfile
GITHUB PR #8638
Optional Tests dupname asflicense compile javac javadoc mvninstall mvnsite unit shadedclient spotbugs checkstyle codespell detsecrets xmllint
uname Linux 76dc6834a2fb 5.15.0-181-generic #191-Ubuntu SMP Fri May 22 19:09:02 UTC 2026 x86_64 x86_64 x86_64 GNU/Linux
Build tool maven
Personality dev-support/bin/hadoop.sh
git revision trunk / b0ce37d
Default Java Ubuntu-17.0.19+10-1-24.04.2-Ubuntu
Multi-JDK versions /usr/lib/jvm/java-21-openjdk-amd64:Ubuntu-21.0.11+10-1-24.04.2-Ubuntu /usr/lib/jvm/java-17-openjdk-amd64:Ubuntu-17.0.19+10-1-24.04.2-Ubuntu
Test Results https://ci-hadoop.apache.org/job/hadoop-multibranch/job/PR-8638/2/testReport/
Max. process+thread count 3112 (vs. ulimit of 10000)
modules C: hadoop-hdfs-project/hadoop-hdfs U: hadoop-hdfs-project/hadoop-hdfs
Console output https://ci-hadoop.apache.org/job/hadoop-multibranch/job/PR-8638/2/console
versions git=2.43.0 maven=3.9.15 spotbugs=4.9.7
Powered by Apache Yetus 0.14.1 https://yetus.apache.org

This message was automatically generated.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants