forked from mirrors/relay
Add support for binding TLS
This commit is contained in:
parent
a3eb785b9e
commit
5d33dba103
6 changed files with 88 additions and 10 deletions
4
Cargo.lock
generated
4
Cargo.lock
generated
|
@ -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",
|
||||||
|
|
|
@ -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"] }
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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(
|
||||||
|
|
24
src/main.rs
24
src/main.rs
|
@ -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");
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue