mirror of
https://github.com/NLnetLabs/krill.git
synced 2026-09-12 04:27:41 +02:00
Sign BGP router certificates (#827)
This commit is contained in:
Generated
+2
-2
@@ -1624,8 +1624,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rpki"
|
||||
version = "0.14.3-dev"
|
||||
source = "git+https://github.com/NLnetLabs/rpki-rs/#61ce3bf5817d5c62eb38267b4bcdd1a7112d1f38"
|
||||
version = "0.15.0-dev"
|
||||
source = "git+https://github.com/NLnetLabs/rpki-rs/#666e09357493aec6f0dc030f0e387c756423e2cb"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"bcder",
|
||||
|
||||
+1
-1
@@ -51,7 +51,7 @@ regex = { version = "1.5.5", optional = true, default_features =
|
||||
reqwest = { version = "0.11", features = ["json"] }
|
||||
rpassword = { version = "^5.0", optional = true }
|
||||
# rpki = { version = "0.13.2", features = [ "compat", "repository", "rrdp", "serde" ] }
|
||||
rpki = { version = "0.14.3-dev", git = "https://github.com/NLnetLabs/rpki-rs/", features = [ "ca", "rrdp" ] }
|
||||
rpki = { version = "0.15.0-dev", git = "https://github.com/NLnetLabs/rpki-rs/", features = [ "ca", "rrdp" ] }
|
||||
# rpki = { version = "0.14.3-dev", path = "../rpki-rs/", features = [ "ca", "repository", "rrdp" ] }
|
||||
scrypt = { version = "^0.6", optional = true, default-features = false }
|
||||
serde = { version = "^1.0", features = ["derive"] }
|
||||
|
||||
@@ -21,6 +21,23 @@ For more information please refer to the [documentation](https://krill.docs.nlne
|
||||
|
||||
# Changelog
|
||||
|
||||
## 0.10.0
|
||||
|
||||
In this release we introduce the following feature:
|
||||
- BGPSec Router Certificate Signing (CLI/API only)
|
||||
|
||||
## 0.9.6 'Newer ROAs Please'
|
||||
|
||||
This release fixes an issue introduced in 0.9.5 where the background job to
|
||||
automatically renew ROAs was not added to Krill's task queue on startup. Thanks
|
||||
to Alberto Leiva for finding this issue!
|
||||
|
||||
All users who upgraded to 0.9.5 are advised to upgrade to this version as soon
|
||||
as possible. Not doing so can lead to ROAs expiring and becoming invalid. If you
|
||||
did not upgrade to 0.9.5 you are not affected by this issue.
|
||||
|
||||
This release contains no other changes.
|
||||
|
||||
## 0.9.5 'Have You considered these Upgrades?'
|
||||
|
||||
This release is primarily intended to improve support for migrations of pre-0.9.0
|
||||
|
||||
@@ -70,6 +70,7 @@ role_allow("readonly", action: Permission) if
|
||||
ROUTES_ANALYSIS,
|
||||
ASPAS_READ,
|
||||
ASPAS_ANALYSIS,
|
||||
BGPSEC_READ,
|
||||
RTA_LIST,
|
||||
RTA_READ
|
||||
];
|
||||
@@ -101,6 +102,8 @@ role_allow("readwrite", action: Permission) if
|
||||
ASPAS_READ,
|
||||
ASPAS_UPDATE,
|
||||
ASPAS_ANALYSIS,
|
||||
BGPSEC_READ,
|
||||
BGPSEC_UPDATE,
|
||||
RTA_LIST,
|
||||
RTA_READ,
|
||||
RTA_UPDATE
|
||||
|
||||
+23
-3
@@ -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
@@ -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
@@ -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 {}
|
||||
|
||||
|
||||
@@ -0,0 +1,232 @@
|
||||
use std::{fmt, str::FromStr};
|
||||
|
||||
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
|
||||
|
||||
use rpki::{
|
||||
ca::{csr::BgpsecCsr, publication::Base64},
|
||||
crypto::KeyIdentifier,
|
||||
repository::resources::Asn,
|
||||
};
|
||||
|
||||
use super::ObjectName;
|
||||
|
||||
//------------ BgpSecDefinition --------------------------------------------
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct BgpSecDefinition {
|
||||
asn: Asn,
|
||||
csr: BgpsecCsr,
|
||||
}
|
||||
|
||||
impl BgpSecDefinition {
|
||||
pub fn new(asn: Asn, csr: BgpsecCsr) -> Self {
|
||||
BgpSecDefinition { asn, csr }
|
||||
}
|
||||
|
||||
pub fn asn(&self) -> Asn {
|
||||
self.asn
|
||||
}
|
||||
|
||||
pub fn csr(&self) -> &BgpsecCsr {
|
||||
&self.csr
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for BgpSecDefinition {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.asn == other.asn && self.csr.to_captured().as_slice() == other.csr.to_captured().as_slice()
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for BgpSecDefinition {}
|
||||
|
||||
//------------ BgpSecAsnKey ------------------------------------------------
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
||||
pub struct BgpSecAsnKey {
|
||||
asn: Asn,
|
||||
key: KeyIdentifier,
|
||||
}
|
||||
|
||||
impl BgpSecAsnKey {
|
||||
pub fn new(asn: Asn, key: KeyIdentifier) -> Self {
|
||||
BgpSecAsnKey { asn, key }
|
||||
}
|
||||
|
||||
pub fn asn(&self) -> Asn {
|
||||
self.asn
|
||||
}
|
||||
|
||||
pub fn key_identifier(&self) -> KeyIdentifier {
|
||||
self.key
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for BgpSecAsnKey {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
// We use a format similar to the recommendation for the router
|
||||
// certificate subject from section 3.1.1 in RFC 8209.
|
||||
write!(f, "ROUTER-{:08X}-{}", self.asn.into_u32(), self.key)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&BgpSecDefinition> for BgpSecAsnKey {
|
||||
fn from(def: &BgpSecDefinition) -> Self {
|
||||
BgpSecAsnKey {
|
||||
asn: def.asn(),
|
||||
key: def.csr().public_key().key_identifier(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for BgpSecAsnKey {
|
||||
type Err = BgpSecAsnKeyFmtError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let s = s.strip_prefix("ROUTER-").ok_or(BgpSecAsnKeyFmtError)?;
|
||||
|
||||
let parts: Vec<_> = s.split('-').collect();
|
||||
|
||||
if parts.len() != 2 {
|
||||
return Err(BgpSecAsnKeyFmtError);
|
||||
}
|
||||
|
||||
let asn_hex = parts.get(0).ok_or(BgpSecAsnKeyFmtError)?;
|
||||
let key_id_str = parts.get(1).ok_or(BgpSecAsnKeyFmtError)?;
|
||||
|
||||
let asn_nr = u32::from_str_radix(asn_hex, 16).map_err(|_| BgpSecAsnKeyFmtError)?;
|
||||
let asn = Asn::from_u32(asn_nr);
|
||||
|
||||
let key = KeyIdentifier::from_str(key_id_str).map_err(|_| BgpSecAsnKeyFmtError)?;
|
||||
|
||||
Ok(BgpSecAsnKey { asn, key })
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct BgpSecAsnKeyFmtError;
|
||||
|
||||
impl std::error::Error for BgpSecAsnKeyFmtError {}
|
||||
|
||||
impl fmt::Display for BgpSecAsnKeyFmtError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"Invalid BGPSec ASN and Key format. Expected: ROUTER-<hex-encoded-asn>-<hex-encoded-key-identifier>"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// We use BgpSecAsnKey as (JSON) map keys and therefore we need it
|
||||
/// to be serializable to a single simple string.
|
||||
impl Serialize for BgpSecAsnKey {
|
||||
fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
self.to_string().serialize(s)
|
||||
}
|
||||
}
|
||||
|
||||
/// We use BgpSecAsnKey as (JSON) map keys and therefore we need it
|
||||
/// to be deserializable from a single simple string.
|
||||
impl<'de> Deserialize<'de> for BgpSecAsnKey {
|
||||
fn deserialize<D>(d: D) -> Result<BgpSecAsnKey, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let string = String::deserialize(d)?;
|
||||
BgpSecAsnKey::from_str(string.as_str()).map_err(de::Error::custom)
|
||||
}
|
||||
}
|
||||
|
||||
//------------ BgpSecDefinitionUpdates -------------------------------------
|
||||
|
||||
/// Contains BGPSec definition updates sent to the API.
|
||||
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
|
||||
pub struct BgpSecDefinitionUpdates {
|
||||
add: Vec<BgpSecDefinition>,
|
||||
remove: Vec<BgpSecAsnKey>,
|
||||
}
|
||||
|
||||
impl BgpSecDefinitionUpdates {
|
||||
pub fn new(add: Vec<BgpSecDefinition>, remove: Vec<BgpSecAsnKey>) -> Self {
|
||||
BgpSecDefinitionUpdates { add, remove }
|
||||
}
|
||||
|
||||
pub fn unpack(self) -> (Vec<BgpSecDefinition>, Vec<BgpSecAsnKey>) {
|
||||
(self.add, self.remove)
|
||||
}
|
||||
}
|
||||
|
||||
/// This type is shown through the API
|
||||
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
|
||||
pub struct BgpSecCsrInfo {
|
||||
asn: Asn,
|
||||
key_identifier: KeyIdentifier,
|
||||
csr: Base64,
|
||||
}
|
||||
|
||||
impl BgpSecCsrInfo {
|
||||
pub fn new(asn: Asn, key_identifier: KeyIdentifier, csr: Base64) -> Self {
|
||||
BgpSecCsrInfo {
|
||||
asn,
|
||||
key_identifier,
|
||||
csr,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn asn(&self) -> Asn {
|
||||
self.asn
|
||||
}
|
||||
|
||||
pub fn key_identifier(&self) -> KeyIdentifier {
|
||||
self.key_identifier
|
||||
}
|
||||
|
||||
pub fn csr(&self) -> &Base64 {
|
||||
&self.csr
|
||||
}
|
||||
|
||||
pub fn object_name(&self) -> ObjectName {
|
||||
ObjectName::bgpsec(self.asn, self.key_identifier)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
|
||||
pub struct BgpSecCsrInfoList(Vec<BgpSecCsrInfo>);
|
||||
|
||||
impl BgpSecCsrInfoList {
|
||||
pub fn new(list: Vec<BgpSecCsrInfo>) -> Self {
|
||||
BgpSecCsrInfoList(list)
|
||||
}
|
||||
|
||||
pub fn unpack(self) -> Vec<BgpSecCsrInfo> {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for BgpSecCsrInfoList {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
writeln!(f, "ASN, key identifier, CSR base64")?;
|
||||
for info in self.0.iter() {
|
||||
writeln!(f, "{}, {}, {}", info.asn, info.key_identifier, info.csr)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::str::FromStr;
|
||||
|
||||
use super::BgpSecAsnKey;
|
||||
|
||||
#[test]
|
||||
fn bgp_sec_to_from_str() {
|
||||
let string = "ROUTER-0000FDE8-17316903F0671229E8808BA8E8AB0105FA915A07";
|
||||
let key = BgpSecAsnKey::from_str(string).unwrap();
|
||||
let to_string = key.to_string();
|
||||
assert_eq!(string, &to_string);
|
||||
}
|
||||
}
|
||||
+30
-2
@@ -20,11 +20,11 @@ use rpki::{
|
||||
},
|
||||
publication::Base64,
|
||||
},
|
||||
crypto::KeyIdentifier,
|
||||
repository::{
|
||||
aspa::Aspa,
|
||||
cert::Cert,
|
||||
crl::{Crl, CrlEntry},
|
||||
crypto::KeyIdentifier,
|
||||
manifest::Manifest,
|
||||
resources::{Asn, ResourceSet},
|
||||
roa::Roa,
|
||||
@@ -34,6 +34,7 @@ use rpki::{
|
||||
uri,
|
||||
};
|
||||
|
||||
use crate::daemon::ca::BgpSecCertInfo;
|
||||
use crate::{
|
||||
commons::{
|
||||
api::{
|
||||
@@ -46,6 +47,8 @@ use crate::{
|
||||
daemon::ca::RouteAuthorization,
|
||||
};
|
||||
|
||||
use super::BgpSecAsnKey;
|
||||
|
||||
//------------ IdCertPem -----------------------------------------------------
|
||||
|
||||
/// A PEM encoded IdCert and sha256 of the encoding, for easier
|
||||
@@ -524,6 +527,10 @@ impl ObjectName {
|
||||
pub fn aspa(customer: Asn) -> Self {
|
||||
ObjectName(format!("{}.asa", customer))
|
||||
}
|
||||
|
||||
pub fn bgpsec(asn: Asn, key: KeyIdentifier) -> Self {
|
||||
ObjectName(format!("ROUTER-{:08X}-{}.cer", asn.into_u32(), key))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Cert> for ObjectName {
|
||||
@@ -571,6 +578,18 @@ impl From<&AspaDefinition> for ObjectName {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&BgpSecCertInfo> for ObjectName {
|
||||
fn from(info: &BgpSecCertInfo) -> Self {
|
||||
Self::bgpsec(info.asn(), info.public_key().key_identifier())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&BgpSecAsnKey> for ObjectName {
|
||||
fn from(asn_key: &BgpSecAsnKey) -> Self {
|
||||
Self::bgpsec(asn_key.asn(), asn_key.key_identifier())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for ObjectName {
|
||||
fn from(s: &str) -> Self {
|
||||
ObjectName(s.to_string())
|
||||
@@ -640,6 +659,15 @@ impl From<&Aspa> for Revocation {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&BgpSecCertInfo> for Revocation {
|
||||
fn from(info: &BgpSecCertInfo) -> Self {
|
||||
Revocation {
|
||||
serial: info.serial(),
|
||||
expires: info.expires(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//------------ Revocations ---------------------------------------------------
|
||||
|
||||
#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
|
||||
@@ -2102,7 +2130,7 @@ mod test {
|
||||
use bytes::Bytes;
|
||||
use std::convert::TryFrom;
|
||||
|
||||
use rpki::repository::crypto::PublicKeyFormat;
|
||||
use rpki::crypto::PublicKeyFormat;
|
||||
|
||||
use crate::{commons::crypto::OpenSslSigner, test};
|
||||
|
||||
|
||||
@@ -7,7 +7,8 @@ use rpki::{
|
||||
idexchange::{ChildHandle, MyHandle, ParentHandle, PublisherHandle, ServiceUri},
|
||||
provisioning::{RequestResourceLimit, ResourceClassName, RevocationRequest},
|
||||
},
|
||||
repository::{crypto::KeyIdentifier, resources::ResourceSet, x509::Time},
|
||||
crypto::KeyIdentifier,
|
||||
repository::{resources::ResourceSet, x509::Time},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
@@ -487,6 +488,7 @@ pub enum StorableCaCommand {
|
||||
AspaRemove {
|
||||
customer: AspaCustomer,
|
||||
},
|
||||
BgpSecDefinitionUpdates, // details in events
|
||||
RepoUpdate {
|
||||
service_uri: ServiceUri,
|
||||
},
|
||||
@@ -597,6 +599,9 @@ impl WithStorableDetails for StorableCaCommand {
|
||||
StorableCaCommand::AspasUpdateExisting { .. } => CommandSummary::new("cmd-ca-aspas-update-existing", &self),
|
||||
StorableCaCommand::AspaRemove { .. } => CommandSummary::new("cmd-ca-aspas-remove", &self),
|
||||
|
||||
// BGPSec
|
||||
StorableCaCommand::BgpSecDefinitionUpdates => CommandSummary::new("cmd-bgpsec-update", &self),
|
||||
|
||||
// REPO
|
||||
StorableCaCommand::RepoUpdate { service_uri } => {
|
||||
CommandSummary::new("cmd-ca-repo-update", &self).with_service_uri(service_uri)
|
||||
@@ -770,6 +775,11 @@ impl fmt::Display for StorableCaCommand {
|
||||
write!(f, "Remove ASPA for customer AS: {}", customer)
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------
|
||||
// BGPSec Support
|
||||
// ------------------------------------------------------------
|
||||
StorableCaCommand::BgpSecDefinitionUpdates => write!(f, "Update BGPSec definitions"),
|
||||
|
||||
// ------------------------------------------------------------
|
||||
// Publishing
|
||||
// ------------------------------------------------------------
|
||||
|
||||
+12
-1
@@ -6,6 +6,9 @@ pub use self::admin::*;
|
||||
mod aspa;
|
||||
pub use self::aspa::*;
|
||||
|
||||
mod bgpsec;
|
||||
pub use self::bgpsec::*;
|
||||
|
||||
mod ca;
|
||||
pub use self::ca::*;
|
||||
|
||||
@@ -19,12 +22,15 @@ pub mod rrdp;
|
||||
|
||||
use std::{collections::HashMap, fmt};
|
||||
|
||||
use rpki::ca::csr::BgpsecCsr;
|
||||
use rpki::ca::provisioning::ResourceClassName;
|
||||
use rpki::ca::publication::Base64;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use rpki::{
|
||||
ca::idexchange::{CaHandle, ChildHandle, ParentHandle, PublisherHandle},
|
||||
repository::{crypto::KeyIdentifier, resources::Asn},
|
||||
crypto::KeyIdentifier,
|
||||
repository::resources::Asn,
|
||||
};
|
||||
|
||||
use crate::{commons::error::RoaDeltaError, daemon::ca::RouteAuthorization};
|
||||
@@ -112,6 +118,11 @@ impl ErrorResponse {
|
||||
self.with_arg("asn", asn)
|
||||
}
|
||||
|
||||
pub fn with_bgpsec_csr(self, csr: &BgpsecCsr) -> Self {
|
||||
let base64 = Base64::from_content(csr.to_captured().as_slice());
|
||||
self.with_arg("bgpsec_csr", base64)
|
||||
}
|
||||
|
||||
pub fn with_roa_delta_error(mut self, roa_delta_error: &RoaDeltaError) -> Self {
|
||||
self.delta_error = Some(roa_delta_error.clone());
|
||||
self
|
||||
|
||||
@@ -1,18 +1,23 @@
|
||||
use std::{path::Path, sync::Arc, time::Duration};
|
||||
|
||||
use rpki::{
|
||||
ca::{idcert::IdCert, idexchange::RepoInfo, provisioning, publication},
|
||||
ca::{
|
||||
csr::{Csr, RpkiCaCsr},
|
||||
idcert::IdCert,
|
||||
idexchange::RepoInfo,
|
||||
provisioning, publication,
|
||||
},
|
||||
crypto::{KeyIdentifier, PublicKey, PublicKeyFormat, RpkiSignature, RpkiSignatureAlgorithm, Signer},
|
||||
repository::{
|
||||
aspa::{Aspa, AspaBuilder},
|
||||
cert::TbsCert,
|
||||
crl::{CrlEntry, TbsCertList},
|
||||
crypto::{KeyIdentifier, PublicKey, PublicKeyFormat, Signature, SignatureAlgorithm, Signer},
|
||||
manifest::ManifestContent,
|
||||
roa::RoaBuilder,
|
||||
rta,
|
||||
sigobj::SignedObjectBuilder,
|
||||
x509::{Serial, Time, Validity},
|
||||
Cert, Crl, Csr, Manifest, Roa,
|
||||
Cert, Crl, Manifest, Roa,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -217,22 +222,24 @@ impl KrillSigner {
|
||||
Serial::random(&self.router).map_err(crypto::Error::signer)
|
||||
}
|
||||
|
||||
pub fn sign<D: AsRef<[u8]> + ?Sized>(&self, key_id: &KeyIdentifier, data: &D) -> CryptoResult<Signature> {
|
||||
pub fn sign<D: AsRef<[u8]> + ?Sized>(&self, key_id: &KeyIdentifier, data: &D) -> CryptoResult<RpkiSignature> {
|
||||
self.router
|
||||
.sign(key_id, SignatureAlgorithm::default(), data)
|
||||
.sign(key_id, RpkiSignatureAlgorithm::default(), data)
|
||||
.map_err(crypto::Error::signing)
|
||||
}
|
||||
|
||||
pub fn sign_one_off<D: AsRef<[u8]> + ?Sized>(&self, data: &D) -> CryptoResult<(Signature, PublicKey)> {
|
||||
pub fn sign_one_off<D: AsRef<[u8]> + ?Sized>(&self, data: &D) -> CryptoResult<(RpkiSignature, PublicKey)> {
|
||||
self.router
|
||||
.sign_one_off(SignatureAlgorithm::default(), data)
|
||||
.sign_one_off(RpkiSignatureAlgorithm::default(), data)
|
||||
.map_err(crypto::Error::signer)
|
||||
}
|
||||
|
||||
pub fn sign_csr(&self, base_repo: &RepoInfo, name_space: &str, key: &KeyIdentifier) -> CryptoResult<Csr> {
|
||||
let pub_key = self.router.get_key_info(key).map_err(crypto::Error::key_error)?;
|
||||
let mft_file_name = ObjectName::mft_for_key(&pub_key.key_identifier());
|
||||
let enc = Csr::construct(
|
||||
pub fn sign_csr(&self, base_repo: &RepoInfo, name_space: &str, key: &KeyIdentifier) -> CryptoResult<RpkiCaCsr> {
|
||||
let signing_key_id = self.router.get_key_info(key).map_err(crypto::Error::key_error)?;
|
||||
let mft_file_name = ObjectName::mft_for_key(&signing_key_id.key_identifier());
|
||||
|
||||
// The rpki-rs library returns a signed and encoded CSR for a CA certificate.
|
||||
let signed_and_encoded_csr = Csr::construct_rpki_ca(
|
||||
&self.router,
|
||||
key,
|
||||
&base_repo.ca_repository(name_space).join(&[]).unwrap(), // force trailing slash
|
||||
@@ -240,7 +247,9 @@ impl KrillSigner {
|
||||
base_repo.rpki_notify(),
|
||||
)
|
||||
.map_err(crypto::Error::signing)?;
|
||||
Ok(Csr::decode(enc.as_slice())?)
|
||||
|
||||
// Decode the encoded CSR again to get a typed RpkiCaCsr
|
||||
Ok(RpkiCaCsr::decode(signed_and_encoded_csr.as_slice())?)
|
||||
}
|
||||
|
||||
pub fn sign_cert(&self, tbs: TbsCert, key_id: &KeyIdentifier) -> CryptoResult<Cert> {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
use std::{collections::HashMap, fmt, path::Path, str::FromStr};
|
||||
|
||||
use rpki::repository::crypto::{KeyIdentifier, PublicKey};
|
||||
use rpki::crypto::{KeyIdentifier, PublicKey};
|
||||
|
||||
use crate::{
|
||||
commons::{
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use rpki::repository::crypto::{
|
||||
signer::KeyError, KeyIdentifier, PublicKey, PublicKeyFormat, Signature, SignatureAlgorithm, SigningError,
|
||||
use rpki::crypto::{
|
||||
signer::{KeyError, SigningAlgorithm},
|
||||
KeyIdentifier, PublicKey, PublicKeyFormat, RpkiSignature, Signature, SignatureAlgorithm, SigningError,
|
||||
};
|
||||
|
||||
use crate::commons::crypto::{
|
||||
@@ -110,7 +111,7 @@ impl SignerProvider {
|
||||
&self,
|
||||
signer_private_key_id: &str,
|
||||
challenge: &D,
|
||||
) -> Result<Signature, SignerError> {
|
||||
) -> Result<RpkiSignature, SignerError> {
|
||||
match self {
|
||||
SignerProvider::OpenSsl(_, signer) => signer.sign_registration_challenge(signer_private_key_id, challenge),
|
||||
#[cfg(feature = "hsm")]
|
||||
@@ -206,12 +207,17 @@ impl SignerProvider {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sign<D: AsRef<[u8]> + ?Sized>(
|
||||
pub fn sign<Alg: SignatureAlgorithm, D: AsRef<[u8]> + ?Sized>(
|
||||
&self,
|
||||
key: &KeyIdentifier,
|
||||
algorithm: SignatureAlgorithm,
|
||||
algorithm: Alg,
|
||||
data: &D,
|
||||
) -> Result<Signature, SigningError<SignerError>> {
|
||||
) -> Result<Signature<Alg>, SigningError<SignerError>> {
|
||||
let signing_algorithm = algorithm.signing_algorithm();
|
||||
if !matches!(signing_algorithm, SigningAlgorithm::RsaSha256) {
|
||||
return Err(SignerError::UnsupportedSigningAlg(signing_algorithm).into());
|
||||
}
|
||||
|
||||
match self {
|
||||
SignerProvider::OpenSsl(_, signer) => signer.sign(key, algorithm, data),
|
||||
#[cfg(feature = "hsm")]
|
||||
@@ -223,11 +229,16 @@ impl SignerProvider {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sign_one_off<D: AsRef<[u8]> + ?Sized>(
|
||||
pub fn sign_one_off<Alg: SignatureAlgorithm, D: AsRef<[u8]> + ?Sized>(
|
||||
&self,
|
||||
algorithm: SignatureAlgorithm,
|
||||
algorithm: Alg,
|
||||
data: &D,
|
||||
) -> Result<(Signature, PublicKey), SignerError> {
|
||||
) -> Result<(Signature<Alg>, PublicKey), SignerError> {
|
||||
let signing_algorithm = algorithm.signing_algorithm();
|
||||
if !matches!(signing_algorithm, SigningAlgorithm::RsaSha256) {
|
||||
return Err(SignerError::UnsupportedSigningAlg(signing_algorithm));
|
||||
}
|
||||
|
||||
match self {
|
||||
SignerProvider::OpenSsl(_, signer) => signer.sign_one_off(algorithm, data),
|
||||
#[cfg(feature = "hsm")]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use std::sync::Arc;
|
||||
use std::{collections::HashMap, sync::RwLock};
|
||||
|
||||
use rpki::repository::crypto::{
|
||||
use rpki::crypto::{
|
||||
signer::KeyError, KeyIdentifier, PublicKey, PublicKeyFormat, Signature, SignatureAlgorithm, Signer, SigningError,
|
||||
};
|
||||
|
||||
@@ -586,21 +586,21 @@ impl Signer for SignerRouter {
|
||||
self.get_signer_for_key(key_id)?.destroy_key(key_id)
|
||||
}
|
||||
|
||||
fn sign<D: AsRef<[u8]> + ?Sized>(
|
||||
fn sign<Alg: SignatureAlgorithm, D: AsRef<[u8]> + ?Sized>(
|
||||
&self,
|
||||
key_id: &KeyIdentifier,
|
||||
algorithm: SignatureAlgorithm,
|
||||
algorithm: Alg,
|
||||
data: &D,
|
||||
) -> Result<Signature, SigningError<Self::Error>> {
|
||||
) -> Result<Signature<Alg>, SigningError<Self::Error>> {
|
||||
self.bind_ready_signers();
|
||||
self.get_signer_for_key(key_id)?.sign(key_id, algorithm, data)
|
||||
}
|
||||
|
||||
fn sign_one_off<D: AsRef<[u8]> + ?Sized>(
|
||||
fn sign_one_off<Alg: SignatureAlgorithm, D: AsRef<[u8]> + ?Sized>(
|
||||
&self,
|
||||
algorithm: SignatureAlgorithm,
|
||||
algorithm: Alg,
|
||||
data: &D,
|
||||
) -> Result<(Signature, PublicKey), Self::Error> {
|
||||
) -> Result<(Signature<Alg>, PublicKey), Self::Error> {
|
||||
self.bind_ready_signers();
|
||||
self.one_off_signer.sign_one_off(algorithm, data)
|
||||
}
|
||||
@@ -613,6 +613,8 @@ impl Signer for SignerRouter {
|
||||
|
||||
#[cfg(all(test, feature = "hsm"))]
|
||||
pub mod tests {
|
||||
use rpki::crypto::RpkiSignatureAlgorithm;
|
||||
|
||||
use crate::{
|
||||
commons::crypto::{
|
||||
dispatch::signerprovider::SignerFlags,
|
||||
@@ -639,7 +641,7 @@ pub mod tests {
|
||||
pub fn verify_that_a_usable_signer_is_registered_and_can_be_used() {
|
||||
test::test_under_tmp(|d| {
|
||||
#[allow(non_snake_case)]
|
||||
let DEF_SIG_ALG = SignatureAlgorithm::default();
|
||||
let DEF_SIG_ALG = RpkiSignatureAlgorithm::default();
|
||||
|
||||
// Build a mock signer that is contactable and usable for the SignerRouter
|
||||
let call_counts = Arc::new(MockSignerCallCounts::new());
|
||||
@@ -712,7 +714,7 @@ pub mod tests {
|
||||
router.destroy_key(&key_identifier).unwrap();
|
||||
assert_eq!(1, call_counts.get(FnIdx::DestroyKey));
|
||||
|
||||
let err = router.sign(&key_identifier, SignatureAlgorithm::default(), &out_buf);
|
||||
let err = router.sign(&key_identifier, RpkiSignatureAlgorithm::default(), &out_buf);
|
||||
// TODO: Should this error from the SignerRouter actually be SigningError::KeyNotFound instead of
|
||||
// SigningError::Signer(SignerError::KeyNotFound)?
|
||||
assert!(matches!(err, Err(SigningError::Signer(SignerError::KeyNotFound))));
|
||||
|
||||
@@ -5,14 +5,14 @@ use std::convert::TryFrom;
|
||||
|
||||
use bytes::Bytes;
|
||||
use rpki::{
|
||||
ca::provisioning::RequestResourceLimit,
|
||||
ca::{csr::RpkiCaCsr, provisioning::RequestResourceLimit},
|
||||
crypto::{DigestAlgorithm, KeyIdentifier, PublicKey},
|
||||
repository::{
|
||||
cert::{KeyUsage, Overclaim, TbsCert},
|
||||
crypto::{DigestAlgorithm, KeyIdentifier, PublicKey},
|
||||
manifest::FileAndHash,
|
||||
resources::ResourceSet,
|
||||
x509::{Name, Time, Validity},
|
||||
Cert, Crl, Csr,
|
||||
Cert, Crl,
|
||||
},
|
||||
uri,
|
||||
};
|
||||
@@ -75,10 +75,10 @@ impl CsrInfo {
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&Csr> for CsrInfo {
|
||||
impl TryFrom<&RpkiCaCsr> for CsrInfo {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(csr: &Csr) -> KrillResult<CsrInfo> {
|
||||
fn try_from(csr: &RpkiCaCsr) -> KrillResult<CsrInfo> {
|
||||
csr.validate().map_err(|_| Error::invalid_csr("invalid signature"))?;
|
||||
let ca_repository = csr
|
||||
.ca_repository()
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use std::{fmt, path::PathBuf};
|
||||
|
||||
use openssl::error::ErrorStack;
|
||||
use rpki::crypto::signer::SigningAlgorithm;
|
||||
|
||||
use crate::commons::error::KrillIoError;
|
||||
|
||||
@@ -17,6 +18,7 @@ pub enum SignerError {
|
||||
PermanentlyUnusable,
|
||||
Pkcs11Error(String),
|
||||
TemporarilyUnavailable,
|
||||
UnsupportedSigningAlg(SigningAlgorithm),
|
||||
}
|
||||
|
||||
impl fmt::Display for SignerError {
|
||||
@@ -33,6 +35,10 @@ impl fmt::Display for SignerError {
|
||||
SignerError::PermanentlyUnusable => write!(f, "Signer is unusable"),
|
||||
SignerError::Pkcs11Error(e) => write!(f, "PKCS#11 Error: {}", e),
|
||||
SignerError::TemporarilyUnavailable => write!(f, "Signer is unavailable"),
|
||||
SignerError::UnsupportedSigningAlg(key_format) => match key_format {
|
||||
SigningAlgorithm::RsaSha256 => write!(f, "Signing with RSA not supported"),
|
||||
SigningAlgorithm::EcdsaP256Sha256 => write!(f, "Signing with EcdsaP256 not supported"),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,8 +21,11 @@ use openssl::ssl::SslStream;
|
||||
use r2d2::PooledConnection;
|
||||
|
||||
use rpki::{
|
||||
repository::crypto::signer::KeyError,
|
||||
repository::crypto::{KeyIdentifier, PublicKey, PublicKeyFormat, Signature, SignatureAlgorithm, SigningError},
|
||||
crypto::signer::KeyError,
|
||||
crypto::{
|
||||
KeyIdentifier, PublicKey, PublicKeyFormat, RpkiSignature, RpkiSignatureAlgorithm, Signature,
|
||||
SignatureAlgorithm, SigningError,
|
||||
},
|
||||
};
|
||||
|
||||
use crate::commons::{
|
||||
@@ -329,8 +332,12 @@ impl KmipSigner {
|
||||
&self,
|
||||
signer_private_key_id: &str,
|
||||
challenge: &D,
|
||||
) -> Result<Signature, SignerError> {
|
||||
self.sign_with_key(signer_private_key_id, SignatureAlgorithm::default(), challenge.as_ref())
|
||||
) -> Result<RpkiSignature, SignerError> {
|
||||
self.sign_with_key(
|
||||
signer_private_key_id,
|
||||
RpkiSignatureAlgorithm::default(),
|
||||
challenge.as_ref(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -782,12 +789,12 @@ impl KmipSigner {
|
||||
Ok(public_key)
|
||||
}
|
||||
|
||||
pub(super) fn sign_with_key(
|
||||
pub(super) fn sign_with_key<Alg: SignatureAlgorithm>(
|
||||
&self,
|
||||
private_key_id: &str,
|
||||
algorithm: SignatureAlgorithm,
|
||||
algorithm: Alg,
|
||||
data: &[u8],
|
||||
) -> Result<Signature, SignerError> {
|
||||
) -> Result<Signature<Alg>, SignerError> {
|
||||
if algorithm.public_key_format() != PublicKeyFormat::Rsa {
|
||||
return Err(SignerError::KmipError(format!(
|
||||
"Algorithm '{:?}' not supported",
|
||||
@@ -797,7 +804,7 @@ impl KmipSigner {
|
||||
|
||||
let signed = self.with_conn("sign", |conn| conn.sign(&private_key_id, data))?;
|
||||
|
||||
let sig = Signature::new(SignatureAlgorithm::default(), Bytes::from(signed.signature_data));
|
||||
let sig = Signature::new(algorithm, Bytes::from(signed.signature_data));
|
||||
|
||||
Ok(sig)
|
||||
}
|
||||
@@ -913,12 +920,12 @@ impl KmipSigner {
|
||||
res
|
||||
}
|
||||
|
||||
pub fn sign<D: AsRef<[u8]> + ?Sized>(
|
||||
pub fn sign<Alg: SignatureAlgorithm, D: AsRef<[u8]> + ?Sized>(
|
||||
&self,
|
||||
key_id: &KeyIdentifier,
|
||||
algorithm: SignatureAlgorithm,
|
||||
algorithm: Alg,
|
||||
data: &D,
|
||||
) -> Result<Signature, SigningError<SignerError>> {
|
||||
) -> Result<Signature<Alg>, SigningError<SignerError>> {
|
||||
let kmip_key_pair_ids = self.lookup_kmip_key_ids(key_id)?;
|
||||
|
||||
let signature = self
|
||||
@@ -933,11 +940,11 @@ impl KmipSigner {
|
||||
Ok(signature)
|
||||
}
|
||||
|
||||
pub fn sign_one_off<D: AsRef<[u8]> + ?Sized>(
|
||||
pub fn sign_one_off<Alg: SignatureAlgorithm, D: AsRef<[u8]> + ?Sized>(
|
||||
&self,
|
||||
algorithm: SignatureAlgorithm,
|
||||
algorithm: Alg,
|
||||
data: &D,
|
||||
) -> Result<(Signature, PublicKey), SignerError> {
|
||||
) -> Result<(Signature<Alg>, PublicKey), SignerError> {
|
||||
// TODO: Is it possible to use a KMIP batch request to implement the create, activate, sign, deactivate, delete
|
||||
// in one round-trip to the server?
|
||||
let (key, kmip_key_pair_ids) = self.build_key(PublicKeyFormat::Rsa)?;
|
||||
|
||||
@@ -11,8 +11,11 @@ use openssl::{
|
||||
};
|
||||
|
||||
use rpki::{
|
||||
repository::crypto::signer::KeyError,
|
||||
repository::crypto::{KeyIdentifier, PublicKey, PublicKeyFormat, Signature, SignatureAlgorithm, SigningError},
|
||||
crypto::signer::KeyError,
|
||||
crypto::{
|
||||
signer::SigningAlgorithm, KeyIdentifier, PublicKey, PublicKeyFormat, RpkiSignature, RpkiSignatureAlgorithm,
|
||||
Signature, SignatureAlgorithm, SigningError,
|
||||
},
|
||||
};
|
||||
|
||||
use crate::commons::crypto::{dispatch::signerinfo::SignerMapper, SignerError, SignerHandle};
|
||||
@@ -119,10 +122,19 @@ impl MockSigner {
|
||||
Ok((public_key, pkey, key_identifier, internal_id))
|
||||
}
|
||||
|
||||
fn sign_with_key<D: AsRef<[u8]> + ?Sized>(pkey: &PKey<Private>, challenge: &D) -> Result<Signature, SignerError> {
|
||||
fn sign_with_key<Alg: SignatureAlgorithm, D: AsRef<[u8]> + ?Sized>(
|
||||
alg: Alg,
|
||||
pkey: &PKey<Private>,
|
||||
challenge: &D,
|
||||
) -> Result<Signature<Alg>, SignerError> {
|
||||
let signing_algorithm = alg.signing_algorithm();
|
||||
if !matches!(signing_algorithm, SigningAlgorithm::RsaSha256) {
|
||||
return Err(SignerError::UnsupportedSigningAlg(signing_algorithm).into());
|
||||
}
|
||||
|
||||
let mut signer = ::openssl::sign::Signer::new(MessageDigest::sha256(), &pkey)?;
|
||||
signer.update(challenge.as_ref())?;
|
||||
let signature = Signature::new(SignatureAlgorithm::default(), Bytes::from(signer.sign_to_vec()?));
|
||||
let signature = Signature::new(alg, Bytes::from(signer.sign_to_vec()?));
|
||||
Ok(signature)
|
||||
}
|
||||
|
||||
@@ -161,7 +173,7 @@ impl MockSigner {
|
||||
&self,
|
||||
signer_private_key_id: &str,
|
||||
challenge: &D,
|
||||
) -> Result<Signature, SignerError> {
|
||||
) -> Result<RpkiSignature, SignerError> {
|
||||
self.inc_fn_call_count(FnIdx::SignRegistrationChallenge);
|
||||
if let Some(err_cb) = &self.sign_registration_challenge_error_cb {
|
||||
let _ = (err_cb)(&self.fn_call_counts)?;
|
||||
@@ -169,7 +181,7 @@ impl MockSigner {
|
||||
let pkey = self.load_key(signer_private_key_id).ok_or(SignerError::KeyNotFound)?;
|
||||
|
||||
// sign the given data using the loaded private key
|
||||
let signature = Self::sign_with_key(&pkey, challenge)?;
|
||||
let signature = Self::sign_with_key(RpkiSignatureAlgorithm::default(), &pkey, challenge)?;
|
||||
|
||||
// return the generated signature to the caller
|
||||
Ok(signature)
|
||||
@@ -233,26 +245,26 @@ impl MockSigner {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn sign<D: AsRef<[u8]> + ?Sized>(
|
||||
pub fn sign<Alg: SignatureAlgorithm, D: AsRef<[u8]> + ?Sized>(
|
||||
&self,
|
||||
key_identifier: &KeyIdentifier,
|
||||
_algorithm: SignatureAlgorithm,
|
||||
algorithm: Alg,
|
||||
data: &D,
|
||||
) -> Result<Signature, SigningError<SignerError>> {
|
||||
) -> Result<Signature<Alg>, SigningError<SignerError>> {
|
||||
self.inc_fn_call_count(FnIdx::Sign);
|
||||
let internal_id = self.internal_id_from_key_identifier(key_identifier)?;
|
||||
let pkey = self.load_key(&internal_id).ok_or(SignerError::KeyNotFound)?;
|
||||
Self::sign_with_key(&pkey, data).map_err(|err| SigningError::Signer(err))
|
||||
Self::sign_with_key(algorithm, &pkey, data).map_err(|err| SigningError::Signer(err))
|
||||
}
|
||||
|
||||
pub fn sign_one_off<D: AsRef<[u8]> + ?Sized>(
|
||||
pub fn sign_one_off<Alg: SignatureAlgorithm, D: AsRef<[u8]> + ?Sized>(
|
||||
&self,
|
||||
_algorithm: SignatureAlgorithm,
|
||||
algorithm: Alg,
|
||||
data: &D,
|
||||
) -> Result<(Signature, PublicKey), SignerError> {
|
||||
) -> Result<(Signature<Alg>, PublicKey), SignerError> {
|
||||
self.inc_fn_call_count(FnIdx::SignOneOff);
|
||||
let (public_key, pkey, _, internal_id) = self.build_key().unwrap();
|
||||
let signature = Self::sign_with_key(&pkey, data).unwrap();
|
||||
let signature = Self::sign_with_key(algorithm, &pkey, data).unwrap();
|
||||
let _ = self.keys.write().unwrap().remove(&internal_id);
|
||||
Ok((signature, public_key))
|
||||
}
|
||||
|
||||
@@ -20,8 +20,11 @@ use cryptoki::{
|
||||
};
|
||||
|
||||
use rpki::{
|
||||
repository::crypto::signer::KeyError,
|
||||
repository::crypto::{KeyIdentifier, PublicKey, PublicKeyFormat, Signature, SignatureAlgorithm, SigningError},
|
||||
crypto::signer::KeyError,
|
||||
crypto::{
|
||||
KeyIdentifier, PublicKey, PublicKeyFormat, RpkiSignature, RpkiSignatureAlgorithm, Signature,
|
||||
SignatureAlgorithm, SigningError,
|
||||
},
|
||||
};
|
||||
|
||||
use crate::commons::crypto::{
|
||||
@@ -243,14 +246,14 @@ impl Pkcs11Signer {
|
||||
&self,
|
||||
key_id: &str,
|
||||
challenge: &D,
|
||||
) -> Result<Signature, SignerError> {
|
||||
) -> Result<RpkiSignature, SignerError> {
|
||||
let priv_handle = self
|
||||
.find_key(key_id, ObjectClass::PRIVATE_KEY)
|
||||
.map_err(|err| match err {
|
||||
KeyError::KeyNotFound => SignerError::KeyNotFound,
|
||||
KeyError::Signer(err) => err,
|
||||
})?;
|
||||
self.sign_with_key(priv_handle, SignatureAlgorithm::default(), challenge.as_ref())
|
||||
self.sign_with_key(priv_handle, RpkiSignatureAlgorithm::default(), challenge.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -633,7 +636,7 @@ impl Pkcs11Signer {
|
||||
impl Pkcs11Signer {
|
||||
pub(super) fn remember_key_id(
|
||||
&self,
|
||||
key_id: &rpki::repository::crypto::KeyIdentifier,
|
||||
key_id: &rpki::crypto::KeyIdentifier,
|
||||
internal_key_id: String,
|
||||
) -> Result<(), SignerError> {
|
||||
let readable_handle = self.handle.read().unwrap();
|
||||
@@ -765,12 +768,12 @@ impl Pkcs11Signer {
|
||||
Ok(public_key)
|
||||
}
|
||||
|
||||
pub(super) fn sign_with_key(
|
||||
pub(super) fn sign_with_key<Alg: SignatureAlgorithm>(
|
||||
&self,
|
||||
private_key_handle: ObjectHandle,
|
||||
algorithm: SignatureAlgorithm,
|
||||
algorithm: Alg,
|
||||
data: &[u8],
|
||||
) -> Result<Signature, SignerError> {
|
||||
) -> Result<Signature<Alg>, SignerError> {
|
||||
if algorithm.public_key_format() != PublicKeyFormat::Rsa {
|
||||
return Err(SignerError::KmipError(format!(
|
||||
"Algorithm '{:?}' not supported",
|
||||
@@ -798,7 +801,7 @@ impl Pkcs11Signer {
|
||||
|
||||
let signature_data = self.with_conn("sign", |conn| conn.sign(&mechanism, private_key_handle, data))?;
|
||||
|
||||
let sig = Signature::new(SignatureAlgorithm::default(), Bytes::from(signature_data));
|
||||
let sig = Signature::new(algorithm, Bytes::from(signature_data));
|
||||
|
||||
Ok(sig)
|
||||
}
|
||||
@@ -918,12 +921,12 @@ impl Pkcs11Signer {
|
||||
res
|
||||
}
|
||||
|
||||
pub fn sign<D: AsRef<[u8]> + ?Sized>(
|
||||
pub fn sign<Alg: SignatureAlgorithm, D: AsRef<[u8]> + ?Sized>(
|
||||
&self,
|
||||
key_id: &KeyIdentifier,
|
||||
algorithm: SignatureAlgorithm,
|
||||
algorithm: Alg,
|
||||
data: &D,
|
||||
) -> Result<Signature, SigningError<SignerError>> {
|
||||
) -> Result<Signature<Alg>, SigningError<SignerError>> {
|
||||
let internal_key_id = self.lookup_key_id(key_id)?;
|
||||
let priv_handle = self
|
||||
.find_key(&internal_key_id, ObjectClass::PRIVATE_KEY)
|
||||
@@ -936,11 +939,11 @@ impl Pkcs11Signer {
|
||||
.map_err(|err| SigningError::Signer(err))
|
||||
}
|
||||
|
||||
pub fn sign_one_off<D: AsRef<[u8]> + ?Sized>(
|
||||
pub fn sign_one_off<Alg: SignatureAlgorithm, D: AsRef<[u8]> + ?Sized>(
|
||||
&self,
|
||||
algorithm: SignatureAlgorithm,
|
||||
algorithm: Alg,
|
||||
data: &D,
|
||||
) -> Result<(Signature, PublicKey), SignerError> {
|
||||
) -> Result<(Signature<Alg>, PublicKey), SignerError> {
|
||||
let (key, pub_handle, priv_handle, _) = self.build_key(PublicKeyFormat::Rsa)?;
|
||||
|
||||
let signature_res = self
|
||||
|
||||
@@ -328,19 +328,19 @@ pub mod tests {
|
||||
assert_eq!(None, conn.last_probe_time()?);
|
||||
|
||||
// The first call to .get() should trigger a probe
|
||||
let _ = conn.status(|_, _| Err(ProbeError::AwaitingNextProbe));
|
||||
let _probe1 = conn.status(|_, _| Err(ProbeError::AwaitingNextProbe));
|
||||
let t1 = conn.last_probe_time()?;
|
||||
assert!(t1.is_some());
|
||||
|
||||
// A call to .get() before the next probe interval should NOT result in an updated last probe time
|
||||
std::thread::sleep(Duration::from_millis(10));
|
||||
let _ = conn.status(|_, _| Err(ProbeError::AwaitingNextProbe));
|
||||
let _probe2 = conn.status(|_, _| Err(ProbeError::AwaitingNextProbe));
|
||||
let t2 = conn.last_probe_time()?;
|
||||
assert!(t2 == t1);
|
||||
|
||||
// A call to .get() after the next probe interval SHOULD result in an updated last probe time
|
||||
std::thread::sleep(Duration::from_millis(200));
|
||||
let _ = conn.status(|_, _| Err(ProbeError::AwaitingNextProbe));
|
||||
let _probe3 = conn.status(|_, _| Err(ProbeError::AwaitingNextProbe));
|
||||
let t3 = conn.last_probe_time()?;
|
||||
assert!(t3 > t1);
|
||||
|
||||
|
||||
@@ -19,8 +19,10 @@ use openssl::{
|
||||
rsa::Rsa,
|
||||
};
|
||||
|
||||
use rpki::repository::crypto::{
|
||||
signer::KeyError, KeyIdentifier, PublicKey, PublicKeyFormat, Signature, SignatureAlgorithm, SigningError,
|
||||
use rpki::crypto::{
|
||||
signer::{KeyError, SigningAlgorithm},
|
||||
KeyIdentifier, PublicKey, PublicKeyFormat, RpkiSignature, RpkiSignatureAlgorithm, Signature, SignatureAlgorithm,
|
||||
SigningError,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
@@ -112,10 +114,10 @@ impl OpenSslSigner {
|
||||
&self,
|
||||
signer_private_key_id: &str,
|
||||
challenge: &D,
|
||||
) -> Result<Signature, SignerError> {
|
||||
) -> Result<RpkiSignature, SignerError> {
|
||||
let key_id = KeyIdentifier::from_str(signer_private_key_id).map_err(|_| SignerError::KeyNotFound)?;
|
||||
let key_pair = self.load_key(&key_id)?;
|
||||
let signature = Self::sign_with_key(key_pair.pkey.as_ref(), challenge)?;
|
||||
let signature = Self::sign_with_key(key_pair.pkey.as_ref(), RpkiSignatureAlgorithm::default(), challenge)?;
|
||||
Ok(signature)
|
||||
}
|
||||
}
|
||||
@@ -165,11 +167,20 @@ impl OpenSslSigner {
|
||||
Ok(key_id)
|
||||
}
|
||||
|
||||
fn sign_with_key<D: AsRef<[u8]> + ?Sized>(pkey: &PKeyRef<Private>, data: &D) -> Result<Signature, SignerError> {
|
||||
fn sign_with_key<Alg: SignatureAlgorithm, D: AsRef<[u8]> + ?Sized>(
|
||||
pkey: &PKeyRef<Private>,
|
||||
algorithm: Alg,
|
||||
data: &D,
|
||||
) -> Result<Signature<Alg>, SignerError> {
|
||||
let signing_algorithm = algorithm.signing_algorithm();
|
||||
if !matches!(signing_algorithm, SigningAlgorithm::RsaSha256) {
|
||||
return Err(SignerError::UnsupportedSigningAlg(signing_algorithm));
|
||||
}
|
||||
|
||||
let mut signer = ::openssl::sign::Signer::new(MessageDigest::sha256(), pkey)?;
|
||||
signer.update(data.as_ref())?;
|
||||
|
||||
let signature = Signature::new(SignatureAlgorithm::default(), Bytes::from(signer.sign_to_vec()?));
|
||||
let signature = Signature::new(algorithm, Bytes::from(signer.sign_to_vec()?));
|
||||
|
||||
Ok(signature)
|
||||
}
|
||||
@@ -238,25 +249,23 @@ impl OpenSslSigner {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn sign<D: AsRef<[u8]> + ?Sized>(
|
||||
pub fn sign<Alg: SignatureAlgorithm, D: AsRef<[u8]> + ?Sized>(
|
||||
&self,
|
||||
key_id: &KeyIdentifier,
|
||||
_algorithm: SignatureAlgorithm,
|
||||
algorithm: Alg,
|
||||
data: &D,
|
||||
) -> Result<Signature, SigningError<SignerError>> {
|
||||
) -> Result<Signature<Alg>, SigningError<SignerError>> {
|
||||
let key_pair = self.load_key(key_id)?;
|
||||
Self::sign_with_key(key_pair.pkey.as_ref(), data).map_err(SigningError::Signer)
|
||||
Self::sign_with_key(key_pair.pkey.as_ref(), algorithm, data).map_err(SigningError::Signer)
|
||||
}
|
||||
|
||||
pub fn sign_one_off<D: AsRef<[u8]> + ?Sized>(
|
||||
pub fn sign_one_off<Alg: SignatureAlgorithm, D: AsRef<[u8]> + ?Sized>(
|
||||
&self,
|
||||
_algorithm: SignatureAlgorithm,
|
||||
algorithm: Alg,
|
||||
data: &D,
|
||||
) -> Result<(Signature, PublicKey), SignerError> {
|
||||
) -> Result<(Signature<Alg>, PublicKey), SignerError> {
|
||||
let kp = OpenSslKeyPair::build()?;
|
||||
|
||||
let signature = Self::sign_with_key(kp.pkey.as_ref(), data)?;
|
||||
|
||||
let signature = Self::sign_with_key(kp.pkey.as_ref(), algorithm, data)?;
|
||||
let key = kp.subject_public_key_info()?;
|
||||
|
||||
Ok((signature, key))
|
||||
|
||||
+35
-1
@@ -11,7 +11,8 @@ use rpki::{
|
||||
provisioning::ResourceClassName,
|
||||
publication,
|
||||
},
|
||||
repository::{crypto::KeyIdentifier, x509::ValidationError},
|
||||
crypto::KeyIdentifier,
|
||||
repository::x509::ValidationError,
|
||||
uri,
|
||||
};
|
||||
|
||||
@@ -26,6 +27,8 @@ use crate::{
|
||||
upgrades::PrepareUpgradeError,
|
||||
};
|
||||
|
||||
use super::api::{BgpSecAsnKey, BgpSecDefinition};
|
||||
|
||||
//------------ RoaDeltaError -----------------------------------------------
|
||||
|
||||
/// This type contains a detailed error report for a ROA delta
|
||||
@@ -273,6 +276,13 @@ pub enum Error {
|
||||
AspaProvidersUpdateEmpty(CaHandle, AspaCustomer),
|
||||
AspaProvidersUpdateConflict(CaHandle, AspaProvidersUpdateConflict),
|
||||
|
||||
//-----------------------------------------------------------------
|
||||
// BGP Sec
|
||||
//-----------------------------------------------------------------
|
||||
BgpSecDefinitionUnknown(CaHandle, BgpSecAsnKey),
|
||||
BgpSecDefinitionInvalidlySigned(CaHandle, BgpSecDefinition),
|
||||
BgpSecDefinitionNotEntitled(CaHandle, BgpSecAsnKey),
|
||||
|
||||
//-----------------------------------------------------------------
|
||||
// Key Usage Issues
|
||||
//-----------------------------------------------------------------
|
||||
@@ -442,6 +452,14 @@ impl fmt::Display for Error {
|
||||
Error::AspaCustomerUnknown(_ca, asn) => write!(f, "No current ASPA exists for customer AS '{}'", asn),
|
||||
Error::AspaProvidersUpdateEmpty(_ca, asn) => write!(f, "Received empty update for ASPA for customer AS '{}'", asn),
|
||||
Error::AspaProvidersUpdateConflict(_ca, e) => write!(f, "ASPA delta rejected:\n\n'{}'", e),
|
||||
|
||||
//-----------------------------------------------------------------
|
||||
// BGPSec
|
||||
//-----------------------------------------------------------------
|
||||
Error::BgpSecDefinitionUnknown(_ca, key) => write!(f, "Cannot remove BGPSec CSR for unknown combination of ASN '{}' and key '{}'", key.asn(), key.key_identifier()),
|
||||
Error::BgpSecDefinitionInvalidlySigned(_ca, def) => write!(f, "Invalidly signed BGPSec CSR remove BGPSec CSR for ASN '{}' and key '{}'", def.asn(), def.csr().public_key().key_identifier()),
|
||||
Error::BgpSecDefinitionNotEntitled(_ca, key) => write!(f, "AS '{}' is not held by you", key.asn()),
|
||||
|
||||
|
||||
//-----------------------------------------------------------------
|
||||
// Key Usage Issues
|
||||
@@ -830,6 +848,22 @@ impl Error {
|
||||
.with_ca(ca)
|
||||
.with_aspa_providers_conflict(conflict),
|
||||
|
||||
//-----------------------------------------------------------------
|
||||
// BGP Sec
|
||||
//-----------------------------------------------------------------
|
||||
Error::BgpSecDefinitionUnknown(ca, key) => ErrorResponse::new("ca-bgpsec-unknown", &self)
|
||||
.with_ca(ca)
|
||||
.with_asn(key.asn())
|
||||
.with_key_identifier(&key.key_identifier()),
|
||||
Error::BgpSecDefinitionInvalidlySigned(ca, def) => ErrorResponse::new("ca-bgpsec-invalidly-signed", &self)
|
||||
.with_ca(ca)
|
||||
.with_asn(def.asn())
|
||||
.with_key_identifier(&def.csr().public_key().key_identifier())
|
||||
.with_bgpsec_csr(def.csr()),
|
||||
Error::BgpSecDefinitionNotEntitled(ca, key) => ErrorResponse::new("ca-bgpsec-not-entitled", &self)
|
||||
.with_ca(ca)
|
||||
.with_asn(key.asn()),
|
||||
|
||||
//-----------------------------------------------------------------
|
||||
// Key Usage Issues (key-*)
|
||||
//-----------------------------------------------------------------
|
||||
|
||||
@@ -9,9 +9,9 @@ use log::LevelFilter;
|
||||
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
|
||||
use syslog::Facility;
|
||||
|
||||
use rpki::repository::{
|
||||
use rpki::{
|
||||
crypto::PublicKey,
|
||||
resources::{AsBlocks, IpBlocks},
|
||||
repository::resources::{AsBlocks, IpBlocks},
|
||||
};
|
||||
|
||||
//------------ Bytes ---------------------------------------------------------
|
||||
|
||||
@@ -5,7 +5,7 @@ use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
|
||||
|
||||
use bytes::Bytes;
|
||||
use rpki::{
|
||||
repository::crypto::DigestAlgorithm,
|
||||
crypto::DigestAlgorithm,
|
||||
uri::{Https, Rsync},
|
||||
};
|
||||
|
||||
|
||||
@@ -70,6 +70,8 @@ iterable_enum! {
|
||||
ASPAS_READ,
|
||||
ASPAS_UPDATE,
|
||||
ASPAS_ANALYSIS,
|
||||
BGPSEC_READ,
|
||||
BGPSEC_UPDATE,
|
||||
RTA_LIST,
|
||||
RTA_READ,
|
||||
RTA_UPDATE
|
||||
|
||||
@@ -0,0 +1,318 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use rpki::{
|
||||
ca::{csr::BgpsecCsr, publication::Base64},
|
||||
crypto::PublicKey,
|
||||
repository::{
|
||||
cert::{ExtendedKeyUsage, KeyUsage, Overclaim, TbsCert},
|
||||
resources::Asn,
|
||||
x509::{Serial, Time},
|
||||
Cert,
|
||||
},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
commons::{
|
||||
api::{BgpSecAsnKey, BgpSecCsrInfo, BgpSecCsrInfoList},
|
||||
crypto::{KrillSigner, SignSupport},
|
||||
KrillResult,
|
||||
},
|
||||
daemon::config::{Config, IssuanceTimingConfig},
|
||||
};
|
||||
|
||||
use super::{BgpSecCertificateUpdates, CertifiedKey};
|
||||
|
||||
//------------ BgpSecCertificates ------------------------------------------
|
||||
|
||||
/// The issued BGPSec certificates under a resource class in a CA.
|
||||
#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
|
||||
pub struct BgpSecCertificates(HashMap<BgpSecAsnKey, BgpSecCertInfo>);
|
||||
|
||||
impl BgpSecCertificates {
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.0.is_empty()
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.0.len()
|
||||
}
|
||||
|
||||
/// Update issued BGPSec certificates
|
||||
///
|
||||
/// Will issue new BGPSec certificates for definitions using the resources of
|
||||
/// this certified key which did not yet exist.
|
||||
///
|
||||
/// Will remove any existing BGPSec certificates which:
|
||||
/// - are no longer present in the definitions; or
|
||||
/// - for which the certified key no longer holds the asn.
|
||||
///
|
||||
///
|
||||
/// Note that we pass in ALL BGPSec definitions, including definitions that may only
|
||||
/// be eligible under another owning RC.
|
||||
pub fn update(
|
||||
&self,
|
||||
definitions: &BgpSecDefinitions,
|
||||
certified_key: &CertifiedKey,
|
||||
config: &Config,
|
||||
signer: &KrillSigner,
|
||||
) -> KrillResult<BgpSecCertificateUpdates> {
|
||||
let mut updates = BgpSecCertificateUpdates::default();
|
||||
|
||||
let resources = certified_key.incoming_cert().resources();
|
||||
let issuance_timing = &config.issuance_timing;
|
||||
|
||||
// Issue BGPSec certificates for any ASN held by the certified key
|
||||
// for which the required router key has not yet been certified.
|
||||
for (key, csr) in definitions
|
||||
.iter()
|
||||
.filter(|(k, _)| !self.0.contains_key(k) && resources.contains_asn(k.asn()))
|
||||
{
|
||||
// resource held here, but BGPSec certificate was not yet issued.
|
||||
let cert = self.make_bgpsec_cert(key.asn(), csr.key().clone(), certified_key, issuance_timing, signer)?;
|
||||
updates.add_updated(cert);
|
||||
}
|
||||
|
||||
// Will remove any existing BGPSec certificates which:
|
||||
// - are no longer present in the definitions; or
|
||||
// - for which the certified key no longer holds the asn.
|
||||
for (key, _) in self
|
||||
.0
|
||||
.iter()
|
||||
.filter(|(k, _)| !definitions.has(k) || !resources.contains_asn(k.asn()))
|
||||
{
|
||||
updates.add_removed(*key);
|
||||
}
|
||||
|
||||
Ok(updates)
|
||||
}
|
||||
|
||||
/// Re-new BGPSec certificates
|
||||
///
|
||||
/// Used to renew certificates which would expire, in which case the renew_threshold
|
||||
/// should be specified. Or, to re-issue all existing certificates during a key rollover
|
||||
/// activation of a new certified_key - in which case the renew_threshold is expected to
|
||||
/// be None, and the certified_key is expected to have changed.
|
||||
pub fn renew(
|
||||
&self,
|
||||
certified_key: &CertifiedKey,
|
||||
renew_threshold: Option<Time>,
|
||||
issuance_timing: &IssuanceTimingConfig,
|
||||
signer: &KrillSigner,
|
||||
) -> KrillResult<BgpSecCertificateUpdates> {
|
||||
let mut updates = BgpSecCertificateUpdates::default();
|
||||
|
||||
for cert in self.0.values().filter(|cert| {
|
||||
renew_threshold
|
||||
.map(|threshold| cert.expires() < threshold) // will expire
|
||||
.unwrap_or(true) // always renew if no renew_threshold was given
|
||||
}) {
|
||||
let asn = cert.asn();
|
||||
let public_key = cert.public_key().clone();
|
||||
let cert = self.make_bgpsec_cert(asn, public_key, certified_key, issuance_timing, signer)?;
|
||||
updates.add_updated(cert);
|
||||
}
|
||||
|
||||
Ok(updates)
|
||||
}
|
||||
|
||||
fn make_bgpsec_cert(
|
||||
&self,
|
||||
asn: Asn,
|
||||
public_key: PublicKey,
|
||||
certified_key: &CertifiedKey,
|
||||
issuance_timing: &IssuanceTimingConfig,
|
||||
signer: &KrillSigner,
|
||||
) -> KrillResult<BgpSecCertInfo> {
|
||||
let serial_number = signer.random_serial()?;
|
||||
|
||||
let incoming_cert = certified_key.incoming_cert();
|
||||
let issuer = incoming_cert.subject().clone();
|
||||
let crl_uri = incoming_cert.crl_uri();
|
||||
let aki = incoming_cert.subject_public_key_info().key_identifier();
|
||||
let aia = incoming_cert.uri().clone();
|
||||
|
||||
let validity = SignSupport::sign_validity_weeks(issuance_timing.timing_bgpsec_valid_weeks);
|
||||
|
||||
// Perhaps implement recommendation of 3.1.1 RFC 8209 somehow. However, it is
|
||||
// not at all clear how/why this is relevant. RPs will typically discard this
|
||||
// information and the subject is not communicated to routers. If this is for
|
||||
// debugging purposes then using a sensible file name (like we do) is more
|
||||
// important.
|
||||
let subject = None;
|
||||
|
||||
let mut router_cert = TbsCert::new(
|
||||
serial_number,
|
||||
issuer,
|
||||
validity,
|
||||
subject,
|
||||
public_key,
|
||||
KeyUsage::Ee,
|
||||
Overclaim::Refuse,
|
||||
);
|
||||
|
||||
router_cert.set_extended_key_usage(Some(ExtendedKeyUsage::create_router()));
|
||||
router_cert.set_authority_key_identifier(Some(aki));
|
||||
router_cert.set_ca_issuer(Some(aia));
|
||||
router_cert.set_crl_uri(Some(crl_uri));
|
||||
router_cert.build_as_resource_blocks(|b| b.push(asn));
|
||||
|
||||
let signing_key = certified_key.key_id();
|
||||
|
||||
let cert = signer.sign_cert(router_cert, signing_key)?;
|
||||
|
||||
Ok(BgpSecCertInfo::new(asn, cert))
|
||||
}
|
||||
|
||||
/// Applies updates from an event.
|
||||
pub fn updated(&mut self, updates: BgpSecCertificateUpdates) {
|
||||
let (updated, removed) = updates.unpack();
|
||||
for info in updated {
|
||||
let key = info.asn_key();
|
||||
self.0.insert(key, info);
|
||||
}
|
||||
for key in removed {
|
||||
self.0.remove(&key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//------------ BgpSecCertInfo ----------------------------------------------
|
||||
|
||||
/// An issued BGPSec certificate under a resource class
|
||||
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
|
||||
pub struct BgpSecCertInfo {
|
||||
asn: Asn,
|
||||
public_key: PublicKey,
|
||||
serial: Serial,
|
||||
expires: Time,
|
||||
cert: Base64,
|
||||
}
|
||||
|
||||
impl BgpSecCertInfo {
|
||||
fn new(asn: Asn, cert: Cert) -> Self {
|
||||
let public_key = cert.subject_public_key_info().clone();
|
||||
let serial = cert.serial_number();
|
||||
let expires = cert.validity().not_after();
|
||||
let cert = Base64::from(&cert);
|
||||
|
||||
BgpSecCertInfo {
|
||||
asn,
|
||||
public_key,
|
||||
serial,
|
||||
expires,
|
||||
cert,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn asn_key(&self) -> BgpSecAsnKey {
|
||||
BgpSecAsnKey::new(self.asn, self.public_key.key_identifier())
|
||||
}
|
||||
|
||||
pub fn asn(&self) -> Asn {
|
||||
self.asn
|
||||
}
|
||||
|
||||
pub fn public_key(&self) -> &PublicKey {
|
||||
&self.public_key
|
||||
}
|
||||
|
||||
pub fn serial(&self) -> Serial {
|
||||
self.serial
|
||||
}
|
||||
|
||||
pub fn expires(&self) -> Time {
|
||||
self.expires
|
||||
}
|
||||
|
||||
pub fn cert(&self) -> &Base64 {
|
||||
&self.cert
|
||||
}
|
||||
}
|
||||
|
||||
//------------ BgpSecDefinitions -------------------------------------------
|
||||
|
||||
/// All BGPSec definitions held by a CA.
|
||||
///
|
||||
/// Actual BGPSec certificates will be issued under the relevant
|
||||
/// resource classes. The resulting published objects are held by
|
||||
/// the CaObjects structure.
|
||||
#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
|
||||
pub struct BgpSecDefinitions(HashMap<BgpSecAsnKey, StoredBgpSecCsr>);
|
||||
|
||||
impl BgpSecDefinitions {
|
||||
pub fn len(&self) -> usize {
|
||||
self.0.len()
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.0.is_empty()
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> impl Iterator<Item = (&BgpSecAsnKey, &StoredBgpSecCsr)> {
|
||||
self.0.iter()
|
||||
}
|
||||
|
||||
pub fn info_list(&self) -> BgpSecCsrInfoList {
|
||||
BgpSecCsrInfoList::new(
|
||||
self.0
|
||||
.iter()
|
||||
.map(|(key, csr)| BgpSecCsrInfo::new(key.asn(), key.key_identifier(), csr.csr().clone()))
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn get_stored_csr(&self, key: &BgpSecAsnKey) -> Option<&StoredBgpSecCsr> {
|
||||
self.0.get(key)
|
||||
}
|
||||
|
||||
pub fn has(&self, key: &BgpSecAsnKey) -> bool {
|
||||
self.0.contains_key(key)
|
||||
}
|
||||
|
||||
/// Inserts or updates the CSR entry for the given key.
|
||||
pub fn add_or_replace(&mut self, key: BgpSecAsnKey, csr: StoredBgpSecCsr) {
|
||||
self.0.insert(key, csr);
|
||||
}
|
||||
|
||||
/// Removes the CSR entry for the given key.
|
||||
pub fn remove(&mut self, key: &BgpSecAsnKey) -> bool {
|
||||
self.0.remove(key).is_some()
|
||||
}
|
||||
}
|
||||
|
||||
//------------ StoredBgpSecCsr ---------------------------------------------
|
||||
|
||||
/// A stored BGP Sec CSR.
|
||||
///
|
||||
/// The original CSR is stored as a base64 structure in order to avoid
|
||||
/// issues if (when?) our CSR parsing should become more strict in a
|
||||
/// future release.
|
||||
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
|
||||
pub struct StoredBgpSecCsr {
|
||||
since: Time,
|
||||
key: PublicKey,
|
||||
csr: Base64,
|
||||
}
|
||||
|
||||
impl StoredBgpSecCsr {
|
||||
pub fn key(&self) -> &PublicKey {
|
||||
&self.key
|
||||
}
|
||||
|
||||
pub fn csr(&self) -> &Base64 {
|
||||
&self.csr
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&BgpsecCsr> for StoredBgpSecCsr {
|
||||
fn from(csr: &BgpsecCsr) -> Self {
|
||||
let since = Time::now();
|
||||
let key = csr.public_key().clone();
|
||||
let binary = Base64::from_content(csr.to_captured().as_slice());
|
||||
StoredBgpSecCsr {
|
||||
since,
|
||||
key,
|
||||
csr: binary,
|
||||
}
|
||||
}
|
||||
}
|
||||
+142
-7
@@ -14,9 +14,9 @@ use rpki::{
|
||||
ResourceClassListResponse, ResourceClassName, RevocationRequest, RevocationResponse, SigningCert,
|
||||
},
|
||||
},
|
||||
crypto::{KeyIdentifier, PublicKey},
|
||||
repository::{
|
||||
cert::{Cert, KeyUsage, Overclaim, TbsCert},
|
||||
crypto::{KeyIdentifier, PublicKey},
|
||||
resources::ResourceSet,
|
||||
rta::RtaBuilder,
|
||||
x509::{Serial, Time, Validity},
|
||||
@@ -27,9 +27,10 @@ use rpki::{
|
||||
use crate::{
|
||||
commons::{
|
||||
api::{
|
||||
AspaCustomer, AspaDefinitionList, AspaDefinitionUpdates, AspaProvidersUpdate, CertAuthInfo,
|
||||
DelegatedCertificate, IdCertPem, ObjectName, ParentCaContact, RcvdCert, RepositoryContact, Revocation,
|
||||
RoaDefinition, RtaList, RtaName, RtaPrepResponse, StorableCaCommand, TaCertDetails, TrustAnchorLocator,
|
||||
AspaCustomer, AspaDefinitionList, AspaDefinitionUpdates, AspaProvidersUpdate, BgpSecAsnKey,
|
||||
BgpSecCsrInfoList, BgpSecDefinitionUpdates, CertAuthInfo, DelegatedCertificate, IdCertPem, ObjectName,
|
||||
ParentCaContact, RcvdCert, RepositoryContact, Revocation, RoaDefinition, RtaList, RtaName, RtaPrepResponse,
|
||||
StorableCaCommand, TaCertDetails, TrustAnchorLocator,
|
||||
},
|
||||
crypto::{CsrInfo, KrillSigner},
|
||||
error::{Error, RoaDeltaError},
|
||||
@@ -41,12 +42,14 @@ use crate::{
|
||||
ca::{
|
||||
events::ChildCertificateUpdates, ta_handle, AspaDefinitions, CaEvt, CaEvtDet, ChildDetails, Cmd, CmdDet,
|
||||
DropReason, Ini, PreparedRta, ResourceClass, ResourceTaggedAttestation, RouteAuthorization,
|
||||
RouteAuthorizationUpdates, Routes, RtaContentRequest, RtaPrepareRequest, Rtas, SignedRta,
|
||||
RouteAuthorizationUpdates, Routes, RtaContentRequest, RtaPrepareRequest, Rtas, SignedRta, StoredBgpSecCsr,
|
||||
},
|
||||
config::{Config, IssuanceTimingConfig},
|
||||
},
|
||||
};
|
||||
|
||||
use super::BgpSecDefinitions;
|
||||
|
||||
//------------ Rfc8183Id ---------------------------------------------------
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
|
||||
@@ -96,6 +99,9 @@ pub struct CertAuth {
|
||||
|
||||
#[serde(skip_serializing_if = "AspaDefinitions::is_empty", default)]
|
||||
aspas: AspaDefinitions,
|
||||
|
||||
#[serde(skip_serializing_if = "BgpSecDefinitions::is_empty", default)]
|
||||
bgpsec_defs: BgpSecDefinitions,
|
||||
}
|
||||
|
||||
impl Aggregate for CertAuth {
|
||||
@@ -109,14 +115,18 @@ impl Aggregate for CertAuth {
|
||||
let (handle, _version, details) = event.unpack();
|
||||
let id = details.unpack();
|
||||
|
||||
let repository = None;
|
||||
let parents = HashMap::new();
|
||||
let resources = HashMap::new();
|
||||
|
||||
let next_class_name = 0;
|
||||
let resources = HashMap::new();
|
||||
|
||||
let children = HashMap::new();
|
||||
|
||||
let routes = Routes::default();
|
||||
let rtas = Rtas::default();
|
||||
let aspas = AspaDefinitions::default();
|
||||
let repository = None;
|
||||
let bgpsec_defs = BgpSecDefinitions::default();
|
||||
|
||||
Ok(CertAuth {
|
||||
handle,
|
||||
@@ -135,6 +145,7 @@ impl Aggregate for CertAuth {
|
||||
routes,
|
||||
rtas,
|
||||
aspas,
|
||||
bgpsec_defs,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -375,6 +386,22 @@ impl Aggregate for CertAuth {
|
||||
.unwrap()
|
||||
.aspa_objects_updated(updates),
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
// BGPSec
|
||||
//-----------------------------------------------------------------------
|
||||
CaEvtDet::BgpSecDefinitionAdded { key, csr } => self.bgpsec_defs.add_or_replace(key, csr),
|
||||
CaEvtDet::BgpSecDefinitionUpdated { key, csr } => self.bgpsec_defs.add_or_replace(key, csr),
|
||||
CaEvtDet::BgpSecDefinitionRemoved { key } => {
|
||||
self.bgpsec_defs.remove(&key);
|
||||
}
|
||||
CaEvtDet::BgpSecCertificatesUpdated {
|
||||
resource_class_name,
|
||||
updates,
|
||||
} => {
|
||||
let rc = self.resources.get_mut(&resource_class_name).unwrap();
|
||||
rc.bgpsec_certificates_updated(updates);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
// Publication
|
||||
//-----------------------------------------------------------------------
|
||||
@@ -460,6 +487,12 @@ impl Aggregate for CertAuth {
|
||||
}
|
||||
CmdDet::AspasRenew(config, signer) => self.aspas_renew(&config, &signer),
|
||||
|
||||
// BGPSec
|
||||
CmdDet::BgpSecUpdateDefinitions(updates, config, signer) => {
|
||||
self.bgpsec_definitions_update(updates, &config, &signer)
|
||||
}
|
||||
CmdDet::BgpSecRenew(config, signer) => self.bgpsec_renew(&config, &signer),
|
||||
|
||||
// Republish
|
||||
CmdDet::RepoUpdate(contact, signer) => self.update_repo(contact, &signer),
|
||||
|
||||
@@ -1444,6 +1477,7 @@ impl CertAuth {
|
||||
rcvd_cert,
|
||||
&self.routes,
|
||||
&self.aspas,
|
||||
&self.bgpsec_defs,
|
||||
config,
|
||||
signer.deref(),
|
||||
)?;
|
||||
@@ -1898,6 +1932,107 @@ impl CertAuth {
|
||||
}
|
||||
}
|
||||
|
||||
/// # BGPSec
|
||||
///
|
||||
impl CertAuth {
|
||||
pub fn bgpsec_definitions_show(&self) -> BgpSecCsrInfoList {
|
||||
self.bgpsec_defs.info_list()
|
||||
}
|
||||
|
||||
/// Process BGPSec Definition updates
|
||||
pub fn bgpsec_definitions_update(
|
||||
&self,
|
||||
updates: BgpSecDefinitionUpdates,
|
||||
config: &Config,
|
||||
signer: &KrillSigner,
|
||||
) -> KrillResult<Vec<CaEvt>> {
|
||||
let mut res = vec![];
|
||||
|
||||
let (additions, removals) = updates.unpack();
|
||||
|
||||
// We keep a copy of the definitions so that we can:
|
||||
// a. remove and then re-add definitions
|
||||
// b. use the updated definitions to generate objects in
|
||||
// applicable RCs
|
||||
//
|
||||
// (note: actual modifications of self are done when the events are applied)
|
||||
let mut definitions = self.bgpsec_defs.clone();
|
||||
|
||||
for key in removals {
|
||||
if !definitions.remove(&key) {
|
||||
return Err(Error::BgpSecDefinitionUnknown(self.handle.clone(), key));
|
||||
} else {
|
||||
res.push(CaEvtDet::BgpSecDefinitionRemoved { key });
|
||||
}
|
||||
}
|
||||
|
||||
// Verify that the CSR in each 'addition' is valid. Then either add
|
||||
// a new or update an existing definition.
|
||||
for definition in additions {
|
||||
// ensure the CSR is validly signed
|
||||
definition
|
||||
.csr()
|
||||
.validate()
|
||||
.map_err(|_| Error::BgpSecDefinitionInvalidlySigned(self.handle.clone(), definition.clone()))?;
|
||||
|
||||
let key = BgpSecAsnKey::from(&definition);
|
||||
let csr = StoredBgpSecCsr::from(definition.csr());
|
||||
|
||||
// ensure this CA holds the AS
|
||||
if !self.all_resources().contains_asn(key.asn()) {
|
||||
return Err(Error::BgpSecDefinitionNotEntitled(self.handle.clone(), key));
|
||||
}
|
||||
|
||||
if let Some(stored_csr) = definitions.get_stored_csr(&key) {
|
||||
if stored_csr != &csr {
|
||||
res.push(CaEvtDet::BgpSecDefinitionUpdated { key, csr: csr.clone() });
|
||||
definitions.add_or_replace(key, csr);
|
||||
}
|
||||
} else {
|
||||
res.push(CaEvtDet::BgpSecDefinitionAdded { key, csr: csr.clone() });
|
||||
definitions.add_or_replace(key, csr);
|
||||
}
|
||||
}
|
||||
|
||||
// Process the updated BGPSec definitions in each RC and add/remove
|
||||
// BGPSec certificates as needed.
|
||||
for (rcn, rc) in self.resources.iter() {
|
||||
let updates = rc.update_bgpsec_certs(&definitions, config, signer)?;
|
||||
if !updates.is_empty() {
|
||||
res.push(CaEvtDet::BgpSecCertificatesUpdated {
|
||||
resource_class_name: rcn.clone(),
|
||||
updates,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Ok(self.events_from_details(res))
|
||||
}
|
||||
|
||||
/// Renew any BGPSec certificates if needed.
|
||||
pub fn bgpsec_renew(&self, config: &Config, signer: &KrillSigner) -> KrillResult<Vec<CaEvt>> {
|
||||
let mut evt_dets = vec![];
|
||||
|
||||
for (rcn, rc) in self.resources.iter() {
|
||||
let updates = rc.renew_bgpsec_certs(&config.issuance_timing, signer)?;
|
||||
|
||||
if updates.contains_changes() {
|
||||
info!(
|
||||
"CA '{}' reissued BGPSec certificates under RC '{}' before they would expire",
|
||||
self.handle, rcn
|
||||
);
|
||||
|
||||
evt_dets.push(CaEvtDet::BgpSecCertificatesUpdated {
|
||||
resource_class_name: rcn.clone(),
|
||||
updates,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Ok(self.events_from_details(evt_dets))
|
||||
}
|
||||
}
|
||||
|
||||
/// # Resource Tagged Attestations
|
||||
///
|
||||
impl CertAuth {
|
||||
|
||||
@@ -4,7 +4,8 @@ use chrono::Duration;
|
||||
|
||||
use rpki::{
|
||||
ca::{idcert::IdCert, idexchange::ChildHandle, provisioning::ResourceClassName},
|
||||
repository::{crypto::KeyIdentifier, resources::ResourceSet, x509::Time},
|
||||
crypto::KeyIdentifier,
|
||||
repository::{resources::ResourceSet, x509::Time},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
@@ -153,6 +154,10 @@ pub struct ChildCertificates {
|
||||
}
|
||||
|
||||
impl ChildCertificates {
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.issued.is_empty() && self.suspended.is_empty()
|
||||
}
|
||||
|
||||
pub fn certificate_issued(&mut self, issued: DelegatedCertificate) {
|
||||
let ki = issued.cert().subject_key_identifier();
|
||||
self.issued.insert(ki, issued);
|
||||
|
||||
@@ -19,8 +19,8 @@ use crate::{
|
||||
commons::{
|
||||
actor::Actor,
|
||||
api::{
|
||||
AspaCustomer, AspaDefinitionUpdates, AspaProvidersUpdate, ParentCaContact, RcvdCert, RepositoryContact,
|
||||
RtaName, StorableCaCommand, StorableRcEntitlement,
|
||||
AspaCustomer, AspaDefinitionUpdates, AspaProvidersUpdate, BgpSecDefinitionUpdates, ParentCaContact,
|
||||
RcvdCert, RepositoryContact, RtaName, StorableCaCommand, StorableRcEntitlement,
|
||||
},
|
||||
crypto::KrillSigner,
|
||||
eventsourcing::{self, StoredCommand},
|
||||
@@ -175,9 +175,21 @@ pub enum CmdDet {
|
||||
// will only be stored if there are any updates to be done.
|
||||
AspasRenew(Arc<Config>, Arc<KrillSigner>),
|
||||
|
||||
// ------------------------------------------------------------
|
||||
// BGPSec Support
|
||||
// ------------------------------------------------------------
|
||||
|
||||
// Update BgpSecDefinitions
|
||||
BgpSecUpdateDefinitions(BgpSecDefinitionUpdates, Arc<Config>, Arc<KrillSigner>),
|
||||
|
||||
// Re-issue any and all BgpSec certificates which would otherwise
|
||||
// expire in some time.
|
||||
BgpSecRenew(Arc<Config>, Arc<KrillSigner>),
|
||||
|
||||
// ------------------------------------------------------------
|
||||
// Publishing
|
||||
// ------------------------------------------------------------
|
||||
|
||||
// Update the repository where this CA publishes
|
||||
RepoUpdate(RepositoryContact, Arc<KrillSigner>),
|
||||
|
||||
@@ -310,6 +322,12 @@ impl From<CmdDet> for StorableCaCommand {
|
||||
}
|
||||
CmdDet::AspasRenew(_, _) => StorableCaCommand::ReissueBeforeExpiring,
|
||||
|
||||
// ------------------------------------------------------------
|
||||
// BGPSec Support
|
||||
// ------------------------------------------------------------
|
||||
CmdDet::BgpSecUpdateDefinitions(_, _, _) => StorableCaCommand::BgpSecDefinitionUpdates,
|
||||
CmdDet::BgpSecRenew(_, _) => StorableCaCommand::ReissueBeforeExpiring,
|
||||
|
||||
// ------------------------------------------------------------
|
||||
// Publishing
|
||||
// ------------------------------------------------------------
|
||||
@@ -549,6 +567,24 @@ impl CmdDet {
|
||||
)
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------
|
||||
// BGPSec
|
||||
//-------------------------------------------------------------------------------
|
||||
pub fn bgpsec_update_definitions(
|
||||
ca: &CaHandle,
|
||||
updates: BgpSecDefinitionUpdates,
|
||||
config: Arc<Config>,
|
||||
signer: Arc<KrillSigner>,
|
||||
actor: &Actor,
|
||||
) -> Cmd {
|
||||
eventsourcing::SentCommand::new(
|
||||
ca,
|
||||
None,
|
||||
CmdDet::BgpSecUpdateDefinitions(updates, config, signer),
|
||||
actor,
|
||||
)
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------
|
||||
// Resource Tagged Attestations
|
||||
//-------------------------------------------------------------------------------
|
||||
|
||||
+120
-4
@@ -6,15 +6,16 @@ use rpki::{
|
||||
idexchange::{CaHandle, ChildHandle, ParentHandle},
|
||||
provisioning::{IssuanceRequest, ParentResourceClassName, ResourceClassName, RevocationRequest},
|
||||
},
|
||||
repository::{crypto::KeyIdentifier, resources::ResourceSet},
|
||||
crypto::KeyIdentifier,
|
||||
repository::resources::ResourceSet,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
commons::{
|
||||
api::{
|
||||
AspaCustomer, AspaDefinition, AspaProvidersUpdate, DelegatedCertificate, ObjectName, ParentCaContact,
|
||||
RcvdCert, RepositoryContact, RevokedObject, RoaAggregateKey, RtaName, SuspendedCert, TaCertDetails,
|
||||
UnsuspendedCert,
|
||||
AspaCustomer, AspaDefinition, AspaProvidersUpdate, BgpSecAsnKey, DelegatedCertificate, ObjectName,
|
||||
ParentCaContact, RcvdCert, RepositoryContact, RevokedObject, RoaAggregateKey, RtaName, SuspendedCert,
|
||||
TaCertDetails, UnsuspendedCert,
|
||||
},
|
||||
crypto::KrillSigner,
|
||||
eventsourcing::StoredEvent,
|
||||
@@ -26,6 +27,8 @@ use crate::{
|
||||
},
|
||||
};
|
||||
|
||||
use super::{BgpSecCertInfo, StoredBgpSecCsr};
|
||||
|
||||
//------------ Ini -----------------------------------------------------------
|
||||
|
||||
pub type Ini = StoredEvent<IniDet>;
|
||||
@@ -417,6 +420,50 @@ impl AspaObjectsUpdates {
|
||||
}
|
||||
}
|
||||
|
||||
//------------ BgpSecCertificateUpdates ------------------------------------
|
||||
#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
|
||||
pub struct BgpSecCertificateUpdates {
|
||||
#[serde(skip_serializing_if = "Vec::is_empty", default)]
|
||||
updated: Vec<BgpSecCertInfo>,
|
||||
|
||||
#[serde(skip_serializing_if = "Vec::is_empty", default)]
|
||||
removed: Vec<BgpSecAsnKey>,
|
||||
}
|
||||
|
||||
impl BgpSecCertificateUpdates {
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.updated.is_empty() && self.removed.is_empty()
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.updated.len() + self.removed.len()
|
||||
}
|
||||
|
||||
pub fn contains_changes(&self) -> bool {
|
||||
!self.is_empty()
|
||||
}
|
||||
|
||||
pub fn updated(&self) -> &Vec<BgpSecCertInfo> {
|
||||
&self.updated
|
||||
}
|
||||
|
||||
pub fn removed(&self) -> &Vec<BgpSecAsnKey> {
|
||||
&self.removed
|
||||
}
|
||||
|
||||
pub fn unpack(self) -> (Vec<BgpSecCertInfo>, Vec<BgpSecAsnKey>) {
|
||||
(self.updated, self.removed)
|
||||
}
|
||||
|
||||
pub fn add_updated(&mut self, update: BgpSecCertInfo) {
|
||||
self.updated.push(update);
|
||||
}
|
||||
|
||||
pub fn add_removed(&mut self, remove: BgpSecAsnKey) {
|
||||
self.removed.push(remove);
|
||||
}
|
||||
}
|
||||
|
||||
//------------ ChildCertificateUpdates -------------------------------------
|
||||
|
||||
/// Describes an update to the set of ROAs under a ResourceClass.
|
||||
@@ -693,6 +740,24 @@ pub enum CaEvtDet {
|
||||
updates: AspaObjectsUpdates,
|
||||
},
|
||||
|
||||
// BGPSec
|
||||
BgpSecDefinitionAdded {
|
||||
key: BgpSecAsnKey,
|
||||
csr: StoredBgpSecCsr,
|
||||
},
|
||||
BgpSecDefinitionUpdated {
|
||||
key: BgpSecAsnKey,
|
||||
csr: StoredBgpSecCsr,
|
||||
},
|
||||
BgpSecDefinitionRemoved {
|
||||
key: BgpSecAsnKey,
|
||||
},
|
||||
BgpSecCertificatesUpdated {
|
||||
// Tracks the actual BGPSec certificates (re-)issued in a resource class
|
||||
resource_class_name: ResourceClassName,
|
||||
updates: BgpSecCertificateUpdates,
|
||||
},
|
||||
|
||||
// Publishing
|
||||
RepoUpdated {
|
||||
// Adds the repository contact for this CA so that publication can commence,
|
||||
@@ -1092,6 +1157,57 @@ impl fmt::Display for CaEvtDet {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// BGPSec
|
||||
CaEvtDet::BgpSecDefinitionAdded { key, .. } => {
|
||||
write!(
|
||||
f,
|
||||
"added BGPSec definition for ASN: {} and key id: {}",
|
||||
key.asn(),
|
||||
key.key_identifier()
|
||||
)
|
||||
}
|
||||
CaEvtDet::BgpSecDefinitionUpdated { key, .. } => {
|
||||
write!(
|
||||
f,
|
||||
"updated CSR for BGPSec definition for ASN: {} and key id: {}",
|
||||
key.asn(),
|
||||
key.key_identifier()
|
||||
)
|
||||
}
|
||||
CaEvtDet::BgpSecDefinitionRemoved { key } => {
|
||||
write!(
|
||||
f,
|
||||
"removed BGPSec definition for ASN: {} and key id: {}",
|
||||
key.asn(),
|
||||
key.key_identifier()
|
||||
)
|
||||
}
|
||||
CaEvtDet::BgpSecCertificatesUpdated {
|
||||
resource_class_name,
|
||||
updates,
|
||||
} => {
|
||||
write!(
|
||||
f,
|
||||
"updated BGPSec certificates under resource class '{}'",
|
||||
resource_class_name
|
||||
)?;
|
||||
let updated = updates.updated();
|
||||
if !updated.is_empty() {
|
||||
write!(f, " added: ")?;
|
||||
for cert in updated {
|
||||
write!(f, "{} ", ObjectName::from(cert))?;
|
||||
}
|
||||
}
|
||||
let removed = updates.removed();
|
||||
if !removed.is_empty() {
|
||||
write!(f, " removed: ")?;
|
||||
for key in removed {
|
||||
write!(f, "{} ", ObjectName::from(key))?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Publishing
|
||||
CaEvtDet::RepoUpdated { contact } => {
|
||||
write!(f, "updated repository to remote server: {}", contact.service_uri())
|
||||
|
||||
@@ -9,7 +9,8 @@ use rpki::{
|
||||
IssuanceRequest, RequestResourceLimit, ResourceClassEntitlements, ResourceClassName, RevocationRequest,
|
||||
},
|
||||
},
|
||||
repository::{crypto::KeyIdentifier, resources::ResourceSet, x509::Time},
|
||||
crypto::KeyIdentifier,
|
||||
repository::{resources::ResourceSet, x509::Time},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
|
||||
@@ -15,14 +15,15 @@ use rpki::{
|
||||
publication,
|
||||
publication::{ListReply, Publish, PublishDelta, Update, Withdraw},
|
||||
},
|
||||
repository::{crypto::KeyIdentifier, resources::ResourceSet},
|
||||
crypto::KeyIdentifier,
|
||||
repository::resources::ResourceSet,
|
||||
uri,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
commons::{
|
||||
actor::Actor,
|
||||
api::{rrdp::PublishElement, Timestamp},
|
||||
api::{rrdp::PublishElement, BgpSecCsrInfoList, BgpSecDefinitionUpdates, Timestamp},
|
||||
api::{
|
||||
AddChildRequest, AspaCustomer, AspaDefinitionList, AspaDefinitionUpdates, AspaProvidersUpdate,
|
||||
CaCommandDetails, CaCommandResult, CertAuthList, CertAuthSummary, ChildCaInfo, CommandHistory,
|
||||
@@ -1824,6 +1825,32 @@ impl CaManager {
|
||||
}
|
||||
}
|
||||
|
||||
/// # BGPSec functions
|
||||
///
|
||||
impl CaManager {
|
||||
pub async fn ca_bgpsec_definitions_show(&self, ca: CaHandle) -> KrillResult<BgpSecCsrInfoList> {
|
||||
let ca = self.get_ca(&ca).await?;
|
||||
Ok(ca.bgpsec_definitions_show())
|
||||
}
|
||||
|
||||
pub async fn ca_bgpsec_definitions_update(
|
||||
&self,
|
||||
ca: CaHandle,
|
||||
updates: BgpSecDefinitionUpdates,
|
||||
actor: &Actor,
|
||||
) -> KrillResult<()> {
|
||||
self.send_command(CmdDet::bgpsec_update_definitions(
|
||||
&ca,
|
||||
updates,
|
||||
self.config.clone(),
|
||||
self.signer.clone(),
|
||||
actor,
|
||||
))
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// # Route Authorization functions
|
||||
///
|
||||
impl CaManager {
|
||||
@@ -1883,11 +1910,22 @@ impl CaManager {
|
||||
if let Err(e) = self.send_command(cmd).await {
|
||||
error!("Renewing ASPAs for CA '{}' failed with error: {}", ca, e);
|
||||
}
|
||||
|
||||
let cmd = Cmd::new(
|
||||
&ca,
|
||||
None,
|
||||
CmdDet::BgpSecRenew(self.config.clone(), self.signer.clone()),
|
||||
actor,
|
||||
);
|
||||
|
||||
if let Err(e) = self.send_command(cmd).await {
|
||||
error!("Renewing BGPSec certificates for CA '{}' failed with error: {}", ca, e);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Force the reissuance of all ROAs in all CAs. This function was added
|
||||
/// Force the re-issuance of all ROAs in all CAs. This function was added
|
||||
/// because we need to re-issue ROAs in Krill 0.9.3 to force that a short
|
||||
/// subject CN is used for the EE certificate: i.e. the SKI rather than the
|
||||
/// full public key. But there may also be other cases in future where
|
||||
|
||||
@@ -7,6 +7,9 @@ use crate::commons::error::Error;
|
||||
mod aspa;
|
||||
pub use self::aspa::*;
|
||||
|
||||
mod bgpsec;
|
||||
pub use self::bgpsec::*;
|
||||
|
||||
mod certauth;
|
||||
pub use self::certauth::CertAuth;
|
||||
pub use self::certauth::Rfc8183Id;
|
||||
|
||||
+121
-7
@@ -14,11 +14,11 @@ use chrono::Duration;
|
||||
|
||||
use rpki::{
|
||||
ca::{idexchange::CaHandle, provisioning::ResourceClassName, publication::Base64},
|
||||
crypto::{DigestAlgorithm, PublicKey},
|
||||
repository::{
|
||||
aspa::Aspa,
|
||||
cert::Cert,
|
||||
crl::{Crl, TbsCertList},
|
||||
crypto::{DigestAlgorithm, PublicKey},
|
||||
manifest::{FileAndHash, Manifest, ManifestContent},
|
||||
roa::Roa,
|
||||
sigobj::SignedObjectBuilder,
|
||||
@@ -44,7 +44,7 @@ use crate::{
|
||||
},
|
||||
};
|
||||
|
||||
use super::AspaObjectsUpdates;
|
||||
use super::{AspaObjectsUpdates, BgpSecCertInfo, BgpSecCertificateUpdates};
|
||||
|
||||
//------------ CaObjectsStore ----------------------------------------------
|
||||
|
||||
@@ -103,6 +103,12 @@ impl PreSaveEventListener<CertAuth> for CaObjectsStore {
|
||||
} => {
|
||||
objects.update_aspas(resource_class_name, updates, timing, signer)?;
|
||||
}
|
||||
super::CaEvtDet::BgpSecCertificatesUpdated {
|
||||
resource_class_name,
|
||||
updates,
|
||||
} => {
|
||||
objects.update_bgpsec_certs(resource_class_name, updates, timing, signer)?;
|
||||
}
|
||||
super::CaEvtDet::ChildCertificatesUpdated {
|
||||
resource_class_name,
|
||||
updates,
|
||||
@@ -498,6 +504,18 @@ impl CaObjects {
|
||||
rco.update_aspas(updates, timing, signer)
|
||||
}
|
||||
|
||||
// Update the BGPSec certificates in the current set
|
||||
fn update_bgpsec_certs(
|
||||
&mut self,
|
||||
rcn: &ResourceClassName,
|
||||
updates: &BgpSecCertificateUpdates,
|
||||
timing: &IssuanceTimingConfig,
|
||||
signer: &KrillSigner,
|
||||
) -> KrillResult<()> {
|
||||
let rco = self.get_class_mut(rcn)?;
|
||||
rco.update_bgpsec_certs(updates, timing, signer)
|
||||
}
|
||||
|
||||
// Update the delegated certificates in the current set
|
||||
fn update_certs(
|
||||
&mut self,
|
||||
@@ -668,6 +686,19 @@ impl ResourceClassObjects {
|
||||
}
|
||||
}
|
||||
|
||||
fn update_bgpsec_certs(
|
||||
&mut self,
|
||||
updates: &BgpSecCertificateUpdates,
|
||||
timing: &IssuanceTimingConfig,
|
||||
signer: &KrillSigner,
|
||||
) -> KrillResult<()> {
|
||||
match self.keys.borrow_mut() {
|
||||
ResourceClassKeyState::Current(state) => state.current_set.update_bgpsec_certs(updates, timing, signer),
|
||||
ResourceClassKeyState::Staging(state) => state.current_set.update_bgpsec_certs(updates, timing, signer),
|
||||
ResourceClassKeyState::Old(state) => state.current_set.update_bgpsec_certs(updates, timing, signer),
|
||||
}
|
||||
}
|
||||
|
||||
fn update_certs(
|
||||
&mut self,
|
||||
cert_updates: &ChildCertificateUpdates,
|
||||
@@ -825,6 +856,12 @@ pub struct CurrentKeyObjectSet {
|
||||
roas: HashMap<ObjectName, PublishedRoa>,
|
||||
#[serde(with = "objects_to_aspas_serde", skip_serializing_if = "HashMap::is_empty", default)]
|
||||
aspas: HashMap<ObjectName, PublishedAspa>,
|
||||
#[serde(
|
||||
with = "objects_to_bgpsec_certs_serde",
|
||||
skip_serializing_if = "HashMap::is_empty",
|
||||
default
|
||||
)]
|
||||
bgpsec_certs: HashMap<ObjectName, BgpSecCertInfo>,
|
||||
#[serde(with = "objects_to_certs_serde")]
|
||||
certs: HashMap<ObjectName, PublishedCert>,
|
||||
}
|
||||
@@ -834,12 +871,14 @@ impl CurrentKeyObjectSet {
|
||||
basic: BasicKeyObjectSet,
|
||||
roas: HashMap<ObjectName, PublishedRoa>,
|
||||
aspas: HashMap<ObjectName, PublishedAspa>,
|
||||
bgpsec_certs: HashMap<ObjectName, BgpSecCertInfo>,
|
||||
certs: HashMap<ObjectName, PublishedCert>,
|
||||
) -> Self {
|
||||
CurrentKeyObjectSet {
|
||||
basic,
|
||||
roas,
|
||||
aspas,
|
||||
bgpsec_certs,
|
||||
certs,
|
||||
}
|
||||
}
|
||||
@@ -873,6 +912,13 @@ impl CurrentKeyObjectSet {
|
||||
));
|
||||
}
|
||||
|
||||
for (name, bgpsec_cert) in &self.bgpsec_certs {
|
||||
elements.push(PublishElement::new(
|
||||
bgpsec_cert.cert().clone(),
|
||||
base_uri.join(name.as_bytes()).unwrap(),
|
||||
));
|
||||
}
|
||||
|
||||
for (name, cert) in &self.certs {
|
||||
elements.push(PublishElement::new(
|
||||
Base64::from(cert.as_ref()),
|
||||
@@ -924,6 +970,29 @@ impl CurrentKeyObjectSet {
|
||||
self.reissue(timing, signer)
|
||||
}
|
||||
|
||||
fn update_bgpsec_certs(
|
||||
&mut self,
|
||||
updates: &BgpSecCertificateUpdates,
|
||||
timing: &IssuanceTimingConfig,
|
||||
signer: &KrillSigner,
|
||||
) -> KrillResult<()> {
|
||||
for bgpsec_cert in updates.updated() {
|
||||
let name = ObjectName::from(bgpsec_cert);
|
||||
if let Some(old) = self.bgpsec_certs.insert(name, bgpsec_cert.clone()) {
|
||||
self.revocations.add(Revocation::from(&old));
|
||||
}
|
||||
}
|
||||
|
||||
for removed in updates.removed() {
|
||||
let name = ObjectName::from(removed);
|
||||
if let Some(old) = self.bgpsec_certs.remove(&name) {
|
||||
self.revocations.add(Revocation::from(&old));
|
||||
}
|
||||
}
|
||||
|
||||
self.reissue(timing, signer)
|
||||
}
|
||||
|
||||
fn update_certs(
|
||||
&mut self,
|
||||
cert_updates: &ChildCertificateUpdates,
|
||||
@@ -998,7 +1067,7 @@ impl CurrentKeyObjectSet {
|
||||
}
|
||||
|
||||
fn reissue_mft(&self, new_crl: &PublishedCrl, signer: &KrillSigner) -> KrillResult<PublishedManifest> {
|
||||
ManifestBuilder::with_objects(new_crl, &self.roas, &self.aspas, &self.certs)
|
||||
ManifestBuilder::with_objects(new_crl, &self.roas, &self.aspas, &self.certs, &self.bgpsec_certs)
|
||||
.build_new_mft(&self.signing_cert, self.next(), signer)
|
||||
.map(|m| m.into())
|
||||
}
|
||||
@@ -1010,6 +1079,7 @@ impl From<BasicKeyObjectSet> for CurrentKeyObjectSet {
|
||||
basic,
|
||||
roas: HashMap::new(),
|
||||
aspas: HashMap::new(),
|
||||
bgpsec_certs: HashMap::new(),
|
||||
certs: HashMap::new(),
|
||||
}
|
||||
}
|
||||
@@ -1071,13 +1141,13 @@ mod objects_to_aspas_serde {
|
||||
use serde::de::{Deserialize, Deserializer};
|
||||
use serde::ser::Serializer;
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct NameRoaItem {
|
||||
struct NameItem {
|
||||
name: ObjectName,
|
||||
aspa: PublishedAspa,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
struct NameRoaItemRef<'a> {
|
||||
struct NameItemRef<'a> {
|
||||
name: &'a ObjectName,
|
||||
aspa: &'a PublishedAspa,
|
||||
}
|
||||
@@ -1086,7 +1156,7 @@ mod objects_to_aspas_serde {
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
serializer.collect_seq(map.iter().map(|(name, aspa)| NameRoaItemRef { name, aspa }))
|
||||
serializer.collect_seq(map.iter().map(|(name, aspa)| NameItemRef { name, aspa }))
|
||||
}
|
||||
|
||||
pub fn deserialize<'de, D>(deserializer: D) -> Result<HashMap<ObjectName, PublishedAspa>, D::Error>
|
||||
@@ -1094,13 +1164,49 @@ mod objects_to_aspas_serde {
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let mut map = HashMap::new();
|
||||
for item in Vec::<NameRoaItem>::deserialize(deserializer)? {
|
||||
for item in Vec::<NameItem>::deserialize(deserializer)? {
|
||||
map.insert(item.name, item.aspa);
|
||||
}
|
||||
Ok(map)
|
||||
}
|
||||
}
|
||||
|
||||
mod objects_to_bgpsec_certs_serde {
|
||||
use super::*;
|
||||
|
||||
use serde::de::{Deserialize, Deserializer};
|
||||
use serde::ser::Serializer;
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct NameItem {
|
||||
name: ObjectName,
|
||||
bgpsec_cert: BgpSecCertInfo,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
struct NameItemRef<'a> {
|
||||
name: &'a ObjectName,
|
||||
bgpsec_cert: &'a BgpSecCertInfo,
|
||||
}
|
||||
|
||||
pub fn serialize<S>(map: &HashMap<ObjectName, BgpSecCertInfo>, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
serializer.collect_seq(map.iter().map(|(name, bgpsec_cert)| NameItemRef { name, bgpsec_cert }))
|
||||
}
|
||||
|
||||
pub fn deserialize<'de, D>(deserializer: D) -> Result<HashMap<ObjectName, BgpSecCertInfo>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let mut map = HashMap::new();
|
||||
for item in Vec::<NameItem>::deserialize(deserializer)? {
|
||||
map.insert(item.name, item.bgpsec_cert);
|
||||
}
|
||||
Ok(map)
|
||||
}
|
||||
}
|
||||
|
||||
mod objects_to_certs_serde {
|
||||
use super::*;
|
||||
|
||||
@@ -1533,6 +1639,7 @@ impl ManifestBuilder {
|
||||
roas: &HashMap<ObjectName, PublishedRoa>,
|
||||
aspas: &HashMap<ObjectName, PublishedAspa>,
|
||||
certs: &HashMap<ObjectName, PublishedCert>,
|
||||
bgpsec_certs: &HashMap<ObjectName, BgpSecCertInfo>,
|
||||
) -> Self {
|
||||
let mut entries: HashMap<Bytes, Bytes> = HashMap::new();
|
||||
|
||||
@@ -1557,6 +1664,13 @@ impl ManifestBuilder {
|
||||
entries.insert(name.clone().into(), hash);
|
||||
}
|
||||
|
||||
// Add all bgpsec certs
|
||||
for (name, info) in bgpsec_certs {
|
||||
let hash = info.cert().to_hash();
|
||||
let hash_bytes = Bytes::copy_from_slice(hash.as_slice());
|
||||
entries.insert(name.clone().into(), hash_bytes);
|
||||
}
|
||||
|
||||
ManifestBuilder {
|
||||
this_update: crl.this_update(),
|
||||
next_update: crl.next_update(),
|
||||
|
||||
+131
-12
@@ -8,9 +8,9 @@ use rpki::{
|
||||
IssuanceRequest, RequestResourceLimit, ResourceClassEntitlements, ResourceClassName, RevocationRequest,
|
||||
},
|
||||
},
|
||||
crypto::KeyIdentifier,
|
||||
repository::{
|
||||
cert::Cert,
|
||||
crypto::KeyIdentifier,
|
||||
resources::ResourceSet,
|
||||
x509::{Time, Validity},
|
||||
},
|
||||
@@ -33,7 +33,7 @@ use crate::{
|
||||
},
|
||||
};
|
||||
|
||||
use super::AspaDefinitions;
|
||||
use super::{AspaDefinitions, BgpSecCertificateUpdates, BgpSecCertificates, BgpSecDefinitions};
|
||||
|
||||
//------------ ResourceClass -----------------------------------------------
|
||||
|
||||
@@ -60,6 +60,11 @@ pub struct ResourceClass {
|
||||
|
||||
#[serde(skip_serializing_if = "AspaObjects::is_empty", default)]
|
||||
aspas: AspaObjects,
|
||||
|
||||
#[serde(skip_serializing_if = "BgpSecCertificates::is_empty", default)]
|
||||
bgpsec_certificates: BgpSecCertificates,
|
||||
|
||||
#[serde(skip_serializing_if = "ChildCertificates::is_empty", default)]
|
||||
certificates: ChildCertificates,
|
||||
|
||||
last_key_change: Time,
|
||||
@@ -85,6 +90,7 @@ impl ResourceClass {
|
||||
roas: Roas::default(),
|
||||
aspas: AspaObjects::default(),
|
||||
certificates: ChildCertificates::default(),
|
||||
bgpsec_certificates: BgpSecCertificates::default(),
|
||||
last_key_change: Time::now(),
|
||||
key_state: KeyState::create(pending_key),
|
||||
}
|
||||
@@ -99,6 +105,7 @@ impl ResourceClass {
|
||||
roas: Roas::default(),
|
||||
aspas: AspaObjects::default(),
|
||||
certificates: ChildCertificates::default(),
|
||||
bgpsec_certificates: BgpSecCertificates::default(),
|
||||
last_key_change: Time::now(),
|
||||
key_state: KeyState::create(pending_key),
|
||||
}
|
||||
@@ -188,12 +195,14 @@ impl ResourceClass {
|
||||
///
|
||||
impl ResourceClass {
|
||||
/// Returns event details for receiving the certificate.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn update_received_cert(
|
||||
&self,
|
||||
handle: &CaHandle,
|
||||
rcvd_cert: RcvdCert,
|
||||
all_routes: &Routes,
|
||||
all_aspas: &AspaDefinitions,
|
||||
all_bgpsecs: &BgpSecDefinitions,
|
||||
config: &Config,
|
||||
signer: &KrillSigner,
|
||||
) -> KrillResult<Vec<CaEvtDet>> {
|
||||
@@ -218,6 +227,9 @@ impl ResourceClass {
|
||||
|
||||
let roa_updates = self.roas.update(all_routes, ¤t_key, config, signer)?;
|
||||
let aspa_updates = self.aspas.update(all_aspas, ¤t_key, config, signer)?;
|
||||
let bgpsec_updates = self
|
||||
.bgpsec_certificates
|
||||
.update(all_bgpsecs, ¤t_key, config, signer)?;
|
||||
|
||||
let mut events = vec![CaEvtDet::KeyPendingToActive {
|
||||
resource_class_name: self.name.clone(),
|
||||
@@ -238,12 +250,26 @@ impl ResourceClass {
|
||||
})
|
||||
}
|
||||
|
||||
if bgpsec_updates.contains_changes() {
|
||||
events.push(CaEvtDet::BgpSecCertificatesUpdated {
|
||||
resource_class_name: self.name.clone(),
|
||||
updates: bgpsec_updates,
|
||||
})
|
||||
}
|
||||
|
||||
Ok(events)
|
||||
}
|
||||
}
|
||||
KeyState::Active(current) => {
|
||||
self.update_rcvd_cert_current(handle, current, rcvd_cert, all_routes, all_aspas, config, signer)
|
||||
}
|
||||
KeyState::Active(current) => self.update_rcvd_cert_current(
|
||||
handle,
|
||||
current,
|
||||
rcvd_cert,
|
||||
all_routes,
|
||||
all_aspas,
|
||||
all_bgpsecs,
|
||||
config,
|
||||
signer,
|
||||
),
|
||||
KeyState::RollPending(pending, current) => {
|
||||
if rcvd_cert_ki == pending.key_id() {
|
||||
let new_key = CertifiedKey::create(rcvd_cert);
|
||||
@@ -252,7 +278,16 @@ impl ResourceClass {
|
||||
new_key,
|
||||
}])
|
||||
} else {
|
||||
self.update_rcvd_cert_current(handle, current, rcvd_cert, all_routes, all_aspas, config, signer)
|
||||
self.update_rcvd_cert_current(
|
||||
handle,
|
||||
current,
|
||||
rcvd_cert,
|
||||
all_routes,
|
||||
all_aspas,
|
||||
all_bgpsecs,
|
||||
config,
|
||||
signer,
|
||||
)
|
||||
}
|
||||
}
|
||||
KeyState::RollNew(new, current) => {
|
||||
@@ -263,12 +298,30 @@ impl ResourceClass {
|
||||
rcvd_cert,
|
||||
}])
|
||||
} else {
|
||||
self.update_rcvd_cert_current(handle, current, rcvd_cert, all_routes, all_aspas, config, signer)
|
||||
self.update_rcvd_cert_current(
|
||||
handle,
|
||||
current,
|
||||
rcvd_cert,
|
||||
all_routes,
|
||||
all_aspas,
|
||||
all_bgpsecs,
|
||||
config,
|
||||
signer,
|
||||
)
|
||||
}
|
||||
}
|
||||
KeyState::RollOld(current, _old) => {
|
||||
// We will never request a new certificate for an old key
|
||||
self.update_rcvd_cert_current(handle, current, rcvd_cert, all_routes, all_aspas, config, signer)
|
||||
self.update_rcvd_cert_current(
|
||||
handle,
|
||||
current,
|
||||
rcvd_cert,
|
||||
all_routes,
|
||||
all_aspas,
|
||||
all_bgpsecs,
|
||||
config,
|
||||
signer,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -279,8 +332,9 @@ impl ResourceClass {
|
||||
handle: &CaHandle,
|
||||
current_key: &CurrentKey,
|
||||
rcvd_cert: RcvdCert,
|
||||
routes: &Routes,
|
||||
aspas: &AspaDefinitions,
|
||||
all_routes: &Routes,
|
||||
all_aspas: &AspaDefinitions,
|
||||
all_bgpsecs: &BgpSecDefinitions,
|
||||
config: &Config,
|
||||
signer: &KrillSigner,
|
||||
) -> KrillResult<Vec<CaEvtDet>> {
|
||||
@@ -325,7 +379,7 @@ impl ResourceClass {
|
||||
// Re-issue ROAs based on updated resources.
|
||||
// Note that route definitions will not have changed in this case, but the decision logic is all the same.
|
||||
{
|
||||
let updates = self.roas.update(routes, &updated_key, config, signer)?;
|
||||
let updates = self.roas.update(all_routes, &updated_key, config, signer)?;
|
||||
if !updates.is_empty() {
|
||||
res.push(CaEvtDet::RoasUpdated {
|
||||
resource_class_name: self.name.clone(),
|
||||
@@ -337,7 +391,7 @@ impl ResourceClass {
|
||||
// Re-issue ASPA objects based on updated resources.
|
||||
// Note that aspa definitions will not have changed in this case, but the decision logic is all the same.
|
||||
{
|
||||
let updates = self.aspas.update(aspas, &updated_key, config, signer)?;
|
||||
let updates = self.aspas.update(all_aspas, &updated_key, config, signer)?;
|
||||
if !updates.is_empty() {
|
||||
res.push(CaEvtDet::AspaObjectsUpdated {
|
||||
resource_class_name: self.name.clone(),
|
||||
@@ -345,6 +399,20 @@ impl ResourceClass {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Re-issue BGPSec certificates based on updated resources.
|
||||
// Note that definitions will not have changed in this case, but the decision logic is all the same.
|
||||
{
|
||||
let updates = self
|
||||
.bgpsec_certificates
|
||||
.update(all_bgpsecs, &updated_key, config, signer)?;
|
||||
if !updates.is_empty() {
|
||||
res.push(CaEvtDet::BgpSecCertificatesUpdated {
|
||||
resource_class_name: self.name.clone(),
|
||||
updates,
|
||||
})
|
||||
}
|
||||
}
|
||||
} else {
|
||||
info!(
|
||||
"Received new certificate for CA '{}' under RC '{}', valid until: {}",
|
||||
@@ -561,6 +629,14 @@ impl ResourceClass {
|
||||
events.push(certs_updated);
|
||||
}
|
||||
|
||||
let bgpsec_updates = self.bgpsec_certificates.renew(new_key, None, issuance_timing, signer)?;
|
||||
if !bgpsec_updates.is_empty() {
|
||||
events.push(CaEvtDet::BgpSecCertificatesUpdated {
|
||||
resource_class_name: self.name.clone(),
|
||||
updates: bgpsec_updates,
|
||||
});
|
||||
}
|
||||
|
||||
Ok(events)
|
||||
}
|
||||
} else {
|
||||
@@ -731,6 +807,49 @@ impl ResourceClass {
|
||||
}
|
||||
}
|
||||
|
||||
/// # BGPSec
|
||||
///
|
||||
impl ResourceClass {
|
||||
/// Updates the BGPSec certificates in accordance with the supplied definitions
|
||||
/// and the resources (still) held in this resource class
|
||||
pub fn update_bgpsec_certs(
|
||||
&self,
|
||||
definitions: &BgpSecDefinitions,
|
||||
config: &Config,
|
||||
signer: &KrillSigner,
|
||||
) -> KrillResult<BgpSecCertificateUpdates> {
|
||||
if let Ok(key) = self.get_current_key() {
|
||||
self.bgpsec_certificates.update(definitions, key, config, signer)
|
||||
} else {
|
||||
debug!("no BGPSec certificates to update - resource class has no current key");
|
||||
Ok(BgpSecCertificateUpdates::default())
|
||||
}
|
||||
}
|
||||
|
||||
/// Renew BGPSec certificates that would expire otherwise.
|
||||
pub fn renew_bgpsec_certs(
|
||||
&self,
|
||||
issuance_timing: &IssuanceTimingConfig,
|
||||
signer: &KrillSigner,
|
||||
) -> KrillResult<BgpSecCertificateUpdates> {
|
||||
if let Ok(key) = self.get_current_key() {
|
||||
let renew_threshold =
|
||||
Some(Time::now() + Duration::weeks(issuance_timing.timing_bgpsec_reissue_weeks_before));
|
||||
|
||||
self.bgpsec_certificates
|
||||
.renew(key, renew_threshold, issuance_timing, signer)
|
||||
} else {
|
||||
debug!("no BGPSec certificates to renew - resource class has no current key");
|
||||
Ok(BgpSecCertificateUpdates::default())
|
||||
}
|
||||
}
|
||||
|
||||
/// Apply BGPSec Certificate changes from events
|
||||
pub fn bgpsec_certificates_updated(&mut self, updates: BgpSecCertificateUpdates) {
|
||||
self.bgpsec_certificates.updated(updates)
|
||||
}
|
||||
}
|
||||
|
||||
/// # Resource Tagged Attestations (RTA)
|
||||
///
|
||||
impl ResourceClass {
|
||||
|
||||
@@ -404,6 +404,10 @@ pub struct Roas {
|
||||
}
|
||||
|
||||
impl Roas {
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.simple.is_empty() && self.aggregate.is_empty()
|
||||
}
|
||||
|
||||
pub fn get(&self, auth: &RouteAuthorization) -> Option<&RoaInfo> {
|
||||
self.simple.get(auth)
|
||||
}
|
||||
|
||||
@@ -4,13 +4,8 @@ use bytes::Bytes;
|
||||
|
||||
use rpki::{
|
||||
ca::{provisioning::ResourceClassName, publication::Base64},
|
||||
repository::{
|
||||
crypto::{DigestAlgorithm, KeyIdentifier},
|
||||
resources::ResourceSet,
|
||||
rta,
|
||||
sigobj::MessageDigest,
|
||||
x509::Validity,
|
||||
},
|
||||
crypto::{DigestAlgorithm, KeyIdentifier},
|
||||
repository::{resources::ResourceSet, rta, sigobj::MessageDigest, x509::Validity},
|
||||
};
|
||||
|
||||
use crate::commons::{
|
||||
|
||||
@@ -211,6 +211,14 @@ impl ConfigDefaults {
|
||||
4
|
||||
}
|
||||
|
||||
fn timing_bgpsec_valid_weeks() -> i64 {
|
||||
52
|
||||
}
|
||||
|
||||
fn timing_bgpsec_reissue_weeks_before() -> i64 {
|
||||
4
|
||||
}
|
||||
|
||||
pub fn signers() -> Vec<SignerConfig> {
|
||||
#[cfg(not(any(feature = "hsm-tests-kmip", feature = "hsm-tests-pkcs11")))]
|
||||
{
|
||||
@@ -489,6 +497,10 @@ pub struct IssuanceTimingConfig {
|
||||
pub timing_aspa_valid_weeks: i64,
|
||||
#[serde(default = "ConfigDefaults::timing_aspa_reissue_weeks_before")]
|
||||
pub timing_aspa_reissue_weeks_before: i64,
|
||||
#[serde(default = "ConfigDefaults::timing_bgpsec_valid_weeks")]
|
||||
pub timing_bgpsec_valid_weeks: i64,
|
||||
#[serde(default = "ConfigDefaults::timing_bgpsec_reissue_weeks_before")]
|
||||
pub timing_bgpsec_reissue_weeks_before: i64,
|
||||
}
|
||||
|
||||
impl IssuanceTimingConfig {
|
||||
@@ -831,6 +843,8 @@ impl Config {
|
||||
let timing_roa_reissue_weeks_before = ConfigDefaults::timing_roa_reissue_weeks_before();
|
||||
let timing_aspa_valid_weeks = ConfigDefaults::timing_aspa_valid_weeks();
|
||||
let timing_aspa_reissue_weeks_before = ConfigDefaults::timing_aspa_reissue_weeks_before();
|
||||
let timing_bgpsec_valid_weeks = ConfigDefaults::timing_bgpsec_valid_weeks();
|
||||
let timing_bgpsec_reissue_weeks_before = ConfigDefaults::timing_bgpsec_reissue_weeks_before();
|
||||
|
||||
let issuance_timing = IssuanceTimingConfig {
|
||||
timing_publish_next_hours,
|
||||
@@ -842,6 +856,8 @@ impl Config {
|
||||
timing_roa_reissue_weeks_before,
|
||||
timing_aspa_valid_weeks,
|
||||
timing_aspa_reissue_weeks_before,
|
||||
timing_bgpsec_valid_weeks,
|
||||
timing_bgpsec_reissue_weeks_before,
|
||||
};
|
||||
|
||||
let repository_retention = RepositoryRetentionConfig {
|
||||
|
||||
@@ -40,7 +40,8 @@ use crate::{
|
||||
bgp::BgpAnalysisAdvice,
|
||||
error::Error,
|
||||
eventsourcing::AggregateStoreError,
|
||||
util::file, KrillResult,
|
||||
util::file,
|
||||
KrillResult,
|
||||
},
|
||||
constants::{
|
||||
KRILL_ENV_HTTP_LOG_INFO, KRILL_ENV_UPGRADE_ONLY, KRILL_VERSION_MAJOR, KRILL_VERSION_MINOR, KRILL_VERSION_PATCH,
|
||||
@@ -192,7 +193,8 @@ pub async fn start_krill_daemon(config: Arc<Config>) -> Result<(), Error> {
|
||||
lock.handle_ctrl_c(),
|
||||
#[cfg(unix)]
|
||||
lock.handle_sig_term()
|
||||
).map(|_| ())
|
||||
)
|
||||
.map(|_| ())
|
||||
}
|
||||
|
||||
struct RequestLogger {
|
||||
@@ -1102,6 +1104,7 @@ async fn api_cas(req: Request, path: &mut RequestPath) -> RoutingResult {
|
||||
_ => render_unknown_method(),
|
||||
},
|
||||
Some("aspas") => api_ca_aspas(req, path, ca).await,
|
||||
Some("bgpsec") => api_ca_bgpsec(req, path, ca).await,
|
||||
Some("children") => api_ca_children(req, path, ca).await,
|
||||
Some("history") => api_ca_history(req, path, ca).await,
|
||||
|
||||
@@ -1544,6 +1547,37 @@ async fn api_ca_aspas(req: Request, path: &mut RequestPath, ca: CaHandle) -> Rou
|
||||
}
|
||||
}
|
||||
|
||||
async fn api_ca_bgpsec(req: Request, path: &mut RequestPath, ca: CaHandle) -> RoutingResult {
|
||||
// Handles /api/v1/cas/{ca}/bgpsec/:
|
||||
// GET /api/v1/cas/{ca}/bgpsec/ -> List BGPSec Definitions
|
||||
// POST /api/v1/cas/{ca}/bgpsec/ -> Send BgpSecDefinitionUpdates
|
||||
match path.next() {
|
||||
None => match *req.method() {
|
||||
Method::GET => api_ca_bgpsec_definitions_show(req, ca).await,
|
||||
Method::POST => api_ca_bgpsec_definitions_update(req, ca).await,
|
||||
_ => render_unknown_method(),
|
||||
},
|
||||
_ => render_unknown_method(),
|
||||
}
|
||||
}
|
||||
|
||||
async fn api_ca_bgpsec_definitions_show(req: Request, ca: CaHandle) -> RoutingResult {
|
||||
aa!(req, Permission::BGPSEC_READ, Handle::from(&ca), {
|
||||
render_json_res(req.state().ca_bgpsec_definitions_show(ca).await)
|
||||
})
|
||||
}
|
||||
|
||||
async fn api_ca_bgpsec_definitions_update(req: Request, ca: CaHandle) -> RoutingResult {
|
||||
aa!(req, Permission::BGPSEC_UPDATE, Handle::from(&ca), {
|
||||
let actor = req.actor();
|
||||
let server = req.state().clone();
|
||||
match req.json().await {
|
||||
Ok(updates) => render_empty_res(server.ca_bgpsec_definitions_update(ca, updates, &actor).await),
|
||||
Err(e) => render_error(e),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
async fn api_ca_children(req: Request, path: &mut RequestPath, ca: CaHandle) -> RoutingResult {
|
||||
match path.path_arg() {
|
||||
Some(child) => match path.next() {
|
||||
@@ -2106,7 +2140,6 @@ async fn api_ca_rta_multi_sign(req: Request, ca: CaHandle, name: RtaName) -> Rou
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
/// A naive lock implementation used to prevent that two Krill instances
|
||||
/// access the same krill data directory simultaneously.
|
||||
struct KrillLock(PathBuf);
|
||||
@@ -2116,12 +2149,19 @@ impl KrillLock {
|
||||
let lock_file_path = config.data_dir.join("krill.lock");
|
||||
|
||||
if lock_file_path.exists() {
|
||||
error!("Cannot start Krill: existing lock file found at: {}", lock_file_path.display());
|
||||
error!(
|
||||
"Cannot start Krill: existing lock file found at: {}",
|
||||
lock_file_path.display()
|
||||
);
|
||||
::std::process::exit(1);
|
||||
}
|
||||
|
||||
|
||||
if let Err(e) = file::save(b"lock", &lock_file_path) {
|
||||
error!("Cannot start Krill: cannot create lock file at: {}. Error: {}", lock_file_path.display(), e);
|
||||
error!(
|
||||
"Cannot start Krill: cannot create lock file at: {}. Error: {}",
|
||||
lock_file_path.display(),
|
||||
e
|
||||
);
|
||||
::std::process::exit(1);
|
||||
}
|
||||
|
||||
@@ -2138,10 +2178,13 @@ impl KrillLock {
|
||||
self.clean();
|
||||
std::process::exit(0)
|
||||
}
|
||||
|
||||
|
||||
#[cfg(unix)]
|
||||
async fn handle_sig_term(&self) -> KrillResult<()> {
|
||||
tokio::signal::unix::signal(SignalKind::terminate()).unwrap().recv().await;
|
||||
tokio::signal::unix::signal(SignalKind::terminate())
|
||||
.unwrap()
|
||||
.recv()
|
||||
.await;
|
||||
self.clean();
|
||||
std::process::exit(0)
|
||||
}
|
||||
@@ -2174,4 +2217,3 @@ mod tests {
|
||||
let _ = fs::remove_dir_all(dir);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+25
-22
@@ -13,10 +13,8 @@ use openssl::{
|
||||
|
||||
use rpki::{
|
||||
ca::idcert::IdCert,
|
||||
repository::{
|
||||
crypto::{KeyIdentifier, PublicKey, Signature, SignatureAlgorithm},
|
||||
x509::{Time, Validity},
|
||||
},
|
||||
crypto::{signer::SigningAlgorithm, KeyIdentifier, PublicKey, Signature, SignatureAlgorithm},
|
||||
repository::x509::{Time, Validity},
|
||||
};
|
||||
|
||||
use crate::commons::{api::IdCertPem, error::KrillIoError, util::file};
|
||||
@@ -70,40 +68,36 @@ struct HttpsSigner {
|
||||
private: PKey<Private>,
|
||||
}
|
||||
|
||||
impl rpki::repository::crypto::Signer for HttpsSigner {
|
||||
impl rpki::crypto::Signer for HttpsSigner {
|
||||
type KeyId = KeyIdentifier;
|
||||
type Error = Error;
|
||||
|
||||
fn create_key(&self, _algorithm: rpki::repository::crypto::PublicKeyFormat) -> Result<Self::KeyId, Self::Error> {
|
||||
fn create_key(&self, _algorithm: rpki::crypto::PublicKeyFormat) -> Result<Self::KeyId, Self::Error> {
|
||||
unimplemented!("not needed in this context")
|
||||
}
|
||||
|
||||
fn get_key_info(
|
||||
&self,
|
||||
_key: &Self::KeyId,
|
||||
) -> Result<PublicKey, rpki::repository::crypto::signer::KeyError<Self::Error>> {
|
||||
self.public_key_info()
|
||||
.map_err(rpki::repository::crypto::signer::KeyError::Signer)
|
||||
fn get_key_info(&self, _key: &Self::KeyId) -> Result<PublicKey, rpki::crypto::signer::KeyError<Self::Error>> {
|
||||
self.public_key_info().map_err(rpki::crypto::signer::KeyError::Signer)
|
||||
}
|
||||
|
||||
fn destroy_key(&self, _key: &Self::KeyId) -> Result<(), rpki::repository::crypto::signer::KeyError<Self::Error>> {
|
||||
fn destroy_key(&self, _key: &Self::KeyId) -> Result<(), rpki::crypto::signer::KeyError<Self::Error>> {
|
||||
unimplemented!("not needed in this context")
|
||||
}
|
||||
|
||||
fn sign<D: AsRef<[u8]> + ?Sized>(
|
||||
fn sign<Alg: SignatureAlgorithm, D: AsRef<[u8]> + ?Sized>(
|
||||
&self,
|
||||
_key: &Self::KeyId,
|
||||
_algorithm: SignatureAlgorithm,
|
||||
algorithm: Alg,
|
||||
data: &D,
|
||||
) -> Result<Signature, rpki::repository::crypto::SigningError<Self::Error>> {
|
||||
self.sign(data).map_err(rpki::repository::crypto::SigningError::Signer)
|
||||
) -> Result<Signature<Alg>, rpki::crypto::SigningError<Self::Error>> {
|
||||
self.sign(algorithm, data).map_err(rpki::crypto::SigningError::Signer)
|
||||
}
|
||||
|
||||
fn sign_one_off<D: AsRef<[u8]> + ?Sized>(
|
||||
fn sign_one_off<Alg: SignatureAlgorithm, D: AsRef<[u8]> + ?Sized>(
|
||||
&self,
|
||||
_algorithm: SignatureAlgorithm,
|
||||
_algorithm: Alg,
|
||||
_data: &D,
|
||||
) -> Result<(Signature, PublicKey), Self::Error> {
|
||||
) -> Result<(Signature<Alg>, PublicKey), Self::Error> {
|
||||
unimplemented!("not needed in this context")
|
||||
}
|
||||
|
||||
@@ -140,11 +134,20 @@ impl HttpsSigner {
|
||||
}
|
||||
|
||||
// See OpenSslSigner::sign_with_key for reference.
|
||||
fn sign<D: AsRef<[u8]> + ?Sized>(&self, data: &D) -> Result<Signature, Error> {
|
||||
fn sign<Alg: SignatureAlgorithm, D: AsRef<[u8]> + ?Sized>(
|
||||
&self,
|
||||
algorithm: Alg,
|
||||
data: &D,
|
||||
) -> Result<Signature<Alg>, Error> {
|
||||
let signing_algorithm = algorithm.signing_algorithm();
|
||||
if !matches!(signing_algorithm, SigningAlgorithm::RsaSha256) {
|
||||
return Err(Error::SignerError("Only RSA SHA256 signing is supported.".to_string()));
|
||||
}
|
||||
|
||||
let mut signer = ::openssl::sign::Signer::new(MessageDigest::sha256(), &self.private)?;
|
||||
signer.update(data.as_ref())?;
|
||||
|
||||
let signature = Signature::new(SignatureAlgorithm::default(), Bytes::from(signer.sign_to_vec()?));
|
||||
let signature = Signature::new(algorithm, Bytes::from(signer.sign_to_vec()?));
|
||||
Ok(signature)
|
||||
}
|
||||
|
||||
|
||||
@@ -21,11 +21,11 @@ use crate::{
|
||||
actor::{Actor, ActorDef},
|
||||
api::{
|
||||
AddChildRequest, AllCertAuthIssues, AspaCustomer, AspaDefinitionList, AspaDefinitionUpdates,
|
||||
AspaProvidersUpdate, CaCommandDetails, CaRepoDetails, CertAuthInfo, CertAuthInit, CertAuthIssues,
|
||||
CertAuthList, CertAuthStats, ChildCaInfo, ChildrenConnectionStats, CommandHistory, CommandHistoryCriteria,
|
||||
ParentCaContact, ParentCaReq, PublicationServerUris, PublisherDetails, RepositoryContact, RoaDefinition,
|
||||
RoaDefinitionUpdates, RtaList, RtaName, RtaPrepResponse, ServerInfo, TaCertDetails, Timestamp,
|
||||
UpdateChildRequest,
|
||||
AspaProvidersUpdate, BgpSecCsrInfoList, BgpSecDefinitionUpdates, CaCommandDetails, CaRepoDetails,
|
||||
CertAuthInfo, CertAuthInit, CertAuthIssues, CertAuthList, CertAuthStats, ChildCaInfo,
|
||||
ChildrenConnectionStats, CommandHistory, CommandHistoryCriteria, ParentCaContact, ParentCaReq,
|
||||
PublicationServerUris, PublisherDetails, RepositoryContact, RoaDefinition, RoaDefinitionUpdates, RtaList,
|
||||
RtaName, RtaPrepResponse, ServerInfo, TaCertDetails, Timestamp, UpdateChildRequest,
|
||||
},
|
||||
bgp::{BgpAnalyser, BgpAnalysisReport, BgpAnalysisSuggestion},
|
||||
crypto::KrillSignerBuilder,
|
||||
@@ -850,6 +850,23 @@ impl KrillServer {
|
||||
}
|
||||
}
|
||||
|
||||
/// # Handle BGPSec requests
|
||||
///
|
||||
impl KrillServer {
|
||||
pub async fn ca_bgpsec_definitions_show(&self, ca: CaHandle) -> KrillResult<BgpSecCsrInfoList> {
|
||||
self.ca_manager.ca_bgpsec_definitions_show(ca).await
|
||||
}
|
||||
|
||||
pub async fn ca_bgpsec_definitions_update(
|
||||
&self,
|
||||
ca: CaHandle,
|
||||
updates: BgpSecDefinitionUpdates,
|
||||
actor: &Actor,
|
||||
) -> KrillResult<()> {
|
||||
self.ca_manager.ca_bgpsec_definitions_update(ca, updates, actor).await
|
||||
}
|
||||
}
|
||||
|
||||
/// # Handle route authorization requests
|
||||
///
|
||||
impl KrillServer {
|
||||
|
||||
@@ -207,6 +207,7 @@ impl eventsourcing::PostSaveEventListener<CertAuth> for TaskQueue {
|
||||
CaEvtDet::RoasUpdated { .. }
|
||||
| CaEvtDet::AspaObjectsUpdated { .. }
|
||||
| CaEvtDet::ChildCertificatesUpdated { .. }
|
||||
| CaEvtDet::BgpSecCertificatesUpdated { .. }
|
||||
| CaEvtDet::ChildKeyRevoked { .. }
|
||||
| CaEvtDet::KeyPendingToNew { .. }
|
||||
| CaEvtDet::KeyPendingToActive { .. }
|
||||
|
||||
@@ -205,6 +205,7 @@ impl Scheduler {
|
||||
}
|
||||
|
||||
self.tasks.republish_if_needed(now());
|
||||
self.tasks.renew_if_needed(now());
|
||||
self.tasks.refresh_announcements_info(now());
|
||||
|
||||
#[cfg(feature = "multi-user")]
|
||||
|
||||
@@ -14,7 +14,8 @@ use rpki::{
|
||||
publication,
|
||||
publication::{ListReply, PublicationCms, PublishDelta},
|
||||
},
|
||||
repository::{crypto::KeyIdentifier, x509::Time},
|
||||
crypto::KeyIdentifier,
|
||||
repository::x509::Time,
|
||||
rrdp::Hash,
|
||||
uri,
|
||||
};
|
||||
|
||||
+27
-5
@@ -21,7 +21,8 @@ use rpki::{
|
||||
idexchange::{CaHandle, ChildHandle, ParentHandle, PublisherHandle},
|
||||
provisioning::ResourceClassName,
|
||||
},
|
||||
repository::{crypto::KeyIdentifier, resources::ResourceSet},
|
||||
crypto::KeyIdentifier,
|
||||
repository::resources::ResourceSet,
|
||||
uri,
|
||||
};
|
||||
|
||||
@@ -33,10 +34,11 @@ use crate::{
|
||||
},
|
||||
commons::{
|
||||
api::{
|
||||
AddChildRequest, AspaCustomer, AspaDefinition, AspaDefinitionList, AspaProvidersUpdate, CertAuthInfo,
|
||||
CertAuthInit, CertifiedKeyInfo, ObjectName, ParentCaContact, ParentCaReq, ParentStatuses,
|
||||
PublicationServerUris, PublisherDetails, PublisherList, RepositoryContact, ResourceClassKeysInfo,
|
||||
RoaDefinition, RoaDefinitionUpdates, RtaList, RtaName, RtaPrepResponse, TypedPrefix, UpdateChildRequest,
|
||||
AddChildRequest, AspaCustomer, AspaDefinition, AspaDefinitionList, AspaProvidersUpdate, BgpSecAsnKey,
|
||||
BgpSecCsrInfoList, BgpSecDefinition, CertAuthInfo, CertAuthInit, CertifiedKeyInfo, ObjectName,
|
||||
ParentCaContact, ParentCaReq, ParentStatuses, PublicationServerUris, PublisherDetails, PublisherList,
|
||||
RepositoryContact, ResourceClassKeysInfo, RoaDefinition, RoaDefinitionUpdates, RtaList, RtaName,
|
||||
RtaPrepResponse, TypedPrefix, UpdateChildRequest,
|
||||
},
|
||||
bgp::{Announcement, BgpAnalysisReport, BgpAnalysisSuggestion},
|
||||
crypto::SignSupport,
|
||||
@@ -423,6 +425,26 @@ pub async fn ca_route_authorization_dryrun(ca: &CaHandle, updates: RoaDefinition
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn ca_bgpsec_add(ca: &CaHandle, definition: BgpSecDefinition) {
|
||||
krill_admin(Command::CertAuth(CaCommand::BgpSecAdd(ca.clone(), definition))).await;
|
||||
}
|
||||
|
||||
pub async fn ca_bgpsec_add_expect_error(ca: &CaHandle, definition: BgpSecDefinition) {
|
||||
krill_admin_expect_error(Command::CertAuth(CaCommand::BgpSecAdd(ca.clone(), definition))).await;
|
||||
}
|
||||
|
||||
pub async fn ca_bgpsec_remove(ca: &CaHandle, key: BgpSecAsnKey) {
|
||||
krill_admin(Command::CertAuth(CaCommand::BgpSecRemove(ca.clone(), key))).await;
|
||||
}
|
||||
|
||||
pub async fn ca_bgpsec_list(ca: &CaHandle) -> BgpSecCsrInfoList {
|
||||
let res = krill_admin(Command::CertAuth(CaCommand::BgpSecList(ca.clone()))).await;
|
||||
match res {
|
||||
ApiResponse::BgpSecDefinitions(list) => list,
|
||||
_ => panic!("Expected BGPSec definitions"),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn ca_aspas_add(ca: &CaHandle, aspa: AspaDefinition) {
|
||||
krill_admin(Command::CertAuth(CaCommand::AspasAddOrReplace(ca.clone(), aspa))).await;
|
||||
}
|
||||
|
||||
+2
-5
@@ -23,7 +23,7 @@ use crate::{
|
||||
};
|
||||
|
||||
#[cfg(feature = "hsm")]
|
||||
use rpki::repository::crypto::KeyIdentifier;
|
||||
use rpki::crypto::KeyIdentifier;
|
||||
|
||||
#[cfg(feature = "hsm")]
|
||||
use crate::{
|
||||
@@ -297,10 +297,7 @@ pub trait UpgradeStore {
|
||||
/// started, it will call this again - to do the final preparation for a migration -
|
||||
/// knowing that no changes are added to the event history at this time. After this,
|
||||
/// the migration will be finalised.
|
||||
pub fn prepare_upgrade_data_migrations(
|
||||
mode: UpgradeMode,
|
||||
config: Arc<Config>,
|
||||
) -> UpgradeResult<Option<UpgradeReport>> {
|
||||
pub fn prepare_upgrade_data_migrations(mode: UpgradeMode, config: Arc<Config>) -> UpgradeResult<Option<UpgradeReport>> {
|
||||
match upgrade_versions(config.as_ref()) {
|
||||
None => Ok(None),
|
||||
Some(versions) => {
|
||||
|
||||
@@ -9,7 +9,8 @@ use rpki::{
|
||||
idexchange::{CaHandle, ChildHandle, ParentHandle, RepoInfo},
|
||||
provisioning::{IssuanceRequest, ResourceClassName, RevocationRequest},
|
||||
},
|
||||
repository::{crl::Crl, crypto::KeyIdentifier, manifest::Manifest, resources::ResourceSet, x509::Time},
|
||||
crypto::KeyIdentifier,
|
||||
repository::{crl::Crl, manifest::Manifest, resources::ResourceSet, x509::Time},
|
||||
rrdp::Hash,
|
||||
uri,
|
||||
};
|
||||
@@ -789,7 +790,7 @@ impl OldResourceClass {
|
||||
certs: HashMap<ObjectName, PublishedCert>,
|
||||
) -> CurrentKeyObjectSet {
|
||||
let basic = Self::object_set_for_certified_key(key);
|
||||
CurrentKeyObjectSet::new(basic, roas, HashMap::new(), certs)
|
||||
CurrentKeyObjectSet::new(basic, roas, HashMap::new(), HashMap::new(), certs)
|
||||
}
|
||||
|
||||
fn object_set_for_certified_key(key: &OldCertifiedKey) -> BasicKeyObjectSet {
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
use rpki::repository::{crypto::KeyIdentifier, x509::Time};
|
||||
|
||||
use std::{
|
||||
collections::{BTreeMap, HashMap},
|
||||
fmt,
|
||||
@@ -10,7 +8,8 @@ use rpki::{
|
||||
idexchange::{CaHandle, ChildHandle, MyHandle, ParentHandle, PublisherHandle, ServiceUri},
|
||||
provisioning::{RequestResourceLimit, ResourceClassName, RevocationRequest},
|
||||
},
|
||||
repository::resources::ResourceSet,
|
||||
crypto::KeyIdentifier,
|
||||
repository::{resources::ResourceSet, x509::Time},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
|
||||
@@ -13,8 +13,8 @@ use rpki::{
|
||||
provisioning::{IssuanceRequest, ResourceClassName, RevocationRequest},
|
||||
publication::Base64,
|
||||
},
|
||||
crypto::KeyIdentifier,
|
||||
repository::{
|
||||
crypto::KeyIdentifier,
|
||||
resources::ResourceSet,
|
||||
roa::Roa,
|
||||
x509::{Serial, Time},
|
||||
|
||||
@@ -12,7 +12,8 @@ use rpki::{
|
||||
idcert::IdCert,
|
||||
idexchange::{MyHandle, PublisherHandle},
|
||||
},
|
||||
repository::{crypto::KeyIdentifier, x509::Time},
|
||||
crypto::KeyIdentifier,
|
||||
repository::x509::Time,
|
||||
rrdp::Hash,
|
||||
uri,
|
||||
};
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1,170 @@
|
||||
//! Perform functional tests on a Krill instance, using the API
|
||||
//!
|
||||
use std::fs;
|
||||
use std::str::FromStr;
|
||||
|
||||
use bytes::Bytes;
|
||||
use rpki::{
|
||||
ca::{csr::BgpsecCsr, idexchange::CaHandle, provisioning::ResourceClassName},
|
||||
repository::resources::{Asn, ResourceSet},
|
||||
};
|
||||
|
||||
use krill::{
|
||||
commons::api::{
|
||||
AspaCustomer, AspaDefinition, AspaDefinitionList, AspaProvidersUpdate, BgpSecAsnKey, BgpSecCsrInfo,
|
||||
BgpSecDefinition, ObjectName,
|
||||
},
|
||||
daemon::ca::ta_handle,
|
||||
test::*,
|
||||
};
|
||||
use rpki::repository::aspa::ProviderAs;
|
||||
|
||||
#[tokio::test]
|
||||
async fn functional_bgpsec() {
|
||||
let krill_dir = start_krill_with_default_test_config(true, false, false, false).await;
|
||||
|
||||
info("##################################################################");
|
||||
info("# #");
|
||||
info("# Test BGPSec support. #");
|
||||
info("# #");
|
||||
info("# Uses the following lay-out: #");
|
||||
info("# #");
|
||||
info("# TA #");
|
||||
info("# | #");
|
||||
info("# testbed #");
|
||||
info("# | #");
|
||||
info("# CA #");
|
||||
info("# #");
|
||||
info("# #");
|
||||
info("##################################################################");
|
||||
info("");
|
||||
|
||||
let ta = ta_handle();
|
||||
let testbed = ca_handle("testbed");
|
||||
let ca = ca_handle("CA");
|
||||
let ca_res = resources("AS65000", "10.0.0.0/16", "");
|
||||
let ca_res_shrunk = resources("", "10.0.0.0/16", "");
|
||||
|
||||
let rcn_0 = rcn(0);
|
||||
|
||||
info("##################################################################");
|
||||
info("# #");
|
||||
info("# Wait for the *testbed* CA to get its certificate, this means #");
|
||||
info("# that all CAs which are set up as part of krill_start under the #");
|
||||
info("# testbed config have been set up. #");
|
||||
info("# #");
|
||||
info("##################################################################");
|
||||
info("");
|
||||
assert!(ca_contains_resources(&testbed, &ResourceSet::all()).await);
|
||||
|
||||
// Verify that the TA published expected objects
|
||||
{
|
||||
let mut expected_files = expected_mft_and_crl(&ta, &rcn_0).await;
|
||||
expected_files.push(expected_issued_cer(&testbed, &rcn_0).await);
|
||||
assert!(
|
||||
will_publish_embedded(
|
||||
"TA should have manifest, crl and cert for testbed",
|
||||
&ta,
|
||||
&expected_files
|
||||
)
|
||||
.await
|
||||
);
|
||||
}
|
||||
|
||||
{
|
||||
info("##################################################################");
|
||||
info("# #");
|
||||
info("# Set up CA under testbed #");
|
||||
info("# #");
|
||||
info("##################################################################");
|
||||
info("");
|
||||
set_up_ca_with_repo(&ca).await;
|
||||
set_up_ca_under_parent_with_resources(&ca, &testbed, &ca_res).await;
|
||||
}
|
||||
|
||||
// short hand to expect published BGPSec certs under CA
|
||||
async fn expect_bgpsec_objects(ca: &CaHandle, definitions: &[BgpSecCsrInfo]) {
|
||||
let rcn_0 = ResourceClassName::from(0);
|
||||
|
||||
let mut expected_files = expected_mft_and_crl(ca, &rcn_0).await;
|
||||
|
||||
for csr_info in definitions {
|
||||
expected_files.push(csr_info.object_name().to_string());
|
||||
}
|
||||
|
||||
assert!(
|
||||
will_publish_embedded(
|
||||
"published BGPSec certificates do not match expectations",
|
||||
ca,
|
||||
&expected_files
|
||||
)
|
||||
.await
|
||||
);
|
||||
}
|
||||
|
||||
let csr_bytes = include_bytes!("../test-resources/bgpsec/router-csr.der");
|
||||
let csr_bytes = Bytes::copy_from_slice(csr_bytes);
|
||||
let csr = BgpsecCsr::decode(csr_bytes.as_ref()).unwrap();
|
||||
|
||||
let asn_owned = Asn::from_u32(65000);
|
||||
let asn_not_owned = Asn::from_u32(65001);
|
||||
|
||||
let bgpsec_def_owned = BgpSecDefinition::new(asn_owned, csr.clone());
|
||||
let bgpsec_def_not_owned = BgpSecDefinition::new(asn_not_owned, csr);
|
||||
let bgpsec_def_key = BgpSecAsnKey::from(&bgpsec_def_owned);
|
||||
|
||||
// Refuse adding BGPSec definition for ASN which is not held
|
||||
ca_bgpsec_add_expect_error(&ca, bgpsec_def_not_owned).await;
|
||||
|
||||
// Add BGPSec definition
|
||||
{
|
||||
ca_bgpsec_add(&ca, bgpsec_def_owned).await;
|
||||
|
||||
// List definitions
|
||||
let definitions = ca_bgpsec_list(&ca).await.unpack();
|
||||
assert_eq!(1, definitions.len());
|
||||
|
||||
// Expect it's published
|
||||
expect_bgpsec_objects(&ca, &definitions).await;
|
||||
}
|
||||
|
||||
// Shrink resources.
|
||||
{
|
||||
update_child(&testbed, &ca.convert(), &ca_res_shrunk).await;
|
||||
ca_equals_resources(&ca, &ca_res_shrunk).await;
|
||||
|
||||
// Expect the definition still exists
|
||||
let definitions = ca_bgpsec_list(&ca).await.unpack();
|
||||
assert_eq!(1, definitions.len());
|
||||
|
||||
// But expect that the BGPSec certificate is removed.
|
||||
expect_bgpsec_objects(&ca, &[]).await;
|
||||
}
|
||||
|
||||
// Grow resources
|
||||
{
|
||||
update_child(&testbed, &ca.convert(), &ca_res).await;
|
||||
ca_equals_resources(&ca, &ca_res).await;
|
||||
|
||||
// Expect the definition still exists
|
||||
let definitions = ca_bgpsec_list(&ca).await.unpack();
|
||||
assert_eq!(1, definitions.len());
|
||||
|
||||
// And expect that the BGPSec certificate is published again.
|
||||
expect_bgpsec_objects(&ca, &definitions).await;
|
||||
}
|
||||
|
||||
// Remove BGPSec definition
|
||||
{
|
||||
ca_bgpsec_remove(&ca, bgpsec_def_key).await;
|
||||
|
||||
// Expect the definition is removed
|
||||
let definitions = ca_bgpsec_list(&ca).await.unpack();
|
||||
assert_eq!(0, definitions.len());
|
||||
|
||||
// Expect that the BGPSec certificate is removed.
|
||||
expect_bgpsec_objects(&ca, &[]).await;
|
||||
}
|
||||
|
||||
let _ = fs::remove_dir_all(krill_dir);
|
||||
}
|
||||
Reference in New Issue
Block a user