gl_plugin/
hsm.rs

1//! Service used to talk to the `hsmd` that is passing us the signer
2//! requests.
3
4use crate::config::NodeInfo;
5use crate::pb::{hsm_server::Hsm, Empty, HsmRequest, HsmResponse, NodeConfig};
6use crate::stager;
7use anyhow::{Context, Result};
8use futures::TryFutureExt;
9use log::{debug, info, trace, warn};
10use std::path::PathBuf;
11use std::sync::Arc;
12use tonic::{Request, Response, Status};
13
14/// The StagingHsmServer is used by the plugin to receive incoming requests
15/// from the hsmproxy and stages the requests for clients of the Node
16/// interface to stream and reply to.
17#[derive(Clone)]
18pub struct StagingHsmServer {
19    stage: Arc<stager::Stage>,
20    hsmd_sock_path: PathBuf,
21    node_info: NodeInfo,
22    node_config: NodeConfig,
23}
24
25impl StagingHsmServer {
26    pub fn new(
27        hsmd_sock_path: PathBuf,
28        stage: Arc<stager::Stage>,
29        node_info: NodeInfo,
30        node_config: NodeConfig,
31    ) -> StagingHsmServer {
32        StagingHsmServer {
33            stage,
34            hsmd_sock_path,
35            node_info,
36            node_config,
37        }
38    }
39
40    /// We have some canned responses from the signer, this gives us access.
41    fn find_canned_response(&self, msg: &Vec<u8>) -> Option<Vec<u8>> {
42        self.node_config
43            .startupmsgs
44            .iter()
45            .find(|m| &m.request == msg)
46            .map(|m| m.response.clone())
47    }
48}
49
50#[tonic::async_trait]
51impl Hsm for StagingHsmServer {
52    async fn request(&self, request: Request<HsmRequest>) -> Result<Response<HsmResponse>, Status> {
53        let req = request.into_inner();
54        debug!("Received request from hsmproxy: {:?}", req);
55
56        // Start by looking in the canned responses and return it if it is known
57        if let Some(response) = self.find_canned_response(&req.raw) {
58            debug!(
59                "Returning canned response={:?} for request={:?}",
60                response, req.raw
61            );
62            return Ok(Response::new(HsmResponse {
63                request_id: req.request_id,
64                raw: response,
65                signer_state: Vec::new(),
66                error: "".into(),
67            }));
68        } else if req.get_type() == 11 {
69            debug!("Returning stashed init msg: {:?}", self.node_info.initmsg);
70            return Ok(Response::new(HsmResponse {
71                request_id: req.request_id,
72                raw: self.node_info.initmsg.clone(),
73                signer_state: Vec::new(), // the signerproxy doesn't care about state
74                error: "".into(),
75            }));
76        } else if req.get_type() == 33 {
77            debug!("Returning stashed dev-memleak response");
78            return Ok(Response::new(HsmResponse {
79                request_id: req.request_id,
80                raw: vec![0, 133, 0],
81                signer_state: Vec::new(), // the signerproxy doesn't care about state
82                error: "".into(),
83            }));
84        }
85
86        let mut chan = match self.stage.send(req).await {
87            Err(e) => {
88                return Err(Status::unknown(format!(
89                    "Error while queuing request from node: {:?}",
90                    e
91                )))
92            }
93            Ok(c) => c,
94        };
95
96        let res = match chan.recv().await {
97            None => {
98                return Err(Status::unknown(format!(
99                    "Channel closed while waiting for response",
100                )))
101            }
102            Some(r) => r,
103        };
104
105        Ok(Response::new(res))
106    }
107
108    async fn ping(&self, _request: Request<Empty>) -> Result<Response<Empty>, Status> {
109        trace!("Got a ping");
110        Ok(Response::new(Empty::default()))
111    }
112}
113
114impl StagingHsmServer {
115    pub async fn run(self) -> Result<()> {
116        let mut path = std::path::PathBuf::new();
117        path.push(std::env::current_dir().unwrap());
118        path.push(&self.hsmd_sock_path);
119        info!(
120            "Configuring hsmd interface to listen on {}",
121            path.to_str().unwrap()
122        );
123        std::fs::create_dir_all(std::path::Path::new(&path).parent().unwrap())?;
124
125        if path.exists() {
126            warn!(
127                "Socket path {} already exists, deleting",
128                path.to_string_lossy()
129            );
130            std::fs::remove_file(&path).context("removing stale hsmd socket")?;
131        }
132        let incoming = {
133            let uds = tokio::net::UnixListener::bind(path)?;
134
135            async_stream::stream! {
136                loop {
137            yield  uds.accept().map_ok(|(st, _)| crate::unix::UnixStream(st)).await;
138                }
139            }
140        };
141
142        info!("HSM server interface starting.");
143        tonic::transport::Server::builder()
144            .add_service(crate::pb::hsm_server::HsmServer::new(self))
145            .serve_with_incoming(incoming)
146            .await
147            .context("serving HsmServer interface")
148    }
149}