Skip to content

Commit

Permalink
Merge pull request #25 from jacob-pro/main
Browse files Browse the repository at this point in the history
Add an example for how to use `replace_acceptor` to change certificate
  • Loading branch information
tmccombs authored May 18, 2022
2 parents b9e4a0b + 137d53b commit 72c179a
Show file tree
Hide file tree
Showing 6 changed files with 154 additions and 32 deletions.
5 changes: 5 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,11 @@ name = "http-low-level"
path = "examples/http-low-level.rs"
required-features = ["hyper-h1"]

[[example]]
name = "http-change-certificate"
path = "examples/http-change-certificate.rs"
required-features = ["hyper-h1"]

[package.metadata.docs.rs]
features = ["rustls", "native-tls", "openssl", "hyper-h1", "hyper-h2", "rt"]
rustdoc-args = ["--cfg", "docsrs"]
72 changes: 72 additions & 0 deletions examples/http-change-certificate.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
use hyper::server::conn::{AddrIncoming, Http};
use hyper::service::service_fn;
use hyper::{Body, Request, Response};
use std::convert::Infallible;
use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::Arc;

mod tls_config;
use tls_config::{tls_acceptor, tls_acceptor2, Acceptor};
use tokio::sync::mpsc;

/// To view the current certificate try:
/// `echo "Q" |openssl s_client -showcerts -connect 127.0.0.1:3000 | grep subject=CN`
///
/// To change the certificate make a HTTP request:
/// `curl https://127.0.0.1:3000 -k`
#[tokio::main(flavor = "current_thread")]
async fn main() {
let addr = ([127, 0, 0, 1], 3000).into();
let counter = Arc::new(AtomicU64::new(0));

let mut listener = tls_listener::builder(tls_acceptor())
.max_handshakes(10)
.listen(AddrIncoming::bind(&addr).unwrap());

let (tx, mut rx) = mpsc::channel::<Acceptor>(1);

let http = Http::new();
loop {
tokio::select! {
conn = listener.accept() => {
match conn.expect("Tls listener stream should be infinite") {
Ok(conn) => {
let http = http.clone();
let tx = tx.clone();
let counter = counter.clone();
tokio::spawn(async move {
let svc = service_fn(move |request| handle_request(tx.clone(), counter.clone(), request));
if let Err(err) = http.serve_connection(conn, svc).await {
eprintln!("Application error: {}", err);
}
});
},
Err(e) => {
eprintln!("Bad connection: {}", e);
}
}
},
message = rx.recv() => {
// Certificate is loaded on another task; we don't want to block the listener loop
let acceptor = message.expect("Channel should not be closed");
println!("Rotating certificate...");
listener.replace_acceptor(acceptor);
}
}
}
}

async fn handle_request(
change_certificate: mpsc::Sender<Acceptor>,
counter: Arc<AtomicU64>,
_request: Request<Body>,
) -> Result<Response<Body>, Infallible> {
let counter = counter.fetch_add(1, Ordering::Relaxed) + 1;
let new_cert = if counter % 2 == 0 {
tls_acceptor()
} else {
tls_acceptor2()
};
change_certificate.send(new_cert).await.ok();
Ok(Response::new(Body::from("Changing certificate...")))
}
Binary file added examples/tls_config/local2.cert
Binary file not shown.
Binary file added examples/tls_config/local2.key
Binary file not shown.
Binary file added examples/tls_config/local2.pfx
Binary file not shown.
109 changes: 77 additions & 32 deletions examples/tls_config/mod.rs
Original file line number Diff line number Diff line change
@@ -1,52 +1,97 @@
#[cfg(feature = "rustls")]
mod cert {
pub const CERT: &[u8] = include_bytes!("local.cert");
pub const PKEY: &[u8] = include_bytes!("local.key");
}
#[cfg(all(feature = "native-tls", not(feature = "rustls")))]
const PFX: &[u8] = include_bytes!("local.pfx");

