mpc_relay_server/
config.rs

1//! Server configuration.
2use mpc_protocol::{decode_keypair, hex, Keypair};
3use serde::{Deserialize, Serialize};
4use std::path::{Path, PathBuf};
5use tokio::fs;
6use url::Url;
7
8use crate::{Error, Result};
9
10/// Environment variable name for a keypair.
11const ENV_PEM: &str = "MPC_RELAY_KEYPAIR";
12
13/// Configuration for the web server.
14#[derive(Default, Serialize, Deserialize)]
15#[serde(default)]
16pub struct ServerConfig {
17    /// Path to the server key.
18    pub key: PathBuf,
19
20    /// Optional noise parameters pattern.
21    pub pattern: Option<String>,
22
23    /// Settings for session management.
24    pub session: SessionConfig,
25
26    /// Configuration for TLS encryption.
27    pub tls: Option<TlsConfig>,
28
29    /// Allow access to clients with these
30    /// public keys.
31    pub allow: Option<Vec<AccessKey>>,
32
33    /// Deny access to clients with these
34    /// public keys.
35    pub deny: Option<Vec<AccessKey>>,
36
37    /// Configuration for CORS.
38    pub cors: CorsConfig,
39}
40
41impl ServerConfig {
42    /// Determine if a public key is allowed access.
43    pub fn is_allowed_access(&self, key: impl AsRef<[u8]>) -> bool {
44        //let restricted = self.allow.is_some() || self.deny.is_some();
45
46        if let Some(deny) = &self.deny {
47            if deny.iter().any(|k| k.public_key == key.as_ref()) {
48                return false;
49            }
50        }
51
52        if let Some(allow) = &self.allow {
53            if allow.iter().any(|k| k.public_key == key.as_ref()) {
54                return true;
55            }
56            false
57        } else {
58            true
59        }
60    }
61}
62
63#[derive(Default, Serialize, Deserialize)]
64pub struct AccessKey {
65    #[serde(with = "hex::serde")]
66    public_key: Vec<u8>,
67}
68
69/// Certificate and key for TLS.
70#[derive(Debug, Default, Clone, Serialize, Deserialize)]
71pub struct TlsConfig {
72    /// Path to the certificate.
73    pub cert: PathBuf,
74    /// Path to the certificate key file.
75    pub key: PathBuf,
76}
77
78/// Configuration for server sessions.
79#[derive(Debug, Serialize, Deserialize)]
80#[serde(rename_all = "kebab-case")]
81pub struct SessionConfig {
82    /// Timeout for sessions in seconds.
83    ///
84    /// Sessions that have not seen any message activity
85    /// for this amount of time are marked for deletion.
86    ///
87    /// Default is 5 minutes.
88    pub timeout: u64,
89
90    /// Interval in seconds to reap expired sessions.
91    ///
92    /// Default is every 15 minutes.
93    pub interval: u64,
94
95    /// The interval used to poll a session for the ready
96    /// and active states.
97    ///
98    /// A session is ready when all participants have completed
99    /// the server handshake and is active when all participants
100    /// have established their peer connections.
101    ///
102    /// Default is 15 seconds.
103    pub wait_interval: u64,
104
105    /// Wait timeout controls the timeout when waiting
106    /// for all clients in a session to become active.
107    ///
108    /// Default is 5 minutes.
109    pub wait_timeout: u64,
110}
111
112impl Default for SessionConfig {
113    fn default() -> Self {
114        Self {
115            timeout: 300,
116            interval: 900,
117            wait_interval: 15,
118            wait_timeout: 300,
119        }
120    }
121}
122
123impl ServerConfig {
124    /// Load a server config from a file path.
125    pub async fn load<P: AsRef<Path>>(
126        path: P,
127    ) -> Result<(Self, Keypair)> {
128        if !fs::try_exists(path.as_ref()).await? {
129            return Err(Error::NotFile(path.as_ref().to_path_buf()));
130        }
131
132        let contents = fs::read_to_string(path.as_ref()).await?;
133        let mut config: ServerConfig = toml::from_str(&contents)?;
134
135        if config.session.interval <= config.session.timeout {
136            return Err(Error::SessionTimeoutConfig);
137        }
138
139        if config.session.wait_timeout <= config.session.wait_interval
140        {
141            return Err(Error::SessionWaitConfig);
142        }
143
144        let dir = Self::directory(path.as_ref())?;
145
146        let contents = if let Ok(env_keypair) = std::env::var(ENV_PEM)
147        {
148            tracing::info!("use environmet keypair PEM");
149            env_keypair.to_owned()
150        } else {
151            if config.key == PathBuf::default() {
152                return Err(Error::KeyFileRequired);
153            }
154
155            if config.key.is_relative() {
156                config.key = dir.join(&config.key).canonicalize()?;
157            }
158
159            if !fs::try_exists(&config.key).await? {
160                return Err(Error::KeyNotFound(config.key.clone()));
161            }
162
163            fs::read_to_string(&config.key).await?
164        };
165
166        let keypair = decode_keypair(contents)?;
167
168        if let Some(tls) = config.tls.as_mut() {
169            if tls.cert.is_relative() {
170                tls.cert = dir.join(&tls.cert).canonicalize()?;
171            }
172            if tls.key.is_relative() {
173                tls.key = dir.join(&tls.key).canonicalize()?;
174            }
175        }
176
177        Ok((config, keypair))
178    }
179
180    /// Parent directory of the configuration file.
181    fn directory(file: impl AsRef<Path>) -> Result<PathBuf> {
182        file.as_ref()
183            .parent()
184            .map(|p| p.to_path_buf())
185            .ok_or_else(|| Error::NoParentDir)
186    }
187}
188
189/// Configuration for CORS.
190#[derive(Debug, Default, Serialize, Deserialize)]
191pub struct CorsConfig {
192    /// List of additional CORS origins for the server.
193    pub origins: Vec<Url>,
194}