authly_common/
mtls_server.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
//! Utilities for creating mTLS servers participating in an Authly service mesh.

use http::Request;
use hyper::body::Incoming;
use tracing::warn;
use x509_parser::prelude::{FromDer, X509Certificate};

use crate::{certificate::oid::ENTITY_UNIQUE_IDENTIFIER, id::ServiceId};

/// A [Request] extension representing the peer Authly service that connected to the local server.
#[derive(Clone, Copy, Debug)]
pub struct PeerServiceEntity(pub ServiceId);

/// A middleware for setting up mTLS with [tower_server].
#[derive(Clone)]
pub struct MTLSMiddleware;

/// The
#[derive(Default)]
pub struct MTLSConnectionData {
    peer_service_entity: Option<ServiceId>,
}

impl tower_server::tls::TlsConnectionMiddleware for MTLSMiddleware {
    type Data = Option<MTLSConnectionData>;

    fn data(&self, connection: &rustls::ServerConnection) -> Self::Data {
        let peer_der = connection.peer_certificates()?.first()?;
        let (_, peer_cert) = X509Certificate::from_der(peer_der).ok()?;

        let mut data = MTLSConnectionData::default();

        for rdn in peer_cert.subject.iter() {
            for attr in rdn.iter() {
                if let Some(attr_type) = attr.attr_type().iter() {
                    if attr_type.eq(ENTITY_UNIQUE_IDENTIFIER.iter().copied()) {
                        if let Ok(value) = attr.attr_value().as_str() {
                            if let Ok(entity_id) = value.parse() {
                                data.peer_service_entity = Some(entity_id);
                            } else {
                                warn!("failed to parse entity ID: `{value}`");
                            }
                        }
                    }
                } else {
                    warn!("unparsable attribute");
                }
            }
        }

        Some(data)
    }

    fn call(&self, req: &mut Request<Incoming>, data: &Self::Data) {
        let Some(data) = data else {
            return;
        };
        if let Some(id) = data.peer_service_entity {
            req.extensions_mut().insert(PeerServiceEntity(id));
        }
    }
}