1 #[cfg(feature = "multi-user")] 2 use std::collections::HashMap; 3 4 use std::{ 5 convert::TryFrom, 6 path::PathBuf, 7 str::{from_utf8_unchecked, FromStr}, 8 {env, fmt}, 9 }; 10 11 use bytes::Bytes; 12 use clap::{App, Arg, ArgMatches, SubCommand}; 13 14 use rpki::{ 15 repository::{ 16 aspa::{DuplicateProviderAs, ProviderAs}, 17 crypto::KeyIdentifier, 18 x509::Time, 19 }, 20 uri, 21 }; 22 23 use crate::{ 24 cli::report::{ReportError, ReportFormat}, 25 commons::{ 26 api::{ 27 AddChildRequest, AspaCustomer, AspaDefinition, AspaDefinitionFormatError, AspaProvidersUpdate, 28 AuthorizationFmtError, CertAuthInit, ChildHandle, Handle, ParentCaContact, ParentCaReq, ParentHandle, 29 PublicationServerUris, PublisherHandle, RepositoryContact, ResourceSet, ResourceSetError, RoaDefinition, 30 RoaDefinitionUpdates, RtaName, Token, UpdateChildRequest, 31 }, 32 crypto::{IdCert, SignSupport}, 33 error::KrillIoError, 34 remote::rfc8183, 35 util::file, 36 }, 37 constants::*, 38 daemon::ca::{ResourceTaggedAttestation, RtaContentRequest, RtaPrepareRequest}, 39 }; 40 41 struct GeneralArgs { 42 server: uri::Https, 43 token: Token, 44 format: ReportFormat, 45 api: bool, 46 } 47 48 impl GeneralArgs { from_matches(matches: &ArgMatches) -> Result<Self, Error>49 fn from_matches(matches: &ArgMatches) -> Result<Self, Error> { 50 let server = { 51 let mut server = match env::var(KRILL_CLI_SERVER_ENV) { 52 Ok(server_str) => Some(uri::Https::try_from(server_str)?), 53 Err(_) => None, 54 }; 55 56 if let Some(server_str) = matches.value_of(KRILL_CLI_SERVER_ARG) { 57 server = Some(uri::Https::from_str(server_str)?); 58 } 59 60 server.unwrap_or_else(|| uri::Https::from_str(KRILL_CLI_SERVER_DFLT).unwrap()) 61 }; 62 63 let token = { 64 let mut token = env::var(KRILL_CLI_TOKEN_ENV).ok().map(Token::from); 65 66 if let Some(token_str) = matches.value_of(KRILL_CLI_ADMIN_TOKEN_ARG) { 67 token = Some(Token::from(token_str)); 68 } 69 70 token.ok_or_else(|| Error::missing_arg_with_env(KRILL_CLI_ADMIN_TOKEN_ARG, KRILL_CLI_TOKEN_ENV))? 71 }; 72 73 let format = { 74 let mut format = match env::var(KRILL_CLI_FORMAT_ENV) { 75 Ok(fmt_str) => Some(ReportFormat::from_str(&fmt_str)?), 76 Err(_) => None, 77 }; 78 79 if let Some(fmt_str) = matches.value_of(KRILL_CLI_FORMAT_ARG) { 80 format = Some(ReportFormat::from_str(fmt_str)?); 81 } 82 83 format.unwrap_or(ReportFormat::Text) 84 }; 85 86 let api = env::var(KRILL_CLI_API_ENV).is_ok() || matches.is_present(KRILL_CLI_API_ARG); 87 88 Ok(GeneralArgs { 89 server, 90 token, 91 format, 92 api, 93 }) 94 } 95 } 96 97 impl Default for GeneralArgs { default() -> Self98 fn default() -> Self { 99 GeneralArgs { 100 server: uri::Https::from_str(KRILL_CLI_SERVER_DFLT).unwrap(), 101 token: Token::from(""), 102 format: ReportFormat::Text, 103 api: false, 104 } 105 } 106 } 107 108 /// This type holds all the necessary data to connect to a Krill daemon, and 109 /// authenticate, and perform a specific action. 110 pub struct Options { 111 pub server: uri::Https, 112 pub token: Token, 113 pub format: ReportFormat, 114 pub api: bool, 115 pub command: Command, 116 } 117 118 impl Options { make(general: GeneralArgs, command: Command) -> Self119 fn make(general: GeneralArgs, command: Command) -> Self { 120 Options { 121 server: general.server, 122 token: general.token, 123 format: general.format, 124 api: general.api, 125 command, 126 } 127 } 128 format(&self) -> ReportFormat129 pub fn format(&self) -> ReportFormat { 130 self.format 131 } 132 133 /// Creates a new Options explicitly (useful for testing) new(server: uri::Https, token: &str, format: ReportFormat, command: Command) -> Self134 pub fn new(server: uri::Https, token: &str, format: ReportFormat, command: Command) -> Self { 135 Options { 136 server, 137 token: Token::from(token), 138 format, 139 api: false, 140 command, 141 } 142 } 143 add_general_args<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b>144 fn add_general_args<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> { 145 app.arg( 146 Arg::with_name(KRILL_CLI_SERVER_ARG) 147 .short("s") 148 .long(KRILL_CLI_SERVER_ARG) 149 .value_name("URI") 150 .help("The full URI to the krill server. Or set env: KRILL_CLI_SERVER") 151 .required(false), 152 ) 153 .arg( 154 Arg::with_name(KRILL_CLI_ADMIN_TOKEN_ARG) 155 .short("t") 156 .long(KRILL_CLI_ADMIN_TOKEN_ARG) 157 .value_name("string") 158 .help("The secret token for the krill server. Or set env: KRILL_CLI_TOKEN") 159 .required(false), 160 ) 161 .arg( 162 Arg::with_name(KRILL_CLI_FORMAT_ARG) 163 .short("f") 164 .long(KRILL_CLI_FORMAT_ARG) 165 .value_name("type") 166 .help("Report format: none|json|text (default). Or set env: KRILL_CLI_FORMAT") 167 .required(false), 168 ) 169 .arg( 170 Arg::with_name(KRILL_CLI_API_ARG) 171 .long(KRILL_CLI_API_ARG) 172 .help("Only show the API call and exit. Or set env: KRILL_CLI_API=1") 173 .required(false), 174 ) 175 } 176 add_my_ca_arg<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b>177 fn add_my_ca_arg<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> { 178 app.arg( 179 Arg::with_name(KRILL_CLI_MY_CA_ARG) 180 .value_name("name") 181 .short("c") 182 .long(KRILL_CLI_MY_CA_ARG) 183 .help("The name of the CA you wish to control. Or set env: KRILL_CLI_MY_CA") 184 .required(false), 185 ) 186 } 187 add_child_arg<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b>188 fn add_child_arg<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> { 189 app.arg( 190 Arg::with_name("child") 191 .value_name("name") 192 .long("child") 193 .help("The name of the child CA you wish to control") 194 .required(true), 195 ) 196 } 197 add_resource_args<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b>198 fn add_resource_args<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> { 199 app.arg( 200 Arg::with_name("asn") 201 .short("a") 202 .long("asn") 203 .value_name("AS resources") 204 .help("The AS resources: e.g. AS1, AS3-4") 205 .required(false), 206 ) 207 .arg( 208 Arg::with_name("ipv4") 209 .short("4") 210 .long("ipv4") 211 .value_name("IPv4 resources") 212 .help("The IPv4 resources: e.g. 192.168.0.0/16") 213 .required(false), 214 ) 215 .arg( 216 Arg::with_name("ipv6") 217 .short("6") 218 .long("ipv6") 219 .value_name("IPv6 resources") 220 .help("The IPv6 resources: e.g. 2001:db8::/32") 221 .required(false), 222 ) 223 } 224 add_parent_arg<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b>225 fn add_parent_arg<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> { 226 app.arg( 227 Arg::with_name("parent") 228 .long("parent") 229 .short("p") 230 .value_name("name") 231 .help("The local name by which the CA refers to this parent") 232 .required(true), 233 ) 234 } 235 make_config_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b>236 fn make_config_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> { 237 let mut config_sub = 238 SubCommand::with_name("config").about("Creates a configuration file for krill and prints it to STDOUT"); 239 240 fn add_data_dir_arg<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> { 241 app.arg( 242 Arg::with_name("data") 243 .long("data") 244 .short("d") 245 .value_name("path") 246 .help("Override the default path (./data/) for the data directory (must end with '/')") 247 .required(false), 248 ) 249 } 250 251 fn add_log_file_arg<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> { 252 app.arg( 253 Arg::with_name("logfile") 254 .long("logfile") 255 .short("l") 256 .value_name("path") 257 .help("Override the default path (./krill.log) for the log file") 258 .required(false), 259 ) 260 } 261 262 #[cfg(feature = "multi-user")] 263 fn add_id_arg<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> { 264 app.arg( 265 Arg::with_name("id") 266 .long("id") 267 .value_name("id") 268 .help("Specify the id (e.g. username, email) to generate configuration for") 269 .required(true), 270 ) 271 } 272 273 #[cfg(feature = "multi-user")] 274 fn add_attr_arg<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> { 275 app.arg( 276 Arg::with_name("attr") 277 .short("a") 278 .long("attribute") 279 .value_name("attr") 280 .help("Specify key=value pair attributes to give the user") 281 .required(false) 282 .multiple(true), 283 ) 284 } 285 286 let mut simple = SubCommand::with_name("simple").about("Use a 3rd party repository for publishing"); 287 288 simple = Self::add_general_args(simple); 289 simple = add_data_dir_arg(simple); 290 simple = add_log_file_arg(simple); 291 292 config_sub = config_sub.subcommand(simple); 293 294 #[cfg(feature = "multi-user")] 295 { 296 let mut with_user = 297 SubCommand::with_name("user").about("Generate a user authentication configuration file fragment"); 298 299 with_user = Self::add_general_args(with_user); 300 with_user = add_id_arg(with_user); 301 with_user = add_attr_arg(with_user); 302 303 config_sub = config_sub.subcommand(with_user); 304 } 305 306 app.subcommand(config_sub) 307 } 308 make_cas_list_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b>309 fn make_cas_list_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> { 310 let sub = SubCommand::with_name("list").about("List the current CAs"); 311 312 let sub = Self::add_general_args(sub); 313 314 app.subcommand(sub) 315 } 316 make_cas_show_ca_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b>317 fn make_cas_show_ca_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> { 318 let mut sub = SubCommand::with_name("show").about("Show details of a CA"); 319 320 sub = Self::add_general_args(sub); 321 sub = Self::add_my_ca_arg(sub); 322 323 app.subcommand(sub) 324 } 325 make_cas_show_history_details_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b>326 fn make_cas_show_history_details_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> { 327 let mut sub = SubCommand::with_name("details").about("Show details for a command in the history of a CA"); 328 329 sub = Self::add_general_args(sub); 330 sub = Self::add_my_ca_arg(sub); 331 332 sub = sub.arg( 333 Arg::with_name("key") 334 .long("key") 335 .value_name("command key string") 336 .help("The command key as shown in 'history commands'") 337 .required(true), 338 ); 339 340 app.subcommand(sub) 341 } 342 make_cas_show_history_list_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b>343 fn make_cas_show_history_list_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> { 344 let mut sub = SubCommand::with_name("commands").about("Show the commands sent to a CA"); 345 346 sub = Self::add_general_args(sub); 347 sub = Self::add_my_ca_arg(sub); 348 349 sub = sub.arg( 350 Arg::with_name("rows") 351 .long("rows") 352 .help("Number of rows (max 250)") 353 .value_name("<number>") 354 .required(false), 355 ); 356 357 sub = sub.arg( 358 Arg::with_name("offset") 359 .long("offset") 360 .help("Number of results to skip") 361 .value_name("<number>") 362 .required(false), 363 ); 364 365 sub = sub.arg( 366 Arg::with_name("after") 367 .long("after") 368 .help("Show commands issued after date/time in RFC 3339 format, e.g. 2020-04-09T19:37:02Z") 369 .value_name("<RFC 3339 DateTime>") 370 .required(false), 371 ); 372 373 sub = sub.arg( 374 Arg::with_name("before") 375 .long("before") 376 .help("Show commands issued after date/time in RFC 3339 format, e.g. 2020-04-09T19:37:02Z") 377 .value_name("<RFC 3339 DateTime>") 378 .required(false), 379 ); 380 381 app.subcommand(sub) 382 } 383 make_cas_show_history_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b>384 fn make_cas_show_history_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> { 385 let mut sub = SubCommand::with_name("history").about("Show the history of a CA"); 386 387 sub = Self::make_cas_show_history_list_sc(sub); 388 sub = Self::make_cas_show_history_details_sc(sub); 389 390 app.subcommand(sub) 391 } 392 make_cas_add_ca_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b>393 fn make_cas_add_ca_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> { 394 let mut sub = SubCommand::with_name("add").about("Add a new CA"); 395 396 sub = Self::add_general_args(sub); 397 sub = Self::add_my_ca_arg(sub); 398 399 app.subcommand(sub) 400 } 401 make_cas_delete_ca_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b>402 fn make_cas_delete_ca_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> { 403 let mut sub = SubCommand::with_name("delete") 404 .about("Delete a CA and let it withdraw its objects and request revocation. WARNING: Irreversible!"); 405 406 sub = Self::add_general_args(sub); 407 sub = Self::add_my_ca_arg(sub); 408 409 app.subcommand(sub) 410 } 411 make_cas_children_add_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b>412 fn make_cas_children_add_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> { 413 let mut sub = SubCommand::with_name("add").about("Add a child to a CA"); 414 415 sub = Self::add_general_args(sub); 416 sub = Self::add_my_ca_arg(sub); 417 sub = Self::add_child_arg(sub); 418 sub = Self::add_resource_args(sub); 419 let sub = sub.arg( 420 Arg::with_name("request") 421 .long("request") 422 .short("r") 423 .help("The location of the RFC8183 Child Request XML file") 424 .value_name("<XML file>") 425 .required(true), 426 ); 427 428 app.subcommand(sub) 429 } 430 make_cas_children_update_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b>431 fn make_cas_children_update_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> { 432 let mut sub = SubCommand::with_name("update").about("Update an existing child of a CA"); 433 434 sub = Self::add_general_args(sub); 435 sub = Self::add_my_ca_arg(sub); 436 sub = Self::add_child_arg(sub); 437 sub = Self::add_resource_args(sub); 438 sub = sub.arg( 439 Arg::with_name("idcert") 440 .long("idcert") 441 .help("The child's updated ID certificate") 442 .value_name("DER encoded certificate") 443 .required(false), 444 ); 445 446 app.subcommand(sub) 447 } 448 make_cas_children_response_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b>449 fn make_cas_children_response_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> { 450 let mut sub = SubCommand::with_name("response").about("Show the RFC8183 Parent Response XML"); 451 452 sub = Self::add_general_args(sub); 453 sub = Self::add_my_ca_arg(sub); 454 sub = Self::add_child_arg(sub); 455 456 app.subcommand(sub) 457 } 458 make_cas_children_info_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b>459 fn make_cas_children_info_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> { 460 let mut sub = SubCommand::with_name("info").about("Show info for a child (id and resources)"); 461 462 sub = Self::add_general_args(sub); 463 sub = Self::add_my_ca_arg(sub); 464 sub = Self::add_child_arg(sub); 465 466 app.subcommand(sub) 467 } 468 make_cas_children_remove_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b>469 fn make_cas_children_remove_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> { 470 let mut sub = SubCommand::with_name("remove").about("Remove an existing child from a CA"); 471 472 sub = Self::add_general_args(sub); 473 sub = Self::add_my_ca_arg(sub); 474 sub = Self::add_child_arg(sub); 475 476 app.subcommand(sub) 477 } 478 make_cas_children_connections_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b>479 fn make_cas_children_connections_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> { 480 let mut sub = SubCommand::with_name("connections").about("Show connections stats for children of a CA"); 481 482 sub = Self::add_general_args(sub); 483 sub = Self::add_my_ca_arg(sub); 484 485 app.subcommand(sub) 486 } 487 make_cas_children_suspend_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b>488 fn make_cas_children_suspend_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> { 489 let mut sub = SubCommand::with_name("suspend").about("Suspend a child CA: hide certificate(s) issued to child"); 490 491 sub = Self::add_general_args(sub); 492 sub = Self::add_my_ca_arg(sub); 493 sub = Self::add_child_arg(sub); 494 495 app.subcommand(sub) 496 } 497 make_cas_children_unsuspend_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b>498 fn make_cas_children_unsuspend_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> { 499 let mut sub = 500 SubCommand::with_name("unsuspend").about("Suspend a child CA: republish certificate(s) issued to child"); 501 502 sub = Self::add_general_args(sub); 503 sub = Self::add_my_ca_arg(sub); 504 sub = Self::add_child_arg(sub); 505 506 app.subcommand(sub) 507 } 508 make_cas_children_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b>509 fn make_cas_children_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> { 510 let mut sub = SubCommand::with_name("children").about("Manage children for a CA"); 511 512 sub = Self::make_cas_children_add_sc(sub); 513 sub = Self::make_cas_children_update_sc(sub); 514 sub = Self::make_cas_children_info_sc(sub); 515 sub = Self::make_cas_children_remove_sc(sub); 516 sub = Self::make_cas_children_response_sc(sub); 517 sub = Self::make_cas_children_connections_sc(sub); 518 sub = Self::make_cas_children_suspend_sc(sub); 519 sub = Self::make_cas_children_unsuspend_sc(sub); 520 521 app.subcommand(sub) 522 } 523 make_cas_parents_request_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b>524 fn make_cas_parents_request_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> { 525 let mut sub = SubCommand::with_name("request").about("Show RFC8183 Child Request XML"); 526 527 sub = Self::add_general_args(sub); 528 sub = Self::add_my_ca_arg(sub); 529 530 app.subcommand(sub) 531 } 532 make_cas_parents_add_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b>533 fn make_cas_parents_add_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> { 534 let mut sub = SubCommand::with_name("add").about("Add a parent to, or update a parent of a CA"); 535 536 sub = Self::add_general_args(sub); 537 sub = Self::add_my_ca_arg(sub); 538 sub = Self::add_parent_arg(sub); 539 sub = sub.arg( 540 Arg::with_name("response") 541 .long("response") 542 .short("r") 543 .help("The location of the RFC8183 Parent Response XML file") 544 .value_name("<XML file>") 545 .required(true), 546 ); 547 548 app.subcommand(sub) 549 } 550 make_cas_parents_statuses_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b>551 fn make_cas_parents_statuses_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> { 552 let mut sub = SubCommand::with_name("statuses").about("Show overview of all parent statuses of a CA"); 553 554 sub = Self::add_general_args(sub); 555 sub = Self::add_my_ca_arg(sub); 556 557 app.subcommand(sub) 558 } 559 make_cas_parents_contact_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b>560 fn make_cas_parents_contact_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> { 561 let mut sub = SubCommand::with_name("contact").about("Show contact information for a parent of a CA"); 562 563 sub = Self::add_general_args(sub); 564 sub = Self::add_my_ca_arg(sub); 565 sub = Self::add_parent_arg(sub); 566 567 app.subcommand(sub) 568 } 569 make_cas_parents_remove_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b>570 fn make_cas_parents_remove_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> { 571 let mut sub = SubCommand::with_name("remove").about("Remove an existing parent from a CA"); 572 573 sub = Self::add_general_args(sub); 574 sub = Self::add_my_ca_arg(sub); 575 sub = Self::add_parent_arg(sub); 576 577 app.subcommand(sub) 578 } 579 make_cas_parents_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b>580 fn make_cas_parents_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> { 581 let mut sub = SubCommand::with_name("parents").about("Manage parents for a CA"); 582 583 sub = Self::make_cas_parents_request_sc(sub); 584 sub = Self::make_cas_parents_add_sc(sub); 585 sub = Self::make_cas_parents_contact_sc(sub); 586 sub = Self::make_cas_parents_statuses_sc(sub); 587 sub = Self::make_cas_parents_remove_sc(sub); 588 589 app.subcommand(sub) 590 } 591 make_cas_keyroll_init_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b>592 fn make_cas_keyroll_init_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> { 593 let mut sub = SubCommand::with_name("init").about("Initialize roll for all keys held by a CA"); 594 595 sub = Self::add_general_args(sub); 596 sub = Self::add_my_ca_arg(sub); 597 598 app.subcommand(sub) 599 } 600 make_cas_keyroll_activate_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b>601 fn make_cas_keyroll_activate_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> { 602 let mut sub = SubCommand::with_name("activate").about("Finish roll for all keys held by a CA"); 603 604 sub = Self::add_general_args(sub); 605 sub = Self::add_my_ca_arg(sub); 606 607 app.subcommand(sub) 608 } 609 make_cas_keyroll_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b>610 fn make_cas_keyroll_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> { 611 let mut sub = SubCommand::with_name("keyroll").about("Perform a manual key rollover for a CA"); 612 613 sub = Self::make_cas_keyroll_init_sc(sub); 614 sub = Self::make_cas_keyroll_activate_sc(sub); 615 616 app.subcommand(sub) 617 } 618 make_cas_routes_list_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b>619 fn make_cas_routes_list_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> { 620 let mut sub = SubCommand::with_name("list").about("Show current authorizations"); 621 622 sub = Self::add_general_args(sub); 623 sub = Self::add_my_ca_arg(sub); 624 625 app.subcommand(sub) 626 } 627 make_cas_routes_update_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b>628 fn make_cas_routes_update_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> { 629 let mut sub = SubCommand::with_name("update").about("Update authorizations"); 630 631 sub = Self::add_general_args(sub); 632 sub = Self::add_my_ca_arg(sub); 633 634 sub = sub.arg( 635 Arg::with_name("delta") 636 .long("delta") 637 .help(concat!( 638 "Provide a delta file using the following format:\n", 639 "# Some comment\n", 640 " # Indented comment\n", 641 "\n", // empty line 642 "A: 192.168.0.0/16 => 64496 # inline comment\n", 643 "A: 192.168.1.0/24 => 64496\n", 644 "R: 192.168.3.0/24 => 64496\n", 645 )) 646 .value_name("<file>") 647 .required(false), 648 ); 649 650 sub = sub.arg( 651 Arg::with_name("add") 652 .long("add") 653 .help("One or more ROAs to add, e.g.: 192.168.0.0/16 => 64496") 654 .value_name("<roa definition>") 655 .multiple(true) 656 .required(false), 657 ); 658 659 sub = sub.arg( 660 Arg::with_name("remove") 661 .long("remove") 662 .help("One or more ROAs to remove, e.g.: 192.168.0.0/16 => 64496") 663 .value_name("<roa definition>") 664 .multiple(true) 665 .required(false), 666 ); 667 668 sub = sub.arg( 669 Arg::with_name("dryrun") 670 .long("dryrun") 671 .help("Perform a dry run of the update and return the BGP analysis for the scoped to the update") 672 .required(false), 673 ); 674 675 sub = sub.arg( 676 Arg::with_name("try") 677 .long("try") 678 .help("Try to perform the update, advice in case it would result in errors or invalids") 679 .required(false), 680 ); 681 682 app.subcommand(sub) 683 } 684 make_cas_routes_bgp_full_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b>685 fn make_cas_routes_bgp_full_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> { 686 let mut sub = SubCommand::with_name("analyze").about("Show full report of ROAs vs known BGP announcements"); 687 688 sub = Self::add_general_args(sub); 689 sub = Self::add_my_ca_arg(sub); 690 app.subcommand(sub) 691 } 692 make_cas_routes_bgp_suggestions_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b>693 fn make_cas_routes_bgp_suggestions_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> { 694 let mut sub = SubCommand::with_name("suggest").about("Show ROA suggestions based on known BGP announcements"); 695 696 sub = Self::add_general_args(sub); 697 sub = Self::add_my_ca_arg(sub); 698 699 sub = sub 700 .arg( 701 Arg::with_name("ipv4") 702 .short("4") 703 .long("ipv4") 704 .value_name("IPv4 resources") 705 .help("Scope to these IPv4 resources") 706 .required(false), 707 ) 708 .arg( 709 Arg::with_name("ipv6") 710 .short("6") 711 .long("ipv6") 712 .value_name("IPv6 resources") 713 .help("Scope to these IPv6 resources") 714 .required(false), 715 ); 716 717 app.subcommand(sub) 718 } 719 make_cas_routes_bgp_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b>720 fn make_cas_routes_bgp_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> { 721 let mut sub = 722 SubCommand::with_name("bgp").about("Show current authorizations in relation to known announcements"); 723 724 sub = Self::make_cas_routes_bgp_full_sc(sub); 725 sub = Self::make_cas_routes_bgp_suggestions_sc(sub); 726 727 app.subcommand(sub) 728 } 729 make_cas_routes_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b>730 fn make_cas_routes_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> { 731 let mut sub = SubCommand::with_name("roas").about("Manage ROAs for a CA"); 732 733 sub = Self::make_cas_routes_list_sc(sub); 734 sub = Self::make_cas_routes_update_sc(sub); 735 sub = Self::make_cas_routes_bgp_sc(sub); 736 737 app.subcommand(sub) 738 } 739 740 #[cfg(feature = "aspa")] make_cas_aspas_add_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b>741 fn make_cas_aspas_add_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> { 742 let mut sub = SubCommand::with_name("add").about("Add or replace an ASPA configuration"); 743 744 sub = Self::add_general_args(sub); 745 sub = Self::add_my_ca_arg(sub); 746 747 sub = sub.arg( 748 Arg::with_name("aspa") 749 .long("aspa") 750 .help("ASPA formatted like: 65000 => 65001, 65002(v4), 65003(v6)") 751 .value_name("definition") 752 .required(true), 753 ); 754 755 app.subcommand(sub) 756 } 757 758 #[cfg(feature = "aspa")] make_cas_aspas_remove_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b>759 fn make_cas_aspas_remove_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> { 760 let mut sub = SubCommand::with_name("remove").about("Remove the ASPA for a customer ASN"); 761 762 sub = Self::add_general_args(sub); 763 sub = Self::add_my_ca_arg(sub); 764 765 sub = sub.arg( 766 Arg::with_name("customer") 767 .long("customer") 768 .help("Customer ASN for an existing ASPA definition") 769 .value_name("ASN") 770 .required(true), 771 ); 772 773 app.subcommand(sub) 774 } 775 776 #[cfg(feature = "aspa")] make_cas_aspas_update_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b>777 fn make_cas_aspas_update_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> { 778 let mut sub = SubCommand::with_name("update").about("Update an existing ASPA configuration"); 779 780 sub = Self::add_general_args(sub); 781 sub = Self::add_my_ca_arg(sub); 782 783 sub = sub.arg( 784 Arg::with_name("customer") 785 .long("customer") 786 .help("Customer ASN for an existing ASPA definition") 787 .value_name("ASN") 788 .required(true), 789 ); 790 791 sub = sub.arg( 792 Arg::with_name("add") 793 .long("add") 794 .help("Provider ASNs to add (multiple allowed)") 795 .value_name("<Provider AS>") 796 .multiple(true) 797 .required(false), 798 ); 799 800 sub = sub.arg( 801 Arg::with_name("remove") 802 .long("remove") 803 .help("Provider ASNs to remove (multiple allowed)") 804 .value_name("<Provider AS>") 805 .multiple(true) 806 .required(false), 807 ); 808 809 app.subcommand(sub) 810 } 811 812 #[cfg(feature = "aspa")] make_cas_aspas_list_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b>813 fn make_cas_aspas_list_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> { 814 let mut sub = SubCommand::with_name("list").about("Show current ASPA configurations"); 815 816 sub = Self::add_general_args(sub); 817 sub = Self::add_my_ca_arg(sub); 818 819 app.subcommand(sub) 820 } 821 822 #[cfg(feature = "aspa")] make_cas_aspas_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b>823 fn make_cas_aspas_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> { 824 let mut sub = SubCommand::with_name("aspas").about("Manage ASPAs for a CA (experimental)"); 825 826 sub = Self::make_cas_aspas_add_sc(sub); 827 sub = Self::make_cas_aspas_remove_sc(sub); 828 sub = Self::make_cas_aspas_update_sc(sub); 829 sub = Self::make_cas_aspas_list_sc(sub); 830 831 app.subcommand(sub) 832 } 833 make_cas_repo_request_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b>834 fn make_cas_repo_request_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> { 835 let mut sub = SubCommand::with_name("request").about("Show RFC8183 Publisher Request XML"); 836 837 sub = Self::add_general_args(sub); 838 sub = Self::add_my_ca_arg(sub); 839 840 app.subcommand(sub) 841 } 842 make_cas_repo_show_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b>843 fn make_cas_repo_show_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> { 844 let mut sub = SubCommand::with_name("show").about("Show current repo config"); 845 846 sub = Self::add_general_args(sub); 847 sub = Self::add_my_ca_arg(sub); 848 849 app.subcommand(sub) 850 } 851 make_cas_repo_status_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b>852 fn make_cas_repo_status_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> { 853 let mut sub = SubCommand::with_name("status").about("Show current repo status"); 854 855 sub = Self::add_general_args(sub); 856 sub = Self::add_my_ca_arg(sub); 857 858 app.subcommand(sub) 859 } 860 make_cas_repo_configure_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b>861 fn make_cas_repo_configure_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> { 862 let mut sub = SubCommand::with_name("configure").about("Configure which repository a CA uses"); 863 864 sub = Self::add_general_args(sub); 865 sub = Self::add_my_ca_arg(sub); 866 sub = sub.arg( 867 Arg::with_name("response") 868 .value_name("file") 869 .long("response") 870 .short("r") 871 .help("The location of the RFC8183 Publisher Response XML file") 872 .required(true), 873 ); 874 875 app.subcommand(sub) 876 } 877 make_cas_repo_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b>878 fn make_cas_repo_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> { 879 let mut sub = SubCommand::with_name("repo").about("Manage the repository for a CA"); 880 881 sub = Self::make_cas_repo_request_sc(sub); 882 sub = Self::make_cas_repo_show_sc(sub); 883 sub = Self::make_cas_repo_status_sc(sub); 884 sub = Self::make_cas_repo_configure_sc(sub); 885 886 app.subcommand(sub) 887 } 888 make_cas_issues_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b>889 fn make_cas_issues_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> { 890 let mut sub = SubCommand::with_name("issues").about("Show issues for a CA"); 891 892 sub = Self::add_general_args(sub); 893 sub = Self::add_my_ca_arg(sub); 894 895 app.subcommand(sub) 896 } 897 898 #[cfg(feature = "rta")] make_cas_rta_list<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b>899 fn make_cas_rta_list<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> { 900 let mut sub = SubCommand::with_name("list").about("List RTAs"); 901 902 sub = Self::add_general_args(sub); 903 sub = Self::add_my_ca_arg(sub); 904 905 app.subcommand(sub) 906 } 907 908 #[cfg(feature = "rta")] make_cas_rta_show<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b>909 fn make_cas_rta_show<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> { 910 let mut sub = SubCommand::with_name("show").about("Show RTA"); 911 912 sub = Self::add_general_args(sub); 913 sub = Self::add_my_ca_arg(sub); 914 915 sub = sub.arg( 916 Arg::with_name("name") 917 .long("name") 918 .short("n") 919 .value_name("string") 920 .help("Your local name for this RTA") 921 .required(true), 922 ); 923 924 sub = sub.arg( 925 Arg::with_name("out") 926 .long("out") 927 .short("o") 928 .value_name("path") 929 .help("File to write RTA to") 930 .required(true), 931 ); 932 933 app.subcommand(sub) 934 } 935 936 #[cfg(feature = "rta")] make_cas_rta_sign_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b>937 fn make_cas_rta_sign_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> { 938 let mut sub = SubCommand::with_name("sign").about("Create an RTA signed by a CA"); 939 940 sub = Self::add_general_args(sub); 941 sub = Self::add_my_ca_arg(sub); 942 943 sub = Self::add_resource_args(sub); 944 945 sub = sub.arg( 946 Arg::with_name("days") 947 .long("days") 948 .short("d") 949 .value_name("number of days") 950 .help("Validity time of the RTA in days") 951 .required(true), 952 ); 953 954 sub = sub.arg( 955 Arg::with_name("in") 956 .long("in") 957 .short("i") 958 .value_name("path") 959 .help("Content which needs to be signed") 960 .required(true), 961 ); 962 963 sub = sub.arg( 964 Arg::with_name("name") 965 .long("name") 966 .short("n") 967 .value_name("string") 968 .help("Your local name for this RTA") 969 .required(true), 970 ); 971 972 sub = sub.arg( 973 Arg::with_name("keys") 974 .long("keys") 975 .short("k") 976 .value_name("hex encoded key identifiers") 977 .multiple(true) 978 .help("Optional additional keys to include in this RTA") 979 .required(false), 980 ); 981 982 app.subcommand(sub) 983 } 984 985 #[cfg(feature = "rta")] make_cas_rta_multi_prep_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b>986 fn make_cas_rta_multi_prep_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> { 987 let mut sub = SubCommand::with_name("prep").about("Prepare keys for multi-signed RTA"); 988 989 sub = Self::add_general_args(sub); 990 sub = Self::add_my_ca_arg(sub); 991 992 sub = Self::add_resource_args(sub); 993 994 sub = sub.arg( 995 Arg::with_name("days") 996 .long("days") 997 .short("d") 998 .value_name("number of days") 999 .help("Validity time of the RTA in days") 1000 .required(true), 1001 ); 1002 1003 sub = sub.arg( 1004 Arg::with_name("name") 1005 .long("name") 1006 .short("n") 1007 .value_name("string") 1008 .help("Your local name for this RTA") 1009 .required(true), 1010 ); 1011 1012 app.subcommand(sub) 1013 } 1014 1015 #[cfg(feature = "rta")] make_cas_rta_multi_sign_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b>1016 fn make_cas_rta_multi_sign_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> { 1017 let mut sub = SubCommand::with_name("cosign").about("Co-sign an existing (prepared) RTA"); 1018 1019 sub = Self::add_general_args(sub); 1020 sub = Self::add_my_ca_arg(sub); 1021 1022 sub = sub.arg( 1023 Arg::with_name("name") 1024 .long("name") 1025 .short("n") 1026 .value_name("string") 1027 .help("Your local name for this RTA") 1028 .required(true), 1029 ); 1030 1031 sub = sub.arg( 1032 Arg::with_name("in") 1033 .long("in") 1034 .short("i") 1035 .value_name("path") 1036 .help("RTA which needs to be co-signed") 1037 .required(true), 1038 ); 1039 1040 app.subcommand(sub) 1041 } 1042 1043 #[cfg(feature = "rta")] make_cas_rta_multi_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b>1044 fn make_cas_rta_multi_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> { 1045 let mut sub = SubCommand::with_name("multi").about("Manage RTA signed by multiple parties"); 1046 1047 sub = Self::make_cas_rta_multi_prep_sc(sub); 1048 sub = Self::make_cas_rta_multi_sign_sc(sub); 1049 1050 app.subcommand(sub) 1051 } 1052 1053 #[cfg(feature = "rta")] make_cas_rta_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b>1054 fn make_cas_rta_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> { 1055 let mut sub = SubCommand::with_name("rta").about("Manage Resource Tagged Attestations"); 1056 sub = Self::make_cas_rta_list(sub); 1057 sub = Self::make_cas_rta_show(sub); 1058 sub = Self::make_cas_rta_sign_sc(sub); 1059 sub = Self::make_cas_rta_multi_sc(sub); 1060 app.subcommand(sub) 1061 } 1062 make_bulk_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b>1063 fn make_bulk_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> { 1064 let mut sub = SubCommand::with_name("bulk").about("Manually trigger refresh/republish/resync for all CAs"); 1065 1066 let mut refresh = 1067 SubCommand::with_name("refresh").about("Force that all CAs ask their parents for updated certificates"); 1068 refresh = Self::add_general_args(refresh); 1069 1070 let mut republish = SubCommand::with_name("publish") 1071 .about("Force that all CAs create new objects if needed (in which case they will also sync)"); 1072 republish = Self::add_general_args(republish); 1073 1074 let mut resync = SubCommand::with_name("sync").about("Force that all CAs sync with their repo server"); 1075 resync = Self::add_general_args(resync); 1076 1077 sub = sub.subcommand(refresh).subcommand(republish).subcommand(resync); 1078 1079 app.subcommand(sub) 1080 } 1081 make_health_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b>1082 fn make_health_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> { 1083 let health = SubCommand::with_name("health").about("Perform an authenticated health check"); 1084 let health = Self::add_general_args(health); 1085 app.subcommand(health) 1086 } 1087 make_info_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b>1088 fn make_info_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> { 1089 let info = SubCommand::with_name("info").about("Show server info"); 1090 let info = Self::add_general_args(info); 1091 app.subcommand(info) 1092 } 1093 make_publishers_list_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b>1094 fn make_publishers_list_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> { 1095 let mut sub = SubCommand::with_name("list").about("List all publishers"); 1096 sub = Options::add_general_args(sub); 1097 app.subcommand(sub) 1098 } 1099 make_publishers_stale_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b>1100 fn make_publishers_stale_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> { 1101 let mut sub = SubCommand::with_name("stale").about("List all publishers which have not published in a while"); 1102 sub = Options::add_general_args(sub); 1103 sub = sub.arg( 1104 Arg::with_name("seconds") 1105 .value_name("seconds") 1106 .long("seconds") 1107 .help("The number of seconds since last publication") 1108 .required(true), 1109 ); 1110 app.subcommand(sub) 1111 } 1112 add_publisher_arg<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b>1113 fn add_publisher_arg<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> { 1114 app.arg( 1115 Arg::with_name("publisher") 1116 .value_name("handle") 1117 .short("p") 1118 .long("publisher") 1119 .help("The handle (name) of the publisher") 1120 .required(true), 1121 ) 1122 } 1123 add_rsync_base_arg<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b>1124 fn add_rsync_base_arg<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> { 1125 app.arg( 1126 Arg::with_name("rsync") 1127 .long("rsync") 1128 .value_name("uri") 1129 .help("Specify the base rsync URI for the repository, must end with '/'") 1130 .required(true), 1131 ) 1132 } 1133 add_rrdp_base_uri_arg<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b>1134 fn add_rrdp_base_uri_arg<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> { 1135 app.arg( 1136 Arg::with_name("rrdp") 1137 .long("rrdp") 1138 .value_name("uri") 1139 .help( 1140 "Specify the base https URI for the RRDP (excluding notification.xml), \ 1141 must \ 1142 end with '/'", 1143 ) 1144 .required(true), 1145 ) 1146 } 1147 make_publishers_add_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b>1148 fn make_publishers_add_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> { 1149 let mut sub = SubCommand::with_name("add").about("Add a publisher"); 1150 sub = Options::add_general_args(sub); 1151 1152 sub = sub 1153 .arg( 1154 Arg::with_name("request") 1155 .value_name("file") 1156 .long("request") 1157 .short("r") 1158 .help("The location of the RFC8183 Publisher Request XML file") 1159 .required(true), 1160 ) 1161 .arg( 1162 Arg::with_name("publisher") 1163 .value_name("handle") 1164 .short("p") 1165 .long("publisher") 1166 .help("Override the publisher handle in the XML") 1167 .required(false), 1168 ); 1169 1170 app.subcommand(sub) 1171 } 1172 make_publishers_remove_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b>1173 fn make_publishers_remove_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> { 1174 let mut sub = SubCommand::with_name("remove").about("Remove a publisher"); 1175 sub = Options::add_general_args(sub); 1176 sub = Self::add_publisher_arg(sub); 1177 app.subcommand(sub) 1178 } 1179 make_publishers_show_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b>1180 fn make_publishers_show_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> { 1181 let mut sub = SubCommand::with_name("show").about("Show details for a publisher"); 1182 sub = Options::add_general_args(sub); 1183 sub = Self::add_publisher_arg(sub); 1184 app.subcommand(sub) 1185 } 1186 make_publishers_response_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b>1187 fn make_publishers_response_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> { 1188 let mut sub = SubCommand::with_name("response").about("Show RFC8183 Repository Response XML"); 1189 sub = Options::add_general_args(sub); 1190 sub = Self::add_publisher_arg(sub); 1191 app.subcommand(sub) 1192 } 1193 make_publication_server_stats_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b>1194 fn make_publication_server_stats_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> { 1195 let mut sub = SubCommand::with_name("stats").about("Show publication server stats"); 1196 sub = Options::add_general_args(sub); 1197 app.subcommand(sub) 1198 } 1199 make_publication_server_init_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b>1200 fn make_publication_server_init_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> { 1201 let mut sub = SubCommand::with_name("init").about("Initialize publication server"); 1202 sub = Options::add_general_args(sub); 1203 1204 sub = Self::add_rsync_base_arg(sub); 1205 sub = Self::add_rrdp_base_uri_arg(sub); 1206 1207 app.subcommand(sub) 1208 } 1209 make_publication_server_clear_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b>1210 fn make_publication_server_clear_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> { 1211 let mut sub = SubCommand::with_name("clear").about("Clear the publication server so it can re-initialized"); 1212 sub = Options::add_general_args(sub); 1213 app.subcommand(sub) 1214 } 1215 make_publication_server_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b>1216 fn make_publication_server_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> { 1217 let mut sub = SubCommand::with_name("server").about("Manage the Publication Server (init/stats)"); 1218 sub = Self::make_publication_server_stats_sc(sub); 1219 sub = Self::make_publication_server_init_sc(sub); 1220 sub = Self::make_publication_server_clear_sc(sub); 1221 app.subcommand(sub) 1222 } 1223 make_publishers_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b>1224 fn make_publishers_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> { 1225 let mut sub = SubCommand::with_name("publishers").about("Manage the publishers in your Publication Server"); 1226 1227 sub = Self::make_publishers_list_sc(sub); 1228 sub = Self::make_publishers_stale_sc(sub); 1229 sub = Self::make_publishers_add_sc(sub); 1230 sub = Self::make_publishers_remove_sc(sub); 1231 sub = Self::make_publishers_show_sc(sub); 1232 sub = Self::make_publishers_response_sc(sub); 1233 1234 app.subcommand(sub) 1235 } 1236 make_pubserver_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b>1237 fn make_pubserver_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> { 1238 let mut sub = SubCommand::with_name("pubserver") 1239 .about("Manage your Publication Server (only needed if you run your own)"); 1240 1241 sub = Self::make_publishers_sc(sub); 1242 sub = Self::make_publication_server_sc(sub); 1243 1244 app.subcommand(sub) 1245 } 1246 make_matches<'a>() -> ArgMatches<'a>1247 fn make_matches<'a>() -> ArgMatches<'a> { 1248 let mut app = App::new(KRILL_CLIENT_APP).version(KRILL_VERSION); 1249 1250 app = Self::make_config_sc(app); 1251 app = Self::make_cas_list_sc(app); 1252 app = Self::make_cas_show_ca_sc(app); 1253 app = Self::make_cas_show_history_sc(app); 1254 app = Self::make_cas_add_ca_sc(app); 1255 app = Self::make_cas_delete_ca_sc(app); 1256 app = Self::make_cas_children_sc(app); 1257 app = Self::make_cas_parents_sc(app); 1258 app = Self::make_cas_keyroll_sc(app); 1259 app = Self::make_cas_routes_sc(app); 1260 app = Self::make_cas_repo_sc(app); 1261 app = Self::make_cas_issues_sc(app); 1262 app = Self::make_pubserver_sc(app); 1263 1264 #[cfg(feature = "aspa")] 1265 { 1266 app = Self::make_cas_aspas_sc(app); 1267 } 1268 1269 #[cfg(feature = "rta")] 1270 { 1271 app = Self::make_cas_rta_sc(app); 1272 } 1273 1274 app = Self::make_health_sc(app); 1275 1276 app = Self::make_info_sc(app); 1277 1278 app = Self::make_bulk_sc(app); 1279 1280 app.get_matches() 1281 } 1282 1283 //---------------------- Parsing 1284 read_file_arg(path: &str) -> Result<Bytes, Error>1285 fn read_file_arg(path: &str) -> Result<Bytes, Error> { 1286 let path = PathBuf::from(path); 1287 file::read(&path).map_err(Error::IoError) 1288 } 1289 parse_my_ca(matches: &ArgMatches) -> Result<Handle, Error>1290 fn parse_my_ca(matches: &ArgMatches) -> Result<Handle, Error> { 1291 let my_ca = { 1292 let mut my_ca = None; 1293 1294 if let Ok(my_ca_env) = env::var(KRILL_CLI_MY_CA_ENV) { 1295 my_ca = Some(Handle::from_str(&my_ca_env).map_err(|_| Error::InvalidHandle)?); 1296 } 1297 1298 if let Some(my_ca_str) = matches.value_of(KRILL_CLI_MY_CA_ARG) { 1299 my_ca = Some(Handle::from_str(my_ca_str).map_err(|_| Error::InvalidHandle)?); 1300 } 1301 1302 my_ca.ok_or_else(|| Error::missing_arg_with_env(KRILL_CLI_MY_CA_ARG, KRILL_CLI_MY_CA_ENV))? 1303 }; 1304 1305 Ok(my_ca) 1306 } 1307 parse_resource_args(matches: &ArgMatches) -> Result<Option<ResourceSet>, Error>1308 fn parse_resource_args(matches: &ArgMatches) -> Result<Option<ResourceSet>, Error> { 1309 let asn = matches.value_of("asn"); 1310 let v4 = matches.value_of("ipv4"); 1311 let v6 = matches.value_of("ipv6"); 1312 1313 if asn.is_some() || v4.is_some() || v6.is_some() { 1314 let asn = asn.unwrap_or(""); 1315 let v4 = v4.unwrap_or(""); 1316 let v6 = v6.unwrap_or(""); 1317 1318 Ok(Some(ResourceSet::from_strs(asn, v4, v6)?)) 1319 } else { 1320 Ok(None) 1321 } 1322 } 1323 parse_matches_simple_config(matches: &ArgMatches) -> Result<Options, Error>1324 fn parse_matches_simple_config(matches: &ArgMatches) -> Result<Options, Error> { 1325 let general_args = GeneralArgs::from_matches(matches)?; 1326 1327 #[cfg(not(feature = "multi-user"))] 1328 let mut details = KrillInitDetails::default(); 1329 1330 #[cfg(feature = "multi-user")] 1331 let mut details = KrillInitDetails::multi_user_dflt(); 1332 1333 if let Some(data) = matches.value_of("data") { 1334 if !data.ends_with('/') { 1335 return Err(Error::general("Path for --data MUST end with a '/'")); 1336 } 1337 details.with_data_dir(data); 1338 } 1339 if let Some(log_file) = matches.value_of("logfile") { 1340 details.with_log_file(log_file); 1341 } 1342 1343 let command = Command::Init(details); 1344 Ok(Options::make(general_args, command)) 1345 } 1346 1347 #[cfg(feature = "multi-user")] parse_matches_user_config(matches: &ArgMatches) -> Result<Options, Error>1348 fn parse_matches_user_config(matches: &ArgMatches) -> Result<Options, Error> { 1349 let general_args = GeneralArgs::default(); 1350 let mut details = KrillUserDetails::default(); 1351 if let Some(id) = matches.value_of("id") { 1352 details.with_id(id.to_string()); 1353 } 1354 if let Some(attr_iter) = matches.values_of("attr") { 1355 for attr in attr_iter { 1356 let mut iter = attr.split('='); 1357 let k = iter 1358 .next() 1359 .ok_or_else(|| Error::general(&format!("attribute '{}' must be of the form key=value", attr)))?; 1360 let v = iter 1361 .next() 1362 .ok_or_else(|| Error::general(&format!("attribute '{}' must be of the form key=value", attr)))?; 1363 details.with_attr(k.to_string(), v.to_string()); 1364 } 1365 } 1366 let command = Command::User(details); 1367 Ok(Options::make(general_args, command)) 1368 } 1369 1370 #[cfg(not(feature = "multi-user"))] parse_matches_user_config(_: &ArgMatches) -> Result<Options, Error>1371 fn parse_matches_user_config(_: &ArgMatches) -> Result<Options, Error> { 1372 Err(Error::UnrecognizedSubCommand) 1373 } 1374 parse_matches_config(matches: &ArgMatches) -> Result<Options, Error>1375 fn parse_matches_config(matches: &ArgMatches) -> Result<Options, Error> { 1376 if let Some(m) = matches.subcommand_matches("simple") { 1377 Self::parse_matches_simple_config(m) 1378 } else if let Some(m) = matches.subcommand_matches("user") { 1379 Self::parse_matches_user_config(m) 1380 } else { 1381 Err(Error::UnrecognizedSubCommand) 1382 } 1383 } 1384 parse_matches_cas_list(matches: &ArgMatches) -> Result<Options, Error>1385 fn parse_matches_cas_list(matches: &ArgMatches) -> Result<Options, Error> { 1386 let general_args = GeneralArgs::from_matches(matches)?; 1387 let command = Command::CertAuth(CaCommand::List); 1388 Ok(Options::make(general_args, command)) 1389 } 1390 parse_matches_cas_add(matches: &ArgMatches) -> Result<Options, Error>1391 fn parse_matches_cas_add(matches: &ArgMatches) -> Result<Options, Error> { 1392 let general_args = GeneralArgs::from_matches(matches)?; 1393 let my_ca = Self::parse_my_ca(matches)?; 1394 1395 let init = CertAuthInit::new(my_ca); 1396 1397 let command = Command::CertAuth(CaCommand::Init(init)); 1398 1399 Ok(Options::make(general_args, command)) 1400 } 1401 parse_matches_cas_delete(matches: &ArgMatches) -> Result<Options, Error>1402 fn parse_matches_cas_delete(matches: &ArgMatches) -> Result<Options, Error> { 1403 let general_args = GeneralArgs::from_matches(matches)?; 1404 let my_ca = Self::parse_my_ca(matches)?; 1405 1406 let command = Command::CertAuth(CaCommand::Delete(my_ca)); 1407 1408 Ok(Options::make(general_args, command)) 1409 } 1410 parse_matches_cas_show(matches: &ArgMatches) -> Result<Options, Error>1411 fn parse_matches_cas_show(matches: &ArgMatches) -> Result<Options, Error> { 1412 let general_args = GeneralArgs::from_matches(matches)?; 1413 let my_ca = Self::parse_my_ca(matches)?; 1414 1415 let command = Command::CertAuth(CaCommand::Show(my_ca)); 1416 Ok(Options::make(general_args, command)) 1417 } 1418 parse_matches_cas_history_details(matches: &ArgMatches) -> Result<Options, Error>1419 fn parse_matches_cas_history_details(matches: &ArgMatches) -> Result<Options, Error> { 1420 let general_args = GeneralArgs::from_matches(matches)?; 1421 let my_ca = Self::parse_my_ca(matches)?; 1422 let key = matches.value_of("key").unwrap(); 1423 1424 let command = Command::CertAuth(CaCommand::ShowHistoryDetails(my_ca, key.to_string())); 1425 Ok(Options::make(general_args, command)) 1426 } 1427 parse_matches_cas_history_commands(matches: &ArgMatches) -> Result<Options, Error>1428 fn parse_matches_cas_history_commands(matches: &ArgMatches) -> Result<Options, Error> { 1429 let general_args = GeneralArgs::from_matches(matches)?; 1430 let my_ca = Self::parse_my_ca(matches)?; 1431 1432 let mut options = HistoryOptions::default(); 1433 1434 if let Some(offset) = matches.value_of("offset") { 1435 let offset = 1436 u64::from_str(offset).map_err(|e| Error::general(&format!("invalid number: {}", e.to_string())))?; 1437 options.offset = offset 1438 } 1439 1440 if let Some(rows) = matches.value_of("rows") { 1441 let rows = 1442 u64::from_str(rows).map_err(|e| Error::general(&format!("invalid number: {}", e.to_string())))?; 1443 if rows > 250 { 1444 return Err(Error::general("No more than 250 rows allowed in history")); 1445 } 1446 options.rows = rows 1447 } 1448 1449 if let Some(after) = matches.value_of("after") { 1450 let time = Time::from_str(after) 1451 .map_err(|e| Error::general(&format!("invalid date format: {}", e.to_string())))?; 1452 options.after = Some(time); 1453 } 1454 1455 if let Some(after) = matches.value_of("before") { 1456 let time = Time::from_str(after) 1457 .map_err(|e| Error::general(&format!("invalid date format: {}", e.to_string())))?; 1458 options.before = Some(time); 1459 } 1460 1461 let command = Command::CertAuth(CaCommand::ShowHistoryCommands(my_ca, options)); 1462 Ok(Options::make(general_args, command)) 1463 } 1464 parse_matches_cas_history(matches: &ArgMatches) -> Result<Options, Error>1465 fn parse_matches_cas_history(matches: &ArgMatches) -> Result<Options, Error> { 1466 if let Some(m) = matches.subcommand_matches("commands") { 1467 Self::parse_matches_cas_history_commands(m) 1468 } else if let Some(m) = matches.subcommand_matches("details") { 1469 Self::parse_matches_cas_history_details(m) 1470 } else { 1471 Err(Error::UnrecognizedSubCommand) 1472 } 1473 } 1474 parse_matches_cas_children_add(matches: &ArgMatches) -> Result<Options, Error>1475 fn parse_matches_cas_children_add(matches: &ArgMatches) -> Result<Options, Error> { 1476 let path = matches.value_of("request").unwrap(); 1477 let bytes = Self::read_file_arg(path)?; 1478 let child_request = rfc8183::ChildRequest::validate(bytes.as_ref())?; 1479 1480 let general_args = GeneralArgs::from_matches(matches)?; 1481 let my_ca = Self::parse_my_ca(matches)?; 1482 1483 let child = matches.value_of("child").unwrap(); 1484 let child = Handle::from_str(child).map_err(|_| Error::InvalidHandle)?; 1485 1486 let resources = Self::parse_resource_args(matches)?.ok_or(Error::MissingResources)?; 1487 1488 let (_, _, id_cert) = child_request.unpack(); 1489 let add_child_request = AddChildRequest::new(child, resources, id_cert); 1490 let command = Command::CertAuth(CaCommand::ChildAdd(my_ca, add_child_request)); 1491 Ok(Options::make(general_args, command)) 1492 } 1493 parse_matches_cas_children_update(matches: &ArgMatches) -> Result<Options, Error>1494 fn parse_matches_cas_children_update(matches: &ArgMatches) -> Result<Options, Error> { 1495 let general_args = GeneralArgs::from_matches(matches)?; 1496 let my_ca = Self::parse_my_ca(matches)?; 1497 1498 let child = matches.value_of("child").unwrap(); 1499 let child = Handle::from_str(child).map_err(|_| Error::InvalidHandle)?; 1500 1501 let id_cert = { 1502 if let Some(path) = matches.value_of("idcert") { 1503 let bytes = Self::read_file_arg(path)?; 1504 let id_cert = IdCert::decode(bytes.as_ref()).map_err(|_| Error::InvalidChildIdCert)?; 1505 Some(id_cert) 1506 } else { 1507 None 1508 } 1509 }; 1510 let resources = Self::parse_resource_args(matches)?; 1511 1512 let update = UpdateChildRequest::new(id_cert, resources, None); 1513 1514 let command = Command::CertAuth(CaCommand::ChildUpdate(my_ca, child, update)); 1515 Ok(Options::make(general_args, command)) 1516 } 1517 parse_matches_cas_children_info(matches: &ArgMatches) -> Result<Options, Error>1518 fn parse_matches_cas_children_info(matches: &ArgMatches) -> Result<Options, Error> { 1519 let general_args = GeneralArgs::from_matches(matches)?; 1520 let my_ca = Self::parse_my_ca(matches)?; 1521 1522 let child = matches.value_of("child").unwrap(); 1523 let child = Handle::from_str(child).map_err(|_| Error::InvalidHandle)?; 1524 1525 let command = Command::CertAuth(CaCommand::ChildInfo(my_ca, child)); 1526 Ok(Options::make(general_args, command)) 1527 } 1528 parse_matches_cas_children_response(matches: &ArgMatches) -> Result<Options, Error>1529 fn parse_matches_cas_children_response(matches: &ArgMatches) -> Result<Options, Error> { 1530 let general_args = GeneralArgs::from_matches(matches)?; 1531 let my_ca = Self::parse_my_ca(matches)?; 1532 1533 let child = matches.value_of("child").unwrap(); 1534 let child = Handle::from_str(child).map_err(|_| Error::InvalidHandle)?; 1535 1536 let command = Command::CertAuth(CaCommand::ParentResponse(my_ca, child)); 1537 Ok(Options::make(general_args, command)) 1538 } 1539 parse_matches_cas_children_remove(matches: &ArgMatches) -> Result<Options, Error>1540 fn parse_matches_cas_children_remove(matches: &ArgMatches) -> Result<Options, Error> { 1541 let general_args = GeneralArgs::from_matches(matches)?; 1542 let my_ca = Self::parse_my_ca(matches)?; 1543 1544 let child = matches.value_of("child").unwrap(); 1545 let child = Handle::from_str(child).map_err(|_| Error::InvalidHandle)?; 1546 1547 let command = Command::CertAuth(CaCommand::ChildDelete(my_ca, child)); 1548 Ok(Options::make(general_args, command)) 1549 } 1550 parse_matches_cas_children_connections(matches: &ArgMatches) -> Result<Options, Error>1551 fn parse_matches_cas_children_connections(matches: &ArgMatches) -> Result<Options, Error> { 1552 let general_args = GeneralArgs::from_matches(matches)?; 1553 let my_ca = Self::parse_my_ca(matches)?; 1554 1555 let command = Command::CertAuth(CaCommand::ChildConnections(my_ca)); 1556 Ok(Options::make(general_args, command)) 1557 } 1558 parse_matches_cas_children_suspend(matches: &ArgMatches) -> Result<Options, Error>1559 fn parse_matches_cas_children_suspend(matches: &ArgMatches) -> Result<Options, Error> { 1560 let general_args = GeneralArgs::from_matches(matches)?; 1561 let my_ca = Self::parse_my_ca(matches)?; 1562 1563 let child = matches.value_of("child").unwrap(); 1564 let child = Handle::from_str(child).map_err(|_| Error::InvalidHandle)?; 1565 1566 let update = UpdateChildRequest::suspend(); 1567 1568 let command = Command::CertAuth(CaCommand::ChildUpdate(my_ca, child, update)); 1569 Ok(Options::make(general_args, command)) 1570 } 1571 parse_matches_cas_children_unsuspend(matches: &ArgMatches) -> Result<Options, Error>1572 fn parse_matches_cas_children_unsuspend(matches: &ArgMatches) -> Result<Options, Error> { 1573 let general_args = GeneralArgs::from_matches(matches)?; 1574 let my_ca = Self::parse_my_ca(matches)?; 1575 1576 let child = matches.value_of("child").unwrap(); 1577 let child = Handle::from_str(child).map_err(|_| Error::InvalidHandle)?; 1578 1579 let update = UpdateChildRequest::unsuspend(); 1580 1581 let command = Command::CertAuth(CaCommand::ChildUpdate(my_ca, child, update)); 1582 Ok(Options::make(general_args, command)) 1583 } 1584 parse_matches_cas_children(matches: &ArgMatches) -> Result<Options, Error>1585 fn parse_matches_cas_children(matches: &ArgMatches) -> Result<Options, Error> { 1586 if let Some(m) = matches.subcommand_matches("add") { 1587 Self::parse_matches_cas_children_add(m) 1588 } else if let Some(m) = matches.subcommand_matches("response") { 1589 Self::parse_matches_cas_children_response(m) 1590 } else if let Some(m) = matches.subcommand_matches("info") { 1591 Self::parse_matches_cas_children_info(m) 1592 } else if let Some(m) = matches.subcommand_matches("update") { 1593 Self::parse_matches_cas_children_update(m) 1594 } else if let Some(m) = matches.subcommand_matches("remove") { 1595 Self::parse_matches_cas_children_remove(m) 1596 } else if let Some(m) = matches.subcommand_matches("connections") { 1597 Self::parse_matches_cas_children_connections(m) 1598 } else if let Some(m) = matches.subcommand_matches("suspend") { 1599 Self::parse_matches_cas_children_suspend(m) 1600 } else if let Some(m) = matches.subcommand_matches("unsuspend") { 1601 Self::parse_matches_cas_children_unsuspend(m) 1602 } else { 1603 Err(Error::UnrecognizedSubCommand) 1604 } 1605 } 1606 parse_matches_cas_parents_request(matches: &ArgMatches) -> Result<Options, Error>1607 fn parse_matches_cas_parents_request(matches: &ArgMatches) -> Result<Options, Error> { 1608 let general_args = GeneralArgs::from_matches(matches)?; 1609 let my_ca = Self::parse_my_ca(matches)?; 1610 1611 let command = Command::CertAuth(CaCommand::ChildRequest(my_ca)); 1612 1613 Ok(Options::make(general_args, command)) 1614 } 1615 parse_matches_cas_parents_add(matches: &ArgMatches) -> Result<Options, Error>1616 fn parse_matches_cas_parents_add(matches: &ArgMatches) -> Result<Options, Error> { 1617 let path = matches.value_of("response").unwrap(); 1618 let bytes = Self::read_file_arg(path)?; 1619 let response = rfc8183::ParentResponse::validate(bytes.as_ref())?; 1620 1621 let general_args = GeneralArgs::from_matches(matches)?; 1622 let my_ca = Self::parse_my_ca(matches)?; 1623 1624 let parent = matches.value_of("parent").unwrap(); 1625 let parent = Handle::from_str(parent).map_err(|_| Error::InvalidHandle)?; 1626 let contact = ParentCaContact::for_rfc6492(response); 1627 let parent_req = ParentCaReq::new(parent, contact); 1628 1629 let command = Command::CertAuth(CaCommand::AddParent(my_ca, parent_req)); 1630 Ok(Options::make(general_args, command)) 1631 } 1632 parse_matches_cas_parents_info(matches: &ArgMatches) -> Result<Options, Error>1633 fn parse_matches_cas_parents_info(matches: &ArgMatches) -> Result<Options, Error> { 1634 let general_args = GeneralArgs::from_matches(matches)?; 1635 let my_ca = Self::parse_my_ca(matches)?; 1636 let parent = matches.value_of("parent").unwrap(); 1637 let parent = Handle::from_str(parent).map_err(|_| Error::InvalidHandle)?; 1638 1639 let command = Command::CertAuth(CaCommand::MyParentCaContact(my_ca, parent)); 1640 Ok(Options::make(general_args, command)) 1641 } 1642 parse_matches_cas_parents_statuses(matches: &ArgMatches) -> Result<Options, Error>1643 fn parse_matches_cas_parents_statuses(matches: &ArgMatches) -> Result<Options, Error> { 1644 let general_args = GeneralArgs::from_matches(matches)?; 1645 let my_ca = Self::parse_my_ca(matches)?; 1646 1647 let command = Command::CertAuth(CaCommand::ParentStatuses(my_ca)); 1648 Ok(Options::make(general_args, command)) 1649 } 1650 parse_matches_cas_parents_remove(matches: &ArgMatches) -> Result<Options, Error>1651 fn parse_matches_cas_parents_remove(matches: &ArgMatches) -> Result<Options, Error> { 1652 let general_args = GeneralArgs::from_matches(matches)?; 1653 let my_ca = Self::parse_my_ca(matches)?; 1654 let parent = matches.value_of("parent").unwrap(); 1655 let parent = Handle::from_str(parent).map_err(|_| Error::InvalidHandle)?; 1656 1657 let command = Command::CertAuth(CaCommand::RemoveParent(my_ca, parent)); 1658 Ok(Options::make(general_args, command)) 1659 } 1660 parse_matches_cas_parents(matches: &ArgMatches) -> Result<Options, Error>1661 fn parse_matches_cas_parents(matches: &ArgMatches) -> Result<Options, Error> { 1662 if let Some(m) = matches.subcommand_matches("request") { 1663 Self::parse_matches_cas_parents_request(m) 1664 } else if let Some(m) = matches.subcommand_matches("add") { 1665 Self::parse_matches_cas_parents_add(m) 1666 } else if let Some(m) = matches.subcommand_matches("contact") { 1667 Self::parse_matches_cas_parents_info(m) 1668 } else if let Some(m) = matches.subcommand_matches("statuses") { 1669 Self::parse_matches_cas_parents_statuses(m) 1670 } else if let Some(m) = matches.subcommand_matches("remove") { 1671 Self::parse_matches_cas_parents_remove(m) 1672 } else { 1673 Err(Error::UnrecognizedSubCommand) 1674 } 1675 } 1676 parse_matches_cas_keyroll_init(matches: &ArgMatches) -> Result<Options, Error>1677 fn parse_matches_cas_keyroll_init(matches: &ArgMatches) -> Result<Options, Error> { 1678 let general_args = GeneralArgs::from_matches(matches)?; 1679 let my_ca = Self::parse_my_ca(matches)?; 1680 1681 let command = Command::CertAuth(CaCommand::KeyRollInit(my_ca)); 1682 1683 Ok(Options::make(general_args, command)) 1684 } 1685 parse_matches_cas_keyroll_activate(matches: &ArgMatches) -> Result<Options, Error>1686 fn parse_matches_cas_keyroll_activate(matches: &ArgMatches) -> Result<Options, Error> { 1687 let general_args = GeneralArgs::from_matches(matches)?; 1688 let my_ca = Self::parse_my_ca(matches)?; 1689 1690 let command = Command::CertAuth(CaCommand::KeyRollActivate(my_ca)); 1691 1692 Ok(Options::make(general_args, command)) 1693 } 1694 parse_matches_cas_keyroll(matches: &ArgMatches) -> Result<Options, Error>1695 fn parse_matches_cas_keyroll(matches: &ArgMatches) -> Result<Options, Error> { 1696 if let Some(m) = matches.subcommand_matches("init") { 1697 Self::parse_matches_cas_keyroll_init(m) 1698 } else if let Some(m) = matches.subcommand_matches("activate") { 1699 Self::parse_matches_cas_keyroll_activate(m) 1700 } else { 1701 Err(Error::UnrecognizedSubCommand) 1702 } 1703 } 1704 parse_matches_cas_routes_list(matches: &ArgMatches) -> Result<Options, Error>1705 fn parse_matches_cas_routes_list(matches: &ArgMatches) -> Result<Options, Error> { 1706 let general_args = GeneralArgs::from_matches(matches)?; 1707 let my_ca = Self::parse_my_ca(matches)?; 1708 1709 let command = Command::CertAuth(CaCommand::RouteAuthorizationsList(my_ca)); 1710 1711 Ok(Options::make(general_args, command)) 1712 } 1713 parse_matches_cas_routes_update(matches: &ArgMatches) -> Result<Options, Error>1714 fn parse_matches_cas_routes_update(matches: &ArgMatches) -> Result<Options, Error> { 1715 let general_args = GeneralArgs::from_matches(matches)?; 1716 let my_ca = Self::parse_my_ca(matches)?; 1717 1718 let updates = if let Some(path) = matches.value_of("delta") { 1719 if matches.is_present("add") || matches.is_present("remove") { 1720 return Err(Error::general("Cannot use --add or --remove if --delta is specified")); 1721 } 1722 1723 let bytes = Self::read_file_arg(path)?; 1724 let updates_str = unsafe { from_utf8_unchecked(&bytes) }; 1725 RoaDefinitionUpdates::from_str(updates_str)? 1726 } else { 1727 let mut added = vec![]; 1728 let mut removed = vec![]; 1729 1730 if let Some(add) = matches.values_of("add") { 1731 for roa_str in add { 1732 let roa: RoaDefinition = RoaDefinition::from_str(roa_str)?; 1733 added.push(roa); 1734 } 1735 } 1736 1737 if let Some(remove) = matches.values_of("remove") { 1738 for roa_str in remove { 1739 let roa: RoaDefinition = RoaDefinition::from_str(roa_str)?; 1740 removed.push(roa); 1741 } 1742 } 1743 1744 if added.is_empty() && removed.is_empty() { 1745 return Err(Error::general( 1746 "You MUST specify either --delta, or --add and/or --remove", 1747 )); 1748 } 1749 1750 RoaDefinitionUpdates::new(added, removed) 1751 }; 1752 1753 if matches.is_present("dryrun") && matches.is_present("try") { 1754 return Err(Error::general("You cannot use both --dryrun and --try")); 1755 } 1756 1757 let command = if matches.is_present("dryrun") { 1758 Command::CertAuth(CaCommand::RouteAuthorizationsDryRunUpdate(my_ca, updates)) 1759 } else if matches.is_present("try") { 1760 Command::CertAuth(CaCommand::RouteAuthorizationsTryUpdate(my_ca, updates)) 1761 } else { 1762 Command::CertAuth(CaCommand::RouteAuthorizationsUpdate(my_ca, updates)) 1763 }; 1764 1765 Ok(Options::make(general_args, command)) 1766 } 1767 parse_matches_cas_routes_bgp_full(matches: &ArgMatches) -> Result<Options, Error>1768 fn parse_matches_cas_routes_bgp_full(matches: &ArgMatches) -> Result<Options, Error> { 1769 let general_args = GeneralArgs::from_matches(matches)?; 1770 let my_ca = Self::parse_my_ca(matches)?; 1771 Ok(Options::make( 1772 general_args, 1773 Command::CertAuth(CaCommand::BgpAnalysisFull(my_ca)), 1774 )) 1775 } 1776 parse_matches_cas_routes_bgp_suggest(matches: &ArgMatches) -> Result<Options, Error>1777 fn parse_matches_cas_routes_bgp_suggest(matches: &ArgMatches) -> Result<Options, Error> { 1778 let general_args = GeneralArgs::from_matches(matches)?; 1779 let my_ca = Self::parse_my_ca(matches)?; 1780 1781 let v4 = matches.value_of("ipv4").unwrap_or(""); 1782 let v6 = matches.value_of("ipv6").unwrap_or(""); 1783 1784 let resources = ResourceSet::from_strs("", v4, v6) 1785 .map_err(|e| Error::GeneralArgumentError(format!("Could not parse IP resources: {}", e)))?; 1786 1787 let resources = if resources.is_empty() { None } else { Some(resources) }; 1788 1789 Ok(Options::make( 1790 general_args, 1791 Command::CertAuth(CaCommand::BgpAnalysisSuggest(my_ca, resources)), 1792 )) 1793 } 1794 parse_matches_cas_routes_bgp(matches: &ArgMatches) -> Result<Options, Error>1795 fn parse_matches_cas_routes_bgp(matches: &ArgMatches) -> Result<Options, Error> { 1796 if let Some(m) = matches.subcommand_matches("analyze") { 1797 Self::parse_matches_cas_routes_bgp_full(m) 1798 } else if let Some(m) = matches.subcommand_matches("suggest") { 1799 Self::parse_matches_cas_routes_bgp_suggest(m) 1800 } else { 1801 Err(Error::UnrecognizedSubCommand) 1802 } 1803 } 1804 parse_matches_cas_routes(matches: &ArgMatches) -> Result<Options, Error>1805 fn parse_matches_cas_routes(matches: &ArgMatches) -> Result<Options, Error> { 1806 if let Some(m) = matches.subcommand_matches("list") { 1807 Self::parse_matches_cas_routes_list(m) 1808 } else if let Some(m) = matches.subcommand_matches("update") { 1809 Self::parse_matches_cas_routes_update(m) 1810 } else if let Some(m) = matches.subcommand_matches("bgp") { 1811 Self::parse_matches_cas_routes_bgp(m) 1812 } else { 1813 Err(Error::UnrecognizedSubCommand) 1814 } 1815 } 1816 parse_matches_cas_aspas_add(matches: &ArgMatches) -> Result<Options, Error>1817 fn parse_matches_cas_aspas_add(matches: &ArgMatches) -> Result<Options, Error> { 1818 let general_args = GeneralArgs::from_matches(matches)?; 1819 let my_ca = Self::parse_my_ca(matches)?; 1820 1821 let aspa_config_str = matches.value_of("aspa").unwrap(); // required argument 1822 let aspa = AspaDefinition::from_str(aspa_config_str)?; 1823 1824 let command = Command::CertAuth(CaCommand::AspasAddOrReplace(my_ca, aspa)); 1825 1826 Ok(Options::make(general_args, command)) 1827 } 1828 parse_matches_cas_aspas_remove(matches: &ArgMatches) -> Result<Options, Error>1829 fn parse_matches_cas_aspas_remove(matches: &ArgMatches) -> Result<Options, Error> { 1830 let general_args = GeneralArgs::from_matches(matches)?; 1831 let my_ca = Self::parse_my_ca(matches)?; 1832 let customer_str = matches.value_of("customer").unwrap(); 1833 let customer = AspaCustomer::from_str(customer_str).map_err(|_| Error::invalid_asn(customer_str))?; 1834 1835 let command = Command::CertAuth(CaCommand::AspasRemove(my_ca, customer)); 1836 1837 Ok(Options::make(general_args, command)) 1838 } 1839 parse_matches_cas_aspas_update(matches: &ArgMatches) -> Result<Options, Error>1840 fn parse_matches_cas_aspas_update(matches: &ArgMatches) -> Result<Options, Error> { 1841 let general_args = GeneralArgs::from_matches(matches)?; 1842 let my_ca = Self::parse_my_ca(matches)?; 1843 1844 let mut added = vec![]; 1845 let mut removed = vec![]; 1846 1847 let customer_str = matches.value_of("customer").unwrap(); 1848 let customer = AspaCustomer::from_str(customer_str).map_err(|_| Error::invalid_asn(customer_str))?; 1849 1850 if let Some(add) = matches.values_of("add") { 1851 for provider_str in add { 1852 let provider = ProviderAs::from_str(provider_str).map_err(|_| Error::invalid_asn(provider_str))?; 1853 added.push(provider); 1854 } 1855 } 1856 1857 if let Some(remove) = matches.values_of("remove") { 1858 for provider_as_str in remove { 1859 let provider_as = 1860 ProviderAs::from_str(provider_as_str).map_err(|_| Error::invalid_asn(provider_as_str))?; 1861 1862 if added.iter().any(|added| added.provider() == provider_as.provider()) { 1863 return Err(Error::general("Do not add and remove the same AS in a single update.")); 1864 } 1865 1866 removed.push(provider_as); 1867 } 1868 } 1869 1870 let update = AspaProvidersUpdate::new(added, removed); 1871 if update.is_empty() { 1872 return Err(Error::general("You MUST specify at least one of --add or --remove")); 1873 } 1874 1875 let command = Command::CertAuth(CaCommand::AspasUpdate(my_ca, customer, update)); 1876 1877 Ok(Options::make(general_args, command)) 1878 } 1879 parse_matches_cas_aspas_list(matches: &ArgMatches) -> Result<Options, Error>1880 fn parse_matches_cas_aspas_list(matches: &ArgMatches) -> Result<Options, Error> { 1881 let general_args = GeneralArgs::from_matches(matches)?; 1882 let my_ca = Self::parse_my_ca(matches)?; 1883 1884 let command = Command::CertAuth(CaCommand::AspasList(my_ca)); 1885 1886 Ok(Options::make(general_args, command)) 1887 } 1888 parse_matches_cas_aspas(matches: &ArgMatches) -> Result<Options, Error>1889 fn parse_matches_cas_aspas(matches: &ArgMatches) -> Result<Options, Error> { 1890 if let Some(m) = matches.subcommand_matches("add") { 1891 Self::parse_matches_cas_aspas_add(m) 1892 } else if let Some(m) = matches.subcommand_matches("remove") { 1893 Self::parse_matches_cas_aspas_remove(m) 1894 } else if let Some(m) = matches.subcommand_matches("update") { 1895 Self::parse_matches_cas_aspas_update(m) 1896 } else if let Some(m) = matches.subcommand_matches("list") { 1897 Self::parse_matches_cas_aspas_list(m) 1898 } else { 1899 Err(Error::UnrecognizedSubCommand) 1900 } 1901 } 1902 parse_matches_cas_repo_request(matches: &ArgMatches) -> Result<Options, Error>1903 fn parse_matches_cas_repo_request(matches: &ArgMatches) -> Result<Options, Error> { 1904 let general_args = GeneralArgs::from_matches(matches)?; 1905 let my_ca = Self::parse_my_ca(matches)?; 1906 1907 let command = Command::CertAuth(CaCommand::RepoPublisherRequest(my_ca)); 1908 1909 Ok(Options::make(general_args, command)) 1910 } 1911 parse_matches_cas_repo_details(matches: &ArgMatches) -> Result<Options, Error>1912 fn parse_matches_cas_repo_details(matches: &ArgMatches) -> Result<Options, Error> { 1913 let general_args = GeneralArgs::from_matches(matches)?; 1914 let my_ca = Self::parse_my_ca(matches)?; 1915 1916 let command = Command::CertAuth(CaCommand::RepoDetails(my_ca)); 1917 1918 Ok(Options::make(general_args, command)) 1919 } 1920 parse_matches_cas_repo_status(matches: &ArgMatches) -> Result<Options, Error>1921 fn parse_matches_cas_repo_status(matches: &ArgMatches) -> Result<Options, Error> { 1922 let general_args = GeneralArgs::from_matches(matches)?; 1923 let my_ca = Self::parse_my_ca(matches)?; 1924 1925 let command = Command::CertAuth(CaCommand::RepoStatus(my_ca)); 1926 1927 Ok(Options::make(general_args, command)) 1928 } 1929 parse_matches_cas_repo_configure(matches: &ArgMatches) -> Result<Options, Error>1930 fn parse_matches_cas_repo_configure(matches: &ArgMatches) -> Result<Options, Error> { 1931 let general_args = GeneralArgs::from_matches(matches)?; 1932 let my_ca = Self::parse_my_ca(matches)?; 1933 1934 let path = matches.value_of("response").unwrap(); 1935 let bytes = Self::read_file_arg(path)?; 1936 let response = rfc8183::RepositoryResponse::validate(bytes.as_ref())?; 1937 1938 let repo_contact = RepositoryContact::new(response); 1939 let command = Command::CertAuth(CaCommand::RepoUpdate(my_ca, repo_contact)); 1940 Ok(Options::make(general_args, command)) 1941 } 1942 parse_matches_cas_repo(matches: &ArgMatches) -> Result<Options, Error>1943 fn parse_matches_cas_repo(matches: &ArgMatches) -> Result<Options, Error> { 1944 if let Some(m) = matches.subcommand_matches("request") { 1945 Self::parse_matches_cas_repo_request(m) 1946 } else if let Some(m) = matches.subcommand_matches("show") { 1947 Self::parse_matches_cas_repo_details(m) 1948 } else if let Some(m) = matches.subcommand_matches("status") { 1949 Self::parse_matches_cas_repo_status(m) 1950 } else if let Some(m) = matches.subcommand_matches("configure") { 1951 Self::parse_matches_cas_repo_configure(m) 1952 } else { 1953 Err(Error::UnrecognizedSubCommand) 1954 } 1955 } 1956 parse_matches_cas_issues(matches: &ArgMatches) -> Result<Options, Error>1957 fn parse_matches_cas_issues(matches: &ArgMatches) -> Result<Options, Error> { 1958 let general = GeneralArgs::from_matches(matches)?; 1959 let command = if let Ok(ca) = Self::parse_my_ca(matches) { 1960 Command::CertAuth(CaCommand::Issues(Some(ca))) 1961 } else { 1962 Command::CertAuth(CaCommand::Issues(None)) 1963 }; 1964 Ok(Options::make(general, command)) 1965 } 1966 parse_matches_cas_rta_list(matches: &ArgMatches) -> Result<Options, Error>1967 fn parse_matches_cas_rta_list(matches: &ArgMatches) -> Result<Options, Error> { 1968 let general_args = GeneralArgs::from_matches(matches)?; 1969 let ca = Self::parse_my_ca(matches)?; 1970 let command = Command::CertAuth(CaCommand::RtaList(ca)); 1971 Ok(Options::make(general_args, command)) 1972 } 1973 parse_matches_cas_rta_show(matches: &ArgMatches) -> Result<Options, Error>1974 fn parse_matches_cas_rta_show(matches: &ArgMatches) -> Result<Options, Error> { 1975 let general_args = GeneralArgs::from_matches(matches)?; 1976 let ca = Self::parse_my_ca(matches)?; 1977 let name = matches.value_of("name").unwrap().to_string(); 1978 1979 let out_file = matches.value_of("out").unwrap(); 1980 let out_file = PathBuf::from_str(out_file) 1981 .map_err(|_| Error::GeneralArgumentError(format!("Invalid filename: {}", out_file)))?; 1982 1983 file::save(&[], &out_file).map_err(|e| { 1984 Error::GeneralArgumentError(format!( 1985 "Cannot save to file: {}, error: {}", 1986 out_file.to_string_lossy(), 1987 e 1988 )) 1989 })?; 1990 1991 let command = Command::CertAuth(CaCommand::RtaShow(ca, name, Some(out_file))); 1992 Ok(Options::make(general_args, command)) 1993 } 1994 parse_matches_cas_rta_sign(matches: &ArgMatches) -> Result<Options, Error>1995 fn parse_matches_cas_rta_sign(matches: &ArgMatches) -> Result<Options, Error> { 1996 let general_args = GeneralArgs::from_matches(matches)?; 1997 let ca = Self::parse_my_ca(matches)?; 1998 1999 let days = matches.value_of("days").unwrap(); 2000 let days = 2001 i64::from_str(days).map_err(|e| Error::GeneralArgumentError(format!("Invalid number of days: {}", e)))?; 2002 2003 let in_file = matches.value_of("in").unwrap(); 2004 let in_file = PathBuf::from_str(in_file) 2005 .map_err(|_| Error::GeneralArgumentError(format!("Invalid filename: {}", in_file)))?; 2006 2007 let content = file::read(&in_file).map_err(|e| { 2008 Error::GeneralArgumentError(format!( 2009 "Can't read file '{}', error: {}", 2010 in_file.to_string_lossy().to_string(), 2011 e, 2012 )) 2013 })?; 2014 2015 let name = matches.value_of("name").unwrap().to_string(); 2016 2017 let validity = SignSupport::sign_validity_days(days); 2018 2019 let resources = Self::parse_resource_args(matches)? 2020 .ok_or_else(|| Error::general("You must specify at least one of --ipv4, --ipv6 or --asn"))?; 2021 2022 let keys = if let Some(keys) = matches.values_of("keys") { 2023 let mut res = vec![]; 2024 for key_str in keys { 2025 let ki = KeyIdentifier::from_str(key_str).map_err(|_| Error::general("Invalid key identifier"))?; 2026 res.push(ki) 2027 } 2028 res 2029 } else { 2030 vec![] 2031 }; 2032 2033 let request = RtaContentRequest::new(resources, validity, keys, content); 2034 let command = Command::CertAuth(CaCommand::RtaSign(ca, name, request)); 2035 Ok(Options::make(general_args, command)) 2036 } 2037 parse_matches_cas_rta_multi_sign(matches: &ArgMatches) -> Result<Options, Error>2038 fn parse_matches_cas_rta_multi_sign(matches: &ArgMatches) -> Result<Options, Error> { 2039 let general_args = GeneralArgs::from_matches(matches)?; 2040 let ca = Self::parse_my_ca(matches)?; 2041 let name = matches.value_of("name").unwrap().to_string(); 2042 2043 let in_file = matches.value_of("in").unwrap(); 2044 let in_file = PathBuf::from_str(in_file) 2045 .map_err(|_| Error::GeneralArgumentError(format!("Invalid filename: {}", in_file)))?; 2046 2047 let content = file::read(&in_file).map_err(|e| { 2048 Error::GeneralArgumentError(format!( 2049 "Can't read file '{}', error: {}", 2050 in_file.to_string_lossy().to_string(), 2051 e, 2052 )) 2053 })?; 2054 2055 let rta = ResourceTaggedAttestation::new(content); 2056 2057 let command = Command::CertAuth(CaCommand::RtaMultiCoSign(ca, name, rta)); 2058 Ok(Options::make(general_args, command)) 2059 } 2060 parse_matches_cas_rta_multi_prep(matches: &ArgMatches) -> Result<Options, Error>2061 fn parse_matches_cas_rta_multi_prep(matches: &ArgMatches) -> Result<Options, Error> { 2062 let general_args = GeneralArgs::from_matches(matches)?; 2063 let ca = Self::parse_my_ca(matches)?; 2064 2065 let name = matches.value_of("name").unwrap().to_string(); 2066 let resources = Self::parse_resource_args(matches)? 2067 .ok_or_else(|| Error::general("You must specify at least one of --ipv4, --ipv6 or --asn"))?; 2068 2069 let days = matches.value_of("days").unwrap(); 2070 let days = 2071 i64::from_str(days).map_err(|e| Error::GeneralArgumentError(format!("Invalid number of days: {}", e)))?; 2072 let validity = SignSupport::sign_validity_days(days); 2073 2074 let request = RtaPrepareRequest::new(resources, validity); 2075 2076 let command = Command::CertAuth(CaCommand::RtaMultiPrep(ca, name, request)); 2077 Ok(Options::make(general_args, command)) 2078 } 2079 parse_matches_cas_rta_multi(matches: &ArgMatches) -> Result<Options, Error>2080 fn parse_matches_cas_rta_multi(matches: &ArgMatches) -> Result<Options, Error> { 2081 if let Some(m) = matches.subcommand_matches("prep") { 2082 Self::parse_matches_cas_rta_multi_prep(m) 2083 } else if let Some(m) = matches.subcommand_matches("cosign") { 2084 Self::parse_matches_cas_rta_multi_sign(m) 2085 } else { 2086 Err(Error::UnrecognizedSubCommand) 2087 } 2088 } 2089 parse_matches_cas_rta(matches: &ArgMatches) -> Result<Options, Error>2090 fn parse_matches_cas_rta(matches: &ArgMatches) -> Result<Options, Error> { 2091 if let Some(m) = matches.subcommand_matches("list") { 2092 Self::parse_matches_cas_rta_list(m) 2093 } else if let Some(m) = matches.subcommand_matches("show") { 2094 Self::parse_matches_cas_rta_show(m) 2095 } else if let Some(m) = matches.subcommand_matches("sign") { 2096 Self::parse_matches_cas_rta_sign(m) 2097 } else if let Some(m) = matches.subcommand_matches("multi") { 2098 Self::parse_matches_cas_rta_multi(m) 2099 } else { 2100 Err(Error::UnrecognizedSubCommand) 2101 } 2102 } 2103 parse_matches_bulk(matches: &ArgMatches) -> Result<Options, Error>2104 fn parse_matches_bulk(matches: &ArgMatches) -> Result<Options, Error> { 2105 if let Some(m) = matches.subcommand_matches("publish") { 2106 let general_args = GeneralArgs::from_matches(m)?; 2107 let command = Command::Bulk(BulkCaCommand::Publish); 2108 Ok(Options::make(general_args, command)) 2109 } else if let Some(m) = matches.subcommand_matches("refresh") { 2110 let general_args = GeneralArgs::from_matches(m)?; 2111 let command = Command::Bulk(BulkCaCommand::Refresh); 2112 Ok(Options::make(general_args, command)) 2113 } else if let Some(m) = matches.subcommand_matches("sync") { 2114 let general_args = GeneralArgs::from_matches(m)?; 2115 let command = Command::Bulk(BulkCaCommand::Sync); 2116 Ok(Options::make(general_args, command)) 2117 } else { 2118 Err(Error::UnrecognizedSubCommand) 2119 } 2120 } 2121 parse_matches_health(matches: &ArgMatches) -> Result<Options, Error>2122 fn parse_matches_health(matches: &ArgMatches) -> Result<Options, Error> { 2123 let general_args = GeneralArgs::from_matches(matches)?; 2124 let command = Command::Health; 2125 Ok(Options::make(general_args, command)) 2126 } 2127 parse_matches_info(matches: &ArgMatches) -> Result<Options, Error>2128 fn parse_matches_info(matches: &ArgMatches) -> Result<Options, Error> { 2129 let general_args = GeneralArgs::from_matches(matches)?; 2130 let command = Command::Info; 2131 Ok(Options::make(general_args, command)) 2132 } 2133 parse_publisher_arg(matches: &ArgMatches) -> Result<PublisherHandle, Error>2134 fn parse_publisher_arg(matches: &ArgMatches) -> Result<PublisherHandle, Error> { 2135 let publisher_str = matches.value_of("publisher").unwrap(); 2136 PublisherHandle::from_str(publisher_str).map_err(|_| Error::InvalidHandle) 2137 } 2138 parse_matches_publishers_list(matches: &ArgMatches) -> Result<Options, Error>2139 fn parse_matches_publishers_list(matches: &ArgMatches) -> Result<Options, Error> { 2140 let general_args = GeneralArgs::from_matches(matches)?; 2141 let command = Command::PubServer(PubServerCommand::PublisherList); 2142 Ok(Options::make(general_args, command)) 2143 } 2144 parse_matches_publishers_stale(matches: &ArgMatches) -> Result<Options, Error>2145 fn parse_matches_publishers_stale(matches: &ArgMatches) -> Result<Options, Error> { 2146 let general_args = GeneralArgs::from_matches(matches)?; 2147 let seconds = i64::from_str(matches.value_of("seconds").unwrap()).map_err(|_| Error::InvalidSeconds)?; 2148 let command = Command::PubServer(PubServerCommand::StalePublishers(seconds)); 2149 Ok(Options::make(general_args, command)) 2150 } 2151 parse_matches_publishers_add(matches: &ArgMatches) -> Result<Options, Error>2152 fn parse_matches_publishers_add(matches: &ArgMatches) -> Result<Options, Error> { 2153 let general_args = GeneralArgs::from_matches(matches)?; 2154 2155 let path = matches.value_of("request").unwrap(); 2156 let path = PathBuf::from(path); 2157 let bytes = file::read(&path)?; 2158 let mut req = rfc8183::PublisherRequest::validate(bytes.as_ref())?; 2159 2160 if let Some(publisher_str) = matches.value_of("publisher") { 2161 let publisher = PublisherHandle::from_str(publisher_str).map_err(|_| Error::InvalidHandle)?; 2162 let (tag, _, cert) = req.unpack(); 2163 req = rfc8183::PublisherRequest::new(tag, publisher, cert); 2164 } 2165 2166 let command = Command::PubServer(PubServerCommand::AddPublisher(req)); 2167 Ok(Options::make(general_args, command)) 2168 } 2169 parse_matches_publishers_remove(matches: &ArgMatches) -> Result<Options, Error>2170 fn parse_matches_publishers_remove(matches: &ArgMatches) -> Result<Options, Error> { 2171 let general_args = GeneralArgs::from_matches(matches)?; 2172 let publisher = Self::parse_publisher_arg(matches)?; 2173 let command = Command::PubServer(PubServerCommand::RemovePublisher(publisher)); 2174 Ok(Options::make(general_args, command)) 2175 } 2176 parse_matches_publishers_show(matches: &ArgMatches) -> Result<Options, Error>2177 fn parse_matches_publishers_show(matches: &ArgMatches) -> Result<Options, Error> { 2178 let general_args = GeneralArgs::from_matches(matches)?; 2179 let publisher = Self::parse_publisher_arg(matches)?; 2180 let command = Command::PubServer(PubServerCommand::ShowPublisher(publisher)); 2181 Ok(Options::make(general_args, command)) 2182 } 2183 parse_matches_publishers_repo_response(matches: &ArgMatches) -> Result<Options, Error>2184 fn parse_matches_publishers_repo_response(matches: &ArgMatches) -> Result<Options, Error> { 2185 let general_args = GeneralArgs::from_matches(matches)?; 2186 let publisher = Self::parse_publisher_arg(matches)?; 2187 let command = Command::PubServer(PubServerCommand::RepositoryResponse(publisher)); 2188 Ok(Options::make(general_args, command)) 2189 } 2190 parse_matches_publication_server_stats(matches: &ArgMatches) -> Result<Options, Error>2191 fn parse_matches_publication_server_stats(matches: &ArgMatches) -> Result<Options, Error> { 2192 let general_args = GeneralArgs::from_matches(matches)?; 2193 let command = Command::PubServer(PubServerCommand::RepositoryStats); 2194 Ok(Options::make(general_args, command)) 2195 } 2196 parse_matches_publication_server_init(matches: &ArgMatches) -> Result<Options, Error>2197 fn parse_matches_publication_server_init(matches: &ArgMatches) -> Result<Options, Error> { 2198 let general_args = GeneralArgs::from_matches(matches)?; 2199 2200 let rsync_str = matches.value_of("rsync").unwrap(); 2201 let rrdp_str = matches.value_of("rrdp").unwrap(); 2202 2203 if !rsync_str.ends_with('/') { 2204 return Err(Error::general("rsync base URI must end with '/'")); 2205 } 2206 2207 if !rrdp_str.ends_with('/') { 2208 return Err(Error::general("RRDP base URI must end with '/'")); 2209 } 2210 2211 let rsync = uri::Rsync::from_str(rsync_str) 2212 .map_err(|e| Error::GeneralArgumentError(format!("Invalid rsync URI: {}", e)))?; 2213 2214 let rrdp = uri::Https::from_str(rrdp_str) 2215 .map_err(|e| Error::GeneralArgumentError(format!("Invalid RRDP URI: {}", e)))?; 2216 2217 let uris = PublicationServerUris::new(rrdp, rsync); 2218 2219 let command = Command::PubServer(PubServerCommand::RepositoryInit(uris)); 2220 Ok(Options::make(general_args, command)) 2221 } 2222 parse_matches_publication_server_clear(matches: &ArgMatches) -> Result<Options, Error>2223 fn parse_matches_publication_server_clear(matches: &ArgMatches) -> Result<Options, Error> { 2224 let general_args = GeneralArgs::from_matches(matches)?; 2225 let command = Command::PubServer(PubServerCommand::RepositoryClear); 2226 Ok(Options::make(general_args, command)) 2227 } 2228 parse_matches_publication_server(matches: &ArgMatches) -> Result<Options, Error>2229 fn parse_matches_publication_server(matches: &ArgMatches) -> Result<Options, Error> { 2230 if let Some(m) = matches.subcommand_matches("stats") { 2231 Self::parse_matches_publication_server_stats(m) 2232 } else if let Some(m) = matches.subcommand_matches("init") { 2233 Self::parse_matches_publication_server_init(m) 2234 } else if let Some(m) = matches.subcommand_matches("clear") { 2235 Self::parse_matches_publication_server_clear(m) 2236 } else { 2237 Err(Error::UnrecognizedSubCommand) 2238 } 2239 } 2240 parse_matches_publishers(matches: &ArgMatches) -> Result<Options, Error>2241 fn parse_matches_publishers(matches: &ArgMatches) -> Result<Options, Error> { 2242 if let Some(m) = matches.subcommand_matches("list") { 2243 Self::parse_matches_publishers_list(m) 2244 } else if let Some(m) = matches.subcommand_matches("stale") { 2245 Self::parse_matches_publishers_stale(m) 2246 } else if let Some(m) = matches.subcommand_matches("add") { 2247 Self::parse_matches_publishers_add(m) 2248 } else if let Some(m) = matches.subcommand_matches("remove") { 2249 Self::parse_matches_publishers_remove(m) 2250 } else if let Some(m) = matches.subcommand_matches("show") { 2251 Self::parse_matches_publishers_show(m) 2252 } else if let Some(m) = matches.subcommand_matches("response") { 2253 Self::parse_matches_publishers_repo_response(m) 2254 } else { 2255 Err(Error::UnrecognizedSubCommand) 2256 } 2257 } 2258 parse_matches_pubserver(matches: &ArgMatches) -> Result<Options, Error>2259 fn parse_matches_pubserver(matches: &ArgMatches) -> Result<Options, Error> { 2260 if let Some(m) = matches.subcommand_matches("publishers") { 2261 Self::parse_matches_publishers(m) 2262 } else if let Some(m) = matches.subcommand_matches("server") { 2263 Self::parse_matches_publication_server(m) 2264 } else { 2265 Err(Error::UnrecognizedSubCommand) 2266 } 2267 } 2268 parse_matches(matches: ArgMatches) -> Result<Options, Error>2269 fn parse_matches(matches: ArgMatches) -> Result<Options, Error> { 2270 if let Some(m) = matches.subcommand_matches("config") { 2271 Self::parse_matches_config(m) 2272 } else if let Some(m) = matches.subcommand_matches("list") { 2273 Self::parse_matches_cas_list(m) 2274 } else if let Some(m) = matches.subcommand_matches("add") { 2275 Self::parse_matches_cas_add(m) 2276 } else if let Some(m) = matches.subcommand_matches("delete") { 2277 Self::parse_matches_cas_delete(m) 2278 } else if let Some(m) = matches.subcommand_matches("show") { 2279 Self::parse_matches_cas_show(m) 2280 } else if let Some(m) = matches.subcommand_matches("history") { 2281 Self::parse_matches_cas_history(m) 2282 } else if let Some(m) = matches.subcommand_matches("children") { 2283 Self::parse_matches_cas_children(m) 2284 } else if let Some(m) = matches.subcommand_matches("parents") { 2285 Self::parse_matches_cas_parents(m) 2286 } else if let Some(m) = matches.subcommand_matches("keyroll") { 2287 Self::parse_matches_cas_keyroll(m) 2288 } else if let Some(m) = matches.subcommand_matches("roas") { 2289 Self::parse_matches_cas_routes(m) 2290 } else if let Some(m) = matches.subcommand_matches("aspas") { 2291 Self::parse_matches_cas_aspas(m) 2292 } else if let Some(m) = matches.subcommand_matches("repo") { 2293 Self::parse_matches_cas_repo(m) 2294 } else if let Some(m) = matches.subcommand_matches("issues") { 2295 Self::parse_matches_cas_issues(m) 2296 } else if let Some(m) = matches.subcommand_matches("rta") { 2297 Self::parse_matches_cas_rta(m) 2298 } else if let Some(m) = matches.subcommand_matches("bulk") { 2299 Self::parse_matches_bulk(m) 2300 } else if let Some(m) = matches.subcommand_matches("health") { 2301 Self::parse_matches_health(m) 2302 } else if let Some(m) = matches.subcommand_matches("info") { 2303 Self::parse_matches_info(m) 2304 } else if let Some(m) = matches.subcommand_matches("pubserver") { 2305 Self::parse_matches_pubserver(m) 2306 } else { 2307 Err(Error::UnrecognizedSubCommand) 2308 } 2309 } 2310 from_args() -> Result<Options, Error>2311 pub fn from_args() -> Result<Options, Error> { 2312 let matches = Self::make_matches(); 2313 Self::parse_matches(matches) 2314 } 2315 } 2316 2317 #[derive(Clone, Debug, Eq, PartialEq)] 2318 #[allow(clippy::large_enum_variant)] 2319 pub enum Command { 2320 NotSet, 2321 Health, 2322 Info, 2323 Bulk(BulkCaCommand), 2324 CertAuth(CaCommand), 2325 PubServer(PubServerCommand), 2326 Init(KrillInitDetails), 2327 #[cfg(feature = "multi-user")] 2328 User(KrillUserDetails), 2329 } 2330 2331 #[derive(Clone, Debug, Eq, PartialEq)] 2332 #[allow(clippy::large_enum_variant)] 2333 pub enum CaCommand { 2334 Init(CertAuthInit), // Initialize a CA 2335 UpdateId(Handle), // Update CA id 2336 Delete(Handle), // Delete the CA -> let it withdraw and request revocation as well 2337 2338 // Publishing 2339 RepoPublisherRequest(Handle), // Get the RFC8183 publisher request 2340 RepoDetails(Handle), 2341 RepoUpdate(Handle, RepositoryContact), 2342 RepoStatus(Handle), 2343 2344 // Parents (to this CA) 2345 ChildRequest(Handle), // Get the RFC8183 child request 2346 AddParent(Handle, ParentCaReq), 2347 MyParentCaContact(Handle, ParentHandle), 2348 ParentStatuses(Handle), 2349 RemoveParent(Handle, ParentHandle), 2350 Refresh(Handle), // Refresh with all parents 2351 2352 // Children 2353 ParentResponse(Handle, ChildHandle), // Get an RFC8183 parent response for a child 2354 ChildInfo(Handle, ChildHandle), 2355 ChildAdd(Handle, AddChildRequest), 2356 ChildUpdate(Handle, ChildHandle, UpdateChildRequest), 2357 ChildDelete(Handle, ChildHandle), 2358 ChildConnections(Handle), 2359 2360 // Key Management 2361 KeyRollInit(Handle), 2362 KeyRollActivate(Handle), 2363 2364 // Authorizations 2365 RouteAuthorizationsList(Handle), 2366 RouteAuthorizationsUpdate(Handle, RoaDefinitionUpdates), 2367 RouteAuthorizationsTryUpdate(Handle, RoaDefinitionUpdates), 2368 RouteAuthorizationsDryRunUpdate(Handle, RoaDefinitionUpdates), 2369 BgpAnalysisFull(Handle), 2370 BgpAnalysisSuggest(Handle, Option<ResourceSet>), 2371 2372 // ASPAs 2373 AspasList(Handle), 2374 AspasAddOrReplace(Handle, AspaDefinition), 2375 AspasUpdate(Handle, AspaCustomer, AspaProvidersUpdate), 2376 AspasRemove(Handle, AspaCustomer), 2377 2378 // Show details for this CA 2379 Show(Handle), 2380 ShowHistoryCommands(Handle, HistoryOptions), 2381 ShowHistoryDetails(Handle, String), 2382 Issues(Option<Handle>), 2383 2384 // RTA 2385 RtaList(Handle), 2386 RtaShow(Handle, RtaName, Option<PathBuf>), 2387 RtaSign(Handle, RtaName, RtaContentRequest), 2388 RtaMultiPrep(Handle, RtaName, RtaPrepareRequest), 2389 RtaMultiCoSign(Handle, RtaName, ResourceTaggedAttestation), 2390 2391 // List all CAs 2392 List, 2393 } 2394 2395 #[derive(Clone, Debug, Eq, PartialEq)] 2396 pub struct HistoryOptions { 2397 pub offset: u64, 2398 pub rows: u64, 2399 pub after: Option<Time>, 2400 pub before: Option<Time>, 2401 } 2402 2403 impl Default for HistoryOptions { default() -> Self2404 fn default() -> Self { 2405 HistoryOptions { 2406 offset: 0, 2407 rows: 100, 2408 after: None, 2409 before: None, 2410 } 2411 } 2412 } 2413 2414 impl HistoryOptions { url_path_parameters(&self) -> String2415 pub fn url_path_parameters(&self) -> String { 2416 if let Some(before) = self.before { 2417 let after = self.after.map(|t| t.timestamp()).unwrap_or_else(|| 0); 2418 format!("{}/{}/{}/{}", self.rows, self.offset, after, before.timestamp()) 2419 } else if let Some(after) = self.after { 2420 format!("{}/{}/{}", self.rows, self.offset, after.timestamp()) 2421 } else if self.offset != 0 { 2422 format!("{}/{}", self.rows, self.offset) 2423 } else if self.rows != 100 { 2424 format!("{}", self.rows) 2425 } else { 2426 "".to_string() 2427 } 2428 } 2429 } 2430 2431 #[derive(Clone, Debug, Eq, PartialEq)] 2432 pub enum BulkCaCommand { 2433 Refresh, 2434 Publish, 2435 Sync, 2436 } 2437 2438 #[derive(Clone, Debug, Eq, PartialEq)] 2439 pub struct KrillInitDetails { 2440 data_dir: Option<String>, 2441 log_file: Option<String>, 2442 multi_user: bool, 2443 } 2444 2445 impl KrillInitDetails { multi_user_dflt() -> Self2446 pub fn multi_user_dflt() -> Self { 2447 KrillInitDetails { 2448 data_dir: None, 2449 log_file: None, 2450 multi_user: true, 2451 } 2452 } 2453 with_data_dir(&mut self, data_dir: &str)2454 pub fn with_data_dir(&mut self, data_dir: &str) { 2455 self.data_dir = Some(data_dir.to_string()) 2456 } 2457 with_log_file(&mut self, log_file: &str)2458 pub fn with_log_file(&mut self, log_file: &str) { 2459 self.log_file = Some(log_file.to_string()) 2460 } 2461 data_dir(&self) -> Option<&String>2462 pub fn data_dir(&self) -> Option<&String> { 2463 self.data_dir.as_ref() 2464 } 2465 log_file(&self) -> Option<&String>2466 pub fn log_file(&self) -> Option<&String> { 2467 self.log_file.as_ref() 2468 } 2469 multi_user(&self) -> bool2470 pub fn multi_user(&self) -> bool { 2471 self.multi_user 2472 } 2473 } 2474 2475 impl Default for KrillInitDetails { default() -> Self2476 fn default() -> Self { 2477 KrillInitDetails { 2478 data_dir: None, 2479 log_file: None, 2480 multi_user: false, 2481 } 2482 } 2483 } 2484 2485 #[cfg(feature = "multi-user")] 2486 #[derive(Clone, Debug, Eq, PartialEq)] 2487 pub struct KrillUserDetails { 2488 id: String, 2489 attrs: HashMap<String, String>, 2490 } 2491 2492 #[cfg(feature = "multi-user")] 2493 impl KrillUserDetails { with_id(&mut self, id: String)2494 pub fn with_id(&mut self, id: String) { 2495 self.id = id; 2496 } with_attr(&mut self, attr: String, value: String)2497 pub fn with_attr(&mut self, attr: String, value: String) { 2498 self.attrs.insert(attr, value); 2499 } id(&self) -> &String2500 pub fn id(&self) -> &String { 2501 &self.id 2502 } 2503 attrs(&self) -> HashMap<String, String>2504 pub fn attrs(&self) -> HashMap<String, String> { 2505 self.attrs.clone() 2506 } 2507 } 2508 2509 #[cfg(feature = "multi-user")] 2510 impl Default for KrillUserDetails { default() -> Self2511 fn default() -> Self { 2512 KrillUserDetails { 2513 id: String::new(), 2514 attrs: HashMap::new(), 2515 } 2516 } 2517 } 2518 2519 #[derive(Clone, Debug, Eq, PartialEq)] 2520 #[allow(clippy::large_enum_variant)] 2521 pub enum PubServerCommand { 2522 AddPublisher(rfc8183::PublisherRequest), 2523 ShowPublisher(PublisherHandle), 2524 RemovePublisher(PublisherHandle), 2525 RepositoryResponse(PublisherHandle), 2526 StalePublishers(i64), 2527 PublisherList, 2528 RepositoryStats, 2529 RepositoryInit(PublicationServerUris), 2530 RepositoryClear, 2531 } 2532 2533 //------------ Error --------------------------------------------------------- 2534 2535 #[derive(Debug)] 2536 pub enum Error { 2537 UriError(uri::Error), 2538 IoError(KrillIoError), 2539 ReportError(ReportError), 2540 Rfc8183(rfc8183::Error), 2541 ResSetErr(ResourceSetError), 2542 InvalidRouteDelta(AuthorizationFmtError), 2543 InvalidAsn(String), 2544 DuplicateAspaProvider(DuplicateProviderAs), 2545 InvalidAspaConfig(AspaDefinitionFormatError), 2546 InvalidHandle, 2547 InvalidSeconds, 2548 MissingArgWithEnv(String, String), 2549 MissingResources, 2550 InvalidChildIdCert, 2551 UnrecognizedSubCommand, 2552 GeneralArgumentError(String), 2553 } 2554 2555 impl Error { invalid_asn(asn: &str) -> Self2556 fn invalid_asn(asn: &str) -> Self { 2557 Error::InvalidAsn(asn.to_string()) 2558 } 2559 } 2560 2561 impl fmt::Display for Error { fmt(&self, f: &mut fmt::Formatter) -> fmt::Result2562 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 2563 match self { 2564 Error::UriError(e) => e.fmt(f), 2565 Error::IoError(e) => e.fmt(f), 2566 Error::ReportError(e) => e.fmt(f), 2567 Error::Rfc8183(e) => write!(f, "Invalid RFC8183 XML: {}", e), 2568 Error::ResSetErr(e) => write!(f, "Invalid resources requested: {}", e), 2569 Error::InvalidRouteDelta(e) => e.fmt(f), 2570 Error::InvalidAsn(s) => write!(f, "Invalid ASN format. Expected 'AS#', got: {}", s), 2571 Error::DuplicateAspaProvider(e) => e.fmt(f), 2572 Error::InvalidAspaConfig(e) => e.fmt(f), 2573 Error::InvalidHandle => write!( 2574 f, 2575 "The publisher handle may only contain -_A-Za-z0-9, (\\ /) see issue #83" 2576 ), 2577 Error::InvalidSeconds => write!(f, "Use a number of 0 or more seconds"), 2578 Error::MissingArgWithEnv(arg, var) => write!( 2579 f, 2580 "Missing argument: --{}, alternatively you may use env var: {}", 2581 arg, var 2582 ), 2583 Error::MissingResources => write!(f, "You must specify resources when adding a CA (--asn, --ipv4, --ipv6)"), 2584 Error::InvalidChildIdCert => write!(f, "Invalid ID cert for child"), 2585 Error::UnrecognizedSubCommand => write!(f, "Unrecognized sub-command. Use 'help'"), 2586 Error::GeneralArgumentError(s) => s.fmt(f), 2587 } 2588 } 2589 } 2590 2591 impl Error { missing_arg_with_env(arg: &str, env_var: &str) -> Self2592 fn missing_arg_with_env(arg: &str, env_var: &str) -> Self { 2593 Error::MissingArgWithEnv(arg.to_string(), env_var.to_string()) 2594 } 2595 general(msg: &str) -> Self2596 fn general(msg: &str) -> Self { 2597 Error::GeneralArgumentError(msg.to_string()) 2598 } 2599 } 2600 2601 impl From<rfc8183::Error> for Error { from(e: rfc8183::Error) -> Self2602 fn from(e: rfc8183::Error) -> Self { 2603 Error::Rfc8183(e) 2604 } 2605 } 2606 2607 impl From<uri::Error> for Error { from(e: uri::Error) -> Self2608 fn from(e: uri::Error) -> Self { 2609 Error::UriError(e) 2610 } 2611 } 2612 2613 impl From<KrillIoError> for Error { from(e: KrillIoError) -> Self2614 fn from(e: KrillIoError) -> Self { 2615 Error::IoError(e) 2616 } 2617 } 2618 2619 impl From<ReportError> for Error { from(e: ReportError) -> Self2620 fn from(e: ReportError) -> Self { 2621 Error::ReportError(e) 2622 } 2623 } 2624 2625 impl From<ResourceSetError> for Error { from(e: ResourceSetError) -> Self2626 fn from(e: ResourceSetError) -> Self { 2627 Error::ResSetErr(e) 2628 } 2629 } 2630 2631 impl From<AuthorizationFmtError> for Error { from(e: AuthorizationFmtError) -> Self2632 fn from(e: AuthorizationFmtError) -> Self { 2633 Error::InvalidRouteDelta(e) 2634 } 2635 } 2636 2637 impl From<AspaDefinitionFormatError> for Error { from(e: AspaDefinitionFormatError) -> Self2638 fn from(e: AspaDefinitionFormatError) -> Self { 2639 Error::InvalidAspaConfig(e) 2640 } 2641 } 2642 2643 impl From<DuplicateProviderAs> for Error { from(e: DuplicateProviderAs) -> Self2644 fn from(e: DuplicateProviderAs) -> Self { 2645 Error::DuplicateAspaProvider(e) 2646 } 2647 } 2648