//! Server configuration use serde::{Deserialize, Serialize}; use std::net::{IpAddr, Ipv4Addr, SocketAddr}; /// TLS configuration #[derive(Debug, Clone, Serialize, Deserialize)] pub struct TlsConfig { /// Path to certificate file (PEM) pub cert_file: String, /// Path to private key file (PEM) pub key_file: String, /// Path to CA certificate for client verification (optional, for mTLS) pub ca_file: Option, /// Require client certificates (mTLS) #[serde(default)] pub require_client_cert: bool, } /// Metadata storage backend #[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)] #[serde(rename_all = "lowercase")] pub enum MetadataBackend { /// FlareDB distributed metadata database FlareDb, /// PostgreSQL metadata database Postgres, /// SQLite metadata database (single-node only) Sqlite, } impl Default for MetadataBackend { fn default() -> Self { Self::FlareDb } } /// Server configuration #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ServerConfig { /// gRPC management API address pub grpc_addr: SocketAddr, /// ChainFire endpoint used for cluster coordination only pub chainfire_endpoint: Option, /// FlareDB endpoint used for metadata and tenant data storage pub flaredb_endpoint: Option, /// Metadata backend selection (flaredb, postgres, sqlite) #[serde(default)] pub metadata_backend: MetadataBackend, /// SQL database URL for metadata when backend is postgres or sqlite pub metadata_database_url: Option, /// Allow single-node mode (required for SQLite) #[serde(default)] pub single_node: bool, /// Log level pub log_level: String, /// TLS configuration (optional) pub tls: Option, /// Authentication configuration #[serde(default)] pub auth: AuthConfig, /// Backend health checker configuration #[serde(default)] pub health: HealthRuntimeConfig, /// VIP advertisement reconciliation configuration #[serde(default)] pub vip_advertisement: VipAdvertisementConfig, /// Local VIP ownership configuration. #[serde(default)] pub vip_ownership: VipOwnershipConfig, /// Native BGP speaker configuration #[serde(default)] pub bgp: BgpConfig, } /// Authentication configuration #[derive(Debug, Clone, Serialize, Deserialize)] pub struct AuthConfig { /// IAM server endpoint #[serde(default = "default_iam_server_addr")] pub iam_server_addr: String, } fn default_iam_server_addr() -> String { "127.0.0.1:50051".to_string() } /// Backend health checker runtime configuration. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct HealthRuntimeConfig { /// Interval between backend health check sweeps. #[serde(default = "default_health_check_interval_secs")] pub interval_secs: u64, /// Timeout for individual backend checks. #[serde(default = "default_health_check_timeout_secs")] pub timeout_secs: u64, } fn default_health_check_interval_secs() -> u64 { 5 } fn default_health_check_timeout_secs() -> u64 { 5 } impl Default for HealthRuntimeConfig { fn default() -> Self { Self { interval_secs: default_health_check_interval_secs(), timeout_secs: default_health_check_timeout_secs(), } } } /// VIP advertisement reconciliation runtime configuration. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct VipAdvertisementConfig { /// Interval between BGP advertisement reconciliation sweeps. #[serde(default = "default_vip_check_interval_secs")] pub interval_secs: u64, } fn default_vip_check_interval_secs() -> u64 { 3 } impl Default for VipAdvertisementConfig { fn default() -> Self { Self { interval_secs: default_vip_check_interval_secs(), } } } /// Local VIP ownership runtime configuration. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct VipOwnershipConfig { /// Whether FiberLB should claim VIP /32 addresses on the local node. #[serde(default)] pub enabled: bool, /// Interface used for local VIP ownership. #[serde(default = "default_vip_ownership_interface")] pub interface: String, } fn default_vip_ownership_interface() -> String { "lo".to_string() } impl Default for VipOwnershipConfig { fn default() -> Self { Self { enabled: false, interface: default_vip_ownership_interface(), } } } /// Static BGP peer configuration. #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] pub struct BgpPeerConfig { /// Peer IP address or hostname. pub address: String, /// Peer TCP port. #[serde(default = "default_bgp_peer_port")] pub port: u16, /// Peer AS number. pub asn: u32, /// Optional operator-visible description. #[serde(default)] pub description: String, } fn default_bgp_peer_port() -> u16 { 179 } /// Native BGP speaker configuration. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct BgpConfig { /// Whether FiberLB should originate VIP routes itself. #[serde(default)] pub enabled: bool, /// Local AS number. #[serde(default = "default_bgp_local_as")] pub local_as: u32, /// BGP router ID. Must be IPv4. #[serde(default = "default_bgp_router_id")] pub router_id: String, /// Optional explicit next-hop address. Falls back to router_id. #[serde(default)] pub next_hop: Option, /// Requested hold time in seconds. #[serde(default = "default_bgp_hold_time_secs")] pub hold_time_secs: u16, /// Keepalive interval in seconds. #[serde(default = "default_bgp_keepalive_secs")] pub keepalive_secs: u16, /// Delay before reconnecting to a failed peer. #[serde(default = "default_bgp_connect_retry_secs")] pub connect_retry_secs: u64, /// Static peers for outbound eBGP sessions. #[serde(default)] pub peers: Vec, } fn default_bgp_local_as() -> u32 { 65001 } fn default_bgp_router_id() -> String { Ipv4Addr::new(127, 0, 0, 1).to_string() } fn default_bgp_hold_time_secs() -> u16 { 90 } fn default_bgp_keepalive_secs() -> u16 { 30 } fn default_bgp_connect_retry_secs() -> u64 { 5 } impl BgpConfig { /// Effective next hop advertised in UPDATE messages. pub fn next_hop_addr(&self) -> std::result::Result { self.next_hop.as_deref().unwrap_or(&self.router_id).parse() } /// Parsed router ID as IPv4. pub fn router_id_addr(&self) -> std::result::Result { self.router_id.parse() } } impl Default for BgpConfig { fn default() -> Self { Self { enabled: false, local_as: default_bgp_local_as(), router_id: default_bgp_router_id(), next_hop: None, hold_time_secs: default_bgp_hold_time_secs(), keepalive_secs: default_bgp_keepalive_secs(), connect_retry_secs: default_bgp_connect_retry_secs(), peers: Vec::new(), } } } impl Default for AuthConfig { fn default() -> Self { Self { iam_server_addr: default_iam_server_addr(), } } } impl Default for ServerConfig { fn default() -> Self { Self { grpc_addr: "0.0.0.0:9080".parse().unwrap(), chainfire_endpoint: None, flaredb_endpoint: None, metadata_backend: MetadataBackend::FlareDb, metadata_database_url: None, single_node: false, log_level: "info".to_string(), tls: None, auth: AuthConfig::default(), health: HealthRuntimeConfig::default(), vip_advertisement: VipAdvertisementConfig::default(), vip_ownership: VipOwnershipConfig::default(), bgp: BgpConfig::default(), } } }