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