tokio_rustls_acme2/lib.rs
1//! rustls-acme is an easy-to-use, async compatible ACME client library for rustls.
2//! The validation mechanism used is TLS-ALPN-01 OR HTTP-01, which allow serving acme challenge responses and
3//! regular TLS traffic on the same port.
4//!
5//! rustls-acme is designed to be runtime agnostic and as runtime independent as Rust allows at the
6//! moment.
7//! No persistent tasks are spawned under the hood and the certificate acquisition/renewal process
8//! is folded into the streams and futures being polled by the library user.
9//!
10//! The goal is to provide a [Let's Encrypt](https://letsencrypt.org/) compatible TLS serving and
11//! certificate management using a simple and flexible stream based API.
12//!
13//! To use rustls-acme add the following lines to your `Cargo.toml`:
14//!
15//! ```toml
16//! [dependencies]
17//! rustls-acme = "*"
18//! ```
19//!
20//! ## Validation mechanims
21//!
22//! Using TLS-ALPN-01 is used by default and recommended.
23//! However, if the DNS entry points to an HTTP proxy, which terminates TLS, but you still need a certificate,
24//! you may be able to use HTTP-01 instead (see `examples/low_level_axum_http01.rs`).
25//!
26//! ## High-level API
27//!
28//! The high-level API consists of a single stream [Incoming] of incoming TLS connection.
29//! Polling the next future of the stream takes care of acquisition and renewal of certificates, as
30//! well as accepting TLS connections, which are handed over to the caller on success.
31//!
32//! ```rust,no_run
33//! use futures_util::StreamExt;
34//! use tokio::io::AsyncWriteExt;
35//! use tokio_rustls_acme2::{AcmeConfig, caches::DirCache};
36//!
37//! #[tokio::main]
38//! async fn main() {
39//! simple_logger::init_with_level(log::Level::Info).unwrap();
40//!
41//! let tcp_listener = tokio::net::TcpListener::bind("[::]:443").await.unwrap();
42//!
43//! let tcp_incoming = Box::pin(futures_util::stream::unfold(tcp_listener, |listener| async move {
44//! Some((listener.accept().await.map(|(socket, _)| socket), listener))
45//! }));
46//!
47//! let mut tls_incoming = AcmeConfig::new(["example.com"])
48//! .contact_push("mailto:admin@example.com")
49//! .cache(DirCache::new("./tokio_rustls_acme2_cache"))
50//! .incoming(tcp_incoming, Vec::new());
51//!
52//! while let Some(tls) = tls_incoming.next().await {
53//! let mut tls = tls.unwrap();
54//! tokio::spawn(async move {
55//! tls.write_all(HELLO).await.unwrap();
56//! tls.shutdown().await.unwrap();
57//! });
58//! }
59//! }
60//!
61//! const HELLO: &'static [u8] = br#"HTTP/1.1 200 OK
62//! Content-Length: 10
63//! Content-Type: text/plain; charset=utf-8
64//!
65//! Hello Tls!"#;
66//! ```
67//!
68//! `examples/high_level.rs` implements a "Hello Tls!" server similar to the one above, which accepts
69//! domain, port and cache directory parameters.
70//!
71//! Note that all examples use the let's encrypt staging directory by default.
72//! The production directory imposes strict rate limits, which are easily exhausted accidentally
73//! during testing and development.
74//! For testing with the staging directory you may open `https://<your domain>:<port>` in a browser
75//! that allows TLS connections to servers signed by an untrusted CA (in Firefox click "Advanced..."
76//! -> "Accept the Risk and Continue").
77//!
78//! ## Low-level Rustls API
79//!
80//! For users who may want to interact with [rustls] or [tokio_rustls]
81//! directly, the library exposes the underlying certificate management [AcmeState] as well as a
82//! matching resolver [ResolvesServerCertAcme] which implements the [rustls::server::ResolvesServerCert] trait.
83//! See the server_low_level example on how to use the low-level API directly with [tokio_rustls].
84//!
85//! ## Account and certificate caching
86//!
87//! A production server using the let's encrypt production directory must implement both account and
88//! certificate caching to avoid exhausting the let's encrypt API rate limits.
89//! A file based cache using a cache directory is provided by [caches::DirCache].
90//! Caches backed by other persistence layers may be implemented using the [Cache] trait,
91//! or the underlying [CertCache], [AccountCache] traits (contributions welcome).
92//! [caches::CompositeCache] provides a wrapper to combine two implementors of [CertCache] and
93//! [AccountCache] into a single [Cache].
94//!
95//! Note, that the error type parameters of the cache carries over to some other types in this
96//! crate via the [AcmeConfig] they are added to.
97//! If you want to avoid different specializations based on cache type use the
98//! [AcmeConfig::cache_with_boxed_err] method to construct an [AcmeConfig] object.
99//!
100//!
101//! ## The acme module
102//!
103//! The underlying implementation of an async acme client may be useful to others and is exposed as
104//! a module. It is incomplete (contributions welcome) and not covered by any stability
105//! promises.
106//!
107//! ## Special thanks
108//!
109//! This crate was inspired by the [autocert](https://golang.org/x/crypto/acme/autocert/)
110//! package for [Go](https://golang.org).
111//!
112//! This crate builds on the excellent work of the authors of
113//! [rustls](https://github.com/ctz/rustls),
114//! [futures-rustls](https://github.com/quininer/futures-rustls),
115//! and many others.
116//!
117//! Thanks to:
118//! - [Josh Triplett](https://github.com/joshtriplett) for many contributions and feedback.
119//! - [Jack Klamer](https://github.com/jklamer) for contributing HTTP-01 challenge support.
120
121#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
122
123mod acceptor;
124pub mod acme;
125#[cfg(feature = "axum")]
126pub mod axum;
127mod cache;
128pub mod caches;
129mod config;
130mod helpers;
131mod https_helper;
132mod incoming;
133mod jose;
134mod resolver;
135mod state;
136#[cfg(feature = "tower")]
137pub mod tower;
138
139pub use tokio_rustls;
140
141pub use acceptor::*;
142pub use cache::*;
143pub use config::*;
144pub use helpers::*;
145pub use incoming::*;
146pub use resolver::*;
147pub use state::*;
148
149#[cfg(feature = "aws-lc-rs")]
150use ::aws_lc_rs as crypto;
151#[cfg(all(feature = "ring", not(feature = "aws-lc-rs")))]
152use ::ring as crypto;
153
154// TODO: Can we use something like CryptoProvider for rustls, but for ring to drop this requirement?
155#[cfg(not(any(feature = "ring", feature = "aws-lc-rs")))]
156compile_error!("rustls-acme requires either the ring or aws-lc-rs feature enabled");
157
158#[cfg(any(feature = "ring", feature = "aws-lc-rs"))]
159pub(crate) fn any_ecdsa_type(
160 der: &rustls_pki_types::PrivateKeyDer,
161) -> Result<std::sync::Arc<dyn tokio_rustls::rustls::sign::SigningKey>, tokio_rustls::rustls::Error> {
162 #[cfg(all(feature = "ring", not(feature = "aws-lc-rs")))]
163 return tokio_rustls::rustls::crypto::ring::sign::any_ecdsa_type(der);
164 #[cfg(feature = "aws-lc-rs")]
165 return tokio_rustls::rustls::crypto::aws_lc_rs::sign::any_ecdsa_type(der);
166}
167
168#[cfg(any(feature = "ring", feature = "aws-lc-rs"))]
169pub(crate) fn crypto_provider() -> tokio_rustls::rustls::crypto::CryptoProvider {
170 #[cfg(all(feature = "ring", not(feature = "aws-lc-rs")))]
171 return tokio_rustls::rustls::crypto::ring::default_provider();
172 #[cfg(feature = "aws-lc-rs")]
173 return tokio_rustls::rustls::crypto::aws_lc_rs::default_provider();
174}