refactor: move data_dir override from env var to [server] TOML field
Reverts the NUMA_DATA_DIR env var added in the previous commit and replaces it with a [server] data_dir TOML field. Numa already has a well-developed config system; adding a parallel env-var mechanism for a single knob was wrong. The principle: TOML is for application behavior configuration. Env vars are for bootstrap values (HOME, SUDO_USER to discover paths before config loads) and standard ecosystem conventions (RUST_LOG). data_dir is neither — it's an app knob, so it belongs in the TOML. Changes: - lib.rs::data_dir() reverts to the platform-specific fallback only - config.rs adds `data_dir: Option<PathBuf>` to ServerConfig - main.rs resolves config.server.data_dir with fallback to numa::data_dir() and passes it to build_tls_config, then stores the resolved path on ctx.data_dir for downstream consumers - tls.rs::build_tls_config takes `data_dir: &Path` as an explicit parameter instead of calling crate::data_dir() behind the caller's back. regenerate_tls and dot.rs self_signed_tls now pass &ctx.data_dir, honoring whatever path the config resolved to - tests/integration.sh Suite 6 uses `data_dir = "$NUMA_DATA"` in its test TOML instead of the NUMA_DATA_DIR env var prefix - numa.toml gains a commented-out data_dir example No behavior change for existing production deployments (the default path is unchanged). Test harness is now fully config-driven, and containerized deploys can override data_dir via mount+config without needing env var injection. 127/127 unit tests pass, Suite 6 passes end-to-end. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -2,6 +2,11 @@
|
|||||||
bind_addr = "0.0.0.0:53"
|
bind_addr = "0.0.0.0:53"
|
||||||
api_port = 5380
|
api_port = 5380
|
||||||
# api_bind_addr = "127.0.0.1" # default; set to "0.0.0.0" for LAN dashboard access
|
# api_bind_addr = "127.0.0.1" # default; set to "0.0.0.0" for LAN dashboard access
|
||||||
|
# data_dir = "/usr/local/var/numa" # where numa stores TLS CA and cert material
|
||||||
|
# (default: /usr/local/var/numa on unix,
|
||||||
|
# %PROGRAMDATA%\numa on windows). Override for
|
||||||
|
# containerized deploys or tests that can't
|
||||||
|
# write to the system path.
|
||||||
|
|
||||||
# [upstream]
|
# [upstream]
|
||||||
# mode = "forward" # "forward" (default) — relay to upstream
|
# mode = "forward" # "forward" (default) — relay to upstream
|
||||||
|
|||||||
@@ -41,6 +41,10 @@ pub struct ServerConfig {
|
|||||||
pub api_port: u16,
|
pub api_port: u16,
|
||||||
#[serde(default = "default_api_bind_addr")]
|
#[serde(default = "default_api_bind_addr")]
|
||||||
pub api_bind_addr: String,
|
pub api_bind_addr: String,
|
||||||
|
/// Where numa writes TLS material (CA, leaf certs, regenerated state).
|
||||||
|
/// Defaults to `crate::data_dir()` (platform-specific system path) if unset.
|
||||||
|
#[serde(default)]
|
||||||
|
pub data_dir: Option<PathBuf>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for ServerConfig {
|
impl Default for ServerConfig {
|
||||||
@@ -49,6 +53,7 @@ impl Default for ServerConfig {
|
|||||||
bind_addr: default_bind_addr(),
|
bind_addr: default_bind_addr(),
|
||||||
api_port: default_api_port(),
|
api_port: default_api_port(),
|
||||||
api_bind_addr: default_api_bind_addr(),
|
api_bind_addr: default_api_bind_addr(),
|
||||||
|
data_dir: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ fn load_tls_config(cert_path: &Path, key_path: &Path) -> crate::Result<Arc<Serve
|
|||||||
/// note in tls.rs::generate_service_cert).
|
/// note in tls.rs::generate_service_cert).
|
||||||
fn self_signed_tls(ctx: &ServerCtx) -> Option<Arc<ServerConfig>> {
|
fn self_signed_tls(ctx: &ServerCtx) -> Option<Arc<ServerConfig>> {
|
||||||
let service_names = [ctx.proxy_tld.clone()];
|
let service_names = [ctx.proxy_tld.clone()];
|
||||||
match crate::tls::build_tls_config(&ctx.proxy_tld, &service_names, dot_alpn()) {
|
match crate::tls::build_tls_config(&ctx.proxy_tld, &service_names, dot_alpn(), &ctx.data_dir) {
|
||||||
Ok(cfg) => Some(cfg),
|
Ok(cfg) => Some(cfg),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
warn!(
|
warn!(
|
||||||
|
|||||||
@@ -66,15 +66,12 @@ fn config_dir_unix() -> std::path::PathBuf {
|
|||||||
std::path::PathBuf::from("/usr/local/var/numa")
|
std::path::PathBuf::from("/usr/local/var/numa")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// System-wide data directory for TLS certs.
|
/// Default system-wide data directory for TLS certs. Overridable via
|
||||||
/// Override with `NUMA_DATA_DIR` env var (useful for containerized
|
/// `[server] data_dir = "..."` in numa.toml — this function only provides
|
||||||
/// deployments and integration tests that can't write to the default path).
|
/// the fallback when the config doesn't set it.
|
||||||
/// Unix: /usr/local/var/numa
|
/// Unix: /usr/local/var/numa
|
||||||
/// Windows: %PROGRAMDATA%\numa
|
/// Windows: %PROGRAMDATA%\numa
|
||||||
pub fn data_dir() -> std::path::PathBuf {
|
pub fn data_dir() -> std::path::PathBuf {
|
||||||
if let Ok(dir) = std::env::var("NUMA_DATA_DIR") {
|
|
||||||
return std::path::PathBuf::from(dir);
|
|
||||||
}
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
{
|
{
|
||||||
std::path::PathBuf::from(
|
std::path::PathBuf::from(
|
||||||
|
|||||||
17
src/main.rs
17
src/main.rs
@@ -204,10 +204,23 @@ async fn main() -> numa::Result<()> {
|
|||||||
|
|
||||||
let forwarding_rules = system_dns.forwarding_rules;
|
let forwarding_rules = system_dns.forwarding_rules;
|
||||||
|
|
||||||
|
// Resolve data_dir from config, falling back to the platform default.
|
||||||
|
// Used for TLS CA storage below and stored on ServerCtx for runtime use.
|
||||||
|
let resolved_data_dir = config
|
||||||
|
.server
|
||||||
|
.data_dir
|
||||||
|
.clone()
|
||||||
|
.unwrap_or_else(numa::data_dir);
|
||||||
|
|
||||||
// Build initial TLS config before ServerCtx (so ArcSwap is ready at construction)
|
// Build initial TLS config before ServerCtx (so ArcSwap is ready at construction)
|
||||||
let initial_tls = if config.proxy.enabled && config.proxy.tls_port > 0 {
|
let initial_tls = if config.proxy.enabled && config.proxy.tls_port > 0 {
|
||||||
let service_names = service_store.names();
|
let service_names = service_store.names();
|
||||||
match numa::tls::build_tls_config(&config.proxy.tld, &service_names, Vec::new()) {
|
match numa::tls::build_tls_config(
|
||||||
|
&config.proxy.tld,
|
||||||
|
&service_names,
|
||||||
|
Vec::new(),
|
||||||
|
&resolved_data_dir,
|
||||||
|
) {
|
||||||
Ok(tls_config) => Some(ArcSwap::from(tls_config)),
|
Ok(tls_config) => Some(ArcSwap::from(tls_config)),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
log::warn!("TLS setup failed, HTTPS proxy disabled: {}", e);
|
log::warn!("TLS setup failed, HTTPS proxy disabled: {}", e);
|
||||||
@@ -248,7 +261,7 @@ async fn main() -> numa::Result<()> {
|
|||||||
config_path: resolved_config_path,
|
config_path: resolved_config_path,
|
||||||
config_found,
|
config_found,
|
||||||
config_dir: numa::config_dir(),
|
config_dir: numa::config_dir(),
|
||||||
data_dir: numa::data_dir(),
|
data_dir: resolved_data_dir,
|
||||||
tls_config: initial_tls,
|
tls_config: initial_tls,
|
||||||
upstream_mode: resolved_mode,
|
upstream_mode: resolved_mode,
|
||||||
root_hints,
|
root_hints,
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ pub fn regenerate_tls(ctx: &ServerCtx) {
|
|||||||
names.extend(ctx.lan_peers.lock().unwrap().names());
|
names.extend(ctx.lan_peers.lock().unwrap().names());
|
||||||
let names: Vec<String> = names.into_iter().collect();
|
let names: Vec<String> = names.into_iter().collect();
|
||||||
|
|
||||||
match build_tls_config(&ctx.proxy_tld, &names, Vec::new()) {
|
match build_tls_config(&ctx.proxy_tld, &names, Vec::new(), &ctx.data_dir) {
|
||||||
Ok(new_config) => {
|
Ok(new_config) => {
|
||||||
tls.store(new_config);
|
tls.store(new_config);
|
||||||
info!("TLS cert regenerated for {} services", names.len());
|
info!("TLS cert regenerated for {} services", names.len());
|
||||||
@@ -38,13 +38,15 @@ pub fn regenerate_tls(ctx: &ServerCtx) {
|
|||||||
/// so we list each service explicitly as a SAN.
|
/// so we list each service explicitly as a SAN.
|
||||||
/// `alpn` is advertised in the TLS ServerHello — pass empty for the proxy
|
/// `alpn` is advertised in the TLS ServerHello — pass empty for the proxy
|
||||||
/// (which accepts any ALPN), or `[b"dot"]` for DoT (RFC 7858 §3.2).
|
/// (which accepts any ALPN), or `[b"dot"]` for DoT (RFC 7858 §3.2).
|
||||||
|
/// `data_dir` is where the CA material is stored — taken from
|
||||||
|
/// `[server] data_dir` in numa.toml (defaults to `crate::data_dir()`).
|
||||||
pub fn build_tls_config(
|
pub fn build_tls_config(
|
||||||
tld: &str,
|
tld: &str,
|
||||||
service_names: &[String],
|
service_names: &[String],
|
||||||
alpn: Vec<Vec<u8>>,
|
alpn: Vec<Vec<u8>>,
|
||||||
|
data_dir: &Path,
|
||||||
) -> crate::Result<Arc<ServerConfig>> {
|
) -> crate::Result<Arc<ServerConfig>> {
|
||||||
let dir = crate::data_dir();
|
let (ca_cert, ca_key) = ensure_ca(data_dir)?;
|
||||||
let (ca_cert, ca_key) = ensure_ca(&dir)?;
|
|
||||||
let (cert_chain, key) = generate_service_cert(&ca_cert, &ca_key, tld, service_names)?;
|
let (cert_chain, key) = generate_service_cert(&ca_cert, &ca_key, tld, service_names)?;
|
||||||
|
|
||||||
// Ensure a crypto provider is installed (rustls needs one)
|
// Ensure a crypto provider is installed (rustls needs one)
|
||||||
|
|||||||
@@ -542,8 +542,9 @@ else
|
|||||||
PROXY_HTTPS_PORT=8443
|
PROXY_HTTPS_PORT=8443
|
||||||
NUMA_DATA=/tmp/numa-integration-data
|
NUMA_DATA=/tmp/numa-integration-data
|
||||||
|
|
||||||
# Fresh data dir so we generate a fresh CA for this suite — NUMA_DATA_DIR
|
# Fresh data dir so we generate a fresh CA for this suite. Path is set
|
||||||
# env var lets numa write under $TMPDIR instead of /usr/local/var/numa.
|
# via [server] data_dir in the TOML below, not an env var — numa treats
|
||||||
|
# its config file as the single source of truth for all knobs.
|
||||||
rm -rf "$NUMA_DATA"
|
rm -rf "$NUMA_DATA"
|
||||||
mkdir -p "$NUMA_DATA"
|
mkdir -p "$NUMA_DATA"
|
||||||
|
|
||||||
@@ -551,6 +552,7 @@ else
|
|||||||
[server]
|
[server]
|
||||||
bind_addr = "127.0.0.1:$PORT"
|
bind_addr = "127.0.0.1:$PORT"
|
||||||
api_port = $API_PORT
|
api_port = $API_PORT
|
||||||
|
data_dir = "$NUMA_DATA"
|
||||||
|
|
||||||
[upstream]
|
[upstream]
|
||||||
mode = "forward"
|
mode = "forward"
|
||||||
@@ -582,7 +584,7 @@ value = "10.0.0.1"
|
|||||||
ttl = 60
|
ttl = 60
|
||||||
CONF
|
CONF
|
||||||
|
|
||||||
NUMA_DATA_DIR="$NUMA_DATA" RUST_LOG=info "$BINARY" "$CONFIG" > "$LOG" 2>&1 &
|
RUST_LOG=info "$BINARY" "$CONFIG" > "$LOG" 2>&1 &
|
||||||
NUMA_PID=$!
|
NUMA_PID=$!
|
||||||
sleep 4
|
sleep 4
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user