ocsp-stapler 0.3.1

OCSP stapler & client with support for Rustls
Documentation

ocsp-stapler

The ocsp-stapler crate provides two structs: Client and Stapler.

Client

client::Client is an OCSP client that can be used to query the OCSP responders of the Certificate Authorities. It tries to mostly conform to the lightweight OCSP profile

  • Currently only SHA-1 digest for OCSP request is supported since it's the only one that LetsEncrypt uses
  • Requests <= 255 bytes will be sent using GET and Base64, otherwise POST

Stapler

stapler::Stapler uses client::Client internally and provides a Rustls-compatible API to attach (staple) OCSP responses to the certificates.

It wraps whatever that implements Rustls' rustls::server::ResolvesServerCert trait and also implements the same trait itself.

The workflow is the following:

  • stapler::Stapler receives a ClientHello from Rustls and forwards it to the wrapped resolver to retrieve the certificate chain
  • It calculates the SHA-1 fingerprint over the whole end-entity certificate and uses that to check if it has the same certificate in the local storage:
    • If not, then it sends the certificate to the background worker for eventual processing & stapling. Meanwhilte it returns to Rustls the original unstapled certificate
    • If found, it responds with a stapled version of the certificate

Since the certificates are only stapled eventually then the Must-Staple marked certificates will not work out of the box - first request for them will always be failed by the client. Maybe later an API to pre-staple them will be added.

Background worker duties:

  • Receieves the certificates from Stapler, processes them and inserts into the local storage
  • Wakes up every minute (or when a new certificate is added) to do the following:
  • Obtain OCSP responses for newly added certificates
  • Renew the OCSP responses that are already past 50% of their validity interval
  • Check for expired certificates & purge them
  • Check for expired OCSP responses and clear them
  • Post an updated version of storage that is shared with Stapler

Background worker is spawned by Stapler::new() using tokio::spawn so it must be executed in Tokio context. It runs indefinitely unless stopped with Stapler::stop().

Other notes:

  • Stapler does not check the certificate validity (i.e. does not traverse the chain up to the root)
  • Certificates without the issuer's certificate are passed through as-is since we can't query the OCSP without access to the issuer's public key

Metrics

Stapler supports a few Prometheus metrics - create it using one of new_..._with_registry() constructors and provide a Prometheus Registry reference to register the metrics in.

Example

// Inner service that provides certificates to Rustls, can be anything
let ckey: CertifiedKey = ...;
let mut inner = rustls::server::ResolvesServerCertUsingSni::new();
inner.add("crates.io", ckey).unwrap();

let stapler = Arc::new(ocsp_stapler::Stapler::new(inner));

// Then you can build & use server_config wherever applicable
let server_config = rustls::server::ServerConfig::builder()
        .with_no_client_auth()
        .with_cert_resolver(stapler.clone());

// Stop the background worker to clean up
stapler.stop().await;