clementine_core/rpc/
interceptors.rs

1use tonic::{service::Interceptor, transport::CertificateDer, Request, Status};
2
3#[derive(Debug, Clone)]
4pub enum Interceptors {
5    OnlyAggregatorAndSelf {
6        aggregator_cert: CertificateDer<'static>,
7        our_cert: CertificateDer<'static>,
8    },
9    Noop,
10}
11
12fn is_internal(req: &Request<()>) -> bool {
13    // This normally doesn't exist but we add it in the AddMethodMiddleware
14    let Some(path) = req.metadata().get("grpc-method") else {
15        // No grpc method? this should not happen
16        tracing::error!("Missing grpc-method header in request");
17        return false;
18    };
19    path.as_bytes().starts_with(b"Internal")
20}
21
22impl Interceptor for Interceptors {
23    #[allow(clippy::result_large_err)]
24    fn call(&mut self, req: Request<()>) -> Result<Request<()>, Status> {
25        match self {
26            Interceptors::OnlyAggregatorAndSelf {
27                our_cert,
28                aggregator_cert,
29            } => only_aggregator_and_self(req, our_cert, aggregator_cert),
30            Interceptors::Noop => Ok(req),
31        }
32    }
33}
34
35#[allow(clippy::result_large_err)]
36fn only_aggregator_and_self(
37    req: Request<()>,
38    our_cert: &CertificateDer<'static>,
39    aggregator_cert: &CertificateDer<'static>,
40) -> Result<Request<()>, Status> {
41    let Some(peer_certs) = req.peer_certs() else {
42        if cfg!(test) {
43            // Test mode, we don't need to verify peer certificates
44            return Ok(req);
45        } else {
46            // If we're not in test mode, we need to check peer certificates
47            return Err(Status::unauthenticated(
48                "Failed to verify peer certificate, is TLS enabled?",
49            ));
50        }
51    };
52
53    if is_internal(&req) {
54        if peer_certs.contains(our_cert) {
55            Ok(req)
56        } else {
57            Err(Status::unauthenticated(
58                "Unauthorized call to internal method (not self)",
59            ))
60        }
61    } else if peer_certs.contains(aggregator_cert) || peer_certs.contains(our_cert) {
62        Ok(req)
63    } else {
64        Err(Status::unauthenticated(
65            "Unauthorized call to method (not aggregator or self)",
66        ))
67    }
68}