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_TLS_CSR_SUBJECT_ALT_NAME",
206 help = "The optional list of subject alt names (SAN) for the CSR",
207 long_help = "The optional list of subject alt names (SAN) for the CSR
208
209If omitted, it is set to the same value as common_name.
210If it is set to an empty list, no SAN Extension is added.
211All SANs are considered DNS names, unless they start with \"IP:\" to signal IP names."
212 )]
213 pub subject_alt_names: Option<Vec<String>>,
214
215 #[arg(
216 env = "NETHSM_FORCE",
217 help = "Write to output file even if it exists already",
218 long,
219 short
220 )]
221 pub force: bool,
222
223 #[arg(
224 env = "NETHSM_CONFIG_TLS_CSR_OUTPUT_FILE",
225 help = "The optional path to a specific file that the certificate is written to",
226 long,
227 short
228 )]
229 pub output: Option<PathBuf>,
230}
231
232#[derive(Debug, Parser)]
233#[command(
234 about = "Get the public key for the TLS connection",
235 long_about = ex_format!("Get the public key for the TLS connection
236
237The X.509 public key certificate is returned in Privacy-enhanced Electronic Mail (PEM) format.
238Unless a specific output file is chosen, the certificate is returned on stdout.
239
240Requires authentication of a system-wide user in the \"{Administrator}\" role."
241 )
242)]
243pub struct GetTlsPublicKeyCommand {
244 #[arg(
245 env = "NETHSM_FORCE",
246 help = "Write to output file even if it exists already",
247 long,
248 short
249 )]
250 pub force: bool,
251
252 #[arg(
253 env = "NETHSM_CONFIG_TLS_PUBKEY_OUTPUT_FILE",
254 help = "The optional path to a specific file that the certificate is written to",
255 long,
256 short
257 )]
258 pub output: Option<PathBuf>,
259}
260
261#[derive(Debug, Subcommand)]
263#[command(about = "Set a configuration item for a device")]
264pub enum ConfigSetCommand {
265 BackupPassphrase(SetBackupPassphraseCommand),
267 BootMode(SetBootModeCommand),
269 Logging(SetLoggingCommand),
271 Network(SetNetworkCommand),
273 Time(SetTimeCommand),
275 TlsCertificate(SetTlsCertificateCommand),
277 TlsGenerate(SetTlsGenerateCommand),
279 UnlockPassphrase(SetUnlockPassphraseCommand),
281}
282
283#[derive(Debug, Parser)]
284#[command(
285 about = "Set the backup passphrase",
286 long_about = ex_format!("Set the backup passphrase
287
288The initial backup passphrase is the empty string.
289
290The new passphrase must be >= 10 and <= 200 characters.
291
292By default the passphrases are prompted for interactively, but they can each be provided using a dedicated passphrase file instead.
293
294Requires authentication of a system-wide user in the \"{Administrator}\" role."
295 )
296)]
297pub struct SetBackupPassphraseCommand {
298 #[arg(
299 env = "NETHSM_NEW_PASSPHRASE_FILE",
300 help = "The path to a file containing the new passphrase",
301 long_help = "The path to a file containing the new passphrase
302
303The passphrase must be >= 10 and <= 200 characters long.",
304 long,
305 short
306 )]
307 pub new_passphrase_file: Option<PassphraseFile>,
308
309 #[arg(
310 env = "NETHSM_OLD_PASSPHRASE_FILE",
311 help = "The path to a file containing the old passphrase",
312 long_help = "The path to a file containing the old passphrase
313
314The passphrase must be >= 10 and <= 200 characters long.",
315 long,
316 short
317 )]
318 pub old_passphrase_file: Option<PassphraseFile>,
319}
320
321#[derive(Debug, Parser)]
322#[command(
323 about = "Set the unattended boot mode",
324 long_about = ex_format!("Set the unattended boot mode
325
326Sets whether the device boots into state \"{SystemState::Locked}\" (using boot mode \"{BootMode::Attended}\") or \"{SystemState::Operational}\" (using boot mode \"{BootMode::Unattended}\").
327
328Requires authentication of a system-wide user in the \"{Administrator}\" role."
329 ),
330)]
331pub struct SetBootModeCommand {
332 #[arg(
333 env = "NETHSM_BOOT_MODE",
334 help = "The boot mode to use",
335 long_help = format!("The boot mode to use
336
337One of {:?} (no default).",
338 BootMode::iter().map(Into::into).collect::<Vec<&'static str>>()
339 )
340 )]
341 pub boot_mode: BootMode,
342}
343
344#[derive(Debug, Parser)]
345#[command(
346 about = "Set the logging configuration",
347 long_about = ex_format!("Set the logging configuration
348
349Provide IP address and port of a host to send syslog to at a specified log level.
350
351Requires authentication of a system-wide user in the \"{Administrator}\" role."
352 )
353)]
354pub struct SetLoggingCommand {
355 #[arg(
356 env = "NETHSM_LOGGING_IP_ADDRESS",
357 help = "The IPv4 address of the host to send syslog to"
358 )]
359 pub ip_address: Ipv4Addr,
360
361 #[arg(
362 env = "NETHSM_LOGGING_PORT",
363 help = "The port of the host to send syslog to"
364 )]
365 pub port: u32,
366
367 #[arg(
368 env = "NETHSM_LOGGING_LOG_LEVEL",
369 help = "The log level at which to log",
370 long_help = format!("The log level at which to log
371
372One of {:?} (defaults to \"{:?}\").",
373 LogLevel::iter().map(Into::into).collect::<Vec<&'static str>>(),
374 LogLevel::default(),
375 )
376 )]
377 pub log_level: Option<LogLevel>,
378}
379
380#[derive(Debug, Parser)]
381#[command(
382 about = "Set the network configuration",
383 long_about = ex_format!("Set the network configuration
384
385Provide IPv4 address, netmask and Ipv4 gateway address for the device to use.
386
387Requires authentication of a system-wide user in the \"{Administrator}\" role."
388 )
389)]
390pub struct SetNetworkCommand {
391 #[arg(
392 env = "NETHSM_NETWORK_IP_ADDRESS",
393 help = "The IPv4 address the device is to use"
394 )]
395 pub ip_address: Ipv4Addr,
396
397 #[arg(
398 env = "NETHSM_NETWORK_NETMASK",
399 help = "The IPv4 netmask the device is to use"
400 )]
401 pub netmask: String,
402
403 #[arg(
404 env = "NETHSM_NETWORK_GATEWAY",
405 help = "The IPv4 gateway the device is to use"
406 )]
407 pub gateway: Ipv4Addr,
408}
409
410#[derive(Debug, Parser)]
411#[command(
412 about = "Set the time",
413 long_about = ex_format!("Set the time
414
415The time must be provided as ISO 8601 formatted UTC timestamp.
416If no timestamp is provided, the caller's current system time is used to construct a UTC timestamp.
417
418Requires authentication of a system-wide user in the \"{Administrator}\" role."
419 )
420)]
421pub struct SetTimeCommand {
422 #[arg(
423 env = "NETHSM_SYSTEM_TIME",
424 help = "An optional ISO 8601 formatted UTC timestamp",
425 long_help = "An optional ISO 8601 formatted UTC timestamp
426
427If no timestamp is provided, the caller's current system time is used."
428 )]
429 pub system_time: Option<DateTime<Utc>>,
430}
431
432#[derive(Debug, Parser)]
433#[command(
434 about = "Set a new TLS certificate",
435 long_about = ex_format!("Set a new TLS certificate
436
437The X.509 certificate must be provided in Privacy-enhanced Electronic Mail (PEM) format.
438
439The certificate is only accepted if it is created using a Certificate Signing Request (CSR) generated by the target device.
440
441Requires authentication of a system-wide user in the \"{Administrator}\" role."
442 )
443)]
444pub struct SetTlsCertificateCommand {
445 #[arg(
446 env = "NETHSM_TLS_CERT",
447 help = "A new TLS certificate file",
448 long_help = "A new TLS certificate file
449
450The X.509 certificate must be provided in Privacy-enhanced Electronic Mail (PEM) format."
451 )]
452 pub tls_cert: PathBuf,
453}
454
455#[derive(Debug, Parser)]
456#[command(
457 about = "Generate a new TLS certificate",
458 long_about = ex_format!("Generate a new TLS certificate
459
460The current TLS certificate is replaced by the newly generated one.
461Optionally, the type of key and its length can be specified.
462
463Requires authentication of a system-wide user in the \"{Administrator}\" role."
464 )
465)]
466pub struct SetTlsGenerateCommand {
467 #[arg(
468 env = "NETHSM_TLS_KEY_TYPE",
469 help = "The optional key type of the TLS certificate to generate",
470 long_help = format!("The optional key type of the TLS certificate to generate
471
472One of {:?} (defaults to \"{}\").",
473 TlsKeyType::iter().map(Into::into).collect::<Vec<&'static str>>(),
474 TlsKeyType::default(),
475 ),
476 )]
477 pub tls_key_type: Option<TlsKeyType>,
478
479 #[arg(
480 env = "NETHSM_TLS_KEY_LENGTH",
481 help = "The bit length of the TLS key to generate",
482 long_help = ex_format!("The optional bit length of the TLS key to generate
483
484The bit length must be compatible with the chosen key type.
485
486Requires authentication of a user in the \"{Administrator}\" role.")
487 )]
488 pub tls_key_length: Option<u32>,
489}
490
491#[derive(Debug, Parser)]
492#[command(
493 about = "Set the unlock passphrase",
494 long_about = ex_format!("Set the unlock passphrase
495
496The initial unlock passphrase is set during provisioning.
497
498The new passphrase must be >= 10 and <= 200 characters.
499
500By default the passphrases are prompted for interactively, but they can each be provided using a dedicated passphrase file instead.
501
502Requires authentication of a system-wide user in the \"{Administrator}\" role."
503 )
504)]
505pub struct SetUnlockPassphraseCommand {
506 #[arg(
507 env = "NETHSM_NEW_PASSPHRASE_FILE",
508 help = "The path to a file containing the new passphrase",
509 long_help = "The path to a file containing the new passphrase
510
511The passphrase must be >= 10 and <= 200 characters long.",
512 long,
513 short
514 )]
515 pub new_passphrase_file: Option<PassphraseFile>,
516
517 #[arg(
518 env = "NETHSM_OLD_PASSPHRASE_FILE",
519 help = "The path to a file containing the old passphrase",
520 long_help = "The path to a file containing the old passphrase
521
522The passphrase must be >= 10 and <= 200 characters long.",
523 long,
524 short
525 )]
526 pub old_passphrase_file: Option<PassphraseFile>,
527}