#[cfg(feature = "rustls")]
pub fn tls_acceptor() -> tokio_rustls::TlsAcceptor {
mod config {
use std::sync::Arc;
use tokio_rustls::rustls::{Certificate, PrivateKey, ServerConfig};

let key = PrivateKey(cert::PKEY.into());
let cert = Certificate(cert::CERT.into());

Arc::new(
ServerConfig::builder()
.with_safe_defaults()
.with_no_client_auth()
.with_single_cert(vec![cert], key)
.unwrap(),
)
.into()
const CERT: &[u8] = include_bytes!("local.cert");
const PKEY: &[u8] = include_bytes!("local.key");
const CERT2: &[u8] = include_bytes!("local2.cert");
const PKEY2: &[u8] = include_bytes!("local2.key");

pub type Acceptor = tokio_rustls::TlsAcceptor;

fn tls_acceptor_impl(cert_der: &[u8], key_der: &[u8]) -> Acceptor {
let key = PrivateKey(cert_der.into());
let cert = Certificate(key_der.into());
Arc::new(
ServerConfig::builder()
.with_safe_defaults()
.with_no_client_auth()
.with_single_cert(vec![cert], key)
.unwrap(),
)
.into()
}

pub fn tls_acceptor() -> Acceptor {
tls_acceptor_impl(PKEY, CERT)
}

pub fn tls_acceptor2() -> Acceptor {
tls_acceptor_impl(PKEY2, CERT2)
}
}

#[cfg(all(
feature = "native-tls",
not(any(feature = "rustls", feature = "openssl"))
))]
pub fn tls_acceptor() -> tokio_native_tls::TlsAcceptor {
mod config {
use tokio_native_tls::native_tls::{Identity, TlsAcceptor};

let identity = Identity::from_pkcs12(PFX, "").unwrap();
TlsAcceptor::builder(identity).build().unwrap().into()
const PFX: &[u8] = include_bytes!("local.pfx");
const PFX2: &[u8] = include_bytes!("local2.pfx");

pub type Acceptor = tokio_native_tls::TlsAcceptor;

fn tls_acceptor_impl(pfx: &[u8]) -> Acceptor {
let identity = Identity::from_pkcs12(pfx, "").unwrap();
TlsAcceptor::builder(identity).build().unwrap().into()
}

pub fn tls_acceptor() -> Acceptor {
tls_acceptor_impl(PFX)
}

pub fn tls_acceptor2() -> Acceptor {
tls_acceptor_impl(PFX2)
}
}

#[cfg(all(
feature = "openssl",
not(any(feature = "rustls", feature = "native-tls"))
))]
pub fn tls_acceptor() -> openssl_impl::ssl::SslContext {
mod config {
use openssl_impl::ssl::{SslContext, SslFiletype, SslMethod};
let mut builder = SslContext::builder(SslMethod::tls_server()).unwrap();
builder
.set_certificate_file("./examples/tls_config/local.cert", SslFiletype::ASN1)
.unwrap();
builder
.set_private_key_file("./examples/tls_config/local.key", SslFiletype::ASN1)
.unwrap();
builder.build()
use std::path::Path;

pub type Acceptor = openssl_impl::ssl::SslContext;

fn tls_acceptor_impl<P: AsRef<Path>>(cert_file: P, key_file: P) -> Acceptor {
let mut builder = SslContext::builder(SslMethod::tls_server()).unwrap();
builder
.set_certificate_file(cert_file, SslFiletype::ASN1)
.unwrap();
builder
.set_private_key_file(key_file, SslFiletype::ASN1)
.unwrap();
builder.build()
}

pub fn tls_acceptor() -> Acceptor {
tls_acceptor_impl(
"./examples/tls_config/local.cert",
"./examples/tls_config/local.key",
)
}

pub fn tls_acceptor2() -> Acceptor {
tls_acceptor_impl(
"./examples/tls_config/local2.cert",
"./examples/tls_config/local2.key",
)
}
}

pub use config::*;

0 comments on commit 72c179a

Please sign in to comment.