rustls_acme/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::prelude::*;
34//! use rustls_acme::{AcmeConfig, caches::DirCache};
35//!
36//! #[macro_rules_attribute::apply(smol_macros::main!)]
37//! async fn main() {
38//! simple_logger::init_with_level(log::Level::Info).unwrap();
39//!
40//! let tcp_listener = smol::net::TcpListener::bind("[::]:443").await.unwrap();
41//!
42//! let mut tls_incoming = AcmeConfig::new(["example.com"])
43//! .contact_push("mailto:admin@example.com")
44//! .cache(DirCache::new("./rustls_acme_cache"))
45//! .incoming(tcp_listener.incoming(), Vec::new());
46//!
47//! while let Some(tls) = tls_incoming.next().await {
48//! let mut tls = tls.unwrap();
49//! smol::spawn(async move {
50//! tls.write_all(HELLO).await.unwrap();
51//! tls.close().await.unwrap();
52//! }).detach();
53//! }
54//! }
55//!
56//! const HELLO: &'static [u8] = br#"HTTP/1.1 200 OK
57//! Content-Length: 11
58//! Content-Type: text/plain; charset=utf-8
59//!
60//! Hello Tls!"#;
61//! ```
62//!
63//! `examples/high_level.rs` implements a "Hello Tls!" server similar to the one above, which accepts
64//! domain, port and cache directory parameters.
65//!
66//! Note that all examples use the let's encrypt staging directory by default.
67//! The production directory imposes strict rate limits, which are easily exhausted accidentally
68//! during testing and development.
69//! For testing with the staging directory you may open `https://<your domain>:<port>` in a browser
70//! that allows TLS connections to servers signed by an untrusted CA (in Firefox click "Advanced..."
71//! -> "Accept the Risk and Continue").
72//!
73//! ## Low-level Rustls API
74//!
75//! For users who may want to interact with [rustls] or [futures_rustls]
76//! directly, the library exposes the underlying certificate management [AcmeState] as well as a
77//! matching resolver [ResolvesServerCertAcme] which implements the [rustls::server::ResolvesServerCert] trait.
78//! See the server_low_level example on how to use the low-level API directly with [futures_rustls].
79//!
80//! ## Account and certificate caching
81//!
82//! A production server using the let's encrypt production directory must implement both account and
83//! certificate caching to avoid exhausting the let's encrypt API rate limits.
84//! A file based cache using a cache directory is provided by [caches::DirCache].
85//! Caches backed by other persistence layers may be implemented using the [Cache] trait,
86//! or the underlying [CertCache], [AccountCache] traits (contributions welcome).
87//! [caches::CompositeCache] provides a wrapper to combine two implementors of [CertCache] and
88//! [AccountCache] into a single [Cache].
89//!
90//! Note, that the error type parameters of the cache carries over to some other types in this
91//! crate via the [AcmeConfig] they are added to.
92//! If you want to avoid different specializations based on cache type use the
93//! [AcmeConfig::cache_with_boxed_err] method to construct an [AcmeConfig] object.
94//!
95//!
96//! ## The acme module
97//!
98//! The underlying implementation of an async acme client may be useful to others and is exposed as
99//! a module. It is incomplete (contributions welcome) and not covered by any stability
100//! promises.
101//!
102//! ## Special thanks
103//!
104//! This crate was inspired by the [autocert](https://golang.org/x/crypto/acme/autocert/)
105//! package for [Go](https://golang.org).
106//!
107//! This crate builds on the excellent work of the authors of
108//! [rustls](https://github.com/ctz/rustls),
109//! [futures-rustls](https://github.com/quininer/futures-rustls),
110//! and many others.
111//!
112//! Thanks to:
113//! - [Josh Triplett](https://github.com/joshtriplett) for many contributions and feedback.
114//! - [Jack Klamer](https://github.com/jklamer) for contributing HTTP-01 challenge support.
115
116#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
117
118mod acceptor;
119pub mod acme;
120#[cfg(feature = "axum")]
121pub mod axum;
122mod cache;
123pub mod caches;
124mod config;
125mod helpers;
126mod https_helper;
127mod incoming;
128mod jose;
129mod resolver;
130mod state;
131#[cfg(feature = "tokio")]
132pub mod tokio;
133#[cfg(feature = "tower")]
134pub mod tower;
135
136pub use futures_rustls;
137
138pub use acceptor::*;
139pub use cache::*;
140pub use config::*;
141pub use helpers::*;
142pub use incoming::*;
143pub use resolver::*;
144pub use state::*;
145
146#[cfg(feature = "aws-lc-rs")]
147use ::aws_lc_rs as crypto;
148#[cfg(all(feature = "ring", not(feature = "aws-lc-rs")))]
149use ::ring as crypto;
150
151// TODO: Can we use something like CryptoProvider for rustls, but for ring to drop this requirement?
152#[cfg(not(any(feature = "ring", feature = "aws-lc-rs")))]
153compile_error!("rustls-acme requires either the ring or aws-lc-rs feature enabled");
154
155#[cfg(any(feature = "ring", feature = "aws-lc-rs"))]
156pub(crate) fn any_ecdsa_type(
157 der: &futures_rustls::pki_types::PrivateKeyDer,
158) -> Result<std::sync::Arc<dyn futures_rustls::rustls::sign::SigningKey>, futures_rustls::rustls::Error> {
159 #[cfg(all(feature = "ring", not(feature = "aws-lc-rs")))]
160 return futures_rustls::rustls::crypto::ring::sign::any_ecdsa_type(der);
161 #[cfg(feature = "aws-lc-rs")]
162 return futures_rustls::rustls::crypto::aws_lc_rs::sign::any_ecdsa_type(der);
163}
164
165#[cfg(any(feature = "ring", feature = "aws-lc-rs"))]
166pub(crate) fn crypto_provider() -> futures_rustls::rustls::crypto::CryptoProvider {
167 #[cfg(all(feature = "ring", not(feature = "aws-lc-rs")))]
168 return futures_rustls::rustls::crypto::ring::default_provider();
169 #[cfg(feature = "aws-lc-rs")]
170 return futures_rustls::rustls::crypto::aws_lc_rs::default_provider();
171}