From b4d8d05a85487fd867dacdd3159979abef8eaeeb Mon Sep 17 00:00:00 2001 From: Bryan Helmkamp Date: Thu, 9 Apr 2026 18:54:04 -0400 Subject: [PATCH] refactor(server): move TlsSettings into its own tls_config module MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `TlsSettings` and its `from_settings(&SettingsFile)` constructor lived in `jwt_auth.rs` as a historical artifact from the Stage 6.6g rewrite — the auth resolver only needs to know *whether* TLS is present (for mTLS support), not the contents of the triple. The type is really a listen-side concern that belongs next to the rustls builder. Moves the type into a new `fabro-server/src/tls_config.rs` module (35 LOC). Updates three importers: - `jwt_auth.rs` — imports `TlsSettings` from `crate::tls_config`; drops the `std::path::PathBuf` / `InterpString` / `ServerListenLayer` / `serde::Deserialize` imports that are no longer used after the type moved. - `serve.rs` — splits the multi-item `use crate::jwt_auth::{...}` line so `TlsSettings` comes from `crate::tls_config`. - `tls.rs` — same split. - `tests/it/api/mtls.rs` — same split. Pure relocation; no behavioral change. 156 fabro-server tests pass, `cargo fmt --check --all` and `cargo clippy --workspace -- -D warnings` are clean. Co-Authored-By: Claude Opus 4.6 (1M context) --- lib/crates/fabro-server/src/jwt_auth.rs | 35 +-------------- lib/crates/fabro-server/src/lib.rs | 1 + lib/crates/fabro-server/src/serve.rs | 3 +- lib/crates/fabro-server/src/tls.rs | 3 +- lib/crates/fabro-server/src/tls_config.rs | 46 ++++++++++++++++++++ lib/crates/fabro-server/tests/it/api/mtls.rs | 3 +- 6 files changed, 54 insertions(+), 37 deletions(-) create mode 100644 lib/crates/fabro-server/src/tls_config.rs diff --git a/lib/crates/fabro-server/src/jwt_auth.rs b/lib/crates/fabro-server/src/jwt_auth.rs index a4f12e266..e930f488e 100644 --- a/lib/crates/fabro-server/src/jwt_auth.rs +++ b/lib/crates/fabro-server/src/jwt_auth.rs @@ -1,4 +1,3 @@ -use std::path::PathBuf; use std::sync::Arc; use axum::extract::FromRequestParts; @@ -10,42 +9,10 @@ use serde::Deserialize; use tracing::warn; use crate::error::ApiError; +use crate::tls_config::TlsSettings; use crate::web_auth::SessionCookie; use fabro_types::RunAuthMethod; use fabro_types::settings::SettingsFile; -use fabro_types::settings::interp::InterpString; -use fabro_types::settings::server::ServerListenLayer; - -/// Resolved TLS material used by the rustls config builder in `tls.rs` -/// when the server is listening on TCP with `[server.listen.tls]` set. -#[derive(Debug, Clone, PartialEq)] -pub struct TlsSettings { - pub cert: PathBuf, - pub key: PathBuf, - pub ca: PathBuf, -} - -impl TlsSettings { - /// Extract the `[server.listen.tls]` subtree out of a `SettingsFile`. - /// Returns `None` when the server is on Unix sockets, TLS is unset, or - /// any of the three fields is missing. - #[must_use] - pub fn from_settings(file: &SettingsFile) -> Option { - let listen = file.server.as_ref()?.listen.as_ref()?; - let tls = match listen { - ServerListenLayer::Tcp { tls, .. } => tls.as_ref()?, - ServerListenLayer::Unix { .. } => return None, - }; - let cert = tls.cert.as_ref().map(InterpString::as_source)?; - let key = tls.key.as_ref().map(InterpString::as_source)?; - let ca = tls.ca.as_ref().map(InterpString::as_source)?; - Some(Self { - cert: cert.into(), - key: key.into(), - ca: ca.into(), - }) - } -} /// JWT claims for service-to-service authentication. #[derive(Debug, Deserialize)] diff --git a/lib/crates/fabro-server/src/lib.rs b/lib/crates/fabro-server/src/lib.rs index 507c77f2a..e11d07cdd 100644 --- a/lib/crates/fabro-server/src/lib.rs +++ b/lib/crates/fabro-server/src/lib.rs @@ -17,4 +17,5 @@ pub mod server; mod settings_view; pub mod static_files; pub mod tls; +pub mod tls_config; pub mod web_auth; diff --git a/lib/crates/fabro-server/src/serve.rs b/lib/crates/fabro-server/src/serve.rs index 7075796c8..6fceb88cf 100644 --- a/lib/crates/fabro-server/src/serve.rs +++ b/lib/crates/fabro-server/src/serve.rs @@ -21,13 +21,14 @@ use fabro_types::settings::SettingsFile; use crate::bind::{self, Bind, BindRequest}; use crate::github_webhooks::WebhookManager; -use crate::jwt_auth::{AuthMode, AuthStrategy, TlsSettings, resolve_auth_mode_with_lookup}; +use crate::jwt_auth::{AuthMode, AuthStrategy, resolve_auth_mode_with_lookup}; use crate::secret_store::SecretStore; use crate::server::{ RouterOptions, build_app_state_with_path, build_router_with_options, reconcile_incomplete_runs_on_startup, shutdown_active_workers, spawn_scheduler, }; use crate::tls::{ClientAuth, build_rustls_config, serve_tls_with_shutdown}; +use crate::tls_config::TlsSettings; use fabro_llm::client::Client as LlmClient; use fabro_sandbox::SandboxProvider; diff --git a/lib/crates/fabro-server/src/tls.rs b/lib/crates/fabro-server/src/tls.rs index ff908b3fd..a1b226cd1 100644 --- a/lib/crates/fabro-server/src/tls.rs +++ b/lib/crates/fabro-server/src/tls.rs @@ -8,7 +8,8 @@ use rustls_pki_types::{CertificateDer, PrivateKeyDer}; use tokio::net::TcpListener; use tracing::error; -use crate::jwt_auth::{PeerCertificates, TlsSettings}; +use crate::jwt_auth::PeerCertificates; +use crate::tls_config::TlsSettings; /// How client certificates should be verified. #[derive(Clone, Copy)] diff --git a/lib/crates/fabro-server/src/tls_config.rs b/lib/crates/fabro-server/src/tls_config.rs new file mode 100644 index 000000000..13955639c --- /dev/null +++ b/lib/crates/fabro-server/src/tls_config.rs @@ -0,0 +1,46 @@ +//! Resolved TLS material extracted from `[server.listen.tls]`. +//! +//! This module owns the `(cert, key, ca)` triple that the rustls config +//! builder in [`crate::tls`] consumes when the server is listening on TCP +//! with mTLS enabled. It lives outside `jwt_auth.rs` because TLS material +//! is a listen-side concern, not an authentication strategy — the auth +//! resolver only cares about *whether* TLS is present (for mTLS support), +//! not about its contents. + +use std::path::PathBuf; + +use fabro_types::settings::SettingsFile; +use fabro_types::settings::interp::InterpString; +use fabro_types::settings::server::ServerListenLayer; + +/// Resolved TLS material used by the rustls config builder in +/// [`crate::tls`] when the server is listening on TCP with +/// `[server.listen.tls]` set. +#[derive(Debug, Clone, PartialEq)] +pub struct TlsSettings { + pub cert: PathBuf, + pub key: PathBuf, + pub ca: PathBuf, +} + +impl TlsSettings { + /// Extract the `[server.listen.tls]` subtree out of a `SettingsFile`. + /// Returns `None` when the server is on Unix sockets, TLS is unset, or + /// any of the three fields is missing. + #[must_use] + pub fn from_settings(file: &SettingsFile) -> Option { + let listen = file.server.as_ref()?.listen.as_ref()?; + let tls = match listen { + ServerListenLayer::Tcp { tls, .. } => tls.as_ref()?, + ServerListenLayer::Unix { .. } => return None, + }; + let cert = tls.cert.as_ref().map(InterpString::as_source)?; + let key = tls.key.as_ref().map(InterpString::as_source)?; + let ca = tls.ca.as_ref().map(InterpString::as_source)?; + Some(Self { + cert: cert.into(), + key: key.into(), + ca: ca.into(), + }) + } +} diff --git a/lib/crates/fabro-server/tests/it/api/mtls.rs b/lib/crates/fabro-server/tests/it/api/mtls.rs index 232cc545c..b43f96e6f 100644 --- a/lib/crates/fabro-server/tests/it/api/mtls.rs +++ b/lib/crates/fabro-server/tests/it/api/mtls.rs @@ -4,9 +4,10 @@ use crate::helpers::api; use std::path::{Path, PathBuf}; use std::sync::Arc; -use fabro_server::jwt_auth::{AuthMode, AuthStrategy, TlsSettings}; +use fabro_server::jwt_auth::{AuthMode, AuthStrategy}; use fabro_server::server::{build_router, create_app_state}; use fabro_server::tls::{ClientAuth, build_rustls_config}; +use fabro_server::tls_config::TlsSettings; use tokio::net::TcpListener; fn fixture_path(name: &str) -> PathBuf {