bs_gl_plugin/
config.rs

1use anyhow::{anyhow, Context, Result};
2use log::trace;
3use std::net::SocketAddr;
4use tonic::transport;
5
6/// Enumeration of supported networks
7#[derive(Clone, Debug)]
8pub enum Network {
9    Bitcoin = 0,
10    Testnet = 1,
11    Regtest = 2,
12}
13
14impl TryFrom<i16> for Network {
15    type Error = anyhow::Error;
16
17    fn try_from(i: i16) -> Result<Network> {
18        match i {
19            0 => Ok(Network::Bitcoin),
20            1 => Ok(Network::Testnet),
21            2 => Ok(Network::Regtest),
22            e => Err(anyhow!("Unknown numeric network {}", e)),
23        }
24    }
25}
26
27impl TryFrom<String> for Network {
28    type Error = anyhow::Error;
29
30    fn try_from(s: String) -> Result<Network> {
31        match s.to_lowercase().as_ref() {
32            "bitcoin" => Ok(Network::Bitcoin),
33            "testnet" => Ok(Network::Testnet),
34            "regtest" => Ok(Network::Regtest),
35            o => Err(anyhow!("Unknown network {}", o)),
36        }
37    }
38}
39impl std::fmt::Display for Network {
40    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
41        write!(
42            f,
43            "{}",
44            match *self {
45                Network::Bitcoin => "bitcoin",
46                Network::Testnet => "testnet",
47                Network::Regtest => "regtest",
48            }
49        )
50    }
51}
52
53#[derive(Clone, Debug)]
54pub struct Identity {
55    pub id: transport::Identity,
56    pub ca: transport::Certificate,
57}
58
59impl Identity {
60    /// Loads an identity from the file-system
61    ///
62    /// Starting from the current directory, we go and look for
63    /// certificates and keys for the specified identity. If
64    /// `GL_CERT_PATH` is set we instead use that path to look for
65    /// the certificates.
66    ///
67    /// If any of the files we expect to find is missing
68    /// (`ca.pem`, `<path>.crt` or `<path>-key.pem`), an error is
69    /// returned.
70    pub fn from_path(path: &str) -> Result<Identity> {
71        let mut dir = std::env::current_dir()?;
72        dir.push(std::env::var("GL_CERT_PATH").unwrap_or("./certs/".into()));
73        let mut dir = dir.canonicalize().with_context(|| {
74            format!(
75                "could not canonicalize GL_CERT_PATH={}",
76                dir.to_string_lossy()
77            )
78        })?;
79
80        let cacert = dir.join("ca.pem");
81        trace!("Loading root CA from {}", cacert.to_string_lossy());
82        /* The root CA cert is at the root of the certs directory. */
83        let ca = std::fs::read(cacert).with_context(|| {
84            format!(
85                "Could not load CA certificate from {}",
86                dir.join("ca.pem").to_string_lossy()
87            )
88        })?;
89
90        /* Find the subdirectory. */
91        for p in path.to_string().split("/").skip(1).collect::<Vec<&str>>() {
92            dir = dir.join(p);
93        }
94
95        let client_key_path = format!("{}-key.pem", dir.to_string_lossy());
96        let client_cert_path = format!("{}.crt", dir.to_string_lossy());
97
98        trace!(
99            "Loading identity from {} and certificate from {}",
100            client_key_path,
101            client_cert_path
102        );
103        let client_cert = std::fs::read(&client_cert_path).with_context(|| {
104            format!(
105                "could not read client certificate from {:?}",
106                client_cert_path
107            )
108        })?;
109
110        let client_key = std::fs::read(&client_key_path)
111            .with_context(|| format!("could not read client key from {:?}", client_key_path))?;
112
113        Ok(Identity {
114            id: tonic::transport::Identity::from_pem(client_cert, client_key),
115            ca: tonic::transport::Certificate::from_pem(ca),
116        })
117    }
118}
119
120#[derive(Clone, Debug)]
121pub struct Config {
122    pub identity: Identity,
123    pub hsmd_sock_path: String,
124    pub node_grpc_binding: String,
125    pub node_info: NodeInfo,
126    pub towerd_public_grpc_uri: Option<String>,
127
128    /// The `clientca` is the CA we're enforcing when connecting to
129    /// other services. This means that services must have a valid
130    /// certificate under this CA otherwise the connection is
131    /// closed. This is _not_ the CA we use to enforce User
132    /// identities. See [`Identity`] for this purpose.
133    pub clientca: tonic::transport::Certificate,
134
135    /// The `Nodelet` told us that we're running on this network.
136    pub network: Network,
137
138    pub node_config: NodeConfig,
139}
140
141impl Config {
142    pub fn new() -> Result<Self> {
143        let binding: SocketAddr = std::env::var("GL_NODE_BIND")
144            .context("Missing GL_NODE_BIND environment variable")?
145            .parse()
146            .context("Could not parse address from GL_BIND_NODE")?;
147
148        let towerd_public_grpc_uri: Option<String> = std::env::var("GL_TOWER_PUBLIC_GRPC_URI").ok();
149
150        let clientca_path: String = std::env::var("GL_PLUGIN_CLIENTCA_PATH")
151            .context("Missing GL_PLUGIN_CLIENTCA_PATH environment variable")?;
152
153        let identity = Identity::from_path(&"/users/1/server")?;
154
155        let clientca = tonic::transport::Certificate::from_pem(std::fs::read(clientca_path)?);
156        let network: Network = std::env::var("GL_NODE_NETWORK")
157            .context("Missing GL_NODE_NETWORK")?
158            .try_into()
159            .context("Unknown network in GL_NODE_NETWORK")?;
160
161        let mut cfg = std::env::current_dir()?;
162        cfg.push("node_config.pb");
163        let node_config = NodeConfig::from_file(cfg.as_path())?;
164
165        Ok(Config {
166            identity,
167            hsmd_sock_path: "hsmd.sock".to_string(),
168            node_grpc_binding: binding.to_string(),
169            node_info: NodeInfo::new()?,
170            towerd_public_grpc_uri,
171            clientca,
172            network,
173            node_config,
174        })
175    }
176}
177
178#[derive(Clone, Debug)]
179pub struct NodeInfo {
180    pub node_id: Vec<u8>,
181    pub initmsg: Vec<u8>,
182}
183
184impl NodeInfo {
185    fn new() -> Result<Self> {
186        let node_id = match std::env::var("GL_NODE_ID") {
187            Ok(v) => hex::decode(v)?,
188            Err(_) => return Err(anyhow!("Environment variable GL_NODE_ID is not set")),
189        };
190
191        let initmsg = hex::decode(std::env::var("GL_NODE_INIT").context("Missing GL_NODE_INIT")?)
192            .context("Malformed GL_NODE_INIT")?;
193
194        if node_id.len() != 33 {
195            return Err(anyhow!(
196                "GL_NODE_ID is not a 33 byte hex-encoded public-key",
197            ));
198        }
199
200        Ok(NodeInfo { node_id, initmsg })
201    }
202}
203
204pub use crate::pb::NodeConfig;
205
206impl NodeConfig {
207    pub fn from_file(f: &std::path::Path) -> Result<NodeConfig, anyhow::Error> {
208        log::debug!("Loading node_config from {}", f.display());
209        use prost::Message;
210        let contents = std::fs::read(f)?;
211        NodeConfig::decode(&contents[..]).context("decoding protobuf payload")
212    }
213}