//! OCSP responder configuration. use std::net::SocketAddr; use std::path::PathBuf; use serde::Deserialize; #[derive(Debug, Clone, Deserialize)] pub struct Config { #[serde(default = "default_listen")] pub listen: SocketAddr, #[serde(default)] pub s3: S3Config, #[serde(default)] pub issuers: Vec, #[serde(default = "default_ocsp_next_update_seconds")] pub next_update_seconds: u64, } #[derive(Debug, Clone, Deserialize)] pub struct IssuerConfig { pub name: String, pub ca_id: String, pub ca_cert: PathBuf, pub signer_cert: PathBuf, pub signer_key: PathBuf, } #[derive(Debug, Clone, Deserialize)] pub struct S3Config { pub bucket: String, #[serde(default = "default_prefix")] pub prefix: String, pub region: Option, pub endpoint: Option, #[serde(default)] pub force_path_style: bool, #[serde(default = "default_cache_ttl_ms")] pub cache_ttl_ms: u64, #[serde(default = "default_stale_grace_seconds")] pub stale_grace_seconds: u64, } impl Default for S3Config { fn default() -> Self { Self { bucket: String::new(), prefix: default_prefix(), region: None, endpoint: None, force_path_style: false, cache_ttl_ms: default_cache_ttl_ms(), stale_grace_seconds: default_stale_grace_seconds(), } } } impl S3Config { pub fn object_key(&self, ca_id: &str) -> String { let prefix = self.prefix.trim_matches('/'); let name = format!("{}.json", ca_id.to_lowercase()); if prefix.is_empty() { name } else { format!("{prefix}/{name}") } } } fn default_listen() -> SocketAddr { "0.0.0.0:2560" .parse() .expect("valid default listen address") } fn default_prefix() -> String { "ocsp".to_string() } fn default_cache_ttl_ms() -> u64 { 1000 } fn default_stale_grace_seconds() -> u64 { 60 } fn default_ocsp_next_update_seconds() -> u64 { 10 } pub fn load_config() -> Result> { let path = std::env::var("OCSP_CONFIG").unwrap_or_else(|_| "/etc/ocsp/responder.yaml".to_string()); let text = std::fs::read_to_string(&path)?; let mut config: Config = serde_yaml::from_str(&text)?; if config.issuers.is_empty() { return Err("configuration must contain at least one issuer".into()); } if config.s3.bucket.trim().is_empty() { return Err("s3.bucket must be configured".into()); } if let Ok(listen) = std::env::var("OCSP_LISTEN") { config.listen = listen.parse()?; } Ok(config) } #[cfg(test)] mod tests { use super::*; #[test] fn object_key_uses_ca_id() { let config = S3Config { prefix: "ocsp".to_string(), ..Default::default() }; assert_eq!( config.object_key("123E4567-E89B-12D3-A456-426614174000"), "ocsp/123e4567-e89b-12d3-a456-426614174000.json" ); } }