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