Skip to main content

trz_gateway_server/server/
mod.rs

1//! Terrazzo Gateway server implementation.
2
3use std::net::SocketAddr;
4use std::net::ToSocketAddrs;
5use std::path::PathBuf;
6use std::pin::Pin;
7use std::sync::Arc;
8use std::sync::Mutex;
9use std::time::Duration;
10
11use autoclone::autoclone;
12use axum_server::Handle;
13use axum_server::accept::DefaultAcceptor;
14use axum_server::tls_rustls::RustlsAcceptor;
15use axum_server::tls_rustls::RustlsConfig;
16use futures::FutureExt;
17use futures::future::Shared;
18use gateway_config::app_config::AppConfig;
19use http_or_https::HttpOrHttps;
20use hyper_util::rt::TokioTimer;
21use nameth::NamedEnumValues as _;
22use nameth::nameth;
23use rustls::ClientConfig;
24use tokio::sync::oneshot;
25use tokio_rustls::TlsConnector;
26use tracing::Instrument as _;
27use tracing::debug;
28use tracing::error;
29use tracing::info;
30use tracing::info_span;
31use trz_gateway_common::certificate_info::X509CertificateInfo;
32use trz_gateway_common::dynamic_config::DynamicConfig;
33use trz_gateway_common::dynamic_config::mode::RO;
34use trz_gateway_common::handle::ServerHandle;
35use trz_gateway_common::is_global::IsGlobalError;
36use trz_gateway_common::security_configuration::HasDynamicSecurityConfig;
37use trz_gateway_common::security_configuration::certificate::CertificateConfig;
38use trz_gateway_common::security_configuration::certificate::tls_server::ToTlsServer;
39use trz_gateway_common::security_configuration::certificate::tls_server::ToTlsServerError;
40use trz_gateway_common::security_configuration::custom_server_certificate_verifier::SignedExtensionCertificateVerifier;
41use trz_gateway_common::security_configuration::trusted_store::TrustedStoreConfig;
42use trz_gateway_common::security_configuration::trusted_store::cache::CachedTrustedStoreConfig;
43use trz_gateway_common::security_configuration::trusted_store::tls_client::ToTlsClient;
44use trz_gateway_common::security_configuration::trusted_store::tls_client::ToTlsClientError;
45use trz_gateway_common::tracing::EnableTracingError;
46
47use self::gateway_config::GatewayConfig;
48use self::issuer_config::IssuerConfig;
49use self::issuer_config::IssuerConfigError;
50use crate::connection::Connections;
51
52pub mod acme;
53mod app;
54mod certificate;
55pub mod gateway_config;
56mod http_or_https;
57mod issuer_config;
58pub mod root_ca_configuration;
59mod tunnel;
60
61#[cfg(test)]
62mod tests;
63
64const HTTP_TIMEOUT: Duration = if cfg!(test) {
65    Duration::from_secs(3)
66} else {
67    Duration::from_secs(60 * 5)
68};
69
70/// The Terrazzo Gateway server.
71pub struct Server {
72    shutdown: Shared<Pin<Box<dyn Future<Output = ()> + Send + Sync>>>,
73    root_ca: Arc<X509CertificateInfo>,
74    tls_server: RustlsConfig,
75    tls_client: Arc<DynamicConfig<Result<TlsConnector, Arc<dyn IsGlobalError>>, RO>>,
76    connections: Arc<Connections>,
77    issuer_config: Arc<DynamicConfig<Result<Arc<IssuerConfig>, Arc<dyn IsGlobalError>>, RO>>,
78    app_config: Box<dyn AppConfig>,
79    set_current_endpoint: Option<PathBuf>,
80}
81
82impl Server {
83    /// Runs the server with the given configuration.
84    ///
85    /// The server runs in the background, the method returns
86    /// a [ServerHandle] to stop the server.
87    ///
88    /// It also returns future [RunGatewayError] in case the server fails to start-up.
89    pub async fn run<C: GatewayConfig>(
90        config: C,
91    ) -> Result<
92        (
93            Arc<Self>,
94            ServerHandle<()>,
95            oneshot::Receiver<RunGatewayError>,
96        ),
97        GatewayError<C>,
98    > {
99        if config.enable_tracing() {
100            trz_gateway_common::tracing::enable_tracing()?;
101        }
102
103        let _span = info_span!("Server").entered();
104
105        let (shutdown_rx, terminated_tx, handle) = ServerHandle::new("Server");
106        let shutdown_rx: Pin<Box<dyn Future<Output = ()> + Send + Sync>> = Box::pin(shutdown_rx);
107
108        let root_ca_config = config.root_ca();
109        let root_ca = root_ca_config
110            .certificate()
111            .map_err(|error| GatewayError::RootCa(Box::new(error)))?;
112        info!("Root CA: {:?}", root_ca.certificate.subject_name());
113        debug!("Root CA details: {}", root_ca.display());
114
115        let tls_server = config.tls().to_tls_server()?;
116        debug!("Got TLS server config");
117
118        let dynamic_client_config_view =
119            config
120                .client_certificate_issuer()
121                .as_dyn()
122                .view(move |client_certificate_issuer| {
123                    Arc::new(
124                        make_client_config_view(&root_ca_config, client_certificate_issuer)
125                            .map_err(Arc::<GatewayError<C>>::new),
126                    )
127                });
128
129        let server = Arc::new(Self {
130            shutdown: shutdown_rx.shared(),
131            root_ca,
132            tls_server: RustlsConfig::from_config(tls_server),
133            tls_client: dynamic_client_config_view.view(|tls_client| {
134                (*(tls_client.as_ref()))
135                    .as_ref()
136                    .map(|view| TlsConnector::from(view.tls_client.clone()))
137                    .map_err(|x| x.clone() as Arc<dyn IsGlobalError>)
138            }),
139            connections: Arc::new(Connections::default()),
140            issuer_config: dynamic_client_config_view.view(|tls_client| {
141                (*(tls_client.as_ref()))
142                    .as_ref()
143                    .map(|view| view.issuer_config.clone())
144                    .map_err(|x| x.clone() as Arc<dyn IsGlobalError>)
145            }),
146            app_config: Box::new(config.app_config()),
147            set_current_endpoint: config.set_current_endpoint(),
148        });
149
150        let (host, ports) = (config.host(), config.ports());
151        let socket_addrs = (host.as_str(), *ports.first().unwrap())
152            .to_socket_addrs()
153            .map_err(|error| GatewayError::ToSocketAddrs { host, error })?
154            .flat_map(|socket_addr| {
155                ports
156                    .iter()
157                    .map(move |port| SocketAddr::new(socket_addr.ip(), *port))
158            });
159        drop(config);
160
161        let mut terminated = vec![];
162        let mut handles = vec![];
163
164        let (server_crash_tx, server_crash_rx) = oneshot::channel();
165        let server_crash_tx = Arc::new(Mutex::new(Some(server_crash_tx)));
166        for socket_addr in socket_addrs {
167            let _span = info_span!("Listen", %socket_addr).entered();
168            info!("Setup server");
169            let (handle, serving) = server.clone().run_endpoint(socket_addr);
170            handles.push(handle);
171            let (terminated_tx, terminated_rx) = oneshot::channel();
172            terminated.push(terminated_rx);
173            let server_crash_tx = server_crash_tx.clone();
174            tokio::spawn(
175                async move {
176                    match serving.await {
177                        Ok(()) => (),
178                        Err(error) => {
179                            error!("Failed {error}");
180                            if let Some(server_crash_tx) =
181                                server_crash_tx.lock().expect("server_crash_tx").take()
182                            {
183                                let _ = server_crash_tx.send(error);
184                            }
185                        }
186                    }
187                    let _: Result<(), ()> = terminated_tx.send(());
188                }
189                .in_current_span(),
190            );
191        }
192
193        if let Some(set_current_endpoint) = server.set_current_endpoint.clone() {
194            use futures::future::join_all;
195            let current_endpoints = join_all(handles.into_iter().map(|handle| async move {
196                handle
197                    .listening()
198                    .await
199                    .filter(SocketAddr::is_ipv4)
200                    .map(|local_addr| format!("{}:{}", local_addr.ip(), local_addr.port()))
201            }))
202            .await
203            .into_iter()
204            .flatten()
205            .collect::<Vec<_>>();
206            info!("Reporting current endpoints as: {current_endpoints:?}");
207            std::fs::write(set_current_endpoint, current_endpoints.join(";"))
208                .expect("write endpoint to file");
209        }
210
211        {
212            use futures::future::join_all;
213            let all_terminated = join_all(terminated);
214            tokio::spawn(
215                async move {
216                    let _: Vec<Result<(), oneshot::error::RecvError>> = all_terminated.await;
217                    let _: Result<(), ()> = terminated_tx.send(());
218                }
219                .in_current_span(),
220            );
221        }
222        Ok((server, handle, server_crash_rx))
223    }
224
225    #[autoclone]
226    fn run_endpoint(
227        self: Arc<Self>,
228        socket_addr: SocketAddr,
229    ) -> (
230        Handle<SocketAddr>,
231        impl Future<Output = Result<(), RunGatewayError>>,
232    ) {
233        let app = self.make_app();
234
235        let handle = Handle::new();
236        let mut axum_server = axum_server::bind(socket_addr)
237            .acceptor(HttpOrHttps {
238                tls: RustlsAcceptor::new(self.tls_server.clone()),
239                plaintext: DefaultAcceptor,
240            })
241            .handle(handle.clone());
242        axum_server
243            .http_builder()
244            .http1()
245            .timer(TokioTimer::new())
246            .header_read_timeout(HTTP_TIMEOUT)
247            .http2()
248            .timer(TokioTimer::new())
249            .keep_alive_timeout(HTTP_TIMEOUT);
250
251        let shutdown = self.shutdown.clone();
252        tokio::spawn(
253            async move {
254                autoclone!(handle);
255                let () = shutdown.await;
256                handle.shutdown();
257            }
258            .in_current_span(),
259        );
260
261        let serving = async move {
262            info!("Serving...");
263            let () = axum_server
264                .serve(app.into_make_service_with_connect_info::<SocketAddr>())
265                .await
266                .map_err(RunGatewayError::Serve)?;
267            info!("Serving: done");
268            Ok(())
269        };
270        (handle, serving)
271    }
272
273    /// Returns the list of client connections.
274    pub fn connections(&self) -> &Connections {
275        &self.connections
276    }
277}
278
279fn make_client_config_view<C: GatewayConfig>(
280    root_ca: &C::RootCaConfig,
281    client_certificate_issuer: &<C::ClientCertificateIssuerConfig as HasDynamicSecurityConfig>::HasSecurityConfig,
282) -> Result<ClientConfigView, GatewayError<C>> {
283    let issuer_config = IssuerConfig::new(client_certificate_issuer)?;
284    info!("Signer certificate: {:?}", issuer_config.signer_name);
285    debug!(
286        "Signer certificate details: {}",
287        issuer_config.signer.display()
288    );
289
290    // The TLS client uses:
291    // - the Root CA for the client certificate validation,
292    // - the same PKI as the client certificate issuer for extension validation.
293    let tls_client = root_ca.to_tls_client(SignedExtensionCertificateVerifier {
294        store: CachedTrustedStoreConfig::new(client_certificate_issuer.clone())
295            .map_err(GatewayError::CachedTrustedStoreConfig)?,
296        signer_name: issuer_config.signer_name.clone(),
297    })?;
298    debug!("Got TLS client config");
299    Ok(ClientConfigView {
300        issuer_config: Arc::new(issuer_config),
301        tls_client: Arc::new(tls_client),
302    })
303}
304
305struct ClientConfigView {
306    issuer_config: Arc<IssuerConfig>,
307    tls_client: Arc<ClientConfig>,
308}
309
310#[nameth]
311#[derive(thiserror::Error, Debug)]
312pub enum GatewayError<C: GatewayConfig> {
313    #[error("[{n}] {0}", n = self.name())]
314    EnableTracing(#[from] EnableTracingError),
315
316    #[error("[{n}] Failed to get Root CA: {0}", n = self.name())]
317    RootCa(Box<dyn IsGlobalError>),
318
319    #[error("[{n}] Failed to get the client certificate issuer configuration: {0}", n = self.name())]
320    IssuerConfig(#[from] IssuerConfigError<<C::ClientCertificateIssuerConfig as HasDynamicSecurityConfig>::HasSecurityConfig>),
321
322    #[error("[{n}] Failed to get socket address for host={host}: {error}", n = self.name())]
323    ToSocketAddrs {
324        host: String,
325        error: std::io::Error,
326    },
327
328    #[error("[{n}] {0}", n = self.name())]
329    ToTlsServerConfig(#[from] ToTlsServerError<<C::TlsConfig as CertificateConfig>::Error>),
330
331    #[error("[{n}] {0}", n = self.name())]
332    ToTlsClientConfig(#[from] ToTlsClientError<<C::RootCaConfig as TrustedStoreConfig>::Error>),
333
334    #[error("[{n}] {0}", n = self.name())]
335    CachedTrustedStoreConfig(<<C::ClientCertificateIssuerConfig as HasDynamicSecurityConfig>::HasSecurityConfig as TrustedStoreConfig>::Error),
336
337    #[error("[{n}] {0}", n = self.name())]
338    RunGatewayError(#[from] RunGatewayError),
339}
340
341#[nameth]
342#[derive(thiserror::Error, Debug)]
343pub enum RunGatewayError {
344    #[error("[{n}] {0}", n = self.name())]
345    Serve(std::io::Error),
346}