Enable serving over TLS
Some checks are pending
continuous-integration/drone/push Build is running

This commit is contained in:
asonix 2024-02-01 17:12:14 -06:00
parent e54432d020
commit 26d4c3efe9
3 changed files with 97 additions and 11 deletions

2
Cargo.lock generated
View file

@ -28,6 +28,7 @@ dependencies = [
"actix-codec",
"actix-rt",
"actix-service",
"actix-tls",
"actix-utils",
"ahash 0.8.7",
"base64",
@ -160,6 +161,7 @@ dependencies = [
"actix-rt",
"actix-server",
"actix-service",
"actix-tls",
"actix-utils",
"ahash 0.8.7",
"bytes",

View file

@ -17,7 +17,7 @@ default = []
[dependencies]
actix-rt = "2.7.0"
actix-web = { version = "4.0.0", default-features = false }
actix-web = { version = "4.0.0", default-features = false, features = ["rustls-0_21"] }
anyhow = "1.0"
awc = { version = "3.0.0", default-features = false, features = ["rustls-0_21"] }
clap = { version = "4.0.2", features = ["derive", "env"] }

View file

@ -10,6 +10,7 @@ use actix_web::{
middleware::NormalizePath,
web, App, HttpRequest, HttpResponse, HttpResponseBuilder, HttpServer, ResponseError,
};
use anyhow::Context;
use awc::{Client, Connector};
use clap::Parser;
use console_subscriber::ConsoleLayer;
@ -17,7 +18,10 @@ use once_cell::sync::Lazy;
use opentelemetry::KeyValue;
use opentelemetry_otlp::WithExportConfig;
use opentelemetry_sdk::{propagation::TraceContextPropagator, Resource};
use rustls::{Certificate, ClientConfig, OwnedTrustAnchor, RootCertStore};
use rustls::{
sign::CertifiedKey, Certificate, ClientConfig, OwnedTrustAnchor, PrivateKey, RootCertStore,
ServerConfig,
};
use std::{
io::Cursor,
net::SocketAddr,
@ -96,9 +100,23 @@ struct Config {
short,
long,
env = "PICTRS_PROXY_CERTIFICATE",
help = "Path to the certificate file to connec to pict-rs over TLS"
help = "Path to the certificate file to connect to pict-rs over TLS"
)]
certificate: Option<PathBuf>,
#[arg(
long,
env = "PICTRS_PROXY_SERVER_CERTIFICATE",
help = "Path to the certificate file to serve pict-rs-proxy over TLS"
)]
server_certificate: Option<PathBuf>,
#[arg(
long,
env = "PICTRS_PROXY_SERVER_PRIVATE_KEY",
help = "Path to the private key file to serve pict-rs-proxy over TLS"
)]
server_private_key: Option<PathBuf>,
}
impl Config {
@ -880,7 +898,7 @@ fn init_tracing(
let subscriber = subscriber.with(console_layer);
init_subscriber(subscriber, targets, opentelemetry_url, service_name)?;
tracing::info!("Launched console on {console_addr}");
tracing::info!("Serving tokio-console endpoint on {console_addr}");
} else {
init_subscriber(subscriber, targets, opentelemetry_url, service_name)?;
}
@ -927,7 +945,7 @@ where
Ok(())
}
async fn create_rustls_config() -> anyhow::Result<ClientConfig> {
async fn rustls_client_config() -> anyhow::Result<ClientConfig> {
let mut cert_store = RootCertStore {
roots: webpki_roots::TLS_SERVER_ROOTS
.into_iter()
@ -955,6 +973,37 @@ async fn create_rustls_config() -> anyhow::Result<ClientConfig> {
.with_no_client_auth())
}
async fn rustls_server_key() -> anyhow::Result<Option<CertifiedKey>> {
let certificate_path = if let Some(c) = &CONFIG.server_certificate {
c
} else {
tracing::info!("No server certificate");
return Ok(None);
};
let private_key_path = if let Some(p) = &CONFIG.server_private_key {
p
} else {
tracing::info!("No server private_key");
return Ok(None);
};
let cert_bytes = tokio::fs::read(certificate_path).await?;
let certs = rustls_pemfile::certs(&mut cert_bytes.as_slice())
.map(|res| res.map(|c| Certificate(c.to_vec())))
.collect::<Result<Vec<_>, _>>()?;
let key_bytes = tokio::fs::read(private_key_path).await?;
let key =
rustls_pemfile::private_key(&mut key_bytes.as_slice())?.context("No key in keyfile")?;
let signing_key = rustls::sign::any_supported_type(&PrivateKey(Vec::from(key.secret_der())))?;
Ok(Some(CertifiedKey::new(certs, signing_key)))
}
#[actix_rt::main]
async fn main() -> anyhow::Result<()> {
dotenv::dotenv().ok();
@ -966,9 +1015,9 @@ async fn main() -> anyhow::Result<()> {
CONFIG.console_event_buffer_size,
)?;
let client_config = create_rustls_config().await?;
let client_config = rustls_client_config().await?;
HttpServer::new(move || {
let server = HttpServer::new(move || {
let client = Client::builder()
.wrap(Tracing)
.add_default_header(("User-Agent", "pict-rs-frontend, v0.5.0"))
@ -999,10 +1048,45 @@ async fn main() -> anyhow::Result<()> {
.service(web::resource("/delete").route(web::get().to(delete)))
.service(web::resource("/404").route(web::get().to(not_found)))
.default_service(web::get().to(go_home))
})
.bind(CONFIG.addr)?
.run()
.await?;
});
if let Some(key) = rustls_server_key().await? {
tracing::info!("Serving pict-rs-proxy over TLS on {}", CONFIG.addr);
let (tx, rx) = rustls_channel_resolver::channel::<32>(key);
let handle = actix_rt::spawn(async move {
let mut interval = actix_rt::time::interval(Duration::from_secs(30));
interval.tick().await;
loop {
interval.tick().await;
match rustls_server_key().await {
Ok(Some(key)) => tx.update(key),
Ok(None) => tracing::warn!("Missing server certificates"),
Err(e) => tracing::error!("Failed to read server certificates {e}"),
}
}
});
let server_config = ServerConfig::builder()
.with_safe_defaults()
.with_no_client_auth()
.with_cert_resolver(rx);
server
.bind_rustls_021(CONFIG.addr, server_config)?
.run()
.await?;
handle.abort();
let _ = handle.await;
} else {
tracing::info!("Serving pict-rs-proxy on {}", CONFIG.addr);
server.bind(CONFIG.addr)?.run().await?;
}
Ok(())
}