d_engine_core/config/
tls.rs

1use std::fs;
2use std::path::Path;
3
4use config::ConfigError;
5use serde::Deserialize;
6use serde::Serialize;
7
8use crate::Error;
9use crate::Result;
10
11#[derive(Debug, Serialize, Deserialize, Clone)]
12#[allow(dead_code)]
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 Default for TlsConfig {
50    fn default() -> Self {
51        Self {
52            enable_tls: default_enable_tls(),
53            generate_self_signed_certificates: default_generate_self_signed(),
54            certificate_authority_root_path: default_ca_path(),
55            server_certificate_path: default_server_cert_path(),
56            server_private_key_path: default_server_key_path(),
57            client_certificate_authority_root_path: default_client_ca_path(),
58            enable_mtls: default_enable_mtls(),
59        }
60    }
61}
62
63impl TlsConfig {
64    /// Validates TLS configuration consistency and file existence
65    /// # Errors
66    /// Returns `Error::InvalidConfig` when:
67    /// - mTLS is enabled without base TLS
68    /// - Required certificate files are missing
69    /// - Self-signed generation conflicts with existing paths
70    /// - Invalid certificate file permissions
71    pub fn validate(&self) -> Result<()> {
72        // Check mTLS dependency
73        if self.enable_mtls && !self.enable_tls {
74            return Err(Error::Config(ConfigError::Message(
75                "mTLS requires enable_tls to be true".into(),
76            )));
77        }
78
79        // No further validation needed if TLS is disabled
80        if !self.enable_tls {
81            return Ok(());
82        }
83
84        // Self-signed certificate logic
85        if self.generate_self_signed_certificates {
86            if !self.server_certificate_path.is_empty() || !self.server_private_key_path.is_empty()
87            {
88                return Err(Error::Config(ConfigError::Message(
89                    "Cannot specify certificate paths with generate_self_signed_certificates=true"
90                        .into(),
91                )));
92            }
93
94            if self.enable_mtls {
95                self.validate_cert_file(
96                    &self.client_certificate_authority_root_path,
97                    "client CA certificate",
98                )?;
99            }
100
101            return Ok(());
102        }
103
104        // Validate certificate files
105        self.validate_cert_file(&self.server_certificate_path, "server certificate")?;
106        self.validate_key_file(&self.server_private_key_path, "server private key")?;
107        self.validate_cert_file(&self.certificate_authority_root_path, "CA certificate")?;
108
109        // mTLS requires additional client CA verification
110        if self.enable_mtls {
111            self.validate_cert_file(
112                &self.client_certificate_authority_root_path,
113                "client CA certificate",
114            )?;
115        }
116
117        Ok(())
118    }
119
120    /// Validates a certificate file existence and readability
121    fn validate_cert_file(
122        &self,
123        path: &str,
124        name: &str,
125    ) -> Result<()> {
126        let path = Path::new(path);
127
128        if path.exists() {
129            {
130                // Check file readability
131                fs::File::open(path).map_err(|e| {
132                    Error::Config(ConfigError::Message(format!(
133                        "{} file {} is unreadable: {}",
134                        name,
135                        path.display(),
136                        e
137                    )))
138                })?;
139            }
140            Ok(())
141        } else {
142            Err(Error::Config(ConfigError::Message(format!(
143                "{} file {} not found",
144                name,
145                path.display()
146            ))))
147        }
148    }
149
150    /// Validates a private key file existence and permissions
151    fn validate_key_file(
152        &self,
153        path: &str,
154        name: &str,
155    ) -> Result<()> {
156        let path = Path::new(path);
157
158        if path.exists() {
159            {
160                // Check key file permissions (should be 600)
161                let metadata = fs::metadata(path).map_err(|e| {
162                    Error::Config(ConfigError::Message(format!(
163                        "Cannot access {} permissions: {}",
164                        path.display(),
165                        e
166                    )))
167                })?;
168
169                #[cfg(unix)]
170                {
171                    use std::os::unix::fs::PermissionsExt;
172                    let mode = metadata.permissions().mode();
173                    if mode & 0o777 != 0o600 {
174                        return Err(Error::Config(ConfigError::Message(format!(
175                            "Insecure permissions {:o} for {} (should be 600)",
176                            mode & 0o777,
177                            path.display()
178                        ))));
179                    }
180                }
181            }
182            Ok(())
183        } else {
184            Err(Error::Config(ConfigError::Message(format!(
185                "{} file {} not found",
186                name,
187                path.display()
188            ))))
189        }
190    }
191}
192// Default implementations
193fn default_enable_tls() -> bool {
194    false
195}
196fn default_generate_self_signed() -> bool {
197    false
198}
199fn default_ca_path() -> String {
200    "/etc/ssl/certs/ca.pem".into()
201}
202fn default_server_cert_path() -> String {
203    "./certs/server.pem".into()
204}
205fn default_server_key_path() -> String {
206    "./certs/server.key".into()
207}
208fn default_client_ca_path() -> String {
209    "/etc/ssl/certs/ca.pem".into()
210}
211fn default_enable_mtls() -> bool {
212    false
213}