Sign BGP router certificates (#827)

This commit is contained in:
Tim Bruijnzeels
2022-07-07 13:20:00 +02:00
committed by GitHub
parent 4605bf93da
commit d68c07b59c
53 changed files with 1939 additions and 217 deletions
+23 -3
View File
@@ -11,9 +11,9 @@ use crate::{
},
commons::{
api::{
AllCertAuthIssues, AspaDefinitionUpdates, CaRepoDetails, CertAuthIssues, ChildCaInfo,
ChildrenConnectionStats, ParentCaContact, ParentStatuses, PublisherDetails, PublisherList, RepoStatus,
Token,
AllCertAuthIssues, AspaDefinitionUpdates, BgpSecDefinitionUpdates, CaRepoDetails, CertAuthIssues,
ChildCaInfo, ChildrenConnectionStats, ParentCaContact, ParentStatuses, PublisherDetails, PublisherList,
RepoStatus, Token,
},
bgp::BgpAnalysisAdvice,
error::KrillIoError,
@@ -335,6 +335,26 @@ impl KrillClient {
Ok(ApiResponse::BgpAnalysisSuggestions(suggestions))
}
CaCommand::BgpSecList(handle) => {
let uri = format!("api/v1/cas/{}/bgpsec", handle);
let bgpsec_list = get_json(&self.server, &self.token, &uri).await?;
Ok(ApiResponse::BgpSecDefinitions(bgpsec_list))
}
CaCommand::BgpSecAdd(handle, addition) => {
let uri = format!("api/v1/cas/{}/bgpsec", handle);
let update = BgpSecDefinitionUpdates::new(vec![addition], vec![]);
post_json(&self.server, &self.token, &uri, update).await?;
Ok(ApiResponse::Empty)
}
CaCommand::BgpSecRemove(handle, removal) => {
let uri = format!("api/v1/cas/{}/bgpsec", handle);
let update = BgpSecDefinitionUpdates::new(vec![], vec![removal]);
post_json(&self.server, &self.token, &uri, update).await?;
Ok(ApiResponse::Empty)
}
CaCommand::AspasList(handle) => {
let uri = format!("api/v1/cas/{}/aspas", handle);
let aspas = get_json(&self.server, &self.token, &uri).await?;
+148 -4
View File
@@ -13,14 +13,15 @@ use clap::{App, Arg, ArgMatches, SubCommand};
use rpki::{
ca::{
csr::BgpsecCsr,
idcert::IdCert,
idexchange,
idexchange::{CaHandle, ChildHandle, ParentHandle, PublisherHandle},
},
crypto::KeyIdentifier,
repository::{
aspa::{DuplicateProviderAs, ProviderAs},
crypto::KeyIdentifier,
resources::ResourceSet,
resources::{Asn, ResourceSet},
x509::Time,
},
uri,
@@ -31,8 +32,9 @@ use crate::{
commons::{
api::{
AddChildRequest, AspaCustomer, AspaDefinition, AspaDefinitionFormatError, AspaProvidersUpdate,
AuthorizationFmtError, CertAuthInit, ParentCaContact, ParentCaReq, PublicationServerUris,
RepositoryContact, RoaDefinition, RoaDefinitionUpdates, RtaName, Token, UpdateChildRequest,
AuthorizationFmtError, BgpSecAsnKey, BgpSecDefinition, CertAuthInit, ParentCaContact, ParentCaReq,
PublicationServerUris, RepositoryContact, RoaDefinition, RoaDefinitionUpdates, RtaName, Token,
UpdateChildRequest,
},
crypto::SignSupport,
error::KrillIoError,
@@ -741,6 +743,77 @@ impl Options {
app.subcommand(sub)
}
fn make_cas_bgpsec_list_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> {
let mut sub = SubCommand::with_name("list").about("Show current BGPSec configurations");
sub = Self::add_general_args(sub);
sub = Self::add_my_ca_arg(sub);
app.subcommand(sub)
}
fn make_cas_bgpsec_add_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> {
let mut sub = SubCommand::with_name("add").about("Add BGPSec configurations");
sub = Self::add_general_args(sub);
sub = Self::add_my_ca_arg(sub);
sub = sub
.arg(
Arg::with_name("asn")
.short("a")
.long("asn")
.value_name("ASN")
.help("The ASN of the router for the key used in the CSR. E.g. AS65000")
.required(true),
)
.arg(
Arg::with_name("csr")
.long("csr")
.value_name("CSR")
.help("The file containing the DER encoded Certificate Sign Request")
.required(true),
);
app.subcommand(sub)
}
fn make_cas_bgpsec_remove_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> {
let mut sub = SubCommand::with_name("remove").about("Remove a BGPSec definition");
sub = Self::add_general_args(sub);
sub = Self::add_my_ca_arg(sub);
sub = sub
.arg(
Arg::with_name("asn")
.short("a")
.long("asn")
.value_name("ASN")
.help("The ASN used in the BGPSec definition. E.g. AS65000")
.required(true),
)
.arg(
Arg::with_name("key")
.long("key")
.value_name("key")
.help("The hex encoded key identifier used in the BGPSec definition")
.required(true),
);
app.subcommand(sub)
}
fn make_cas_bgpsec_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> {
let mut sub = SubCommand::with_name("bgpsec").about("Manage BGPSec certificates");
sub = Self::make_cas_bgpsec_list_sc(sub);
sub = Self::make_cas_bgpsec_add_sc(sub);
sub = Self::make_cas_bgpsec_remove_sc(sub);
app.subcommand(sub)
}
#[cfg(feature = "aspa")]
fn make_cas_aspas_add_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> {
let mut sub = SubCommand::with_name("add").about("Add or replace an ASPA configuration");
@@ -1269,6 +1342,7 @@ impl Options {
app = Self::make_cas_parents_sc(app);
app = Self::make_cas_keyroll_sc(app);
app = Self::make_cas_routes_sc(app);
app = Self::make_cas_bgpsec_sc(app);
app = Self::make_cas_repo_sc(app);
app = Self::make_cas_issues_sc(app);
app = Self::make_pubserver_sc(app);
@@ -1824,6 +1898,69 @@ impl Options {
}
}
fn parse_matches_cas_bgpsec_list(matches: &ArgMatches) -> Result<Options, Error> {
let general_args = GeneralArgs::from_matches(matches)?;
let my_ca = Self::parse_my_ca(matches)?;
let command = Command::CertAuth(CaCommand::BgpSecList(my_ca));
Ok(Options::make(general_args, command))
}
fn parse_matches_cas_bgpsec_add(matches: &ArgMatches) -> Result<Options, Error> {
let general_args = GeneralArgs::from_matches(matches)?;
let my_ca = Self::parse_my_ca(matches)?;
let asn_str = matches.value_of("asn").unwrap();
let asn = Asn::from_str(asn_str).map_err(|_| Error::invalid_asn(asn_str))?;
let csr_file = matches.value_of("csr").unwrap();
let csr_file_path = PathBuf::from(csr_file);
let bytes = file::read(&csr_file_path)
.map_err(|e| Error::GeneralArgumentError(format!("Cannot read file '{}', error: {}", csr_file, e,)))?;
let csr = BgpsecCsr::decode(bytes.as_ref())
.map_err(|e| Error::GeneralArgumentError(format!("Cannot parse CSR file '{}', error: {}", csr_file, e)))?;
csr.validate()
.map_err(|_| Error::GeneralArgumentError(format!("CSR in file '{}' is not valid", csr_file)))?;
let definition = BgpSecDefinition::new(asn, csr);
let command = Command::CertAuth(CaCommand::BgpSecAdd(my_ca, definition));
Ok(Options::make(general_args, command))
}
fn parse_matches_cas_bgpsec_remove(matches: &ArgMatches) -> Result<Options, Error> {
let general_args = GeneralArgs::from_matches(matches)?;
let my_ca = Self::parse_my_ca(matches)?;
let asn_str = matches.value_of("asn").unwrap();
let asn = Asn::from_str(asn_str).map_err(|_| Error::invalid_asn(asn_str))?;
let key_str = matches.value_of("key").unwrap();
let key = KeyIdentifier::from_str(key_str).map_err(|_| Error::general("Cannot parse key identifier"))?;
let definition = BgpSecAsnKey::new(asn, key);
let command = Command::CertAuth(CaCommand::BgpSecRemove(my_ca, definition));
Ok(Options::make(general_args, command))
}
fn parse_matches_cas_bgpsec(matches: &ArgMatches) -> Result<Options, Error> {
if let Some(m) = matches.subcommand_matches("list") {
Self::parse_matches_cas_bgpsec_list(m)
} else if let Some(m) = matches.subcommand_matches("add") {
Self::parse_matches_cas_bgpsec_add(m)
} else if let Some(m) = matches.subcommand_matches("remove") {
Self::parse_matches_cas_bgpsec_remove(m)
} else {
Err(Error::UnrecognizedSubCommand)
}
}
fn parse_matches_cas_aspas_add(matches: &ArgMatches) -> Result<Options, Error> {
let general_args = GeneralArgs::from_matches(matches)?;
let my_ca = Self::parse_my_ca(matches)?;
@@ -2297,6 +2434,8 @@ impl Options {
Self::parse_matches_cas_keyroll(m)
} else if let Some(m) = matches.subcommand_matches("roas") {
Self::parse_matches_cas_routes(m)
} else if let Some(m) = matches.subcommand_matches("bgpsec") {
Self::parse_matches_cas_bgpsec(m)
} else if let Some(m) = matches.subcommand_matches("aspas") {
Self::parse_matches_cas_aspas(m)
} else if let Some(m) = matches.subcommand_matches("repo") {
@@ -2385,6 +2524,11 @@ pub enum CaCommand {
AspasUpdate(CaHandle, AspaCustomer, AspaProvidersUpdate),
AspasRemove(CaHandle, AspaCustomer),
// BGPSec
BgpSecList(CaHandle),
BgpSecAdd(CaHandle, BgpSecDefinition),
BgpSecRemove(CaHandle, BgpSecAsnKey),
// Show details for this CA
Show(CaHandle),
ShowHistoryCommands(CaHandle, HistoryOptions),
+10 -3
View File
@@ -8,9 +8,10 @@ use rpki::ca::idexchange;
use crate::{
commons::{
api::{
AllCertAuthIssues, AspaDefinitionList, CaCommandDetails, CaRepoDetails, CertAuthInfo, CertAuthIssues,
CertAuthList, ChildCaInfo, ChildrenConnectionStats, CommandHistory, ParentCaContact, ParentStatuses,
PublisherDetails, PublisherList, RepoStatus, RoaDefinitions, RtaList, RtaPrepResponse, ServerInfo,
AllCertAuthIssues, AspaDefinitionList, BgpSecCsrInfoList, CaCommandDetails, CaRepoDetails, CertAuthInfo,
CertAuthIssues, CertAuthList, ChildCaInfo, ChildrenConnectionStats, CommandHistory, ParentCaContact,
ParentStatuses, PublisherDetails, PublisherList, RepoStatus, RoaDefinitions, RtaList, RtaPrepResponse,
ServerInfo,
},
bgp::{BgpAnalysisAdvice, BgpAnalysisReport, BgpAnalysisSuggestion},
},
@@ -41,6 +42,9 @@ pub enum ApiResponse {
// ASPA related
AspaDefinitions(AspaDefinitionList),
// BGPSec related
BgpSecDefinitions(BgpSecCsrInfoList),
ParentCaContact(ParentCaContact),
ParentStatuses(ParentStatuses),
@@ -88,6 +92,7 @@ impl ApiResponse {
ApiResponse::BgpAnalysisFull(table) => Ok(Some(table.report(fmt)?)),
ApiResponse::BgpAnalysisSuggestions(suggestions) => Ok(Some(suggestions.report(fmt)?)),
ApiResponse::AspaDefinitions(definitions) => Ok(Some(definitions.report(fmt)?)),
ApiResponse::BgpSecDefinitions(definitions) => Ok(Some(definitions.report(fmt)?)),
ApiResponse::ParentCaContact(contact) => Ok(Some(contact.report(fmt)?)),
ApiResponse::ParentStatuses(statuses) => Ok(Some(statuses.report(fmt)?)),
ApiResponse::ChildInfo(info) => Ok(Some(info.report(fmt)?)),
@@ -203,6 +208,8 @@ impl Report for BgpAnalysisSuggestion {}
impl Report for AspaDefinitionList {}
impl Report for BgpSecCsrInfoList {}
impl Report for CaRepoDetails {}
impl Report for RepoStatus {}