MCP bearer token passed via environment is captured in systemd-coredump journal entries on provider crashes
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 23k
- Forks
- 5.9k
- Avg merge
- 11h 14m
- Merged PRs (30d)
- 357
Description
Summary
T3_MCP_BEARER_TOKEN is passed to spawned provider processes through the
environment. On Linux, systemd-coredump records the entire environment of
any process that dumps core into the systemd journal as COREDUMP_ENVIRON,
in plaintext. So every provider crash writes that session's MCP bearer token to
a log readable by the account the providers run as.
Requesting a file- or fd-based alternative, in line with the --bootstrap-fd
mechanism this project already has.
Where it happens
From the shipped bundle:
environment: {
...(options?.environment ?? process.env),
T3_MCP_BEARER_TOKEN: mcpSession.authorizationHeader.replace(/^Bearer\s+/, "")
}
paired with the Codex MCP client config:
-c 'mcp_servers.t3-code.bearer_token_env_var="T3_MCP_BEARER_TOKEN"'
Measured impact on one host
Every core dump over a two-week window on a machine running a provider fleet:
dumps: 18 | with full environment recorded: 18 | carrying a secret-shaped value: 17
COREDUMP_ENVIRON is readable by root, the systemd-journal group, and —
via the journal directory ACL — the user the providers run as. Where multiple
provider sessions share one Unix account, any session that can run
journalctl can read the MCP token of any other session that has crashed.
journalctl --output=json omits the field by default because it is large;
--all reveals it. That makes the exposure easy to miss entirely.
Scope, stated honestly
mcpSession.authorizationHeader reads as a per-session token, and that
matches observation: a token recovered from a dump returned 401 against both
/mcp and /api/orchestration/snapshot after its session ended, while a freshly
minted token returned 200 on the same route. So this is not a long-lived
credential leak.
The exposure is therefore bounded to the lifetime of the session whose provider
crashed — but within that window it is a usable token sitting in a world-of-the-
account-readable log, and provider crashes are not rare on a busy host.
I would call this moderate rather than critical, and I would rather describe it
accurately than inflate it.
Why the environment is the wrong channel specifically
Not a general "env vars are bad" argument — this is a concrete Linux behaviour:
/proc/<pid>/environis readable for the lifetime of the process.systemd-coredumppersists the whole block on any crash, and it outlives the
core file, sinceStorage=controls the core, not the journal metadata.coredump.confon systemd 261 exposes onlyStorage,ProcessSizeMax,
SizeMax,ExternalSizeMax,MaxUse,Compress,EnterNamespace,
CoredumpReceive— there is no way for an operator to exclude that field.
So a deployment cannot mitigate this without disabling crash capture entirely,
which costs the diagnostics operators need for exactly the crashes that leak.
Requested change
A non-environment path for the MCP bearer token, e.g.:
- write it to a
0600file and pass the path, or - pass it on an inherited fd — the same shape as the existing
--bootstrap-fdflag ("Read one-time bootstrap secrets from the given file
descriptor"), which shows the project already treats fd-passing as the right
channel for secrets.
A constraint I cannot resolve from outside: the Codex MCP client is
configured here via bearer_token_env_var, which implies the client itself
expects an environment variable. If that is the only mechanism Codex supports,
this may need a client-side change first, or a T3-side mitigation instead
(shorter session TTL, or revoking the MCP session on provider exit so a
recovered token is dead on arrival).
Environment
t3code-bin 0.0.38-1 (Arch), runtime 0.0.42
Arch Linux, kernel 7.1.9, systemd 261
providers spawned under a single user service; multiple sessions share one UID
Not asking for
No mitigation is needed on my side beyond what an operator can already do. Filing
this because the leak is structural to the channel rather than to any
deployment's configuration, and because no operator-side setting can close it.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by tracing the shipped bundle's provider environment construction and the Codex MCP client configuration using bearer_token_env_var. Check whether Codex supports a file- or fd-based token path alongside the existing --bootstrap-fd mechanism. Done means the bearer token is no longer present in provider environments or systemd-coredump journal entries while provider authentication remains functional.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- linux, typescript
- Domain
- authentication, security
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100