authly_common/
mtls_server.rs

1//! Utilities for creating mTLS servers participating in an Authly service mesh.
2
3use http::Request;
4use hyper::body::Incoming;
5use tracing::warn;
6use x509_parser::prelude::{FromDer, X509Certificate};
7
8use crate::{certificate::oid::ENTITY_UNIQUE_IDENTIFIER, id::ServiceId};
9
10/// A [Request] extension representing the peer Authly service that connected to the local server.
11#[derive(Clone, Copy, Debug)]
12pub struct PeerServiceEntity(pub ServiceId);
13
14/// A middleware for setting up mTLS with [tower_server].
15#[derive(Clone)]
16pub struct MTLSMiddleware;
17
18/// The data extracted from the TLS connection
19#[derive(Default)]
20pub struct MTLSConnectionData {
21    peer_service_entity: Option<ServiceId>,
22}
23
24impl MTLSConnectionData {
25    /// Get the peer service entity
26    pub fn peer_service_entity(&self) -> Option<ServiceId> {
27        self.peer_service_entity
28    }
29}
30
31impl tower_server::tls::TlsConnectionMiddleware for MTLSMiddleware {
32    type Data = Option<MTLSConnectionData>;
33
34    fn data(&self, connection: &rustls::ServerConnection) -> Self::Data {
35        let peer_der = connection.peer_certificates()?.first()?;
36        let (_, peer_cert) = X509Certificate::from_der(peer_der).ok()?;
37
38        let mut data = MTLSConnectionData::default();
39
40        for rdn in peer_cert.subject.iter() {
41            for attr in rdn.iter() {
42                if let Some(attr_type) = attr.attr_type().iter() {
43                    if attr_type.eq(ENTITY_UNIQUE_IDENTIFIER.iter().copied()) {
44                        if let Ok(value) = attr.attr_value().as_str() {
45                            if let Ok(entity_id) = value.parse() {
46                                data.peer_service_entity = Some(entity_id);
47                            } else {
48                                warn!("failed to parse entity ID: `{value}`");
49                            }
50                        }
51                    }
52                } else {
53                    warn!("unparsable attribute");
54                }
55            }
56        }
57
58        Some(data)
59    }
60
61    fn call(&self, req: &mut Request<Incoming>, data: &Self::Data) {
62        let Some(data) = data else {
63            return;
64        };
65        if let Some(id) = data.peer_service_entity {
66            req.extensions_mut().insert(PeerServiceEntity(id));
67        }
68    }
69}