Skip to main content

rust_serv/auto_tls/
mod.rs

1//! Auto TLS (Let's Encrypt) module
2//!
3//! This module provides automatic TLS certificate management using Let's Encrypt.
4
5mod account;
6mod challenge;
7mod client;
8mod store;
9
10pub use account::AcmeAccount;
11pub use challenge::ChallengeHandler;
12pub use client::AcmeClient;
13pub use store::{CertificateStore, StoredCertificate};
14
15use serde::{Deserialize, Serialize};
16
17/// Auto TLS configuration
18#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
19pub struct AutoTlsConfig {
20    /// Enable auto TLS
21    #[serde(default)]
22    pub enabled: bool,
23
24    /// Domains for certificate
25    #[serde(default)]
26    pub domains: Vec<String>,
27
28    /// Email for Let's Encrypt registration
29    #[serde(default)]
30    pub email: String,
31
32    /// ACME challenge type: "http-01" or "dns-01" (default: "http-01")
33    #[serde(default = "default_challenge_type")]
34    pub challenge_type: String,
35
36    /// Certificate cache directory (default: "./certs")
37    #[serde(default = "default_cache_dir")]
38    pub cache_dir: String,
39
40    /// Days before expiration to renew (default: 30)
41    #[serde(default = "default_renew_days")]
42    pub renew_before_days: u32,
43}
44
45fn default_challenge_type() -> String { "http-01".to_string() }
46fn default_cache_dir() -> String { "./certs".to_string() }
47fn default_renew_days() -> u32 { 30 }
48
49impl Default for AutoTlsConfig {
50    fn default() -> Self {
51        Self {
52            enabled: false,
53            domains: vec![],
54            email: String::new(),
55            challenge_type: default_challenge_type(),
56            cache_dir: default_cache_dir(),
57            renew_before_days: default_renew_days(),
58        }
59    }
60}
61
62/// ACME directory URL for Let's Encrypt production
63pub const LETSENCRYPT_PRODUCTION_URL: &str = "https://acme-v02.api.letsencrypt.org/directory";
64
65/// ACME directory URL for Let's Encrypt staging
66pub const LETSENCRYPT_STAGING_URL: &str = "https://acme-staging-v02.api.letsencrypt.org/directory";
67
68/// Error type for auto TLS operations
69#[derive(Debug, thiserror::Error)]
70pub enum AutoTlsError {
71    /// ACME protocol error
72    #[error("ACME error: {0}")]
73    AcmeError(String),
74
75    /// Challenge error
76    #[error("Challenge error: {0}")]
77    ChallengeError(String),
78
79    /// Certificate error
80    #[error("Certificate error: {0}")]
81    CertificateError(String),
82
83    /// IO error
84    #[error("IO error: {0}")]
85    IoError(#[from] std::io::Error),
86
87    /// Configuration error
88    #[error("Configuration error: {0}")]
89    ConfigError(String),
90
91    /// Store error
92    #[error("Store error: {0}")]
93    StoreError(String),
94
95    /// Serialization error
96    #[error("Serialization error: {0}")]
97    SerializationError(#[from] serde_json::Error),
98}
99
100/// Result type for auto TLS operations
101pub type AutoTlsResult<T> = Result<T, AutoTlsError>;
102
103#[cfg(test)]
104mod tests {
105    use super::*;
106
107    #[test]
108    fn test_auto_tls_config_default() {
109        let config = AutoTlsConfig::default();
110        assert!(!config.enabled);
111        assert!(config.domains.is_empty());
112        assert_eq!(config.challenge_type, "http-01");
113        assert_eq!(config.cache_dir, "./certs");
114        assert_eq!(config.renew_before_days, 30);
115    }
116
117    #[test]
118    fn test_auto_tls_config_custom() {
119        let config = AutoTlsConfig {
120            enabled: true,
121            domains: vec!["example.com".to_string()],
122            email: "admin@example.com".to_string(),
123            challenge_type: "dns-01".to_string(),
124            cache_dir: "/etc/certs".to_string(),
125            renew_before_days: 14,
126        };
127        assert!(config.enabled);
128        assert_eq!(config.domains.len(), 1);
129        assert_eq!(config.challenge_type, "dns-01");
130        assert_eq!(config.renew_before_days, 14);
131    }
132
133    #[test]
134    fn test_letsencrypt_urls() {
135        assert!(LETSENCRYPT_PRODUCTION_URL.contains("letsencrypt.org"));
136        assert!(LETSENCRYPT_STAGING_URL.contains("letsencrypt.org"));
137        assert!(LETSENCRYPT_STAGING_URL.contains("staging"));
138    }
139
140    #[test]
141    fn test_auto_tls_error_display() {
142        let error = AutoTlsError::AcmeError("test".to_string());
143        assert!(error.to_string().contains("ACME error"));
144
145        let error = AutoTlsError::ChallengeError("fail".to_string());
146        assert!(error.to_string().contains("Challenge error"));
147    }
148}