rocket/config/tls.rs
1use figment::value::magic::{Either, RelativePathBuf};
2use serde::{Deserialize, Serialize};
3use indexmap::IndexSet;
4
5/// TLS configuration: certificate chain, key, and ciphersuites.
6///
7/// Four parameters control `tls` configuration:
8///
9/// * `certs`, `key`
10///
11/// Both `certs` and `key` can be configured as a path or as raw bytes.
12/// `certs` must be a DER-encoded X.509 TLS certificate chain, while `key`
13/// must be a DER-encoded ASN.1 key in either PKCS#8, PKCS#1, or SEC1
14/// format. When a path is configured in a file, such as `Rocket.toml`,
15/// relative paths are interpreted as relative to the source file's
16/// directory.
17///
18/// * `ciphers`
19///
20/// A list of supported [`CipherSuite`]s in server-preferred order, from
21/// most to least. It is not required and defaults to
22/// [`CipherSuite::DEFAULT_SET`], the recommended setting.
23///
24/// * `prefer_server_cipher_order`
25///
26/// A boolean that indicates whether the server should regard its own
27/// ciphersuite preferences over the client's. The default and recommended
28/// value is `false`.
29///
30/// Additionally, the `mutual` parameter controls if and how the server
31/// authenticates clients via mutual TLS. It works in concert with the
32/// [`mtls`](crate::mtls) module. See [`MutualTls`] for configuration details.
33///
34/// In `Rocket.toml`, configuration might look like:
35///
36/// ```toml
37/// [default.tls]
38/// certs = "private/rsa_sha256_cert.pem"
39/// key = "private/rsa_sha256_key.pem"
40/// ```
41///
42/// With a custom programmatic configuration, this might look like:
43///
44/// ```rust
45/// # #[macro_use] extern crate rocket;
46/// use rocket::config::{Config, TlsConfig, CipherSuite};
47///
48/// #[launch]
49/// fn rocket() -> _ {
50/// let tls_config = TlsConfig::from_paths("/ssl/certs.pem", "/ssl/key.pem")
51/// .with_ciphers(CipherSuite::TLS_V13_SET)
52/// .with_preferred_server_cipher_order(true);
53///
54/// let config = Config {
55/// tls: Some(tls_config),
56/// ..Default::default()
57/// };
58///
59/// rocket::custom(config)
60/// }
61/// ```
62///
63/// Or by creating a custom figment:
64///
65/// ```rust
66/// use rocket::config::Config;
67///
68/// let figment = Config::figment()
69/// .merge(("tls.certs", "path/to/certs.pem"))
70/// .merge(("tls.key", vec![0; 32]));
71/// #
72/// # let config = rocket::Config::from(figment);
73/// # let tls_config = config.tls.as_ref().unwrap();
74/// # assert!(tls_config.certs().is_left());
75/// # assert!(tls_config.key().is_right());
76/// # assert_eq!(tls_config.ciphers().count(), 9);
77/// # assert!(!tls_config.prefer_server_cipher_order());
78/// ```
79#[derive(PartialEq, Debug, Clone, Deserialize, Serialize)]
80#[cfg_attr(nightly, doc(cfg(feature = "tls")))]
81pub struct TlsConfig {
82 /// Path to a PEM file with, or raw bytes for, a DER-encoded X.509 TLS
83 /// certificate chain.
84 pub(crate) certs: Either<RelativePathBuf, Vec<u8>>,
85 /// Path to a PEM file with, or raw bytes for, DER-encoded private key in
86 /// either PKCS#8 or PKCS#1 format.
87 pub(crate) key: Either<RelativePathBuf, Vec<u8>>,
88 /// List of TLS cipher suites in server-preferred order.
89 #[serde(default = "CipherSuite::default_set")]
90 pub(crate) ciphers: IndexSet<CipherSuite>,
91 /// Whether to prefer the server's cipher suite order over the client's.
92 #[serde(default)]
93 pub(crate) prefer_server_cipher_order: bool,
94 /// Configuration for mutual TLS, if any.
95 #[serde(default)]
96 #[cfg(feature = "mtls")]
97 #[cfg_attr(nightly, doc(cfg(feature = "mtls")))]
98 pub(crate) mutual: Option<MutualTls>,
99}
100
101/// Mutual TLS configuration.
102///
103/// Configuration works in concert with the [`mtls`](crate::mtls) module, which
104/// provides a request guard to validate, verify, and retrieve client
105/// certificates in routes.
106///
107/// By default, mutual TLS is disabled and client certificates are not required,
108/// validated or verified. To enable mutual TLS, the `mtls` feature must be
109/// enabled and support configured via two `tls.mutual` parameters:
110///
111/// * `ca_certs`
112///
113/// A required path to a PEM file or raw bytes to a DER-encoded X.509 TLS
114/// certificate chain for the certificate authority to verify client
115/// certificates against. When a path is configured in a file, such as
116/// `Rocket.toml`, relative paths are interpreted as relative to the source
117/// file's directory.
118///
119/// * `mandatory`
120///
121/// An optional boolean that control whether client authentication is
122/// required.
123///
124/// When `true`, client authentication is required. TLS connections where
125/// the client does not present a certificate are immediately terminated.
126/// When `false`, the client is not required to present a certificate. In
127/// either case, if a certificate _is_ presented, it must be valid or the
128/// connection is terminated.
129///
130/// In a `Rocket.toml`, configuration might look like:
131///
132/// ```toml
133/// [default.tls.mutual]
134/// ca_certs = "/ssl/ca_cert.pem"
135/// mandatory = true # when absent, defaults to false
136/// ```
137///
138/// Programmatically, configuration might look like:
139///
140/// ```rust
141/// # #[macro_use] extern crate rocket;
142/// use rocket::config::{Config, TlsConfig, MutualTls};
143///
144/// #[launch]
145/// fn rocket() -> _ {
146/// let tls_config = TlsConfig::from_paths("/ssl/certs.pem", "/ssl/key.pem")
147/// .with_mutual(MutualTls::from_path("/ssl/ca_cert.pem"));
148///
149/// let config = Config {
150/// tls: Some(tls_config),
151/// ..Default::default()
152/// };
153///
154/// rocket::custom(config)
155/// }
156/// ```
157///
158/// Once mTLS is configured, the [`mtls::Certificate`](crate::mtls::Certificate)
159/// request guard can be used to retrieve client certificates in routes.
160#[derive(PartialEq, Debug, Clone, Deserialize, Serialize)]
161#[cfg(feature = "mtls")]
162#[cfg_attr(nightly, doc(cfg(feature = "mtls")))]
163pub struct MutualTls {
164 /// Path to a PEM file with, or raw bytes for, DER-encoded Certificate
165 /// Authority certificates which will be used to verify client-presented
166 /// certificates.
167 // TODO: We should support more than one root.
168 pub(crate) ca_certs: Either<RelativePathBuf, Vec<u8>>,
169 /// Whether the client is required to present a certificate.
170 ///
171 /// When `true`, the client is required to present a valid certificate to
172 /// proceed with TLS. When `false`, the client is not required to present a
173 /// certificate. In either case, if a certificate _is_ presented, it must be
174 /// valid or the connection is terminated.
175 #[serde(default)]
176 #[serde(deserialize_with = "figment::util::bool_from_str_or_int")]
177 pub mandatory: bool,
178}
179
180/// A supported TLS cipher suite.
181#[allow(non_camel_case_types)]
182#[derive(PartialEq, Eq, Debug, Copy, Clone, Hash, Deserialize, Serialize)]
183#[cfg_attr(nightly, doc(cfg(feature = "tls")))]
184#[non_exhaustive]
185pub enum CipherSuite {
186 /// The TLS 1.3 `TLS_CHACHA20_POLY1305_SHA256` cipher suite.
187 TLS_CHACHA20_POLY1305_SHA256,
188 /// The TLS 1.3 `TLS_AES_256_GCM_SHA384` cipher suite.
189 TLS_AES_256_GCM_SHA384,
190 /// The TLS 1.3 `TLS_AES_128_GCM_SHA256` cipher suite.
191 TLS_AES_128_GCM_SHA256,
192
193 /// The TLS 1.2 `TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256` cipher suite.
194 TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
195 /// The TLS 1.2 `TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256` cipher suite.
196 TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
197 /// The TLS 1.2 `TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384` cipher suite.
198 TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
199 /// The TLS 1.2 `TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256` cipher suite.
200 TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
201 /// The TLS 1.2 `TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384` cipher suite.
202 TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
203 /// The TLS 1.2 `TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256` cipher suite.
204 TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
205}
206
207impl CipherSuite {
208 /// The default set and order of cipher suites. These are all of the
209 /// variants in [`CipherSuite`] in their declaration order.
210 pub const DEFAULT_SET: [CipherSuite; 9] = [
211 // TLS v1.3 suites...
212 CipherSuite::TLS_CHACHA20_POLY1305_SHA256,
213 CipherSuite::TLS_AES_256_GCM_SHA384,
214 CipherSuite::TLS_AES_128_GCM_SHA256,
215
216 // TLS v1.2 suites...
217 CipherSuite::TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
218 CipherSuite::TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
219 CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
220 CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
221 CipherSuite::TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
222 CipherSuite::TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
223 ];
224
225 /// The default set and order of cipher suites. These are the TLS 1.3
226 /// variants in [`CipherSuite`] in their declaration order.
227 pub const TLS_V13_SET: [CipherSuite; 3] = [
228 CipherSuite::TLS_CHACHA20_POLY1305_SHA256,
229 CipherSuite::TLS_AES_256_GCM_SHA384,
230 CipherSuite::TLS_AES_128_GCM_SHA256,
231 ];
232
233 /// The default set and order of cipher suites. These are the TLS 1.2
234 /// variants in [`CipherSuite`] in their declaration order.
235 pub const TLS_V12_SET: [CipherSuite; 6] = [
236 CipherSuite::TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
237 CipherSuite::TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
238 CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
239 CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
240 CipherSuite::TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
241 CipherSuite::TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
242 ];
243
244 /// Used as the `serde` default for `ciphers`.
245 fn default_set() -> IndexSet<Self> {
246 Self::DEFAULT_SET.iter().copied().collect()
247 }
248}
249
250impl TlsConfig {
251 fn default() -> Self {
252 TlsConfig {
253 certs: Either::Right(vec![]),
254 key: Either::Right(vec![]),
255 ciphers: CipherSuite::default_set(),
256 prefer_server_cipher_order: false,
257 #[cfg(feature = "mtls")]
258 mutual: None,
259 }
260 }
261
262 /// Constructs a `TlsConfig` from paths to a `certs` certificate chain
263 /// a `key` private-key. This method does no validation; it simply creates a
264 /// structure suitable for passing into a [`Config`](crate::Config).
265 ///
266 /// # Example
267 ///
268 /// ```rust
269 /// use rocket::config::TlsConfig;
270 ///
271 /// let tls_config = TlsConfig::from_paths("/ssl/certs.pem", "/ssl/key.pem");
272 /// ```
273 pub fn from_paths<C, K>(certs: C, key: K) -> Self
274 where C: AsRef<std::path::Path>, K: AsRef<std::path::Path>
275 {
276 TlsConfig {
277 certs: Either::Left(certs.as_ref().to_path_buf().into()),
278 key: Either::Left(key.as_ref().to_path_buf().into()),
279 ..TlsConfig::default()
280 }
281 }
282
283 /// Constructs a `TlsConfig` from byte buffers to a `certs`
284 /// certificate chain a `key` private-key. This method does no validation;
285 /// it simply creates a structure suitable for passing into a
286 /// [`Config`](crate::Config).
287 ///
288 /// # Example
289 ///
290 /// ```rust
291 /// use rocket::config::TlsConfig;
292 ///
293 /// # let certs_buf = &[];
294 /// # let key_buf = &[];
295 /// let tls_config = TlsConfig::from_bytes(certs_buf, key_buf);
296 /// ```
297 pub fn from_bytes(certs: &[u8], key: &[u8]) -> Self {
298 TlsConfig {
299 certs: Either::Right(certs.to_vec()),
300 key: Either::Right(key.to_vec()),
301 ..TlsConfig::default()
302 }
303 }
304
305 /// Sets the cipher suites supported by the server and their order of
306 /// preference from most to least preferred.
307 ///
308 /// If a suite appears more than once in `ciphers`, only the first suite
309 /// (and its relative order) is considered. If all cipher suites for a
310 /// version oF TLS are disabled, the respective protocol itself is disabled
311 /// entirely.
312 ///
313 /// # Example
314 ///
315 /// Disable TLS v1.2 by selecting only TLS v1.3 cipher suites:
316 ///
317 /// ```rust
318 /// use rocket::config::{TlsConfig, CipherSuite};
319 ///
320 /// # let certs_buf = &[];
321 /// # let key_buf = &[];
322 /// let tls_config = TlsConfig::from_bytes(certs_buf, key_buf)
323 /// .with_ciphers(CipherSuite::TLS_V13_SET);
324 /// ```
325 ///
326 /// Enable only ChaCha20-Poly1305 based TLS v1.2 and TLS v1.3 cipher suites:
327 ///
328 /// ```rust
329 /// use rocket::config::{TlsConfig, CipherSuite};
330 ///
331 /// # let certs_buf = &[];
332 /// # let key_buf = &[];
333 /// let tls_config = TlsConfig::from_bytes(certs_buf, key_buf)
334 /// .with_ciphers([
335 /// CipherSuite::TLS_CHACHA20_POLY1305_SHA256,
336 /// CipherSuite::TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
337 /// CipherSuite::TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
338 /// ]);
339 /// ```
340 ///
341 /// Later duplicates are ignored.
342 ///
343 /// ```rust
344 /// use rocket::config::{TlsConfig, CipherSuite};
345 ///
346 /// # let certs_buf = &[];
347 /// # let key_buf = &[];
348 /// let tls_config = TlsConfig::from_bytes(certs_buf, key_buf)
349 /// .with_ciphers([
350 /// CipherSuite::TLS_CHACHA20_POLY1305_SHA256,
351 /// CipherSuite::TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
352 /// CipherSuite::TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
353 /// CipherSuite::TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
354 /// CipherSuite::TLS_CHACHA20_POLY1305_SHA256,
355 /// ]);
356 ///
357 /// let ciphers: Vec<_> = tls_config.ciphers().collect();
358 /// assert_eq!(ciphers, &[
359 /// CipherSuite::TLS_CHACHA20_POLY1305_SHA256,
360 /// CipherSuite::TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
361 /// CipherSuite::TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
362 /// ]);
363 /// ```
364 pub fn with_ciphers<I>(mut self, ciphers: I) -> Self
365 where I: IntoIterator<Item = CipherSuite>
366 {
367 self.ciphers = ciphers.into_iter().collect();
368 self
369 }
370
371 /// Whether to prefer the server's cipher suite order and ignore the
372 /// client's preferences (`true`) or choose the first supported ciphersuite
373 /// in the client's preference list (`false`). The default prefer's the
374 /// client's order (`false`).
375 ///
376 /// During TLS cipher suite negotiation, the client presents a set of
377 /// supported ciphers in its preferred order. From this list, the server
378 /// chooses one cipher suite. By default, the server chooses the first
379 /// cipher it supports from the list.
380 ///
381 /// By setting `prefer_server_order` to `true`, the server instead chooses
382 /// the first ciphersuite in it prefers that the client also supports,
383 /// ignoring the client's preferences.
384 ///
385 /// # Example
386 ///
387 /// ```rust
388 /// use rocket::config::{TlsConfig, CipherSuite};
389 ///
390 /// # let certs_buf = &[];
391 /// # let key_buf = &[];
392 /// let tls_config = TlsConfig::from_bytes(certs_buf, key_buf)
393 /// .with_ciphers(CipherSuite::TLS_V13_SET)
394 /// .with_preferred_server_cipher_order(true);
395 /// ```
396 pub fn with_preferred_server_cipher_order(mut self, prefer_server_order: bool) -> Self {
397 self.prefer_server_cipher_order = prefer_server_order;
398 self
399 }
400
401 /// Configures mutual TLS. See [`MutualTls`] for details.
402 ///
403 /// # Example
404 ///
405 /// ```rust
406 /// use rocket::config::{TlsConfig, MutualTls};
407 ///
408 /// # let certs = &[];
409 /// # let key = &[];
410 /// let mtls_config = MutualTls::from_path("path/to/cert.pem").mandatory(true);
411 /// let tls_config = TlsConfig::from_bytes(certs, key).with_mutual(mtls_config);
412 /// assert!(tls_config.mutual().is_some());
413 /// ```
414 #[cfg(feature = "mtls")]
415 #[cfg_attr(nightly, doc(cfg(feature = "mtls")))]
416 pub fn with_mutual(mut self, config: MutualTls) -> Self {
417 self.mutual = Some(config);
418 self
419 }
420
421 /// Returns the value of the `certs` parameter.
422 ///
423 /// # Example
424 ///
425 /// ```rust
426 /// use rocket::Config;
427 ///
428 /// let figment = Config::figment()
429 /// .merge(("tls.certs", vec![0; 32]))
430 /// .merge(("tls.key", "/etc/ssl/key.pem"));
431 ///
432 /// let config = rocket::Config::from(figment);
433 /// let tls_config = config.tls.as_ref().unwrap();
434 /// let cert_bytes = tls_config.certs().right().unwrap();
435 /// assert!(cert_bytes.iter().all(|&b| b == 0));
436 /// ```
437 pub fn certs(&self) -> either::Either<std::path::PathBuf, &[u8]> {
438 match &self.certs {
439 Either::Left(path) => either::Either::Left(path.relative()),
440 Either::Right(bytes) => either::Either::Right(&bytes),
441 }
442 }
443
444 /// Returns the value of the `key` parameter.
445 ///
446 /// # Example
447 ///
448 /// ```rust
449 /// use std::path::Path;
450 /// use rocket::Config;
451 ///
452 /// let figment = Config::figment()
453 /// .merge(("tls.certs", vec![0; 32]))
454 /// .merge(("tls.key", "/etc/ssl/key.pem"));
455 ///
456 /// let config = rocket::Config::from(figment);
457 /// let tls_config = config.tls.as_ref().unwrap();
458 /// let key_path = tls_config.key().left().unwrap();
459 /// assert_eq!(key_path, Path::new("/etc/ssl/key.pem"));
460 /// ```
461 pub fn key(&self) -> either::Either<std::path::PathBuf, &[u8]> {
462 match &self.key {
463 Either::Left(path) => either::Either::Left(path.relative()),
464 Either::Right(bytes) => either::Either::Right(&bytes),
465 }
466 }
467
468 /// Returns an iterator over the enabled cipher suites in their order of
469 /// preference from most to least preferred.
470 ///
471 /// # Example
472 ///
473 /// ```rust
474 /// use rocket::config::{TlsConfig, CipherSuite};
475 ///
476 /// # let certs_buf = &[];
477 /// # let key_buf = &[];
478 /// // The default set is CipherSuite::DEFAULT_SET.
479 /// let tls_config = TlsConfig::from_bytes(certs_buf, key_buf);
480 /// assert_eq!(tls_config.ciphers().count(), 9);
481 /// assert!(tls_config.ciphers().eq(CipherSuite::DEFAULT_SET.iter().copied()));
482 ///
483 /// // Enable only the TLS v1.3 ciphers.
484 /// let tls_v13_config = TlsConfig::from_bytes(certs_buf, key_buf)
485 /// .with_ciphers(CipherSuite::TLS_V13_SET);
486 ///
487 /// assert_eq!(tls_v13_config.ciphers().count(), 3);
488 /// assert!(tls_v13_config.ciphers().eq(CipherSuite::TLS_V13_SET.iter().copied()));
489 /// ```
490 pub fn ciphers(&self) -> impl Iterator<Item = CipherSuite> + '_ {
491 self.ciphers.iter().copied()
492 }
493
494 /// Whether the server's cipher suite ordering is preferred or not.
495 ///
496 /// # Example
497 ///
498 /// ```rust
499 /// use rocket::config::TlsConfig;
500 ///
501 /// # let certs_buf = &[];
502 /// # let key_buf = &[];
503 /// // The default prefers the server's order.
504 /// let tls_config = TlsConfig::from_bytes(certs_buf, key_buf);
505 /// assert!(!tls_config.prefer_server_cipher_order());
506 ///
507 /// // Which can be overridden with the eponymous builder method.
508 /// let tls_config = TlsConfig::from_bytes(certs_buf, key_buf)
509 /// .with_preferred_server_cipher_order(true);
510 ///
511 /// assert!(tls_config.prefer_server_cipher_order());
512 /// ```
513 pub fn prefer_server_cipher_order(&self) -> bool {
514 self.prefer_server_cipher_order
515 }
516
517 /// Returns the value of the `mutual` parameter.
518 ///
519 /// # Example
520 ///
521 /// ```rust
522 /// use std::path::Path;
523 /// use rocket::config::{TlsConfig, MutualTls};
524 ///
525 /// # let certs = &[];
526 /// # let key = &[];
527 /// let mtls_config = MutualTls::from_path("path/to/cert.pem").mandatory(true);
528 /// let tls_config = TlsConfig::from_bytes(certs, key).with_mutual(mtls_config);
529 ///
530 /// let mtls = tls_config.mutual().unwrap();
531 /// assert_eq!(mtls.ca_certs().unwrap_left(), Path::new("path/to/cert.pem"));
532 /// assert!(mtls.mandatory);
533 /// ```
534 #[cfg(feature = "mtls")]
535 #[cfg_attr(nightly, doc(cfg(feature = "mtls")))]
536 pub fn mutual(&self) -> Option<&MutualTls> {
537 self.mutual.as_ref()
538 }
539}
540
541#[cfg(feature = "mtls")]
542impl MutualTls {
543 /// Constructs a `MutualTls` from a path to a PEM file with a certificate
544 /// authority `ca_certs` DER-encoded X.509 TLS certificate chain. This
545 /// method does no validation; it simply creates a structure suitable for
546 /// passing into a [`TlsConfig`].
547 ///
548 /// These certificates will be used to verify client-presented certificates
549 /// in TLS connections.
550 ///
551 /// # Example
552 ///
553 /// ```rust
554 /// use rocket::config::MutualTls;
555 ///
556 /// let tls_config = MutualTls::from_path("/ssl/ca_certs.pem");
557 /// ```
558 pub fn from_path<C: AsRef<std::path::Path>>(ca_certs: C) -> Self {
559 MutualTls {
560 ca_certs: Either::Left(ca_certs.as_ref().to_path_buf().into()),
561 mandatory: Default::default()
562 }
563 }
564
565 /// Constructs a `MutualTls` from a byte buffer to a certificate authority
566 /// `ca_certs` DER-encoded X.509 TLS certificate chain. This method does no
567 /// validation; it simply creates a structure suitable for passing into a
568 /// [`TlsConfig`].
569 ///
570 /// These certificates will be used to verify client-presented certificates
571 /// in TLS connections.
572 ///
573 /// # Example
574 ///
575 /// ```rust
576 /// use rocket::config::MutualTls;
577 ///
578 /// # let ca_certs_buf = &[];
579 /// let mtls_config = MutualTls::from_bytes(ca_certs_buf);
580 /// ```
581 pub fn from_bytes(ca_certs: &[u8]) -> Self {
582 MutualTls {
583 ca_certs: Either::Right(ca_certs.to_vec()),
584 mandatory: Default::default()
585 }
586 }
587
588 /// Sets whether client authentication is required. Disabled by default.
589 ///
590 /// When `true`, client authentication will be required. TLS connections
591 /// where the client does not present a certificate will be immediately
592 /// terminated. When `false`, the client is not required to present a
593 /// certificate. In either case, if a certificate _is_ presented, it must be
594 /// valid or the connection is terminated.
595 ///
596 /// # Example
597 ///
598 /// ```rust
599 /// use rocket::config::MutualTls;
600 ///
601 /// # let ca_certs_buf = &[];
602 /// let mtls_config = MutualTls::from_bytes(ca_certs_buf).mandatory(true);
603 /// ```
604 pub fn mandatory(mut self, mandatory: bool) -> Self {
605 self.mandatory = mandatory;
606 self
607 }
608
609 /// Returns the value of the `ca_certs` parameter.
610 /// # Example
611 ///
612 /// ```rust
613 /// use rocket::config::MutualTls;
614 ///
615 /// # let ca_certs_buf = &[];
616 /// let mtls_config = MutualTls::from_bytes(ca_certs_buf).mandatory(true);
617 /// assert_eq!(mtls_config.ca_certs().unwrap_right(), ca_certs_buf);
618 /// ```
619 pub fn ca_certs(&self) -> either::Either<std::path::PathBuf, &[u8]> {
620 match &self.ca_certs {
621 Either::Left(path) => either::Either::Left(path.relative()),
622 Either::Right(bytes) => either::Either::Right(&bytes),
623 }
624 }
625}
626
627#[cfg(feature = "tls")]
628mod with_tls_feature {
629 use std::fs;
630 use std::io::{self, Error};
631
632 use crate::http::tls::Config;
633 use crate::http::tls::rustls::SupportedCipherSuite as RustlsCipher;
634 use crate::http::tls::rustls::cipher_suite;
635
636 use yansi::Paint;
637
638 use super::{Either, RelativePathBuf, TlsConfig, CipherSuite};
639
640 type Reader = Box<dyn std::io::BufRead + Sync + Send>;
641
642 fn to_reader(value: &Either<RelativePathBuf, Vec<u8>>) -> io::Result<Reader> {
643 match value {
644 Either::Left(path) => {
645 let path = path.relative();
646 let file = fs::File::open(&path).map_err(move |e| {
647 let source = figment::Source::File(path);
648 let msg = format!("error reading TLS file `{}`: {}", source.primary(), e);
649 Error::new(e.kind(), msg)
650 })?;
651
652 Ok(Box::new(io::BufReader::new(file)))
653 }
654 Either::Right(vec) => Ok(Box::new(io::Cursor::new(vec.clone()))),
655 }
656 }
657
658 impl TlsConfig {
659 /// This is only called when TLS is enabled.
660 pub(crate) fn to_native_config(&self) -> io::Result<Config<Reader>> {
661 Ok(Config {
662 cert_chain: to_reader(&self.certs)?,
663 private_key: to_reader(&self.key)?,
664 ciphersuites: self.rustls_ciphers().collect(),
665 prefer_server_order: self.prefer_server_cipher_order,
666 #[cfg(not(feature = "mtls"))]
667 mandatory_mtls: false,
668 #[cfg(not(feature = "mtls"))]
669 ca_certs: None,
670 #[cfg(feature = "mtls")]
671 mandatory_mtls: self.mutual.as_ref().map_or(false, |m| m.mandatory),
672 #[cfg(feature = "mtls")]
673 ca_certs: match self.mutual {
674 Some(ref mtls) => Some(to_reader(&mtls.ca_certs)?),
675 None => None
676 },
677 })
678 }
679
680 fn rustls_ciphers(&self) -> impl Iterator<Item = RustlsCipher> + '_ {
681 self.ciphers().map(|ciphersuite| match ciphersuite {
682 CipherSuite::TLS_CHACHA20_POLY1305_SHA256 =>
683 cipher_suite::TLS13_CHACHA20_POLY1305_SHA256,
684 CipherSuite::TLS_AES_256_GCM_SHA384 =>
685 cipher_suite::TLS13_AES_256_GCM_SHA384,
686 CipherSuite::TLS_AES_128_GCM_SHA256 =>
687 cipher_suite::TLS13_AES_128_GCM_SHA256,
688 CipherSuite::TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 =>
689 cipher_suite::TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
690 CipherSuite::TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 =>
691 cipher_suite::TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
692 CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 =>
693 cipher_suite::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
694 CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 =>
695 cipher_suite::TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
696 CipherSuite::TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 =>
697 cipher_suite::TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
698 CipherSuite::TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 =>
699 cipher_suite::TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
700 })
701 }
702 }
703}