Skip to main content

manta_shared/common/config/
types.rs

1use std::collections::HashMap;
2
3use crate::common::audit::Auditor;
4
5use manta_backend_dispatcher::types::K8sDetails;
6use serde::{Deserialize, Serialize};
7
8/// Which backend API this site speaks.
9#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
10#[serde(rename_all = "lowercase")]
11pub enum BackendTechnology {
12  /// HPE Cray System Management (CSM) backend.
13  Csm,
14  /// OpenCHAMI backend.
15  Ochami,
16}
17
18impl BackendTechnology {
19  /// Return the lowercase string expected by `StaticBackendDispatcher::new`.
20  pub fn as_str(&self) -> &'static str {
21    match self {
22      Self::Csm => "csm",
23      Self::Ochami => "ochami",
24    }
25  }
26}
27
28#[derive(Serialize, Deserialize, Debug)]
29/// Connection details for a single ALPS site (CSM or OCHAMI instance).
30///
31/// The Vault URL used by handlers requiring vault (sat-file, session,
32/// console, logs) is derived at startup from
33/// `[sites.X.k8s.authentication.vault] base_url`. The vault secret path
34/// is derived from a hard-coded prefix and the site name. Neither is
35/// configured here.
36pub struct Site {
37  pub backend: BackendTechnology,
38  pub socks5_proxy: Option<String>,
39  pub shasta_base_url: String,
40  pub k8s: Option<K8sDetails>,
41  pub root_ca_cert_file: String,
42}
43
44/// Top-level configuration for the `manta-cli` binary. Persisted as TOML
45/// under `~/.config/manta/cli.toml`. Carries only the fields the CLI uses
46/// — every backend connection detail (per-site URLs, TLS certs, vault,
47/// k8s, per-site SOCKS proxies) lives in `ServerConfiguration`. The CLI
48/// only knows about the *one* manta-server it talks to.
49#[derive(Serialize, Deserialize, Debug)]
50pub struct CliConfiguration {
51  pub log: String,
52  pub audit_file: String,
53  /// Active site name, sent as the `X-Manta-Site` header on every
54  /// request to manta-server. Overridable per-invocation with `--site`.
55  /// The server validates that the name matches one of its configured
56  /// sites; the CLI does no local validation.
57  pub site: String,
58  pub parent_hsm_group: String,
59  /// URL of the manta HTTP server this CLI talks to. Required — the CLI
60  /// no longer calls CSM/OCHAMI backends directly; every operation
61  /// (including auth) is forwarded through `manta-server`.
62  pub manta_server_url: String,
63  /// Optional SOCKS5 proxy used to reach `manta_server_url`. Per-site
64  /// proxying for backend traffic is the server's concern.
65  pub socks5_proxy: Option<String>,
66  pub auditor: Option<Auditor>,
67}
68
69/// Server-only settings — TLS, listen address, console behaviour. Lives
70/// under `[server]` in `server.toml`.
71#[derive(Serialize, Deserialize, Debug)]
72pub struct ServerSettings {
73  /// TCP listen address (e.g. "0.0.0.0").
74  pub listen_address: String,
75  /// TCP port for the TLS server.
76  pub port: u16,
77  /// Path to the TLS certificate (PEM).
78  pub cert: Option<String>,
79  /// Path to the TLS private key (PEM).
80  pub key: Option<String>,
81  /// How long a node-console WebSocket stays open without activity
82  /// before the server tears it down.
83  pub console_inactivity_timeout_secs: u64,
84  /// Per-source-IP rate limit for the `/api/v1/auth/*` endpoints,
85  /// in requests per minute. `None` disables in-process rate limiting
86  /// (operators are then expected to enforce it at the reverse proxy).
87  pub auth_rate_limit_per_minute: Option<u32>,
88}
89
90/// Top-level configuration for the `manta-server` binary. Persisted as
91/// TOML under `~/.config/manta/server.toml`. Has no notion of an "active"
92/// site — the server hosts every configured site simultaneously and
93/// clients select per-request via the `X-Manta-Site` header.
94#[derive(Serialize, Deserialize, Debug)]
95pub struct ServerConfiguration {
96  pub log: String,
97  pub audit_file: String,
98  pub server: ServerSettings,
99  pub sites: HashMap<String, Site>,
100  pub auditor: Option<Auditor>,
101}
102
103#[cfg(test)]
104mod tests {
105  use super::*;
106
107  #[test]
108  fn site_deserialize_missing_backend_fails() {
109    let bad_toml = r#"
110      shasta_base_url = "https://api.example.com"
111      root_ca_cert_file = "cert.pem"
112      # missing backend
113    "#;
114    let result = toml::from_str::<Site>(bad_toml);
115    assert!(result.is_err());
116  }
117
118  #[test]
119  fn backend_technology_as_str() {
120    assert_eq!(BackendTechnology::Csm.as_str(), "csm");
121    assert_eq!(BackendTechnology::Ochami.as_str(), "ochami");
122  }
123
124  #[test]
125  fn backend_technology_roundtrip_toml() {
126    // Verify TOML serializes as lowercase "csm" / "ochami"
127    #[derive(Serialize, Deserialize)]
128    struct Wrapper {
129      backend: BackendTechnology,
130    }
131    let w = Wrapper {
132      backend: BackendTechnology::Csm,
133    };
134    let s = toml::to_string(&w).unwrap();
135    assert!(s.contains("\"csm\"") || s.contains("csm"));
136    let parsed: Wrapper = toml::from_str(&s).unwrap();
137    assert_eq!(parsed.backend, BackendTechnology::Csm);
138  }
139
140  fn make_minimal_site() -> Site {
141    Site {
142      backend: BackendTechnology::Csm,
143      socks5_proxy: None,
144      shasta_base_url: "https://api.example.com".to_string(),
145      k8s: None,
146      root_ca_cert_file: "cert.pem".to_string(),
147    }
148  }
149
150  #[test]
151  fn cli_configuration_roundtrip_toml_minimal() {
152    let cfg = CliConfiguration {
153      log: "info".to_string(),
154      audit_file: "/tmp/cli-audit.log".to_string(),
155      site: "alps".to_string(),
156      parent_hsm_group: "nodes_free".to_string(),
157      manta_server_url: "https://manta-server.cscs.ch:8443".to_string(),
158      socks5_proxy: Some("socks5h://127.0.0.1:1080".to_string()),
159      auditor: None,
160    };
161    let toml_str = toml::to_string(&cfg).unwrap();
162    let parsed: CliConfiguration = toml::from_str(&toml_str).unwrap();
163    assert_eq!(parsed.site, "alps");
164    assert_eq!(parsed.parent_hsm_group, "nodes_free");
165    assert_eq!(parsed.manta_server_url, "https://manta-server.cscs.ch:8443");
166    assert_eq!(
167      parsed.socks5_proxy.as_deref(),
168      Some("socks5h://127.0.0.1:1080")
169    );
170  }
171
172  #[test]
173  fn cli_configuration_socks5_proxy_optional() {
174    let toml_str = r#"
175      log = "info"
176      audit_file = "/tmp/cli-audit.log"
177      site = "alps"
178      parent_hsm_group = ""
179      manta_server_url = "https://manta-server.cscs.ch:8443"
180    "#;
181    let parsed: CliConfiguration = toml::from_str(toml_str).unwrap();
182    assert!(parsed.socks5_proxy.is_none());
183  }
184
185  #[test]
186  fn cli_configuration_missing_manta_server_url_fails() {
187    let bad_toml = r#"
188      log = "info"
189      audit_file = "/tmp/cli-audit.log"
190      site = "alps"
191      parent_hsm_group = ""
192      # missing manta_server_url
193    "#;
194    let result = toml::from_str::<CliConfiguration>(bad_toml);
195    assert!(result.is_err());
196  }
197
198  #[test]
199  fn server_configuration_roundtrip_toml_minimal() {
200    let mut sites = HashMap::new();
201    sites.insert("alps".to_string(), make_minimal_site());
202    let cfg = ServerConfiguration {
203      log: "info".to_string(),
204      audit_file: "/var/log/manta/server-audit.log".to_string(),
205      server: ServerSettings {
206        listen_address: "0.0.0.0".to_string(),
207        port: 8443,
208        cert: Some("/etc/manta/tls/server.crt".to_string()),
209        key: Some("/etc/manta/tls/server.key".to_string()),
210        console_inactivity_timeout_secs: 1800,
211        auth_rate_limit_per_minute: Some(60),
212      },
213      sites,
214      auditor: None,
215    };
216    let toml_str = toml::to_string(&cfg).unwrap();
217    let parsed: ServerConfiguration = toml::from_str(&toml_str).unwrap();
218    assert_eq!(parsed.server.port, 8443);
219    assert_eq!(parsed.server.listen_address, "0.0.0.0");
220    assert_eq!(parsed.server.console_inactivity_timeout_secs, 1800);
221    assert_eq!(
222      parsed.server.cert.as_deref(),
223      Some("/etc/manta/tls/server.crt")
224    );
225  }
226
227  #[test]
228  fn server_configuration_deserialize_missing_server_section_fails() {
229    let bad_toml = r#"
230      log = "info"
231      audit_file = "/tmp/server.log"
232      [sites]
233    "#;
234    let result = toml::from_str::<ServerConfiguration>(bad_toml);
235    assert!(result.is_err());
236  }
237
238  #[test]
239  fn server_settings_optional_tls_paths() {
240    // TLS cert/key are optional in the schema — flags can supply them
241    // at runtime when the config omits them.
242    let toml_str = r#"
243      listen_address = "0.0.0.0"
244      port = 8443
245      console_inactivity_timeout_secs = 1800
246    "#;
247    let parsed: ServerSettings = toml::from_str(toml_str).unwrap();
248    assert!(parsed.cert.is_none());
249    assert!(parsed.key.is_none());
250  }
251}