security: configurable admin token path via ADMIN_TOKEN_PATH

Extract resolve_admin_token_path() function to read ADMIN_TOKEN_PATH
env var (falling back to .admin_token). Replaces the previous inline
ADMIN_TOKEN_FILE env var. Updates non-Unix warning to reference the
new env var name.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
whit3rabbit
2026-03-27 15:17:53 -05:00
co-authored by Claude Sonnet 4.6
parent 915a703a5a
commit 4bf6b65bb8
2 changed files with 16 additions and 7 deletions
+13 -4
View File
@@ -324,8 +324,8 @@ async fn main() {
// Admin token: use env var or generate random UUID written to a file.
let admin_token = std::env::var("ADMIN_TOKEN").unwrap_or_else(|_| {
let token = uuid::Uuid::new_v4().to_string();
let token_path = std::env::var("ADMIN_TOKEN_FILE")
.unwrap_or_else(|_| ".admin_token".into());
let token_path = resolve_admin_token_path();
let token_path = token_path.to_string_lossy().to_string();
// Write token to file with restrictive permissions instead of stderr,
// because stderr is captured by container log drivers in production.
if let Err(e) = write_token_file(&token_path, &token) {
@@ -523,6 +523,15 @@ fn parse_env_file(path: &str) -> Vec<(String, String)> {
pairs
}
/// Resolve admin token file path from `ADMIN_TOKEN_PATH` env var,
/// falling back to `.admin_token` in the current directory.
fn resolve_admin_token_path() -> std::path::PathBuf {
match std::env::var("ADMIN_TOKEN_PATH") {
Ok(p) => std::path::PathBuf::from(p),
Err(_) => std::path::PathBuf::from(".admin_token"),
}
}
/// Write the admin token to a file with mode 0600 (owner-only read/write).
/// On Unix, sets permissions atomically at creation to avoid a TOCTOU race
/// where the file is briefly world-readable before chmod.
@@ -544,8 +553,8 @@ fn write_token_file(path: &str, token: &str) -> std::io::Result<()> {
let mut file = {
tracing::warn!(
path = %path,
"non-Unix platform: admin token file may be world-readable. \
Set ADMIN_TOKEN env var explicitly in production."
"admin token file written without restrictive permissions (non-Unix platform); \
secure this file manually or set ADMIN_TOKEN_PATH to a protected location"
);
std::fs::File::create(path)?
};
+3 -3
View File
@@ -151,15 +151,15 @@ The dashboard binds to `localhost:3001` only (never externally accessible). It s
| Variable | Default | Description |
|----------|---------|-------------|
| `ADMIN_PORT` | `3001` | Port for the admin dashboard. Must differ from `LISTEN_PORT`. |
| `ADMIN_TOKEN` | (generated) | Bearer token for the admin API. If unset, a random UUID is generated at startup and written to `ADMIN_TOKEN_FILE`. |
| `ADMIN_TOKEN_FILE` | `.admin_token` | File path where the generated admin token is written. Permissions are set to `0600` on Unix. |
| `ADMIN_TOKEN` | (generated) | Bearer token for the admin API. If unset, a random UUID is generated at startup and written to `ADMIN_TOKEN_PATH`. |
| `ADMIN_TOKEN_PATH` | `.admin_token` | File path where the generated admin token is written. Permissions are set to `0600` on Unix. |
| `ADMIN_DB_PATH` | `admin.db` | SQLite database path for request logging and config overrides (model mappings, log level). Config overrides survive restarts. |
| `ADMIN_LOG_RETENTION_DAYS` | `7` | Days to retain request log entries before automatic purge. |
| `DISABLE_ADMIN` | (unset) | Set to `1`, `true`, or `yes` to force-disable the admin server even when `--webui` is passed. Useful in container deployments where the flag might be baked into the entrypoint. |
### Token security
The admin token is printed to `ADMIN_TOKEN_FILE` (default `.admin_token`) rather than stdout/stderr, because container log drivers capture stderr and persist it in centralized logging systems. On Unix, the file is created with mode `0600`.
The admin token is printed to `ADMIN_TOKEN_PATH` (default `.admin_token`) rather than stdout/stderr, because container log drivers capture stderr and persist it in centralized logging systems. On Unix, the file is created with mode `0600`.
In production, set `ADMIN_TOKEN` explicitly: