d_engine/config/
tls.rs

1#[cfg(not(test))]
2use std::fs;
3use std::path::Path;
4
5use serde::Deserialize;
6use serde::Serialize;
7
8use crate::Error;
9use crate::Result;
10
11#[derive(Debug, Serialize, Deserialize, Clone, Default)]
12#[allow(unused)]
13pub struct TlsConfig {
14    /// Enables TLS encryption for network communication
15    /// Default: false (disabled)
16    #[serde(default = "default_enable_tls")]
17    pub enable_tls: bool,
18
19    /// Automatically generates self-signed certificates on startup
20    /// Default: false (requires pre-configured certificates)
21    #[serde(default = "default_generate_self_signed")]
22    pub generate_self_signed_certificates: bool,
23
24    /// Path to Certificate Authority root certificate
25    /// Default: "/etc/ssl/certs/ca.pem"
26    #[serde(default = "default_ca_path")]
27    pub certificate_authority_root_path: String,
28
29    /// Server certificate chain path in PEM format
30    /// Default: "./certs/server.pem"
31    #[serde(default = "default_server_cert_path")]
32    pub server_certificate_path: String,
33
34    /// Server private key path in PEM format
35    /// Default: "./certs/server.key"
36    #[serde(default = "default_server_key_path")]
37    pub server_private_key_path: String,
38
39    /// Client CA certificate path for mTLS authentication
40    /// Default: "/etc/ssl/certs/ca.pem"
41    #[serde(default = "default_client_ca_path")]
42    pub client_certificate_authority_root_path: String,
43
44    /// Enables mutual TLS (mTLS) for bidirectional authentication
45    /// Default: false (server-side TLS only)
46    #[serde(default = "default_enable_mtls")]
47    pub enable_mtls: bool,
48}
49impl TlsConfig {
50    /// Validates TLS configuration consistency and file existence
51    /// # Errors
52    /// Returns `Error::InvalidConfig` when:
53    /// - mTLS is enabled without base TLS
54    /// - Required certificate files are missing
55    /// - Self-signed generation conflicts with existing paths
56    /// - Invalid certificate file permissions
57    pub fn validate(&self) -> Result<()> {
58        if !self.enable_tls {
59            // Skip validation if TLS is disabled
60            return Ok(());
61        }
62
63        // Validate mTLS dependencies
64        if self.enable_mtls && !self.enable_tls {
65            return Err(Error::InvalidConfig("mTLS requires enable_tls to be true".into()));
66        }
67
68        // Handle self-signed certificate generation case
69        if self.generate_self_signed_certificates {
70            if !self.server_certificate_path.is_empty() || !self.server_private_key_path.is_empty() {
71                return Err(Error::InvalidConfig(
72                    "Cannot specify certificate paths with generate_self_signed_certificates=true".into(),
73                ));
74            }
75            return Ok(());
76        }
77
78        // Validate server certificates
79        self.validate_cert_file(&self.server_certificate_path, "server certificate")?;
80        self.validate_key_file(&self.server_private_key_path, "server private key")?;
81        self.validate_cert_file(&self.certificate_authority_root_path, "CA certificate")?;
82
83        // Validate client certificates for mTLS
84        if self.enable_mtls {
85            self.validate_cert_file(&self.client_certificate_authority_root_path, "client CA certificate")?;
86        }
87
88        Ok(())
89    }
90
91    /// Validates a certificate file existence and readability
92    fn validate_cert_file(
93        &self,
94        path: &str,
95        name: &str,
96    ) -> Result<()> {
97        let path = Path::new(path);
98
99        if path.exists() {
100            #[cfg(not(test))]
101            {
102                // Check file readability
103                fs::File::open(path).map_err(|e| {
104                    Error::InvalidConfig(format!("{} file {} is unreadable: {}", name, path.display(), e))
105                })?;
106            }
107            Ok(())
108        } else {
109            Err(Error::InvalidConfig(format!(
110                "{} file {} not found",
111                name,
112                path.display()
113            )))
114        }
115    }
116
117    /// Validates a private key file existence and permissions
118    fn validate_key_file(
119        &self,
120        path: &str,
121        name: &str,
122    ) -> Result<()> {
123        let path = Path::new(path);
124
125        if path.exists() {
126            #[cfg(not(test))]
127            {
128                // Check key file permissions (should be 600)
129                let metadata = fs::metadata(path).map_err(|e| {
130                    Error::InvalidConfig(format!("Cannot access {} permissions: {}", path.display(), e))
131                })?;
132
133                #[cfg(unix)]
134                {
135                    use std::os::unix::fs::PermissionsExt;
136                    let mode = metadata.permissions().mode();
137                    if mode & 0o777 != 0o600 {
138                        return Err(Error::InvalidConfig(format!(
139                            "Insecure permissions {:o} for {} (should be 600)",
140                            mode & 0o777,
141                            path.display()
142                        )));
143                    }
144                }
145            }
146            Ok(())
147        } else {
148            Err(Error::InvalidConfig(format!(
149                "{} file {} not found",
150                name,
151                path.display()
152            )))
153        }
154    }
155}
156// Default implementations
157fn default_enable_tls() -> bool {
158    false
159}
160fn default_generate_self_signed() -> bool {
161    false
162}
163fn default_ca_path() -> String {
164    "/etc/ssl/certs/ca.pem".into()
165}
166fn default_server_cert_path() -> String {
167    "./certs/server.pem".into()
168}
169fn default_server_key_path() -> String {
170    "./certs/server.key".into()
171}
172fn default_client_ca_path() -> String {
173    "/etc/ssl/certs/ca.pem".into()
174}
175fn default_enable_mtls() -> bool {
176    false
177}