Files
seaweedfs/seaweed-common/src/security_config.rs
T
Chris LuandClaude Opus 5.5 9b5f7bf65b weed-worker: find jwt.filer_signing.key where the Go worker does, and let the chart deliver it (#12373)
* seaweed-common: share the security.toml search path with the volume server

* weed-worker: read jwt.filer_signing from security.toml and WEED_JWT_* like the Go worker

* helm: give the worker-lance container security.toml and the extra environment

* weed-worker: treat an empty signing key in the environment as unset

Viper ignores an empty environment value, so the Go worker falls back to
the security.toml key when WEED_JWT_FILER_SIGNING_KEY is blank. The Rust
worker took the blank value and connected with no token. An empty
--filer-signing-key / WEED_FILER_SIGNING_KEY also falls through to the
security.toml lookup now.

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

* weed-worker: read a quoted expires_after_seconds like viper

The Go worker reads jwt.filer_signing.expires_after_seconds with GetInt,
which also accepts a quoted number. The Rust worker dropped a quoted value
and minted tokens with no expiry.

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

* weed-worker: warn when security.toml cannot be read or parsed

Viper logs a warning when an optional config file is broken. The Rust
worker skipped it silently, so a typo looked like an install with no
signing key. Log the path and the parse message, without the quoted line,
which can hold a key.

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

* helm: keep POD_NAME and LANCE_NAMESPACE_URL out of worker-lance's extra environment

A duplicate name renders after the chart's entry and wins, silently
changing the Lance worker's --id or --namespace.

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

* weed-worker: list /usr/local/etc/seaweedfs in the security.toml search path

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

* weed-worker: an explicit --filer-signing-key-expires-after-seconds wins

The flag was silently ignored whenever the key was discovered, from
WEED_JWT_FILER_SIGNING_KEY as well as from security.toml. Use it when
given, else the expiry configured beside the discovered key, else zero.

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

* weed-worker: test where filer_signing looks for security.toml

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

* weed-worker: read a float expires_after_seconds as viper's GetInt does

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

* weed-worker: cap the minted exp so a huge expiry cannot overflow

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 14:49:39 -08:00

80 lines
2.8 KiB
Rust

//! Where a SeaweedFS process finds security.toml, the way `weed` does.
//!
//! Mirrors the search list in `weed/util/config.go`: the working directory,
//! `$HOME/.seaweedfs`, `/usr/local/etc/seaweedfs` and `/etc/seaweedfs`, first
//! existing file wins. The volume server and the workers each read the file
//! for different sections, but an operator mounts it in one place and expects
//! every binary to pick it up from there.
use std::ffi::OsString;
use std::path::{Path, PathBuf};
pub const SECURITY_CONFIG_FILE_NAME: &str = "security.toml";
/// The locations searched for security.toml, in order.
pub fn candidates(current_dir: Option<&Path>, home_dir: Option<&Path>) -> Vec<PathBuf> {
let mut candidates = Vec::new();
if let Some(dir) = current_dir {
candidates.push(dir.join(SECURITY_CONFIG_FILE_NAME));
}
if let Some(home) = home_dir {
candidates.push(home.join(".seaweedfs").join(SECURITY_CONFIG_FILE_NAME));
}
candidates.push(PathBuf::from("/usr/local/etc/seaweedfs").join(SECURITY_CONFIG_FILE_NAME));
candidates.push(PathBuf::from("/etc/seaweedfs").join(SECURITY_CONFIG_FILE_NAME));
candidates
}
/// The first candidate that is a file.
pub fn find(current_dir: Option<&Path>, home_dir: Option<&Path>) -> Option<PathBuf> {
candidates(current_dir, home_dir)
.into_iter()
.find(|candidate| candidate.is_file())
}
/// `$HOME`, or `%USERPROFILE%` where that is what the platform sets, read
/// through an injected lookup so tests need not touch the process environment.
pub fn home_dir(env: &dyn Fn(&str) -> Option<OsString>) -> Option<PathBuf> {
env("HOME")
.filter(|v| !v.is_empty())
.map(PathBuf::from)
.or_else(|| {
env("USERPROFILE")
.filter(|v| !v.is_empty())
.map(PathBuf::from)
})
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn search_order_matches_go() {
let found = candidates(Some(Path::new("/work")), Some(Path::new("/home/u")));
assert_eq!(
found,
vec![
PathBuf::from("/work/security.toml"),
PathBuf::from("/home/u/.seaweedfs/security.toml"),
PathBuf::from("/usr/local/etc/seaweedfs/security.toml"),
PathBuf::from("/etc/seaweedfs/security.toml"),
]
);
}
#[test]
fn home_falls_back_to_userprofile() {
let env = |key: &str| match key {
"USERPROFILE" => Some(OsString::from("/users/u")),
_ => None,
};
assert_eq!(home_dir(&env), Some(PathBuf::from("/users/u")));
let empty_home = |key: &str| match key {
"HOME" => Some(OsString::new()),
_ => None,
};
assert_eq!(home_dir(&empty_home), None);
}
}