1
0
mirror of https://github.com/matrix-org/matrix-authentication-service.git synced 2025-07-29 22:01:14 +03:00

WIP: better listeners

- listen on UNIX domain sockets
- handle TLS stuff
- allow mounting only some resources
This commit is contained in:
Quentin Gliech
2022-10-03 22:19:08 +02:00
parent 7fbfb74a5e
commit 84ac87f551
12 changed files with 1063 additions and 170 deletions

View File

@ -20,6 +20,7 @@ argon2 = { version = "0.4.1", features = ["password-hash"] }
watchman_client = "0.8.0"
atty = "0.2.14"
listenfd = "1.0.0"
rustls = "0.20.6"
tracing = "0.1.36"
tracing-appender = "0.2.2"
@ -44,6 +45,7 @@ mas-static-files = { path = "../static-files" }
mas-storage = { path = "../storage" }
mas-tasks = { path = "../tasks" }
mas-templates = { path = "../templates" }
mas-listener = { path = "../listener" }
[dev-dependencies]
indoc = "1.0.7"

View File

@ -12,12 +12,12 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use std::{net::SocketAddr, sync::Arc, time::Duration};
use std::{sync::Arc, time::Duration};
use anyhow::Context;
use clap::Parser;
use futures_util::{
future::FutureExt,
future::{FutureExt, OptionFuture},
stream::{StreamExt, TryStreamExt},
};
use hyper::Server;
@ -25,6 +25,7 @@ use mas_config::RootConfig;
use mas_email::Mailer;
use mas_handlers::{AppState, MatrixHomeserver};
use mas_http::ServerLayer;
use mas_listener::{maybe_tls::MaybeTlsAcceptor, unix_or_tcp::UnixOrTcpListener};
use mas_policy::PolicyFactory;
use mas_router::{Route, UrlBuilder};
use mas_storage::MIGRATOR;
@ -245,39 +246,103 @@ impl Options {
});
let signal = shutdown_signal().shared();
let shutdown_signal = signal.clone();
let mut fd_manager = listenfd::ListenFd::from_env();
let futs = listeners_config
.into_iter()
.map(|listener_config| {
let listeners = listeners_config.into_iter().map(|listener_config| {
// We have to borrow it here, not in the nested closure
let fd_manager = &mut fd_manager;
// Let's first grab all the listeners in a synchronous manner
// This helps with the fd_manager mutable borrow
let listeners: Result<Vec<UnixOrTcpListener>, _> = listener_config
.binds
.iter()
.map(move |bind_config| bind_config.listener(fd_manager))
.collect();
Ok((listener_config, listeners?))
});
// Now that we have the listeners ready, we can do the rest concurrently
futures_util::stream::iter(listeners)
.try_for_each_concurrent(None, move |(config, listeners)| {
let signal = signal.clone();
let router = mas_handlers::router(state.clone())
.nest(mas_router::StaticAsset::route(), static_files.clone())
.layer(ServerLayer::default());
let mut futs: Vec<_> = Vec::with_capacity(listener_config.binds.len());
for bind in listener_config.binds {
let listener = bind.listener(&mut fd_manager)?;
let router = router.clone();
let mut router = mas_handlers::empty_router(state.clone());
let addr = listener.local_addr()?;
info!("Listening on http://{addr}");
let fut = Server::from_tcp(listener)?
.serve(router.into_make_service_with_connect_info::<SocketAddr>())
.with_graceful_shutdown(signal.clone());
futs.push(fut);
for resource in config.resources {
router = match resource {
mas_config::HttpResource::Health => {
router.merge(mas_handlers::healthcheck_router(state.clone()))
}
mas_config::HttpResource::Discovery => {
router.merge(mas_handlers::discovery_router(state.clone()))
}
mas_config::HttpResource::Human => {
router.merge(mas_handlers::human_router(state.clone()))
}
mas_config::HttpResource::Static => {
router.nest(mas_router::StaticAsset::route(), static_files.clone())
}
mas_config::HttpResource::OAuth => {
router.merge(mas_handlers::api_router(state.clone()))
}
mas_config::HttpResource::Compat => {
router.merge(mas_handlers::compat_router(state.clone()))
}
}
}
anyhow::Ok(futures_util::future::try_join_all(futs))
})
.collect::<Result<Vec<_>, _>>()?;
let router = router.layer(ServerLayer::default());
futures_util::future::try_join_all(futs).await?;
async move {
let tls_config: OptionFuture<_> = config
.tls
.map(|tls_config| async move {
let (key, chain) = tls_config.load().await?;
let key = rustls::PrivateKey(key);
let chain = chain.into_iter().map(rustls::Certificate).collect();
let mut config = rustls::ServerConfig::builder()
.with_safe_defaults()
.with_no_client_auth()
.with_single_cert(chain, key)
.context("failed to build TLS server config")?;
config.alpn_protocols = vec![b"h2".to_vec(), b"http/1.1".to_vec()];
anyhow::Ok(Arc::new(config))
})
.into();
let tls_config = tls_config.await.transpose()?;
futures_util::stream::iter(listeners)
.map(Ok)
.try_for_each_concurrent(None, move |listener| {
let listener = MaybeTlsAcceptor::new(tls_config.clone(), listener);
// Unless there is something really bad happening, we should be able to
// grab the local_addr here. Panicking here if it is not the case is
// probably fine.
let addr = listener.local_addr().unwrap();
if listener.is_secure() {
info!("Listening on https://{addr:?}");
} else {
info!("Listening on http://{addr:?}");
}
Server::builder(listener)
.serve(router.clone().into_make_service())
.with_graceful_shutdown(signal.clone())
})
.await?;
anyhow::Ok(())
}
})
.await?;
// This ensures we're running, even if no listener are setup
// This is useful for only running the task runner
signal.await;
shutdown_signal.await;
Ok(())
}