#tls-certificates #tls #certificate #rustls #resolver #actix-web #live-reloading

rustls-channel-resolver

A simple single-cert channel-ish rustls resolver for live-reloading certificate files

3 releases (breaking)

0.3.0 May 19, 2024
0.2.0 Feb 4, 2024
0.1.0 Jan 31, 2024

#1252 in HTTP server

40 downloads per month
Used in 5 crates

AGPL-3.0

32KB
111 lines

rustls-channel-resolver

A simple channel-like resolver for live-reloading TLS certificates

Usage

Add the dependency to your project

cargo add rustls-channel-resolver

Configure live-reloading for your certificate file

use std::time::Duration;

use actix_web::{web, App, HttpServer};

async fn index() -> &'static str {
    "Hewwo Mr Obama"
}

#[actix_web::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let initial_key = read_key().await?.unwrap();

    let (tx, rx) = rustls_channel_resolver::channel::<32>(initial_key);

    let handle = actix_web::rt::spawn(async move {
        let mut interval = actix_web::rt::time::interval(Duration::from_secs(30));
        interval.tick().await;

        loop {
            interval.tick().await;
            match read_key().await {
                Ok(Some(key)) => tx.update(key),
                Ok(None) => eprintln!("No key in keyfile"),
                Err(e) => {
                    eprintln!("Failed to read key from fs {e}");
                }
            }
        }
    });

    let server_config = rustls::ServerConfig::builder()
        .with_safe_defaults()
        .with_no_client_auth()
        .with_cert_resolver(rx);

    HttpServer::new(|| App::new().route("/", web::get().to(index)))
        .bind_rustls_0_22("0.0.0.0:8443", server_config)?
        .bind("0.0.0.0:8080")?
        .run()
        .await?;

    handle.abort();
    let _ = handle.await;
    Ok(())
}

async fn read_key() -> Result<Option<rustls::sign::CertifiedKey>, Box<dyn std::error::Error>> {
    let cert_bytes = tokio::fs::read("./out/example.crt").await?;
    let certs = rustls_pemfile::certs(&mut cert_bytes.as_slice())
        .collect::<Result<Vec<_>, _>>()?;

    let key_bytes = tokio::fs::read("./out/example.key").await?;
    let Some(private_key) = rustls_pemfile::private_key(&mut key_bytes.as_slice())? else {
        return Ok(None);
    };

    let private_key =
        rustls::crypto::ring::sign::any_supported_type(&private_key)?;

    Ok(Some(rustls::sign::CertifiedKey::new(certs, private_key)))
}

Contributing

Feel free to open issues for anything you find an issue with. Please note that any contributed code will be licensed under the AGPLv3.

License

Copyright © 2024 asonix

rustls-channel-resolver is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

rustls-channel-resolver is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. This file is part of rustls-channel-resolver.

You should have received a copy of the GNU Affero General Public License along with rustls-channel-resolver. If not, see http://www.gnu.org/licenses/.

Dependencies

~7–14MB
~272K SLoC