Add support for binding TLS

This commit is contained in:
asonix 2022-11-20 21:42:38 -06:00
parent a3eb785b9e
commit 5d33dba103
6 changed files with 88 additions and 10 deletions

4
Cargo.lock generated
View file

@ -63,6 +63,7 @@ dependencies = [
"actix-codec", "actix-codec",
"actix-rt", "actix-rt",
"actix-service", "actix-service",
"actix-tls",
"actix-utils", "actix-utils",
"ahash", "ahash",
"base64", "base64",
@ -192,6 +193,7 @@ dependencies = [
"actix-rt", "actix-rt",
"actix-server", "actix-server",
"actix-service", "actix-service",
"actix-tls",
"actix-utils", "actix-utils",
"ahash", "ahash",
"bytes", "bytes",
@ -311,6 +313,8 @@ dependencies = [
"rsa", "rsa",
"rsa-magic-public-key", "rsa-magic-public-key",
"ructe", "ructe",
"rustls",
"rustls-pemfile",
"serde", "serde",
"serde_json", "serde_json",
"sha2", "sha2",

View file

@ -23,7 +23,9 @@ default = []
[dependencies] [dependencies]
anyhow = "1.0" anyhow = "1.0"
actix-rt = "2.7.0" actix-rt = "2.7.0"
actix-web = { version = "4.0.1", default-features = false } actix-web = { version = "4.0.1", default-features = false, features = [
"rustls",
] }
actix-webfinger = "0.4.0" actix-webfinger = "0.4.0"
activitystreams = "0.7.0-alpha.19" activitystreams = "0.7.0-alpha.19"
activitystreams-ext = "0.1.0-alpha.2" activitystreams-ext = "0.1.0-alpha.2"
@ -48,6 +50,8 @@ quanta = "0.10.1"
rand = "0.8" rand = "0.8"
rsa = "0.7" rsa = "0.7"
rsa-magic-public-key = "0.6.0" rsa-magic-public-key = "0.6.0"
rustls = "0.20.7"
rustls-pemfile = "1.0.1"
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0" serde_json = "1.0"
sha2 = { version = "0.10", features = ["oid"] } sha2 = { version = "0.10", features = ["oid"] }

View file

@ -98,6 +98,8 @@ API_TOKEN=somepasswordishtoken
OPENTELEMETRY_URL=localhost:4317 OPENTELEMETRY_URL=localhost:4317
TELEGRAM_TOKEN=secret TELEGRAM_TOKEN=secret
TELEGRAM_ADMIN_HANDLE=your_handle TELEGRAM_ADMIN_HANDLE=your_handle
TLS_KEY=/path/to/key
TLS_CERT=/path/to/cert
``` ```
#### Descriptions #### Descriptions
@ -131,6 +133,10 @@ A URL for exporting opentelemetry spans. This is mostly useful for debugging. Th
A Telegram Bot Token for running the relay administration bot. There is no default. A Telegram Bot Token for running the relay administration bot. There is no default.
##### `TELEGRAM_ADMIN_HANDLE` ##### `TELEGRAM_ADMIN_HANDLE`
The handle of the telegram user allowed to administer the relay. There is no default. The handle of the telegram user allowed to administer the relay. There is no default.
##### `TLS_KEY`
Optional - This is specified if you are running the relay directly on the internet and have a TLS key to provide HTTPS for your relay
##### `TLS_CERT`
Optional - This is specified if you are running the relay directly on the internet and have a TLS certificate chain to provide HTTPS for your relay
### Subscribing ### Subscribing
Mastodon admins can subscribe to this relay by adding the `/inbox` route to their relay settings. Mastodon admins can subscribe to this relay by adding the `/inbox` route to their relay settings.

View file

@ -14,8 +14,9 @@ use activitystreams::{
}; };
use config::Environment; use config::Environment;
use http_signature_normalization_actix::prelude::{VerifyDigest, VerifySignature}; use http_signature_normalization_actix::prelude::{VerifyDigest, VerifySignature};
use rustls::{Certificate, PrivateKey};
use sha2::{Digest, Sha256}; use sha2::{Digest, Sha256};
use std::{net::IpAddr, path::PathBuf}; use std::{io::BufReader, net::IpAddr, path::PathBuf};
use uuid::Uuid; use uuid::Uuid;
#[derive(Clone, Debug, serde::Deserialize)] #[derive(Clone, Debug, serde::Deserialize)]
@ -34,6 +35,8 @@ pub(crate) struct ParsedConfig {
telegram_token: Option<String>, telegram_token: Option<String>,
telegram_admin_handle: Option<String>, telegram_admin_handle: Option<String>,
api_token: Option<String>, api_token: Option<String>,
tls_key: Option<PathBuf>,
tls_cert: Option<PathBuf>,
} }
#[derive(Clone)] #[derive(Clone)]
@ -52,6 +55,13 @@ pub struct Config {
telegram_token: Option<String>, telegram_token: Option<String>,
telegram_admin_handle: Option<String>, telegram_admin_handle: Option<String>,
api_token: Option<String>, api_token: Option<String>,
tls: Option<TlsConfig>,
}
#[derive(Clone)]
struct TlsConfig {
key: PathBuf,
cert: PathBuf,
} }
#[derive(Debug)] #[derive(Debug)]
@ -100,6 +110,8 @@ impl std::fmt::Debug for Config {
.field("telegram_token", &"[redacted]") .field("telegram_token", &"[redacted]")
.field("telegram_admin_handle", &self.telegram_admin_handle) .field("telegram_admin_handle", &self.telegram_admin_handle)
.field("api_token", &"[redacted]") .field("api_token", &"[redacted]")
.field("tls_key", &"[redacted]")
.field("tls_cert", &"[redacted]")
.finish() .finish()
} }
} }
@ -121,6 +133,8 @@ impl Config {
.set_default("telegram_token", None as Option<&str>)? .set_default("telegram_token", None as Option<&str>)?
.set_default("telegram_admin_handle", None as Option<&str>)? .set_default("telegram_admin_handle", None as Option<&str>)?
.set_default("api_token", None as Option<&str>)? .set_default("api_token", None as Option<&str>)?
.set_default("tls_key", None as Option<&str>)?
.set_default("tls_cert", None as Option<&str>)?
.add_source(Environment::default()) .add_source(Environment::default())
.build()?; .build()?;
@ -129,6 +143,10 @@ impl Config {
let scheme = if config.https { "https" } else { "http" }; let scheme = if config.https { "https" } else { "http" };
let base_uri = iri!(format!("{}://{}", scheme, config.hostname)).into_absolute(); let base_uri = iri!(format!("{}://{}", scheme, config.hostname)).into_absolute();
let tls = config
.tls_key
.and_then(|key| config.tls_cert.map(|cert| TlsConfig { key, cert }));
Ok(Config { Ok(Config {
hostname: config.hostname, hostname: config.hostname,
addr: config.addr, addr: config.addr,
@ -144,9 +162,39 @@ impl Config {
telegram_token: config.telegram_token, telegram_token: config.telegram_token,
telegram_admin_handle: config.telegram_admin_handle, telegram_admin_handle: config.telegram_admin_handle,
api_token: config.api_token, api_token: config.api_token,
tls,
}) })
} }
pub(crate) fn open_keys(&self) -> Result<Option<(Vec<Certificate>, PrivateKey)>, Error> {
let tls = if let Some(tls) = &self.tls {
tls
} else {
return Ok(None);
};
let mut certs_reader = BufReader::new(std::fs::File::open(&tls.cert)?);
let certs = rustls_pemfile::certs(&mut certs_reader)?;
let mut key_reader = BufReader::new(std::fs::File::open(&tls.key)?);
let keys = rustls_pemfile::read_all(&mut key_reader)?;
let certs = certs.into_iter().map(Certificate).collect();
let key = if let Some(key) = keys.into_iter().find_map(|item| match item {
rustls_pemfile::Item::RSAKey(der) => Some(PrivateKey(der)),
rustls_pemfile::Item::PKCS8Key(der) => Some(PrivateKey(der)),
rustls_pemfile::Item::ECKey(der) => Some(PrivateKey(der)),
_ => None,
}) {
key
} else {
return Ok(None);
};
Ok(Some((certs, key)))
}
pub(crate) fn sled_path(&self) -> &PathBuf { pub(crate) fn sled_path(&self) -> &PathBuf {
&self.sled_path &self.sled_path
} }

View file

@ -44,8 +44,8 @@ pub(crate) fn create_workers(
media: MediaCache, media: MediaCache,
config: Config, config: Config,
) -> (Manager, JobServer) { ) -> (Manager, JobServer) {
let parallelism = let parallelism = std::thread::available_parallelism()
std::thread::available_parallelism().unwrap_or_else(|_| NonZeroUsize::try_from(1).expect("nonzero")); .unwrap_or_else(|_| NonZeroUsize::try_from(1).expect("nonzero"));
let shared = WorkerConfig::new_managed(Storage::new(ActixTimer), move |queue_handle| { let shared = WorkerConfig::new_managed(Storage::new(ActixTimer), move |queue_handle| {
JobState::new( JobState::new(

View file

@ -8,6 +8,7 @@ use collector::MemoryCollector;
use console_subscriber::ConsoleLayer; use console_subscriber::ConsoleLayer;
use opentelemetry::{sdk::Resource, KeyValue}; use opentelemetry::{sdk::Resource, KeyValue};
use opentelemetry_otlp::WithExportConfig; use opentelemetry_otlp::WithExportConfig;
use rustls::ServerConfig;
use tracing_actix_web::TracingLogger; use tracing_actix_web::TracingLogger;
use tracing_error::ErrorLayer; use tracing_error::ErrorLayer;
use tracing_log::LogTracer; use tracing_log::LogTracer;
@ -203,9 +204,10 @@ async fn do_server_main(
telegram::start(admin_handle.to_owned(), db.clone(), token); telegram::start(admin_handle.to_owned(), db.clone(), token);
} }
let keys = config.open_keys()?;
let bind_address = config.bind_address(); let bind_address = config.bind_address();
tracing::warn!("Binding to {}:{}", bind_address.0, bind_address.1); let server = HttpServer::new(move || {
HttpServer::new(move || {
let app = App::new() let app = App::new()
.app_data(web::Data::new(db.clone())) .app_data(web::Data::new(db.clone()))
.app_data(web::Data::new(state.clone())) .app_data(web::Data::new(state.clone()))
@ -258,10 +260,24 @@ async fn do_server_main(
.route("/stats", web::get().to(admin::routes::stats)), .route("/stats", web::get().to(admin::routes::stats)),
), ),
) )
}) });
.bind(bind_address)?
if let Some((certs, key)) = keys {
tracing::warn!("Binding to {}:{} with TLS", bind_address.0, bind_address.1);
let server_config = ServerConfig::builder()
.with_safe_default_cipher_suites()
.with_safe_default_kx_groups()
.with_safe_default_protocol_versions()?
.with_no_client_auth()
.with_single_cert(certs, key)?;
server
.bind_rustls(bind_address, server_config)?
.run() .run()
.await?; .await?;
} else {
tracing::warn!("Binding to {}:{}", bind_address.0, bind_address.1);
server.bind(bind_address)?.run().await?;
}
tracing::warn!("Server closed"); tracing::warn!("Server closed");