1mod ca;
19mod date;
20
21use std::collections::HashMap;
22use std::sync::{Arc, Mutex};
23
24use rustls::client::WebPkiServerVerifier;
25use rustls::client::danger::ServerCertVerifier as _;
26use rustls::crypto::CryptoProvider;
27use rustls::pki_types::pem::PemObject as _;
28use rustls::pki_types::{CertificateDer, PrivateKeyDer, PrivatePkcs8KeyDer, ServerName, UnixTime};
29use rustls::sign::CertifiedKey;
30use rustls::{ClientConfig, ClientConnection, RootCertStore, ServerConfig, ServerConnection};
31use zeroize::Zeroizing;
32
33pub(crate) use self::date::Day;
34
35pub const RULE_TLS: &str = "proxy-tls";
38
39const HTTP_1_1: &[u8] = b"http/1.1";
41
42#[derive(Debug, thiserror::Error)]
44#[non_exhaustive]
45pub enum TlsError {
46 #[error("no secret sets `tls: terminate`, so there is no host to make a CA for")]
48 NoHosts,
49 #[error("the system clock is before 1970 or past the year 9000")]
51 Clock,
52 #[error("the CA key is not the ECDSA P-256 key `openmoat proxy ca init` writes")]
54 Key,
55 #[error("the CA certificate file holds no PEM certificate")]
57 Cert,
58 #[error("{0} is not a name a certificate can carry")]
60 Host(String),
61 #[error("the OpenMoat proxy CA does not cover {host} today ({why})")]
64 Uncovered {
65 host: String,
67 why: String,
69 },
70 #[error("no trusted root certificates found on this system to verify upstream hosts")]
72 NoRoots,
73 #[error("making a certificate: {0}")]
75 Certificate(String),
76 #[error("TLS configuration: {0}")]
78 Config(String),
79}
80
81impl TlsError {
82 fn rcgen(e: &rcgen::Error) -> Self {
83 Self::Certificate(e.to_string())
84 }
85
86 fn config(e: &rustls::Error) -> Self {
87 Self::Config(e.to_string())
88 }
89}
90
91pub struct CaFiles {
94 pub cert_pem: String,
96 pub key_pem: Zeroizing<String>,
98}
99
100impl std::fmt::Debug for CaFiles {
101 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
102 f.debug_struct("CaFiles").finish_non_exhaustive()
103 }
104}
105
106pub fn new_ca(hosts: &[String]) -> Result<CaFiles, TlsError> {
109 let made = ca::generate(hosts, Day::today()?)?;
110 Ok(CaFiles {
111 cert_pem: made.cert,
112 key_pem: made.key,
113 })
114}
115
116pub const CA_VALID_DAYS: i64 = ca::CA_DAYS;
118
119#[derive(Debug, Clone)]
121pub enum Roots {
122 System,
124 Pem(String),
126}
127
128pub struct Tls {
131 provider: Arc<CryptoProvider>,
132 signer: ca::Signer,
133 ca: CertificateDer<'static>,
134 upstream: Arc<ClientConfig>,
135 leaves: Mutex<HashMap<String, (Day, Arc<ServerConfig>)>>,
137}
138
139impl std::fmt::Debug for Tls {
140 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
141 f.debug_struct("Tls").finish_non_exhaustive()
142 }
143}
144
145impl Tls {
146 pub fn new(
150 cert_pem: &str,
151 key_pem: &str,
152 roots: &Roots,
153 hosts: &[String],
154 ) -> Result<Self, TlsError> {
155 let provider = Arc::new(rustls::crypto::ring::default_provider());
156 let ca = CertificateDer::from_pem_slice(cert_pem.as_bytes()).map_err(|_| TlsError::Cert)?;
157 let tls = Self {
158 signer: ca::Signer::new(key_pem)?,
159 upstream: Arc::new(upstream_config(roots, &provider)?),
160 provider,
161 ca,
162 leaves: Mutex::new(HashMap::new()),
163 };
164 for host in hosts {
165 tls.check(host)?;
166 }
167 Ok(tls)
168 }
169
170 fn check(&self, host: &str) -> Result<(), TlsError> {
173 let uncovered = |why: String| TlsError::Uncovered {
174 host: host.to_owned(),
175 why,
176 };
177 let mut roots = RootCertStore::empty();
178 roots
179 .add(self.ca.clone())
180 .map_err(|e| uncovered(e.to_string()))?;
181 let verifier =
182 WebPkiServerVerifier::builder_with_provider(Arc::new(roots), self.provider.clone())
183 .build()
184 .map_err(|e| uncovered(e.to_string()))?;
185 let (leaf, _) = self.signer.leaf(host, Day::today()?)?;
186 let name = server_name(host)?;
187 verifier
188 .verify_server_cert(
189 &CertificateDer::from(leaf),
190 &[],
191 &name,
192 &[],
193 UnixTime::now(),
194 )
195 .map(drop)
196 .map_err(|e| uncovered(e.to_string()))
197 }
198
199 pub(crate) fn accept(&self, host: &str) -> Result<ServerConnection, TlsError> {
202 let config = self.server_config(host)?;
203 ServerConnection::new(config).map_err(|e| TlsError::config(&e))
204 }
205
206 pub(crate) fn connect(&self, host: &str) -> Result<ClientConnection, TlsError> {
209 ClientConnection::new(self.upstream.clone(), server_name(host)?)
210 .map_err(|e| TlsError::config(&e))
211 }
212
213 fn server_config(&self, host: &str) -> Result<Arc<ServerConfig>, TlsError> {
214 let today = Day::today()?;
215 let mut leaves = self
217 .leaves
218 .lock()
219 .unwrap_or_else(std::sync::PoisonError::into_inner);
220 if let Some((day, config)) = leaves.get(host)
221 && *day == today
222 {
223 return Ok(config.clone());
224 }
225 let (cert, key) = self.signer.leaf(host, today)?;
226 let key = PrivateKeyDer::Pkcs8(PrivatePkcs8KeyDer::from(key.to_vec()));
227 let certified =
228 CertifiedKey::from_der(vec![CertificateDer::from(cert)], key, &self.provider)
229 .map_err(|e| TlsError::config(&e))?;
230 let mut config = ServerConfig::builder_with_provider(self.provider.clone())
231 .with_safe_default_protocol_versions()
232 .map_err(|e| TlsError::config(&e))?
233 .with_no_client_auth()
234 .with_cert_resolver(Arc::new(rustls::sign::SingleCertAndKey::from(certified)));
235 config.alpn_protocols = vec![HTTP_1_1.to_vec()];
236 let config = Arc::new(config);
237 leaves.insert(host.to_owned(), (today, config.clone()));
238 Ok(config)
239 }
240}
241
242fn server_name(host: &str) -> Result<ServerName<'static>, TlsError> {
243 ServerName::try_from(host.to_owned()).map_err(|_| TlsError::Host(host.to_owned()))
244}
245
246fn upstream_config(
247 roots: &Roots,
248 provider: &Arc<CryptoProvider>,
249) -> Result<ClientConfig, TlsError> {
250 let mut store = RootCertStore::empty();
251 match roots {
252 Roots::System => {
253 let found = rustls_native_certs::load_native_certs();
256 store.add_parsable_certificates(found.certs);
257 }
258 Roots::Pem(pem) => {
259 let certs = CertificateDer::pem_slice_iter(pem.as_bytes())
260 .collect::<Result<Vec<_>, _>>()
261 .map_err(|_| TlsError::Cert)?;
262 store.add_parsable_certificates(certs);
263 }
264 }
265 if store.is_empty() {
266 return Err(TlsError::NoRoots);
267 }
268 let mut config = ClientConfig::builder_with_provider(provider.clone())
269 .with_safe_default_protocol_versions()
270 .map_err(|e| TlsError::config(&e))?
271 .with_root_certificates(store)
272 .with_no_client_auth();
273 config.alpn_protocols = vec![HTTP_1_1.to_vec()];
274 Ok(config)
275}
276
277#[cfg(test)]
278mod tests;