use std::path::Path;
use rustls::{
RootCertStore, ServerConfig as RustlsServerConfig,
server::WebPkiClientVerifier,
version::{TLS12, TLS13},
};
use rustls_pki_types::pem::PemObject;
use rustls_pki_types::{CertificateDer, PrivateKeyDer};
use serde::Deserialize;
use super::common::{Config, ConfigError, RustlsConfigLoader};
use crate::component::configuration::{Configuration, ConfigurationError};
#[derive(Debug, Deserialize, PartialEq, Clone)]
pub struct TlsServerConfig {
#[serde(flatten, default)]
pub config: Config,
#[serde(default = "default_insecure")]
pub insecure: bool,
pub client_ca_file: Option<String>,
pub client_ca_pem: Option<String>,
#[serde(default = "default_reload_client_ca_file")]
pub reload_client_ca_file: bool,
}
impl Default for TlsServerConfig {
fn default() -> Self {
TlsServerConfig {
config: Config::default(),
insecure: default_insecure(),
client_ca_file: None,
client_ca_pem: None,
reload_client_ca_file: default_reload_client_ca_file(),
}
}
}
fn default_insecure() -> bool {
false
}
fn default_reload_client_ca_file() -> bool {
false
}
impl std::fmt::Display for TlsServerConfig {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?}", self)
}
}
impl TlsServerConfig {
pub fn new() -> Self {
TlsServerConfig {
..Default::default()
}
}
pub fn with_insecure(self, insecure: bool) -> Self {
TlsServerConfig { insecure, ..self }
}
pub fn with_client_ca_file(self, client_ca_file: &str) -> Self {
TlsServerConfig {
client_ca_file: Some(client_ca_file.to_string()),
..self
}
}
pub fn with_client_ca_pem(self, client_ca_pem: &str) -> Self {
TlsServerConfig {
client_ca_pem: Some(client_ca_pem.to_string()),
..self
}
}
pub fn with_reload_client_ca_file(self, reload_client_ca_file: bool) -> Self {
TlsServerConfig {
reload_client_ca_file,
..self
}
}
pub fn with_ca_file(self, ca_file: &str) -> Self {
TlsServerConfig {
config: self.config.with_ca_file(ca_file),
..self
}
}
pub fn with_ca_pem(self, ca_pem: &str) -> Self {
TlsServerConfig {
config: self.config.with_ca_pem(ca_pem),
..self
}
}
pub fn with_include_system_ca_certs_pool(self, include_system_ca_certs_pool: bool) -> Self {
TlsServerConfig {
config: self
.config
.with_include_system_ca_certs_pool(include_system_ca_certs_pool),
..self
}
}
pub fn with_cert_file(self, cert_file: &str) -> Self {
TlsServerConfig {
config: self.config.with_cert_file(cert_file),
..self
}
}
pub fn with_cert_pem(self, cert_pem: &str) -> Self {
TlsServerConfig {
config: self.config.with_cert_pem(cert_pem),
..self
}
}
pub fn with_key_file(self, key_file: &str) -> Self {
TlsServerConfig {
config: self.config.with_key_file(key_file),
..self
}
}
pub fn with_key_pem(self, key_pem: &str) -> Self {
TlsServerConfig {
config: self.config.with_key_pem(key_pem),
..self
}
}
pub fn with_tls_version(self, tls_version: &str) -> Self {
TlsServerConfig {
config: self.config.with_tls_version(tls_version),
..self
}
}
pub fn with_reload_interval(self, reload_interval: Option<std::time::Duration>) -> Self {
TlsServerConfig {
config: self.config.with_reload_interval(reload_interval),
..self
}
}
pub fn load_rustls_server_config(&self) -> Result<Option<RustlsServerConfig>, ConfigError> {
if self.insecure {
return Ok(None);
}
let tls_version = match self.config.tls_version.as_str() {
"tls1.2" => &TLS12,
"tls1.3" => &TLS13,
_ => {
return Err(ConfigError::InvalidTlsVersion(
self.config.tls_version.clone(),
));
}
};
let (cert, key) = match (
self.config.has_cert_file() && self.config.has_key_file(),
self.config.has_cert_pem() && self.config.has_key_pem(),
) {
(true, true) => {
return Err(ConfigError::CannotUseBoth("cert".to_string()));
}
(false, false) => {
return Err(ConfigError::MissingServerCertAndKey);
}
(true, false) => (
CertificateDer::from_pem_file(Path::new(self.config.cert_file.as_ref().unwrap()))
.map_err(ConfigError::InvalidPem)?,
PrivateKeyDer::from_pem_file(Path::new(self.config.key_file.as_ref().unwrap()))
.map_err(ConfigError::InvalidPem)?,
),
(false, true) => (
CertificateDer::from_pem_slice(self.config.cert_pem.as_ref().unwrap().as_bytes())
.map_err(ConfigError::InvalidPem)?,
PrivateKeyDer::from_pem_slice(self.config.key_pem.as_ref().unwrap().as_bytes())
.map_err(ConfigError::InvalidPem)?,
),
};
let config_builder = RustlsServerConfig::builder_with_protocol_versions(&[tls_version]);
let client_ca = match (&self.client_ca_file, &self.client_ca_pem) {
(Some(_), Some(_)) => return Err(ConfigError::CannotUseBoth("client_ca".to_string())),
(Some(_), None) => Option::Some(
CertificateDer::from_pem_file(Path::new(self.client_ca_file.as_ref().unwrap()))
.map_err(ConfigError::InvalidPem)?,
),
(None, Some(_)) => Option::Some(
CertificateDer::from_pem_slice(self.client_ca_pem.as_ref().unwrap().as_bytes())
.map_err(ConfigError::InvalidPem)?,
),
(None, None) => Option::None,
};
let server_config = match client_ca {
Some(client_ca) => {
let mut root_store = RootCertStore::empty();
root_store.add(client_ca).map_err(ConfigError::RootStore)?;
let verifier = WebPkiClientVerifier::builder(root_store.into())
.build()
.map_err(ConfigError::VerifierBuilder)?;
config_builder
.with_client_cert_verifier(verifier)
.with_single_cert(vec![cert], key)
.map_err(ConfigError::ConfigBuilder)?
}
None => config_builder
.with_no_client_auth()
.with_single_cert(vec![cert], key)
.map_err(ConfigError::ConfigBuilder)?,
};
Ok(Some(server_config))
}
}
impl RustlsConfigLoader<RustlsServerConfig> for TlsServerConfig {
fn load_rustls_config(&self) -> Result<Option<RustlsServerConfig>, ConfigError> {
let server_config = self.load_rustls_server_config()?;
Ok(server_config)
}
}
impl Configuration for TlsServerConfig {
fn validate(&self) -> Result<(), ConfigurationError> {
Ok(())
}
}