Skip to content

branch-4.1: [fix](arrow-flight) Stop writing bearer tokens to fe.log - #67146

Open
CalvinKirs wants to merge 1 commit into
apache:branch-4.1from
CalvinKirs:backport-66572-branch-4.1
Open

branch-4.1: [fix](arrow-flight) Stop writing bearer tokens to fe.log#67146
CalvinKirs wants to merge 1 commit into
apache:branch-4.1from
CalvinKirs:backport-66572-branch-4.1

Conversation

@CalvinKirs

Copy link
Copy Markdown
Member

### What problem does this PR solve?

Issue Number: close #xxx

Related PR: #xxx

Problem Summary:

Arrow Flight SQL bearer tokens are written to `fe.log` in cleartext.

`FlightTokenManagerImpl` logs the token verbatim at INFO when it is
minted, evicted from either cache, and invalidated, and it also puts the
token into the `IllegalArgumentException` messages that
`FlightBearerTokenAuthenticator.validateBearer` logs at ERROR:

```java
LOG.info("Created flight token for user: {}, token: {}", username, token);
```

A bearer token is a complete credential until it expires —
`arrow_flight_token_alive_time_second` defaults to 86400s. So anyone who
can read `fe.log`, or the log aggregation platform it is shipped to, or
a backup of either, can take a live token, send it as `Authorization:
Bearer <token>` to the Arrow Flight SQL port (`arrow_flight_sql_port`,
default 8070), and run queries as that user without ever knowing their
password. Logs routinely reach a much wider audience than the credential
store does, which is what makes this worth fixing even though the log
file itself is not world readable.

**What this PR does**

Adds `org.apache.doris.common.util.TokenMasker`, which offers the two
renderings a secret can reasonably have in a message:

- `tokenId(t)` → `sha256:1a2b3c4d`, a truncated SHA-256. It is stable,
so a log line and the error message returned to the client still point
at the same token and can be matched up, but no part of the secret
survives in it. This is what the flight token paths now use. The
existing "search for this token in fe.log to see the evict reason" hint
therefore still works — it now says *token id*, and the id appears both
in the client's error and in the log.
- `maskPrefix(t)` → `abc***`, revealing only a short leading prefix, for
the case where a human has to recognize *which* configured secret was
involved (token rotation). This is the helper that already existed
privately in `MetaService`; it is moved into the utility and reused
rather than duplicated.

Every token-valued site in the Arrow Flight path is converted: the four
`LOG.info` calls in `FlightTokenManagerImpl`, the four
`IllegalArgumentException` messages in
`validateToken`/`getTokenDetails`, the one in
`FlightSessionsWithTokenManager.createConnectContext`, and the teardown
warning in `FlightSqlConnectPoolMgr.unregisterConnection`. That last one
is worth spelling out: a Flight SQL `ConnectContext`'s **`peerIdentity`
is the bearer token itself** —
`FlightBearerTokenAuthenticator.createAuthResultWithBearerToken` returns
the token as the peer identity, and `FlightSqlConnectPoolMgr` keys its
`flightToken2ConnectionId` map by it — so `ctx.getPeerIdentity()` in a
log line leaks a live token under a name that does not look like one.

Two more credentials with the same problem, found while auditing for
other instances:

- `Env` logs the cluster token adopted from a helper node at INFO (`get
token from helper node. token={}`). That token authenticates metadata
access between FE nodes, so it gets `maskPrefix`, consistent with how
`MetaService` already renders the same token.
- `Auth` echoes `initial_root_password` into a WARN — and it does so
from the branch that runs when the configured value failed 2-staged
SHA-1 validation, which is exactly the case where an operator put a
plaintext password in the config. The value is simply dropped from the
message; it adds nothing to the diagnosis that the config key name does
not already give.

Finally, a checkstyle rule rejects a value whose name says it holds a
token/password/secret/peer identity being passed straight into a
`LOG.x(...)` call, as a parameter or concatenated into the message. It
matches across lines, because the credential argument frequently sits on
a continuation line — that is true of the `FlightSqlConnectPoolMgr` case
above, which a line-based rule silently misses.

It is a backstop, not a substitute for review, and the honest limitation
is that it only knows the naming convention: `peerIdentity` had to be
taught to it by hand once it turned out to be a token, and any other
alias would be equally invisible. It reports **no violation anywhere in
`fe/`** after this PR, so it lands without a single suppression.

### Release note

Arrow Flight SQL bearer tokens are no longer written to `fe.log`. Log
lines and error messages now carry a non-reversible token id (`sha256:`
prefix) instead of the token itself.

### Check List (For Author)

- Test <!-- At least one of them must be included. -->
    - [ ] Regression test
    - [x] Unit Test
    - [ ] Manual test (add detailed scripts or steps below)
    - [ ] No need to test or manual test. Explain why:
- [ ] This is a refactor/code format and no logic has been changed.
        - [ ] Previous test can cover this change.
        - [ ] No code files have been changed.
        - [ ] Other reason <!-- Add your reason?  -->

`TokenMaskerTest` covers that the token id is a digest and cannot
contain any part of the token, that it is stable for the same token and
differs across tokens, the empty/null handling, and `maskPrefix`
including its too-short-to-reveal branch.

The checkstyle rule was verified to actually fire, not just to be quiet:
re-adding the original `LOG.info(..., username, token)` line, and
separately un-masking the multi-line `FlightSqlConnectPoolMgr` call,
each fail the build at that line with the new message; restoring them
goes back to green.

- Behavior changed:
    - [ ] No.
    - [x] Yes. <!-- Explain the behavior change -->

The text of some Arrow Flight error messages changes: where they used to
echo the bearer token, they now carry `token id: sha256:...`. Anything
that parsed the token out of an error message or out of `fe.log` would
need to use the id instead. No API, wire format or configuration
changes.

- Does this need documentation?
    - [x] No.
- [ ] Yes. <!-- Add document PR link here. eg:
apache/doris-website#1214 -->
@CalvinKirs
CalvinKirs requested a review from yiguolei as a code owner August 26, 2026 03:21
@hello-stephen

Copy link
Copy Markdown
Contributor

Thank you for your contribution to Apache Doris.
Don't know what should be done next? See How to process your PR.

Please clearly describe your PR:

  1. What problem was fixed (it's best to include specific error reporting information). How it was fixed.
  2. Which behaviors were modified. What was the previous behavior, what is it now, why was it modified, and what possible impacts might there be.
  3. What features were added. Why was this function added?
  4. Which code was refactored and why was this part of the code refactored?
  5. Which functions were optimized and what is the difference before and after the optimization?

@CalvinKirs

Copy link
Copy Markdown
Member Author

run buildall

@hello-stephen

Copy link
Copy Markdown
Contributor

FE UT Coverage Report

Increment line coverage 33.33% (10/30) 🎉
Increment coverage report
Complete coverage report

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants