diff --git a/approve_or_deny.sh b/approve_or_deny.sh index 691f2796..fd5033c2 100755 --- a/approve_or_deny.sh +++ b/approve_or_deny.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # We expect to receive: set -euo pipefail -x diff --git a/approve_or_deny_signed.sh b/approve_or_deny_signed.sh index 47c84b4d..cbd75482 100755 --- a/approve_or_deny_signed.sh +++ b/approve_or_deny_signed.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # We expect to receive: set -euo pipefail -x diff --git a/src/manager.rs b/src/manager.rs index 4794b44e..0c8422b6 100644 --- a/src/manager.rs +++ b/src/manager.rs @@ -4,6 +4,7 @@ use arc_swap::ArcSwap; use log::{debug, info}; use std::collections::HashMap; use std::fmt::Display; +use std::path::PathBuf; use std::str::FromStr; use std::sync::Arc; use tokio::sync::mpsc::{self, Receiver, Sender}; @@ -261,6 +262,11 @@ impl Manager { let tsig_key = std::env::var("ZL_TSIG_KEY") .unwrap_or("hmac-sha256:zlCZbVJPIhobIs1gJNQfrsS3xCxxsR9pMUrGwG8OgG8=".into()); + // Global settings for dnst keyset + // TODO: Should probably be configurable + let dnst_keyset_bin_path: PathBuf = "dnst".into(); + let dnst_keyset_data_dir: PathBuf = "/tmp/keyset/".into(); + self.spawn_unit( "ZL", Unit::ZoneLoader(ZoneLoader { @@ -291,8 +297,8 @@ impl Manager { self.spawn_unit( "KM", Unit::KeyManager(KeyManagerUnit { - dnst_keyset_bin_path: "/tmp/dnst".into(), - dnst_keyset_data_dir: "/tmp".into(), + dnst_keyset_bin_path, + dnst_keyset_data_dir: dnst_keyset_data_dir.clone(), update_tx: update_tx.clone(), }), ); @@ -300,7 +306,6 @@ impl Manager { self.spawn_unit( "ZS", Unit::ZoneSigner(ZoneSignerUnit { - keys_path: "/tmp/keys".into(), treat_single_keys_as_csks: true, max_concurrent_operations: 1, max_concurrent_rrsig_generation_tasks: 32, @@ -310,6 +315,7 @@ impl Manager { rrsig_expiration_offset_secs: 60 * 60 * 24 * 14, kmip_server_conn_settings, update_tx: update_tx.clone(), + dnst_keyset_data_dir, }), ); diff --git a/src/units/key_manager.rs b/src/units/key_manager.rs index bd0682fe..9b8aabd0 100644 --- a/src/units/key_manager.rs +++ b/src/units/key_manager.rs @@ -3,8 +3,10 @@ use crate::comms::{ApplicationCommand, GraphStatus, Terminated}; use crate::manager::Component; use crate::metrics; use crate::payload::Update; +use bytes::Bytes; use core::fmt::Display; use core::time::Duration; +use domain::base::Name; use domain::dnssec::sign::keys::keyset::{KeySet, UnixTime}; use domain::zonetree::Zone; use log::{error, info}; @@ -86,20 +88,65 @@ impl KeyManager { self.tick().await; } cmd = cmd_rx.recv() => { - if let Some(ApplicationCommand::Terminate) = cmd { - return Err(Terminated); - } + self.run_cmd(cmd)?; + } + } + } + } + + fn run_cmd(&self, cmd: Option) -> Result<(), Terminated> { + match cmd { + Some(ApplicationCommand::Terminate) | None => Err(Terminated), + Some(ApplicationCommand::RegisterZone { + register: crate::api::ZoneAdd { name, .. }, + }) => { + let state_path = self.dnst_keyset_data_dir.join(format!("{name}.state")); + + let status = self + .keyset_cmd(&name) + .arg("create") + .arg("-n") + .arg(name.to_string()) + .arg("-s") + .arg(&state_path) + .status(); + + if status.is_err() { + error!("[ZL]: Error creating keyset"); + return Err(Terminated); + } + + // TODO: This should not happen immediately after + // `keyset create` but only once the zone is enabled. + // We currently do not have a good mechanism for that + // so we init the key immediately. + let status = self.keyset_cmd(&name).arg("init").status(); + + if status.is_err() { + error!("[ZL]: Error initializing keyset"); + return Err(Terminated); } + + Ok(()) } + Some(_) => Ok(()), // not for us } } + /// Create a keyset command with the config file for the given zone + fn keyset_cmd(&self, name: impl Display) -> Command { + let cfg_path = self.dnst_keyset_data_dir.join(format!("{name}.cfg")); + let mut cmd = Command::new(&self.dnst_keyset_bin_path); + cmd.arg("keyset").arg("-c").arg(&cfg_path); + cmd + } + async fn tick(&self) { let zone_tree = self.component.unsigned_zones(); let mut ks_info = self.ks_info.lock().await; for zone in zone_tree.load().iter_zones() { let apex_name = zone.apex_name().to_string(); - let state_path = Path::new("/tmp/").join(format!("{apex_name}.state")); + let state_path = self.dnst_keyset_data_dir.join(format!("{apex_name}.state")); if !state_path.exists() { continue; } @@ -130,16 +177,8 @@ impl KeyManager { }; if *cron_next < UnixTime::now() { - // Run cron - let cfg_path = self.dnst_keyset_data_dir.join(format!("{apex_name}.cfg")); - let mut args = vec!["keyset", "-c"]; - args.push(cfg_path.to_str().unwrap()); - args.push("cron"); - println!( - "Invoking keyset cron for zone {apex_name} with {}", - args.join(" ") - ); - let Ok(res) = Command::new(&self.dnst_keyset_bin_path).args(args).output() else { + println!("Invoking keyset cron for zone {apex_name}"); + let Ok(res) = self.keyset_cmd(&apex_name).arg("cron").output() else { error!( "Failed to invoke keyset binary at '{}", self.dnst_keyset_bin_path.display() @@ -186,7 +225,8 @@ impl KeyManager { }; if new_info.retries >= CRON_MAX_RETRIES { error!( - "The command 'dnst keyset cron' for config {} failed to update state file {}", cfg_path.display(), state_path.display() + "The command 'dnst keyset cron' failed to update state file {}", + state_path.display() ); // Clear cron_next. diff --git a/src/units/zone_signer.rs b/src/units/zone_signer.rs index 5e1f6a44..d1bf9237 100644 --- a/src/units/zone_signer.rs +++ b/src/units/zone_signer.rs @@ -116,8 +116,6 @@ use core::sync::atomic::AtomicBool; #[derive(Debug)] pub struct ZoneSignerUnit { - pub keys_path: PathBuf, - pub rrsig_inception_offset_secs: u32, pub rrsig_expiration_offset_secs: u32, @@ -135,6 +133,8 @@ pub struct ZoneSignerUnit { pub kmip_server_conn_settings: HashMap, pub update_tx: mpsc::UnboundedSender, + + pub dnst_keyset_data_dir: PathBuf, } #[allow(dead_code)] @@ -226,8 +226,8 @@ impl ZoneSignerUnit { self.max_concurrent_rrsig_generation_tasks, self.treat_single_keys_as_csks, self.update_tx, - self.keys_path, kmip_servers, + self.dnst_keyset_data_dir, ) .run(cmd_rx) .await?; @@ -292,8 +292,8 @@ struct ZoneSigner { signer_status: Arc>, treat_single_keys_as_csks: bool, update_tx: mpsc::UnboundedSender, - _keys_path: PathBuf, kmip_servers: HashMap, + dnst_keyset_data_dir: PathBuf, } impl ZoneSigner { @@ -308,8 +308,8 @@ impl ZoneSigner { max_concurrent_rrsig_generation_tasks: usize, treat_single_keys_as_csks: bool, update_tx: mpsc::UnboundedSender, - keys_path: PathBuf, kmip_servers: HashMap, + dnst_keyset_data_dir: PathBuf, ) -> Self { Self { component, @@ -322,8 +322,8 @@ impl ZoneSigner { signer_status: Default::default(), treat_single_keys_as_csks, update_tx, - _keys_path: keys_path, kmip_servers, + dnst_keyset_data_dir, } } @@ -446,7 +446,7 @@ impl ZoneSigner { trace!("Reading dnst keyset DNSKEY RRs and RRSIG RRs"); // Read the DNSKEY RRs and DNSKEY RRSIG RR from the keyset state. let apex_name = zone.apex_name().to_string(); - let state_path = Path::new("/tmp/").join(format!("{apex_name}.state")); + let state_path = self.dnst_keyset_data_dir.join(format!("{apex_name}.state")); let state = std::fs::read_to_string(&state_path).map_err(|err| format!("Unable to read `dnst keyset` state file '{}' while signing zone {zone_name}: {err}", state_path.display()))?; let state: KeySetState = serde_json::from_str(&state).unwrap(); for dnskey_rr in state.dnskey_rrset {