mirror of
https://github.com/NLnetLabs/dnst.git
synced 2026-09-23 10:15:04 +02:00
The CDS/CDNSKEY RRsets also need to be signed. Cron command.
This commit is contained in:
+126
-7
@@ -108,6 +108,9 @@ impl Keyset {
|
||||
dnskey_inception_offset: Duration::from_secs(ONE_DAY),
|
||||
dnskey_signature_lifetime: Duration::from_secs(FOUR_WEEKS),
|
||||
dnskey_remain_time: Duration::from_secs(FOUR_WEEKS / 2),
|
||||
cds_inception_offset: Duration::from_secs(ONE_DAY),
|
||||
cds_signature_lifetime: Duration::from_secs(FOUR_WEEKS),
|
||||
cds_remain_time: Duration::from_secs(FOUR_WEEKS / 2),
|
||||
ds_algorithm: DsAlgorithm::Sha256,
|
||||
autoremove: false,
|
||||
};
|
||||
@@ -511,9 +514,12 @@ impl Keyset {
|
||||
for (roll, state) in kss.keyset.rollstates().iter() {
|
||||
println!("{roll:?}: {state:?}");
|
||||
}
|
||||
if dnskey_sig_renew(&kss.dnskey_rrset, &ksc.dnskey_remain_time) {
|
||||
if sig_renew(&kss.dnskey_rrset, &ksc.dnskey_remain_time) {
|
||||
println!("DNSKEY RRSIG(s) need to be renewed");
|
||||
}
|
||||
if sig_renew(&kss.cds_rrset, &ksc.cds_remain_time) {
|
||||
println!("CDS/CDNSKEY RRSIG(s) need to be renewed");
|
||||
}
|
||||
|
||||
// Check for expired keys.
|
||||
for (pubref, k) in kss.keyset.keys() {
|
||||
@@ -626,6 +632,9 @@ impl Keyset {
|
||||
let arg = self.value.ok_or::<Error>("argument expected\n".into())?;
|
||||
ksc.ds_algorithm = DsAlgorithm::new(&arg)?;
|
||||
config_changed = true;
|
||||
} else if self.cmd == "set-dnskey-inception-offset" {
|
||||
ksc.dnskey_inception_offset = parse_duration_from_opt(&self.value)?;
|
||||
config_changed = true;
|
||||
} else if self.cmd == "get-dnskey-lifetime" {
|
||||
let span = Span::try_from(ksc.dnskey_signature_lifetime).expect("should not fail");
|
||||
let signeddur = span
|
||||
@@ -638,6 +647,21 @@ impl Keyset {
|
||||
} else if self.cmd == "set-dnskey-remain-time" {
|
||||
ksc.dnskey_remain_time = parse_duration_from_opt(&self.value)?;
|
||||
config_changed = true;
|
||||
} else if self.cmd == "set-cds-inception-offset" {
|
||||
ksc.cds_inception_offset = parse_duration_from_opt(&self.value)?;
|
||||
config_changed = true;
|
||||
} else if self.cmd == "get-cds-lifetime" {
|
||||
let span = Span::try_from(ksc.cds_signature_lifetime).expect("should not fail");
|
||||
let signeddur = span
|
||||
.to_duration(SpanRelativeTo::days_are_24_hours())
|
||||
.expect("should not fail");
|
||||
println!("{signeddur:#}");
|
||||
} else if self.cmd == "set-cds-lifetime" {
|
||||
ksc.cds_signature_lifetime = parse_duration_from_opt(&self.value)?;
|
||||
config_changed = true;
|
||||
} else if self.cmd == "set-cds-remain-time" {
|
||||
ksc.cds_remain_time = parse_duration_from_opt(&self.value)?;
|
||||
config_changed = true;
|
||||
} else if self.cmd == "set-ksk-validity" {
|
||||
ksc.ksk_validity = parse_opt_duration_from_opt(&self.value)?;
|
||||
config_changed = true;
|
||||
@@ -662,6 +686,9 @@ impl Keyset {
|
||||
ksc.dnskey_signature_lifetime
|
||||
);
|
||||
println!("dnskey-remain-time: {:?}", ksc.dnskey_remain_time);
|
||||
println!("cds-inception-offset: {:?}", ksc.cds_inception_offset);
|
||||
println!("cds-signature-lifetime: {:?}", ksc.cds_signature_lifetime);
|
||||
println!("cds-remain-time: {:?}", ksc.cds_remain_time);
|
||||
println!("ds-algorithm: {:?}", ksc.ds_algorithm);
|
||||
println!("autoremove: {:?}", ksc.autoremove);
|
||||
} else if self.cmd == "get-dnskey" {
|
||||
@@ -676,6 +703,17 @@ impl Keyset {
|
||||
for r in &kss.ds_rrset {
|
||||
println!("{r}");
|
||||
}
|
||||
} else if self.cmd == "cron" {
|
||||
if sig_renew(&kss.dnskey_rrset, &ksc.dnskey_remain_time) {
|
||||
println!("DNSKEY RRSIG(s) need to be renewed");
|
||||
update_dnskey_rrset(&mut kss, &ksc, env)?;
|
||||
state_changed = true;
|
||||
}
|
||||
if sig_renew(&kss.cds_rrset, &ksc.cds_remain_time) {
|
||||
println!("CDS/CDNSKEY RRSIGs need to be renewed");
|
||||
create_cds_rrset(&mut kss, &ksc, ksc.ds_algorithm.to_digest_algorithm(), env)?;
|
||||
state_changed = true;
|
||||
}
|
||||
} else {
|
||||
return Err(format!("unknown subcommand {}\n", self.cmd).into());
|
||||
}
|
||||
@@ -720,6 +758,15 @@ struct KeySetConfig {
|
||||
// DNSKEY resign
|
||||
dnskey_remain_time: Duration,
|
||||
|
||||
// CDS/CDNSKEY inception offset
|
||||
cds_inception_offset: Duration,
|
||||
|
||||
// CDS/CDNSKEY sig lifetime
|
||||
cds_signature_lifetime: Duration,
|
||||
|
||||
// CDS/CDNSKEY resign
|
||||
cds_remain_time: Duration,
|
||||
|
||||
// DS hash algorithm
|
||||
ds_algorithm: DsAlgorithm,
|
||||
|
||||
@@ -980,6 +1027,7 @@ fn update_dnskey_rrset(
|
||||
|
||||
fn create_cds_rrset(
|
||||
kss: &mut KeySetState,
|
||||
ksc: &KeySetConfig,
|
||||
digest_alg: DigestAlgorithm,
|
||||
env: &impl Env,
|
||||
) -> Result<(), Error> {
|
||||
@@ -1042,12 +1090,75 @@ fn create_cds_rrset(
|
||||
"Infallible because the digest won't be too long since it's a valid digest",
|
||||
);
|
||||
|
||||
let cds_record =
|
||||
Record::new(record.owner().clone(), record.class(), record.ttl(), cds);
|
||||
let cds_record = Record::new(
|
||||
record
|
||||
.owner()
|
||||
.try_to_name::<Bytes>()
|
||||
.expect("should not fail"),
|
||||
record.class(),
|
||||
record.ttl(),
|
||||
cds,
|
||||
);
|
||||
|
||||
cds_list.push(cds_record);
|
||||
}
|
||||
}
|
||||
|
||||
// Need to sign
|
||||
}
|
||||
|
||||
let now = Timestamp::now().into_int();
|
||||
let inception = (now - ksc.cds_inception_offset.as_secs() as u32).into();
|
||||
let expiration = (now + ksc.cds_signature_lifetime.as_secs() as u32).into();
|
||||
|
||||
let mut cds_sigs = Vec::new();
|
||||
let mut cdnskey_sigs = Vec::new();
|
||||
for (k, v) in kss.keyset.keys() {
|
||||
let dnskey_signer = match v.keytype() {
|
||||
KeyType::Ksk(key_state) => key_state.signer(),
|
||||
KeyType::Zsk(_) => false,
|
||||
KeyType::Csk(key_state, _) => key_state.signer(),
|
||||
KeyType::Include(_) => false,
|
||||
};
|
||||
|
||||
let cds_rrset = Rrset::new(&cds_list)
|
||||
.map_err::<Error, _>(|e| format!("unable to create Rrset: {e}\n").into())?;
|
||||
let cdnskey_rrset = Rrset::new(&cdnskey_list)
|
||||
.map_err::<Error, _>(|e| format!("unable to create Rrset: {e}\n").into())?;
|
||||
|
||||
if dnskey_signer {
|
||||
let privref = v.privref().ok_or("missing private key")?;
|
||||
let private_data = std::fs::read_to_string(privref)?;
|
||||
let secret_key =
|
||||
SecretKeyBytes::parse_from_bind(&private_data).map_err::<Error, _>(|e| {
|
||||
format!("unable to parse private key file {privref}: {e}").into()
|
||||
})?;
|
||||
let public_data = std::fs::read_to_string(k)?;
|
||||
let public_key = parse_from_bind(&public_data).map_err::<Error, _>(|e| {
|
||||
format!("unable to parse public key file {k}: {e}").into()
|
||||
})?;
|
||||
|
||||
let key_pair = KeyPair::from_bytes(&secret_key, public_key.data())
|
||||
.map_err::<Error, _>(|e| {
|
||||
format!("private key {privref} and public key {k} do not match: {e}").into()
|
||||
})?;
|
||||
let signing_key = SigningKey::new(
|
||||
public_key.owner().clone(),
|
||||
public_key.data().flags(),
|
||||
key_pair,
|
||||
);
|
||||
let sig = sign_rrset::<_, _, Bytes, _>(&signing_key, &cds_rrset, inception, expiration)
|
||||
.map_err::<Error, _>(|e| {
|
||||
format!("error signing CDS RRset with private key {privref}: {e}").into()
|
||||
})?;
|
||||
cds_sigs.push(sig);
|
||||
let sig =
|
||||
sign_rrset::<_, _, Bytes, _>(&signing_key, &cdnskey_rrset, inception, expiration)
|
||||
.map_err::<Error, _>(|e| {
|
||||
format!("error signing CDNSKEY RRset with private key {privref}: {e}").into()
|
||||
})?;
|
||||
cdnskey_sigs.push(sig);
|
||||
}
|
||||
}
|
||||
|
||||
kss.cds_rrset.truncate(0);
|
||||
@@ -1055,10 +1166,18 @@ fn create_cds_rrset(
|
||||
kss.cds_rrset
|
||||
.push(r.display_zonefile(DisplayKind::Simple).to_string());
|
||||
}
|
||||
for r in cdnskey_sigs {
|
||||
kss.cds_rrset
|
||||
.push(r.display_zonefile(DisplayKind::Simple).to_string());
|
||||
}
|
||||
for r in cds_list {
|
||||
kss.cds_rrset
|
||||
.push(r.display_zonefile(DisplayKind::Simple).to_string());
|
||||
}
|
||||
for r in cds_sigs {
|
||||
kss.cds_rrset
|
||||
.push(r.display_zonefile(DisplayKind::Simple).to_string());
|
||||
}
|
||||
|
||||
println!("Got CDS/CDNSKEY RRset: {:?}", kss.cds_rrset);
|
||||
Ok(())
|
||||
@@ -1068,7 +1187,7 @@ fn remove_cds_rrset(kss: &mut KeySetState) {
|
||||
kss.cds_rrset.truncate(0);
|
||||
}
|
||||
|
||||
fn update_cd_rrset(
|
||||
fn update_ds_rrset(
|
||||
kss: &mut KeySetState,
|
||||
digest_alg: DigestAlgorithm,
|
||||
env: &impl Env,
|
||||
@@ -1140,11 +1259,11 @@ fn handle_actions(
|
||||
match action {
|
||||
Action::UpdateDnskeyRrset => update_dnskey_rrset(kss, ksc, env)?,
|
||||
Action::CreateCdsRrset => {
|
||||
create_cds_rrset(kss, ksc.ds_algorithm.to_digest_algorithm(), env)?
|
||||
create_cds_rrset(kss, ksc, ksc.ds_algorithm.to_digest_algorithm(), env)?
|
||||
}
|
||||
Action::RemoveCdsRrset => remove_cds_rrset(kss),
|
||||
Action::UpdateDsRrset => {
|
||||
update_cd_rrset(kss, ksc.ds_algorithm.to_digest_algorithm(), env)?
|
||||
update_ds_rrset(kss, ksc.ds_algorithm.to_digest_algorithm(), env)?
|
||||
}
|
||||
Action::UpdateRrsig => (),
|
||||
Action::ReportDnskeyPropagated => (),
|
||||
@@ -1190,7 +1309,7 @@ fn parse_opt_duration_from_opt(value: &Option<String>) -> Result<Option<Duration
|
||||
Ok(Some(duration))
|
||||
}
|
||||
|
||||
fn dnskey_sig_renew(dnskey_rrset: &[String], remain_time: &Duration) -> bool {
|
||||
fn sig_renew(dnskey_rrset: &[String], remain_time: &Duration) -> bool {
|
||||
let mut zonefile = Zonefile::new();
|
||||
for r in dnskey_rrset {
|
||||
zonefile.extend_from_slice(r.as_ref());
|
||||
|
||||
Reference in New Issue
Block a user