feat: Add score and opnsense implementation to register dhcp leases in dns server
This commit is contained in:
parent
51c6f1818c
commit
478fd9e941
11
harmony-rs/Cargo.lock
generated
11
harmony-rs/Cargo.lock
generated
@ -711,6 +711,17 @@ dependencies = [
|
||||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fqm"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"cidr",
|
||||
"env_logger",
|
||||
"harmony",
|
||||
"log",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "funty"
|
||||
version = "2.0.0"
|
||||
|
@ -12,12 +12,14 @@ use super::{
|
||||
|
||||
pub enum InterpretName {
|
||||
OPNSenseDHCP,
|
||||
OPNSenseDns
|
||||
}
|
||||
|
||||
impl std::fmt::Display for InterpretName {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
InterpretName::OPNSenseDHCP => f.write_str("OPNSenseDHCP"),
|
||||
InterpretName::OPNSenseDns => f.write_str("OPNSenseDns"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -57,7 +57,9 @@ impl std::fmt::Debug for dyn DhcpServer {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
pub trait DnsServer: Send + Sync {
|
||||
async fn register_dhcp_leases(&self, register: bool) -> Result<(), ExecutorError>;
|
||||
fn add_record(
|
||||
&mut self,
|
||||
name: &str,
|
||||
@ -72,6 +74,7 @@ pub trait DnsServer: Send + Sync {
|
||||
fn list_records(&self) -> Vec<DnsRecord>;
|
||||
fn get_ip(&self) -> IpAddress;
|
||||
fn get_host(&self) -> LogicalHost;
|
||||
async fn commit_config(&self) -> Result<(), ExecutorError>;
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for dyn DnsServer {
|
||||
|
@ -156,6 +156,7 @@ impl DhcpServer for OPNSenseFirewall {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl DnsServer for OPNSenseFirewall {
|
||||
fn add_record(
|
||||
&mut self,
|
||||
@ -185,4 +186,26 @@ impl DnsServer for OPNSenseFirewall {
|
||||
fn get_host(&self) -> LogicalHost {
|
||||
self.host.clone()
|
||||
}
|
||||
|
||||
async fn register_dhcp_leases(&self, register: bool) -> Result<(), ExecutorError> {
|
||||
let mut writable_opnsense = self.opnsense_config.write().await;
|
||||
let mut dns = writable_opnsense.dns();
|
||||
dns.register_dhcp_leases(register);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn commit_config(&self) -> Result<(), ExecutorError> {
|
||||
let opnsense = self.opnsense_config.read().await;
|
||||
|
||||
opnsense
|
||||
.save()
|
||||
.await
|
||||
.map_err(|e| ExecutorError::UnexpectedError(e.to_string()))?;
|
||||
|
||||
opnsense
|
||||
.restart_dns()
|
||||
.await
|
||||
.map_err(|e| ExecutorError::UnexpectedError(e.to_string()))
|
||||
}
|
||||
}
|
||||
|
101
harmony-rs/harmony/src/modules/dns.rs
Normal file
101
harmony-rs/harmony/src/modules/dns.rs
Normal file
@ -0,0 +1,101 @@
|
||||
use async_trait::async_trait;
|
||||
use derive_new::new;
|
||||
use log::info;
|
||||
|
||||
use crate::{
|
||||
data::{Id, Version},
|
||||
interpret::{Interpret, InterpretError, InterpretName, InterpretStatus, Outcome},
|
||||
inventory::Inventory,
|
||||
score::Score,
|
||||
topology::HAClusterTopology,
|
||||
};
|
||||
|
||||
#[derive(Debug, new, Clone)]
|
||||
pub struct DnsScore {
|
||||
register_dhcp_leases: Option<bool>,
|
||||
}
|
||||
|
||||
impl Score for DnsScore {
|
||||
type InterpretType = DnsInterpret;
|
||||
|
||||
fn create_interpret(self) -> Self::InterpretType {
|
||||
DnsInterpret::new(self)
|
||||
}
|
||||
}
|
||||
|
||||
// https://docs.opnsense.org/manual/dhcp.html#advanced-settings
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DnsInterpret {
|
||||
score: DnsScore,
|
||||
version: Version,
|
||||
id: Id,
|
||||
name: String,
|
||||
status: InterpretStatus,
|
||||
}
|
||||
|
||||
impl DnsInterpret {
|
||||
pub fn new(score: DnsScore) -> Self {
|
||||
let version = Version::from("1.0.0").expect("Version should be valid");
|
||||
let name = "DnsInterpret".to_string();
|
||||
let id = Id::from_string(format!("{name}_{version}"));
|
||||
|
||||
Self {
|
||||
version,
|
||||
id,
|
||||
name,
|
||||
score,
|
||||
status: InterpretStatus::QUEUED,
|
||||
}
|
||||
}
|
||||
async fn serve_dhcp_entries(
|
||||
&self,
|
||||
_inventory: &Inventory,
|
||||
topology: &HAClusterTopology,
|
||||
) -> Result<Outcome, InterpretError> {
|
||||
let dns = topology.dns_server.clone();
|
||||
if let Some(register) = self.score.register_dhcp_leases {
|
||||
dns.register_dhcp_leases(register).await?;
|
||||
}
|
||||
|
||||
Ok(Outcome::new(
|
||||
InterpretStatus::SUCCESS,
|
||||
"DNS Interpret execution successfull".to_string(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Interpret for DnsInterpret {
|
||||
fn get_name(&self) -> InterpretName {
|
||||
InterpretName::OPNSenseDns
|
||||
}
|
||||
|
||||
fn get_version(&self) -> crate::domain::data::Version {
|
||||
self.version.clone()
|
||||
}
|
||||
|
||||
fn get_status(&self) -> InterpretStatus {
|
||||
self.status.clone()
|
||||
}
|
||||
|
||||
fn get_children(&self) -> Vec<crate::domain::data::Id> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn execute(
|
||||
&self,
|
||||
inventory: &Inventory,
|
||||
topology: &HAClusterTopology,
|
||||
) -> Result<Outcome, InterpretError> {
|
||||
info!("Executing {} on inventory {inventory:?}", self.get_name());
|
||||
|
||||
self.serve_dhcp_entries(inventory, topology).await?;
|
||||
|
||||
topology.dns_server.commit_config().await?;
|
||||
|
||||
Ok(Outcome::new(
|
||||
InterpretStatus::SUCCESS,
|
||||
format!("Dns Interpret execution successful"),
|
||||
))
|
||||
}
|
||||
}
|
@ -1,2 +1,3 @@
|
||||
pub mod dhcp;
|
||||
pub mod dns;
|
||||
pub mod okd;
|
||||
|
@ -426,7 +426,7 @@ pub struct OPNsenseXmlSection {
|
||||
pub syslog: Option<ConfigSyslog>,
|
||||
#[yaserde(rename = "TrafficShaper")]
|
||||
pub traffic_shaper: Option<RawXml>,
|
||||
pub unboundplus: Option<RawXml>,
|
||||
pub unboundplus: Option<UnboundPlus>,
|
||||
#[yaserde(rename = "DHCRelay")]
|
||||
pub dhcrelay: Option<RawXml>,
|
||||
pub trust: Option<RawXml>,
|
||||
@ -858,17 +858,17 @@ pub struct Proxy {
|
||||
|
||||
#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)]
|
||||
pub struct ConfigGeneral {
|
||||
pub enabled: i32,
|
||||
pub enabled: i8,
|
||||
#[yaserde(rename = "error_pages")]
|
||||
pub error_pages: String,
|
||||
pub icpPort: MaybeString,
|
||||
pub logging: Logging,
|
||||
pub alternateDNSservers: MaybeString,
|
||||
pub dnsV4First: i32,
|
||||
pub dnsV4First: i8,
|
||||
pub forwardedForHandling: String,
|
||||
pub uriWhitespaceHandling: String,
|
||||
pub enablePinger: i32,
|
||||
pub useViaHeader: i32,
|
||||
pub enablePinger: i8,
|
||||
pub useViaHeader: i8,
|
||||
pub suppressVersion: i32,
|
||||
pub connecttimeout: MaybeString,
|
||||
#[yaserde(rename = "VisibleEmail")]
|
||||
@ -889,8 +889,8 @@ pub struct Logging {
|
||||
|
||||
#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)]
|
||||
pub struct Enable {
|
||||
pub accessLog: i32,
|
||||
pub storeLog: i32,
|
||||
pub accessLog: i8,
|
||||
pub storeLog: i8,
|
||||
}
|
||||
|
||||
#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)]
|
||||
@ -900,7 +900,7 @@ pub struct Cache {
|
||||
|
||||
#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)]
|
||||
pub struct LocalCache {
|
||||
pub enabled: i32,
|
||||
pub enabled: i8,
|
||||
pub directory: String,
|
||||
pub cache_mem: i32,
|
||||
pub maximum_object_size: MaybeString,
|
||||
@ -1069,7 +1069,7 @@ pub struct UnboundPlus {
|
||||
pub dots: MaybeString,
|
||||
pub hosts: Hosts,
|
||||
pub aliases: MaybeString,
|
||||
pub domains: MaybeString,
|
||||
pub domains: Option<MaybeString>,
|
||||
}
|
||||
|
||||
#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)]
|
||||
@ -1082,9 +1082,9 @@ pub struct UnboundGeneral {
|
||||
pub dns64: MaybeString,
|
||||
pub dns64prefix: MaybeString,
|
||||
pub noarecords: MaybeString,
|
||||
pub regdhcp: i32,
|
||||
pub regdhcp: i8,
|
||||
pub regdhcpdomain: MaybeString,
|
||||
pub regdhcpstatic: i32,
|
||||
pub regdhcpstatic: i8,
|
||||
pub noreglladdr6: MaybeString,
|
||||
pub noregrecords: MaybeString,
|
||||
pub txtsupport: MaybeString,
|
||||
@ -1096,12 +1096,13 @@ pub struct UnboundGeneral {
|
||||
|
||||
#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)]
|
||||
pub struct Advanced {
|
||||
pub hideidentity: i32,
|
||||
pub hideversion: i32,
|
||||
pub prefetch: i32,
|
||||
pub prefetchkey: i32,
|
||||
pub dnssecstripped: i32,
|
||||
pub serveexpired: i32,
|
||||
pub hideidentity: i8,
|
||||
pub hideversion: i8,
|
||||
pub prefetch: i8,
|
||||
pub prefetchkey: i8,
|
||||
pub dnssecstripped: i8,
|
||||
pub aggressivensec: i8,
|
||||
pub serveexpired: i8,
|
||||
pub serveexpiredreplyttl: MaybeString,
|
||||
pub serveexpiredttl: MaybeString,
|
||||
pub serveexpiredttlreset: i32,
|
||||
@ -1125,6 +1126,7 @@ pub struct Advanced {
|
||||
pub numqueriesperthread: MaybeString,
|
||||
pub outgoingrange: MaybeString,
|
||||
pub jostletimeout: MaybeString,
|
||||
pub discardtimeout: MaybeString,
|
||||
pub cachemaxttl: MaybeString,
|
||||
pub cachemaxnegativettl: MaybeString,
|
||||
pub cacheminttl: MaybeString,
|
||||
|
@ -1,6 +1,6 @@
|
||||
use std::{net::Ipv4Addr, sync::Arc, time::Duration};
|
||||
|
||||
use crate::{config::{SshConfigManager, SshCredentials, SshOPNSenseShell}, error::Error, modules::dhcp::DhcpConfig};
|
||||
use crate::{config::{SshConfigManager, SshCredentials, SshOPNSenseShell}, error::Error, modules::{dhcp::DhcpConfig, dns::DnsConfig}};
|
||||
use log::trace;
|
||||
use opnsense_config_xml::OPNsense;
|
||||
use russh::client;
|
||||
@ -35,6 +35,25 @@ impl Config {
|
||||
DhcpConfig::new(&mut self.opnsense, self.shell.clone())
|
||||
}
|
||||
|
||||
pub fn dns(&mut self) -> DnsConfig {
|
||||
DnsConfig::new(&mut self.opnsense, self.shell.clone())
|
||||
}
|
||||
|
||||
pub async fn restart_dns(&self) -> Result<(), Error> {
|
||||
self.shell.exec("configctl unbound restart").await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Save the config to the repository. This method is meant NOT to reload services, only save
|
||||
/// the config to the live file/database and perhaps take a backup when relevant.
|
||||
pub async fn save(&self) -> Result<(), Error> {
|
||||
self.repository
|
||||
.save_config(&self.opnsense.to_xml())
|
||||
.await
|
||||
}
|
||||
|
||||
/// Save the configuration and reload all services. Be careful with this one as it will cause
|
||||
/// downtime in many cases, such as a PPPoE renegociation
|
||||
pub async fn apply(&self) -> Result<(), Error> {
|
||||
self.repository
|
||||
.apply_new_config(&self.opnsense.to_xml())
|
||||
|
@ -20,7 +20,11 @@ impl ConfigManager for LocalFileConfigManager {
|
||||
Ok(fs::read_to_string(&self.file_path)?)
|
||||
}
|
||||
|
||||
async fn apply_new_config(&self, content: &str) -> Result<(), Error> {
|
||||
async fn save_config(&self, content: &str) -> Result<(), Error> {
|
||||
Ok(fs::write(&self.file_path, content)?)
|
||||
}
|
||||
|
||||
async fn apply_new_config(&self, content: &str) -> Result<(), Error> {
|
||||
self.save_config(content).await
|
||||
}
|
||||
}
|
||||
|
@ -9,5 +9,6 @@ use crate::Error;
|
||||
#[async_trait]
|
||||
pub trait ConfigManager: std::fmt::Debug + Send + Sync {
|
||||
async fn load_as_str(&self) -> Result<String, Error>;
|
||||
async fn save_config(&self, content: &str) -> Result<(), Error>;
|
||||
async fn apply_new_config(&self, content: &str) -> Result<(), Error>;
|
||||
}
|
||||
|
@ -50,13 +50,18 @@ impl ConfigManager for SshConfigManager {
|
||||
self.opnsense_shell.exec("cat /conf/config.xml").await
|
||||
}
|
||||
|
||||
async fn apply_new_config(&self, content: &str) -> Result<(), Error> {
|
||||
async fn save_config(&self, content: &str) -> Result<(), Error> {
|
||||
let temp_filename = self
|
||||
.opnsense_shell
|
||||
.write_content_to_temp_file(content)
|
||||
.await?;
|
||||
self.backup_config_remote().await?;
|
||||
self.move_to_live_config(&temp_filename).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn apply_new_config(&self, content: &str) -> Result<(), Error> {
|
||||
self.save_config(content).await?;
|
||||
self.reload_all_services().await?;
|
||||
Ok(())
|
||||
}
|
||||
|
29
harmony-rs/opnsense-config/src/modules/dns.rs
Normal file
29
harmony-rs/opnsense-config/src/modules/dns.rs
Normal file
@ -0,0 +1,29 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use opnsense_config_xml::OPNsense;
|
||||
|
||||
use crate::config::OPNsenseShell;
|
||||
|
||||
pub struct DnsConfig<'a> {
|
||||
opnsense: &'a mut OPNsense,
|
||||
opnsense_shell: Arc<dyn OPNsenseShell>,
|
||||
}
|
||||
|
||||
impl<'a> DnsConfig<'a> {
|
||||
pub fn new(opnsense: &'a mut OPNsense, opnsense_shell: Arc<dyn OPNsenseShell>) -> Self {
|
||||
Self {
|
||||
opnsense,
|
||||
opnsense_shell,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn register_dhcp_leases(&mut self, register: bool) {
|
||||
let unbound = match &mut self.opnsense.opnsense.unboundplus {
|
||||
Some(unbound) => unbound,
|
||||
None => todo!("Handle case where unboundplus is not used"),
|
||||
};
|
||||
|
||||
unbound.general.regdhcp = register as i8;
|
||||
unbound.general.regdhcpstatic = register as i8;
|
||||
}
|
||||
}
|
@ -1 +1,2 @@
|
||||
pub mod dhcp;
|
||||
pub mod dns;
|
||||
|
Loading…
Reference in New Issue
Block a user