relay/src/main.rs

424 lines
13 KiB
Rust
Raw Normal View History

2021-12-03 22:20:37 +00:00
// need this for ructe
#![allow(clippy::needless_borrow)]
use std::time::Duration;
2022-01-17 22:54:45 +00:00
use activitystreams::iri_string::types::IriString;
use actix_rt::task::JoinHandle;
2022-11-23 18:57:56 +00:00
use actix_web::{middleware::Compress, web, App, HttpServer};
2023-03-23 18:51:32 +00:00
use collector::MemoryCollector;
2022-02-01 17:47:17 +00:00
#[cfg(feature = "console")]
use console_subscriber::ConsoleLayer;
use error::Error;
use http_signature_normalization_actix::middleware::VerifySignature;
use metrics_exporter_prometheus::PrometheusBuilder;
2023-03-23 18:51:32 +00:00
use metrics_util::layers::FanoutBuilder;
use opentelemetry::KeyValue;
use opentelemetry_otlp::WithExportConfig;
use opentelemetry_sdk::Resource;
use reqwest_middleware::ClientWithMiddleware;
2022-11-21 03:42:38 +00:00
use rustls::ServerConfig;
2021-09-18 17:55:39 +00:00
use tracing_actix_web::TracingLogger;
use tracing_error::ErrorLayer;
use tracing_log::LogTracer;
use tracing_subscriber::{filter::Targets, fmt::format::FmtSpan, layer::SubscriberExt, Layer};
2020-03-15 02:05:40 +00:00
2022-11-17 19:14:29 +00:00
mod admin;
2020-03-15 02:05:40 +00:00
mod apub;
mod args;
2022-11-19 23:28:15 +00:00
mod collector;
mod config;
2020-03-23 22:17:53 +00:00
mod data;
mod db;
2020-03-16 03:36:46 +00:00
mod error;
2022-11-17 19:14:29 +00:00
mod extractors;
2023-09-09 20:46:22 +00:00
mod future;
mod jobs;
2020-03-23 17:38:39 +00:00
mod middleware;
mod requests;
2020-03-23 17:38:39 +00:00
mod routes;
2023-07-27 15:19:20 +00:00
mod spawner;
2022-11-02 22:58:52 +00:00
mod telegram;
2020-03-15 02:05:40 +00:00
use crate::config::UrlKind;
2020-03-20 18:40:18 +00:00
use self::{
args::Args,
config::Config,
2021-02-10 04:05:06 +00:00
data::{ActorCache, MediaCache, State},
db::Db,
2021-10-30 00:26:57 +00:00
jobs::create_workers,
middleware::{DebugPayload, MyVerify, RelayResolver, Timings},
routes::{actor, healthz, inbox, index, nodeinfo, nodeinfo_meta, statics},
2023-07-27 15:19:20 +00:00
spawner::Spawner,
2020-03-20 18:40:18 +00:00
};
2020-03-20 04:06:16 +00:00
2021-09-20 17:49:07 +00:00
fn init_subscriber(
software_name: &'static str,
2022-01-17 22:54:45 +00:00
opentelemetry_url: Option<&IriString>,
2021-09-20 17:49:07 +00:00
) -> Result<(), anyhow::Error> {
2021-09-18 17:55:39 +00:00
LogTracer::init()?;
2020-03-15 22:37:53 +00:00
let targets: Targets = std::env::var("RUST_LOG")
2022-11-22 05:12:31 +00:00
.unwrap_or_else(|_| "warn,actix_web=debug,actix_server=debug,tracing_actix_web=info".into())
.parse()?;
2021-09-18 17:55:39 +00:00
let format_layer = tracing_subscriber::fmt::layer()
.with_span_events(FmtSpan::NEW | FmtSpan::CLOSE)
.with_filter(targets.clone());
2022-02-01 17:47:17 +00:00
#[cfg(feature = "console")]
let console_layer = ConsoleLayer::builder()
.with_default_env()
.server_addr(([0, 0, 0, 0], 6669))
.event_buffer_capacity(1024 * 1024)
.spawn();
2021-09-18 17:55:39 +00:00
let subscriber = tracing_subscriber::Registry::default()
.with(format_layer)
.with(ErrorLayer::default());
2021-09-18 17:55:39 +00:00
2022-02-01 17:47:17 +00:00
#[cfg(feature = "console")]
let subscriber = subscriber.with(console_layer);
2021-09-20 17:49:07 +00:00
if let Some(url) = opentelemetry_url {
let tracer = opentelemetry_otlp::new_pipeline()
.tracing()
.with_trace_config(
opentelemetry_sdk::trace::config().with_resource(Resource::new(vec![
KeyValue::new("service.name", software_name),
])),
)
.with_exporter(
opentelemetry_otlp::new_exporter()
.tonic()
.with_endpoint(url.as_str()),
)
.install_batch(opentelemetry_sdk::runtime::Tokio)?;
2021-09-18 17:55:39 +00:00
let otel_layer = tracing_opentelemetry::layer()
.with_tracer(tracer)
.with_filter(targets);
let subscriber = subscriber.with(otel_layer);
tracing::subscriber::set_global_default(subscriber)?;
} else {
tracing::subscriber::set_global_default(subscriber)?;
}
2021-09-20 17:49:07 +00:00
Ok(())
}
fn build_client(
user_agent: &str,
timeout_seconds: u64,
proxy: Option<(&IriString, Option<(&str, &str)>)>,
) -> Result<ClientWithMiddleware, Error> {
let builder = reqwest::Client::builder().user_agent(user_agent.to_string());
let builder = if let Some((url, auth)) = proxy {
let proxy = reqwest::Proxy::all(url.as_str())?;
let proxy = if let Some((username, password)) = auth {
proxy.basic_auth(username, password)
} else {
proxy
};
builder.proxy(proxy)
} else {
builder
};
let client = builder
.timeout(Duration::from_secs(timeout_seconds))
.build()?;
let client_with_middleware = reqwest_middleware::ClientBuilder::new(client)
.with(reqwest_tracing::TracingMiddleware::default())
.build();
Ok(client_with_middleware)
}
#[actix_rt::main]
async fn main() -> Result<(), anyhow::Error> {
2021-09-20 17:49:07 +00:00
dotenv::dotenv().ok();
let config = Config::build()?;
init_subscriber(Config::software_name(), config.opentelemetry_url())?;
let args = Args::new();
if args.any() {
return client_main(config, args).await?;
}
let collector = MemoryCollector::new();
if let Some(bind_addr) = config.prometheus_bind_address() {
let (recorder, exporter) = PrometheusBuilder::new()
.with_http_listener(bind_addr)
.build()?;
actix_rt::spawn(exporter);
2023-03-23 18:51:32 +00:00
let recorder = FanoutBuilder::default()
.add_recorder(recorder)
.add_recorder(collector.clone())
.build();
metrics::set_boxed_recorder(Box::new(recorder))?;
} else {
collector.install()?;
}
2021-09-20 17:49:07 +00:00
2022-11-20 18:07:27 +00:00
tracing::warn!("Opening DB");
let db = Db::build(&config)?;
2022-11-20 18:07:27 +00:00
tracing::warn!("Building caches");
let actors = ActorCache::new(db.clone());
let media = MediaCache::new(db.clone());
server_main(db, actors, media, collector, config).await??;
tracing::warn!("Application exit");
Ok(())
}
fn client_main(config: Config, args: Args) -> JoinHandle<Result<(), anyhow::Error>> {
actix_rt::spawn(do_client_main(config, args))
}
async fn do_client_main(config: Config, args: Args) -> Result<(), anyhow::Error> {
let client = build_client(
2023-07-25 21:06:56 +00:00
&config.user_agent(),
config.client_timeout(),
config.proxy_config(),
)?;
if !args.blocks().is_empty() || !args.allowed().is_empty() {
if args.undo() {
admin::client::unblock(&client, &config, args.blocks().to_vec()).await?;
admin::client::disallow(&client, &config, args.allowed().to_vec()).await?;
} else {
admin::client::block(&client, &config, args.blocks().to_vec()).await?;
admin::client::allow(&client, &config, args.allowed().to_vec()).await?;
}
println!("Updated lists");
}
2022-11-17 20:13:41 +00:00
if args.contacted() {
let last_seen = admin::client::last_seen(&client, &config).await?;
let mut report = String::from("Contacted:");
if !last_seen.never.is_empty() {
report += "\nNever seen:\n";
}
for domain in last_seen.never {
report += "\t";
report += &domain;
report += "\n";
}
if !last_seen.last_seen.is_empty() {
report += "\nSeen:\n";
}
for (datetime, domains) in last_seen.last_seen {
for domain in domains {
report += "\t";
report += &datetime.to_string();
report += " - ";
report += &domain;
report += "\n";
}
}
report += "\n";
println!("{report}");
}
if args.list() {
let (blocked, allowed, connected) = tokio::try_join!(
admin::client::blocked(&client, &config),
admin::client::allowed(&client, &config),
admin::client::connected(&client, &config)
)?;
let mut report = String::from("Report:\n");
if !allowed.allowed_domains.is_empty() {
report += "\nAllowed\n\t";
report += &allowed.allowed_domains.join("\n\t");
}
if !blocked.blocked_domains.is_empty() {
report += "\n\nBlocked\n\t";
report += &blocked.blocked_domains.join("\n\t");
2022-11-19 23:45:01 +00:00
}
if !connected.connected_actors.is_empty() {
report += "\n\nConnected\n\t";
report += &connected.connected_actors.join("\n\t");
}
report += "\n";
println!("{report}");
}
2022-11-19 23:45:01 +00:00
if args.stats() {
let stats = admin::client::stats(&client, &config).await?;
stats.present();
}
2022-11-20 05:44:35 +00:00
Ok(())
}
2022-11-17 20:13:41 +00:00
fn server_main(
db: Db,
actors: ActorCache,
media: MediaCache,
collector: MemoryCollector,
config: Config,
) -> JoinHandle<Result<(), anyhow::Error>> {
actix_rt::spawn(do_server_main(db, actors, media, collector, config))
}
const VERIFY_RATIO: usize = 7;
async fn do_server_main(
db: Db,
actors: ActorCache,
media: MediaCache,
collector: MemoryCollector,
config: Config,
) -> Result<(), anyhow::Error> {
let client = build_client(
&config.user_agent(),
config.client_timeout(),
config.proxy_config(),
)?;
2022-11-02 22:58:52 +00:00
tracing::warn!("Creating state");
2022-11-21 03:42:38 +00:00
let (signature_threads, verify_threads) = match config.signature_threads() {
0 | 1 => (1, 1),
2023-07-27 16:10:29 +00:00
n if n <= VERIFY_RATIO => (n, 1),
n => {
let verify_threads = (n / VERIFY_RATIO).max(1);
let signature_threads = n.saturating_sub(verify_threads).max(VERIFY_RATIO);
(signature_threads, verify_threads)
}
};
2023-11-26 02:14:18 +00:00
let verify_spawner = Spawner::build("verify-cpu", verify_threads.try_into()?);
let sign_spawner = Spawner::build("sign-cpu", signature_threads.try_into()?);
let key_id = config.generate_url(UrlKind::MainKey).to_string();
2023-11-26 02:14:18 +00:00
let state = State::build(db.clone(), key_id, sign_spawner.clone(), client).await?;
if let Some((token, admin_handle)) = config.telegram_info() {
tracing::warn!("Creating telegram handler");
telegram::start(admin_handle.to_owned(), db.clone(), token);
}
let keys = config.open_keys()?;
let bind_address = config.bind_address();
2023-11-26 02:14:18 +00:00
let sign_spawner2 = sign_spawner.clone();
let verify_spawner2 = verify_spawner.clone();
2022-11-21 03:42:38 +00:00
let server = HttpServer::new(move || {
let job_server =
create_workers(state.clone(), actors.clone(), media.clone(), config.clone());
2023-06-23 20:08:59 +00:00
2022-11-17 19:14:29 +00:00
let app = App::new()
2021-06-26 23:14:43 +00:00
.app_data(web::Data::new(db.clone()))
.app_data(web::Data::new(state.clone()))
2023-07-27 18:39:31 +00:00
.app_data(web::Data::new(
state.requests.clone().spawner(verify_spawner.clone()),
2023-07-27 18:39:31 +00:00
))
2021-06-26 23:14:43 +00:00
.app_data(web::Data::new(actors.clone()))
.app_data(web::Data::new(config.clone()))
2023-06-23 20:08:59 +00:00
.app_data(web::Data::new(job_server))
2022-11-19 23:28:15 +00:00
.app_data(web::Data::new(media.clone()))
2023-07-26 23:11:44 +00:00
.app_data(web::Data::new(collector.clone()))
.app_data(web::Data::new(verify_spawner.clone()));
2022-11-17 19:14:29 +00:00
let app = if let Some(data) = config.admin_config() {
app.app_data(data)
} else {
app
};
2022-11-23 18:57:56 +00:00
app.wrap(Compress::default())
.wrap(TracingLogger::default())
2022-11-20 02:35:45 +00:00
.wrap(Timings)
.route("/healthz", web::get().to(healthz))
2020-03-15 02:05:40 +00:00
.service(web::resource("/").route(web::get().to(index)))
.service(web::resource("/media/{path}").route(web::get().to(routes::media)))
.service(
web::resource("/inbox")
2023-07-27 17:20:05 +00:00
.wrap(config.digest_middleware().spawner(verify_spawner.clone()))
.wrap(VerifySignature::new(
MyVerify(
state.requests.clone().spawner(verify_spawner.clone()),
actors.clone(),
state.clone(),
verify_spawner.clone(),
),
2023-07-27 17:20:05 +00:00
http_signature_normalization_actix::Config::new(),
))
2020-07-10 20:34:18 +00:00
.wrap(DebugPayload(config.debug()))
2020-03-23 17:38:39 +00:00
.route(web::post().to(inbox)),
)
2020-03-23 17:38:39 +00:00
.service(web::resource("/actor").route(web::get().to(actor)))
.service(web::resource("/nodeinfo/2.0.json").route(web::get().to(nodeinfo)))
2020-03-19 19:05:16 +00:00
.service(
web::scope("/.well-known")
2020-04-21 19:12:10 +00:00
.service(actix_webfinger::scoped::<RelayResolver>())
2020-03-23 17:38:39 +00:00
.service(web::resource("/nodeinfo").route(web::get().to(nodeinfo_meta))),
2020-03-19 19:05:16 +00:00
)
2020-03-23 17:38:39 +00:00
.service(web::resource("/static/{filename}").route(web::get().to(statics)))
2022-11-17 19:14:29 +00:00
.service(
web::scope("/api/v1").service(
web::scope("/admin")
.route("/allow", web::post().to(admin::routes::allow))
2022-11-17 20:13:41 +00:00
.route("/disallow", web::post().to(admin::routes::disallow))
2022-11-17 19:14:29 +00:00
.route("/block", web::post().to(admin::routes::block))
2022-11-17 20:13:41 +00:00
.route("/unblock", web::post().to(admin::routes::unblock))
2022-11-17 19:14:29 +00:00
.route("/allowed", web::get().to(admin::routes::allowed))
.route("/blocked", web::get().to(admin::routes::blocked))
2022-11-19 23:28:15 +00:00
.route("/connected", web::get().to(admin::routes::connected))
.route("/stats", web::get().to(admin::routes::stats))
.route("/last_seen", web::get().to(admin::routes::last_seen)),
2022-11-17 19:14:29 +00:00
),
)
2022-11-21 03:42:38 +00:00
});
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
2023-08-30 03:15:32 +00:00
.bind_rustls_021(bind_address, server_config)?
2022-11-21 03:42:38 +00:00
.run()
.await?;
} else {
tracing::warn!("Binding to {}:{}", bind_address.0, bind_address.1);
server.bind(bind_address)?.run().await?;
}
2021-09-20 17:49:07 +00:00
2023-11-26 02:14:18 +00:00
sign_spawner2.close().await;
verify_spawner2.close().await;
2022-11-20 03:51:04 +00:00
tracing::warn!("Server closed");
2020-03-15 02:05:40 +00:00
Ok(())
}
2020-03-20 18:40:18 +00:00
include!(concat!(env!("OUT_DIR"), "/templates.rs"));