1use std::{net::Ipv4Addr, path::PathBuf};
2
3use chrono::{DateTime, Utc};
4use clap::{Parser, Subcommand};
5use expression_format::ex_format;
6use nethsm::{BootMode, LogLevel, SystemState, TlsKeyType, UserRole::Administrator};
7use strum::IntoEnumIterator;
8
9use crate::passphrase_file::PassphraseFile;
10
11#[derive(Debug, Subcommand)]
12#[command(
13 about = "Manage the configuration of a device",
14 long_about = "Manage the configuration of a device
15
16Allows adding, removing and listing of configuration items"
17)]
18pub enum ConfigCommand {
19 #[command(subcommand)]
20 Get(ConfigGetCommand),
21
22 #[command(subcommand)]
23 Set(ConfigSetCommand),
24}
25
26#[derive(Debug, Subcommand)]
27#[command(about = "Get a configuration item for a device")]
28pub enum ConfigGetCommand {
29 BootMode(GetBootModeCommand),
30 Logging(GetLoggingCommand),
31 Network(GetNetworkCommand),
32 Time(GetTimeCommand),
33 TlsCertificate(GetTlsCertificateCommand),
34 TlsCsr(GetTlsCsrCommand),
35 TlsPublicKey(GetTlsPublicKeyCommand),
36}
37
38#[derive(Debug, Parser)]
39#[command(
40 about = "Get the unattended boot configuration",
41 long_about = ex_format!("Get the unattended boot configuration
42
43* \"{BootMode::Attended}\" if the device needs to be unlocked during boot
44* \"{BootMode::Unattended}\" if the device does not need to be unlocked during boot
45
46Requires authentication of a system-wide user in the \"{Administrator}\" role."
47 )
48)]
49pub struct GetBootModeCommand {}
50
51#[derive(Debug, Parser)]
52#[command(
53 about = "Get the logging configuration",
54 long_about = ex_format!("Get the logging configuration
55
56Shows IP address and port number of the host the target device logs to at a given log level.
57
58Requires authentication of a system-wide user in the \"{Administrator}\" role."
59 )
60)]
61pub struct GetLoggingCommand {}
62
63#[derive(Debug, Parser)]
64#[command(
65 about = "Get the network configuration",
66 long_about = ex_format!("Get the network configuration
67
68Shows IP address, netmask and gateway of the target device.
69
70Requires authentication of a system-wide user in the \"{Administrator}\" role."
71 )
72)]
73pub struct GetNetworkCommand {}
74
75#[derive(Debug, Parser)]
76#[command(
77 about = "Get the time",
78 long_about = ex_format!("Get the time
79
80Returns the current time as ISO 8601 formatted timestamp.
81
82Requires authentication of a system-wide user in the \"{Administrator}\" role."
83 )
84)]
85pub struct GetTimeCommand {}
86
87#[derive(Debug, Parser)]
88#[command(
89 about = "Get the certificate for the TLS connection",
90 long_about = ex_format!("Get the certificate for the TLS connection
91
92The X.509 certificate is returned in Privacy-enhanced Electronic Mail (PEM) format.
93Unless a specific output file is chosen, the certificate is returned on stdout.
94
95Requires authentication of a system-wide user in the \"{Administrator}\" role."
96 )
97)]
98pub struct GetTlsCertificateCommand {
99 #[arg(
100 env = "NETHSM_FORCE",
101 help = "Write to output file even if it exists already",
102 long,
103 short
104 )]
105 pub force: bool,
106
107 #[arg(
108 env = "NETHSM_CONFIG_TLS_CERT_OUTPUT_FILE",
109 help = "The optional path to a specific file that the certificate is written to",
110 long,
111 short
112 )]
113 pub output: Option<PathBuf>,
114}
115
116#[derive(Debug, Parser)]
117#[command(
118 about = "Get a Certificate Signing Request for the TLS certificate",
119 long_about = ex_format!("Get a Certificate Signing Request for the TLS certificate
120
121The PKCS#10 Certificate Signing Request (CSR) is returned in Privacy-enhanced Electronic Mail (PEM) format.
122Unless a specific output file is chosen, the certificate is returned on stdout.
123
124At a minimum, the \"Common Name\" (CN) attribute for the CSR has to be provided.
125
126Requires authentication of a system-wide user in the \"{Administrator}\" role."
127 )
128)]
129pub struct GetTlsCsrCommand {
130 #[arg(
131 env = "NETHSM_TLS_CSR_COMMON_NAME",
132 help = "The mandatory \"Common Name\" (CN) attribute for the CSR",
133 long_help = "The mandatory \"Common Name\" (CN) attribute for the CSR
134
135A fully qualified domain name (FQDN) that should be secured using the CSR."
136 )]
137 pub common_name: String,
138
139 #[arg(
140 env = "NETHSM_TLS_CSR_ORG_NAME",
141 help = "The optional \"Organization Name\" (O) attribute for the CSR",
142 long_help = "The optional \"Organization Name\" (O) attribute for the CSR
143
144Usually the legal name of a company or entity and should include any suffixes such as Ltd., Inc., or Corp."
145 )]
146 pub org_name: Option<String>,
147
148 #[arg(
149 env = "NETHSM_TLS_CSR_ORG_UNIT",
150 help = "The optional \"Organizational Unit\" (OU) attribute for the CSR",
151 long_help = "The optional \"Organizational Unit\" (OU) attribute for the CSR
152
153Internal organization department/division name."
154 )]
155 pub org_unit: Option<String>,
156
157 #[arg(
158 env = "NETHSM_TLS_CSR_LOCALITY",
159 help = "The optional \"Locality\" (L) attribute for the CSR",
160 long_help = "The optional \"Locality\" (L) attribute for the CSR
161
162Name of town, city, village, etc."
163 )]
164 pub locality: Option<String>,
165
166 #[arg(
167 env = "NETHSM_TLS_CSR_STATE",
168 help = "The optional \"State\" (ST) attribute for the CSR",
169 long_help = "The optional \"State\" (ST) attribute for the CSR
170
171Province, region, county or state."
172 )]
173 pub state: Option<String>,
174
175 #[arg(
176 env = "NETHSM_TLS_CSR_COUNTRY",
177 help = "The optional \"Country\" (C) attribute for the CSR",
178 long_help = "The optional \"Country\" (C) attribute for the CSR
179
180The two-letter ISO code for the country where the \"Organization\" (O) is located."
181 )]
182 pub country: Option<String>,
183
184 #[arg(
185 env = "NETHSM_TLS_CSR_EMAIL",
186 help = "The optional \"Email Address\" (EMAIL) attribute for the CSR",
187 long_help = "The optional \"Email Address\" (EMAIL) attribute for the CSR
188
189The organization contact, usually of the certificate administrator or IT department."
190 )]
191 pub email: Option<String>,
192
193 #[arg(
194 env = "NETHSM_FORCE",
195 help = "Write to output file even if it exists already",
196 long,
197 short
198 )]
199 pub force: bool,
200
201 #[arg(
202 env = "NETHSM_CONFIG_TLS_CSR_OUTPUT_FILE",
203 help = "The optional path to a specific file that the certificate is written to",
204 long,
205 short
206 )]
207 pub output: Option<PathBuf>,
208}
209
210#[derive(Debug, Parser)]
211#[command(
212 about = "Get the public key for the TLS connection",
213 long_about = ex_format!("Get the public key for the TLS connection
214
215The X.509 public key certificate is returned in Privacy-enhanced Electronic Mail (PEM) format.
216Unless a specific output file is chosen, the certificate is returned on stdout.
217
218Requires authentication of a system-wide user in the \"{Administrator}\" role."
219 )
220)]
221pub struct GetTlsPublicKeyCommand {
222 #[arg(
223 env = "NETHSM_FORCE",
224 help = "Write to output file even if it exists already",
225 long,
226 short
227 )]
228 pub force: bool,
229
230 #[arg(
231 env = "NETHSM_CONFIG_TLS_PUBKEY_OUTPUT_FILE",
232 help = "The optional path to a specific file that the certificate is written to",
233 long,
234 short
235 )]
236 pub output: Option<PathBuf>,
237}
238
239#[derive(Debug, Subcommand)]
240#[command(about = "Set a configuration item for a device")]
241pub enum ConfigSetCommand {
242 BackupPassphrase(SetBackupPassphraseCommand),
243 BootMode(SetBootModeCommand),
244 Logging(SetLoggingCommand),
245 Network(SetNetworkCommand),
246 Time(SetTimeCommand),
247 TlsCertificate(SetTlsCertificateCommand),
248 TlsGenerate(SetTlsGenerateCommand),
249 UnlockPassphrase(SetUnlockPassphraseCommand),
250}
251
252#[derive(Debug, Parser)]
253#[command(
254 about = "Set the backup passphrase",
255 long_about = ex_format!("Set the backup passphrase
256
257The initial backup passphrase is the empty string.
258
259The new passphrase must be >= 10 and <= 200 characters.
260
261By default the passphrases are prompted for interactively, but they can each be provided using a dedicated passphrase file instead.
262
263Requires authentication of a system-wide user in the \"{Administrator}\" role."
264 )
265)]
266pub struct SetBackupPassphraseCommand {
267 #[arg(
268 env = "NETHSM_NEW_PASSPHRASE_FILE",
269 help = "The path to a file containing the new passphrase",
270 long_help = "The path to a file containing the new passphrase
271
272The passphrase must be >= 10 and <= 200 characters long.",
273 long,
274 short
275 )]
276 pub new_passphrase_file: Option<PassphraseFile>,
277
278 #[arg(
279 env = "NETHSM_OLD_PASSPHRASE_FILE",
280 help = "The path to a file containing the old passphrase",
281 long_help = "The path to a file containing the old passphrase
282
283The passphrase must be >= 10 and <= 200 characters long.",
284 long,
285 short
286 )]
287 pub old_passphrase_file: Option<PassphraseFile>,
288}
289
290#[derive(Debug, Parser)]
291#[command(
292 about = "Set the unattended boot mode",
293 long_about = ex_format!("Set the unattended boot mode
294
295Sets whether the device boots into state \"{SystemState::Locked}\" (using boot mode \"{BootMode::Attended}\") or \"{SystemState::Operational}\" (using boot mode \"{BootMode::Unattended}\").
296
297Requires authentication of a system-wide user in the \"{Administrator}\" role."
298 ),
299)]
300pub struct SetBootModeCommand {
301 #[arg(
302 env = "NETHSM_BOOT_MODE",
303 help = "The boot mode to use",
304 long_help = format!("The boot mode to use
305
306One of {:?} (no default).",
307 BootMode::iter().map(Into::into).collect::<Vec<&'static str>>()
308 )
309 )]
310 pub boot_mode: BootMode,
311}
312
313#[derive(Debug, Parser)]
314#[command(
315 about = "Set the logging configuration",
316 long_about = ex_format!("Set the logging configuration
317
318Provide IP address and port of a host to send syslog to at a specified log level.
319
320Requires authentication of a system-wide user in the \"{Administrator}\" role."
321 )
322)]
323pub struct SetLoggingCommand {
324 #[arg(
325 env = "NETHSM_LOGGING_IP_ADDRESS",
326 help = "The IPv4 address of the host to send syslog to"
327 )]
328 pub ip_address: Ipv4Addr,
329
330 #[arg(
331 env = "NETHSM_LOGGING_PORT",
332 help = "The port of the host to send syslog to"
333 )]
334 pub port: u32,
335
336 #[arg(
337 env = "NETHSM_LOGGING_LOG_LEVEL",
338 help = "The log level at which to log",
339 long_help = format!("The log level at which to log
340
341One of {:?} (defaults to \"{:?}\").",
342 LogLevel::iter().map(Into::into).collect::<Vec<&'static str>>(),
343 LogLevel::default(),
344 )
345 )]
346 pub log_level: Option<LogLevel>,
347}
348
349#[derive(Debug, Parser)]
350#[command(
351 about = "Set the network configuration",
352 long_about = ex_format!("Set the network configuration
353
354Provide IPv4 address, netmask and Ipv4 gateway address for the device to use.
355
356Requires authentication of a system-wide user in the \"{Administrator}\" role."
357 )
358)]
359pub struct SetNetworkCommand {
360 #[arg(
361 env = "NETHSM_NETWORK_IP_ADDRESS",
362 help = "The IPv4 address the device is to use"
363 )]
364 pub ip_address: Ipv4Addr,
365
366 #[arg(
367 env = "NETHSM_NETWORK_NETMASK",
368 help = "The IPv4 netmask the device is to use"
369 )]
370 pub netmask: String,
371
372 #[arg(
373 env = "NETHSM_NETWORK_GATEWAY",
374 help = "The IPv4 gateway the device is to use"
375 )]
376 pub gateway: Ipv4Addr,
377}
378
379#[derive(Debug, Parser)]
380#[command(
381 about = "Set the time",
382 long_about = ex_format!("Set the time
383
384The time must be provided as ISO 8601 formatted UTC timestamp.
385If no timestamp is provided, the caller's current system time is used to construct a UTC timestamp.
386
387Requires authentication of a system-wide user in the \"{Administrator}\" role."
388 )
389)]
390pub struct SetTimeCommand {
391 #[arg(
392 env = "NETHSM_SYSTEM_TIME",
393 help = "An optional ISO 8601 formatted UTC timestamp",
394 long_help = "An optional ISO 8601 formatted UTC timestamp
395
396If no timestamp is provided, the caller's current system time is used."
397 )]
398 pub system_time: Option<DateTime<Utc>>,
399}
400
401#[derive(Debug, Parser)]
402#[command(
403 about = "Set a new TLS certificate",
404 long_about = ex_format!("Set a new TLS certificate
405
406The X.509 certificate must be provided in Privacy-enhanced Electronic Mail (PEM) format.
407
408The certificate is only accepted if it is created using a Certificate Signing Request (CSR) generated by the target device.
409
410Requires authentication of a system-wide user in the \"{Administrator}\" role."
411 )
412)]
413pub struct SetTlsCertificateCommand {
414 #[arg(
415 env = "NETHSM_TLS_CERT",
416 help = "A new TLS certificate file",
417 long_help = "A new TLS certificate file
418
419The X.509 certificate must be provided in Privacy-enhanced Electronic Mail (PEM) format."
420 )]
421 pub tls_cert: PathBuf,
422}
423
424#[derive(Debug, Parser)]
425#[command(
426 about = "Generate a new TLS certificate",
427 long_about = ex_format!("Generate a new TLS certificate
428
429The current TLS certificate is replaced by the newly generated one.
430Optionally, the type of key and its length can be specified.
431
432Requires authentication of a system-wide user in the \"{Administrator}\" role."
433 )
434)]
435pub struct SetTlsGenerateCommand {
436 #[arg(
437 env = "NETHSM_TLS_KEY_TYPE",
438 help = "The optional key type of the TLS certificate to generate",
439 long_help = format!("The optional key type of the TLS certificate to generate
440
441One of {:?} (defaults to \"{}\").",
442 TlsKeyType::iter().map(Into::into).collect::<Vec<&'static str>>(),
443 TlsKeyType::default(),
444 ),
445 )]
446 pub tls_key_type: Option<TlsKeyType>,
447
448 #[arg(
449 env = "NETHSM_TLS_KEY_LENGTH",
450 help = "The bit length of the TLS key to generate",
451 long_help = ex_format!("The optional bit length of the TLS key to generate
452
453The bit length must be compatible with the chosen key type.
454
455Requires authentication of a user in the \"{Administrator}\" role.")
456 )]
457 pub tls_key_length: Option<u32>,
458}
459
460#[derive(Debug, Parser)]
461#[command(
462 about = "Set the unlock passphrase",
463 long_about = ex_format!("Set the unlock passphrase
464
465The initial unlock passphrase is set during provisioning.
466
467The new passphrase must be >= 10 and <= 200 characters.
468
469By default the passphrases are prompted for interactively, but they can each be provided using a dedicated passphrase file instead.
470
471Requires authentication of a system-wide user in the \"{Administrator}\" role."
472 )
473)]
474pub struct SetUnlockPassphraseCommand {
475 #[arg(
476 env = "NETHSM_NEW_PASSPHRASE_FILE",
477 help = "The path to a file containing the new passphrase",
478 long_help = "The path to a file containing the new passphrase
479
480The passphrase must be >= 10 and <= 200 characters long.",
481 long,
482 short
483 )]
484 pub new_passphrase_file: Option<PassphraseFile>,
485
486 #[arg(
487 env = "NETHSM_OLD_PASSPHRASE_FILE",
488 help = "The path to a file containing the old passphrase",
489 long_help = "The path to a file containing the old passphrase
490
491The passphrase must be >= 10 and <= 200 characters long.",
492 long,
493 short
494 )]
495 pub old_passphrase_file: Option<PassphraseFile>,
496}