Implement Kea DHCP v4

This commit is contained in:
Samuel Lorch 2024-02-19 01:28:39 +01:00
parent f43e44c820
commit 142e85f08f
5 changed files with 268 additions and 7 deletions

View file

@ -269,6 +269,7 @@ export const editTypes: { [key: string]: { [key: string]: any } } = {
name: 'DHCP Server',
idType: 'Number',
fields: {
name: { is: 'TextBox', label: 'Name'},
interface: { is: 'SingleSelect', label: 'Interface', props: { searchProvider: GetInterfaces} },
pool: { is: 'MultiSelect', label: 'Pool', props: { searchProvider: GetAddresses} },
gateway_mode: { is: 'EnumInput', label: 'Gateway Mode', props: { variants: {
@ -287,7 +288,7 @@ export const editTypes: { [key: string]: { [key: string]: any } } = {
'specify': {
display: 'Specify',
fields: {
servers: { is: 'MultiSelect', label: 'DNS Servers', props: { searchProvider: GetAddresses} },
dns_servers: { is: 'MultiSelect', label: 'DNS Servers', props: { searchProvider: GetAddresses} },
},
},
}}},
@ -297,12 +298,11 @@ export const editTypes: { [key: string]: { [key: string]: any } } = {
'specify': {
display: 'Specify',
fields: {
servers: { is: 'MultiSelect', label: 'NTP Servers', props: { searchProvider: GetAddresses} },
ntp_servers: { is: 'MultiSelect', label: 'NTP Servers', props: { searchProvider: GetAddresses} },
},
},
}}},
default_lease_time: { is: 'NumberBox', label: 'Default Lease Time'},
max_lease_time: { is: 'NumberBox', label: 'Max Lease Time'},
lease_time: { is: 'NumberBox', label: 'Lease Time'},
comment: { is: 'MultilineTextBox', label: 'Comment'},
},
},

257
src/apply/kea.rs Normal file
View file

@ -0,0 +1,257 @@
use super::ApplyError;
use crate::definitions::{
config::Config,
network::{AddressingMode, NetworkInterfaceType},
object::AddressType,
service::{DNSServerMode, GatewayMode, NTPServerMode},
};
use ipnet::IpNet;
use serde::Serialize;
use std::io::Write;
use std::process::Command;
use tracing::info;
const KEA_V4_CONFIG_PATH: &str = "/etc/kea/kea-dhcp4.conf";
// const KEA_V6_CONFIG_PATH: &str = "/etc/kea/kea-dhcp6.conf";
#[derive(Serialize, Clone, Debug)]
pub struct KeaConfig {
#[serde(rename = "Dhcp4")]
pub dhcpv4: KeaDHCPv4,
}
#[derive(Serialize, Clone, Debug)]
pub struct KeaDHCPv4 {
#[serde(rename = "valid-lifetime")]
pub valid_lifetime: u64,
#[serde(rename = "renew-timer")]
pub renew_timer: u64,
#[serde(rename = "rebind-timer")]
pub rebind_timer: u64,
#[serde(rename = "interfaces-config")]
pub interfaces_config: KeaInterfaces,
#[serde(rename = "lease-database")]
pub lease_database: KeaLeases,
#[serde(rename = "subnet4")]
pub subnet4: Vec<KeaSubnet4>,
}
#[derive(Serialize, Clone, Debug)]
pub struct KeaInterfaces {
#[serde(rename = "interfaces")]
pub interfaces: Vec<String>,
}
#[derive(Serialize, Clone, Debug)]
pub struct KeaLeases {
#[serde(rename = "type")]
pub database_type: String,
#[serde(rename = "persist")]
pub persist: bool,
#[serde(rename = "name")]
pub name: String,
}
#[derive(Serialize, Clone, Debug)]
pub struct KeaSubnet4 {
// TODO add subnet Id https://kea.readthedocs.io/en/kea-2.2.0/arm/dhcp4-srv.html#ipv4-subnet-identifier
#[serde(rename = "subnet")]
pub subnet: IpNet,
#[serde(rename = "pools")]
pub pools: Vec<KeaPool>,
#[serde(rename = "valid-lifetime")]
pub valid_lifetime: u64,
#[serde(rename = "option-data")]
pub option_data: Vec<KeaOption>,
}
#[derive(Serialize, Clone, Debug)]
pub struct KeaPool {
#[serde(rename = "pool")]
pub pool: String,
}
#[derive(Serialize, Clone, Debug)]
pub struct KeaOption {
#[serde(rename = "code")]
pub code: u64,
#[serde(rename = "data")]
pub data: String,
}
pub fn apply_kea(pending_config: Config, _current_config: Config) -> Result<(), ApplyError> {
let mut conf: KeaConfig = KeaConfig {
dhcpv4: KeaDHCPv4 {
valid_lifetime: 4000,
renew_timer: 1000,
rebind_timer: 2000,
interfaces_config: KeaInterfaces { interfaces: vec![] },
lease_database: KeaLeases {
database_type: "memfile".to_string(),
persist: true,
name: "/var/lib/kea/dhcp4.leases".to_string(),
},
subnet4: vec![],
},
};
for dhcp_server in pending_config.service.dhcp_servers.clone() {
let interface = dhcp_server.interface(pending_config.clone());
// TODO specify main ip of interface https://kea.readthedocs.io/en/kea-2.2.0/arm/dhcp4-srv.html#interface-configuration
match interface.interface_type {
NetworkInterfaceType::Hardware { device } => {
conf.dhcpv4.interfaces_config.interfaces.push(device)
}
_ => conf
.dhcpv4
.interfaces_config
.interfaces
.push(interface.name),
}
let mut subnet = KeaSubnet4 {
subnet: match interface.addressing_mode {
AddressingMode::Static { address } => address.clone(),
_ => panic!("Unsupported Addressing mode"),
},
pools: vec![],
valid_lifetime: dhcp_server.lease_time,
option_data: vec![],
};
match dhcp_server.gateway_mode.clone() {
GatewayMode::Interface => subnet.option_data.push(KeaOption {
code: 3,
data: match interface.addressing_mode {
AddressingMode::Static { address } => address.addr().to_string(),
_ => panic!("Unsupported Address Type"),
},
}),
GatewayMode::Specify { .. } => subnet.option_data.push(KeaOption {
code: 3,
data: match dhcp_server
.gateway_mode
.gateway(pending_config.clone())
.address_type
{
AddressType::Host { address } => address.to_string(),
_ => panic!("Unsupported Address Type"),
},
}),
GatewayMode::None => (),
}
match dhcp_server.dns_server_mode.clone() {
DNSServerMode::Interface => subnet.option_data.push(KeaOption {
code: 6,
data: match interface.addressing_mode {
AddressingMode::Static { address } => address.addr().to_string(),
_ => panic!("Unsupported Address Type"),
},
}),
DNSServerMode::Specify { .. } => {
let mut servers = "".to_string();
let dns_servers = dhcp_server
.dns_server_mode
.dns_servers(pending_config.clone());
for i in 0..dns_servers.len() {
match dns_servers[i].address_type {
AddressType::Host { address } => {
if i > 0 {
servers += ", ";
}
servers += &address.to_string();
}
_ => panic!("Unsupported Address Type"),
}
}
subnet.option_data.push(KeaOption {
code: 6,
data: servers,
});
}
DNSServerMode::None => (),
}
match dhcp_server.ntp_server_mode.clone() {
NTPServerMode::Interface => subnet.option_data.push(KeaOption {
code: 42,
data: match interface.addressing_mode {
AddressingMode::Static { address } => address.addr().to_string(),
_ => panic!("Unsupported Address Type"),
},
}),
NTPServerMode::Specify { .. } => {
let mut servers = "".to_string();
let ntp_servers = dhcp_server
.ntp_server_mode
.ntp_servers(pending_config.clone());
for i in 0..ntp_servers.len() {
match ntp_servers[i].address_type {
AddressType::Host { address } => {
if i > 0 {
servers += ", ";
}
servers += &address.to_string();
}
_ => panic!("Unsupported Address Type"),
}
}
subnet.option_data.push(KeaOption {
code: 42,
data: servers,
});
}
NTPServerMode::None => (),
}
let pools = dhcp_server.pool(pending_config.clone());
for pool in pools {
match pool.address_type {
AddressType::Host { address } => subnet.pools.push(KeaPool {
pool: address.to_string() + "/32",
}),
AddressType::Range { .. } => panic!("TODO fix range type"),
AddressType::Network { network } => subnet.pools.push(KeaPool {
pool: network.to_string(),
}),
AddressType::Group { .. } => panic!("TODO"),
}
}
conf.dhcpv4.subnet4.push(subnet);
}
info!("Serializeing Kea v4 Config");
let config_data: String = serde_json::to_string_pretty(&conf)?;
info!("Deleting old Kea v4 Config");
std::fs::remove_file(KEA_V4_CONFIG_PATH)?;
info!("Writing new Kea v4 Config");
let mut f = std::fs::File::create(KEA_V4_CONFIG_PATH)?;
f.write_all(config_data.as_bytes())?;
info!("Restarting Kea");
match Command::new("systemctl")
.arg("restart")
.arg("kea-dhcp4")
.output()
{
Ok(out) => {
if out.status.success() {
Ok(())
} else {
Err(ApplyError::ServiceRestartFailed)
}
}
Err(err) => Err(ApplyError::IOError(err)),
}
}

View file

@ -1,6 +1,7 @@
use thiserror::Error;
pub mod chrony;
pub mod kea;
pub mod networkd;
pub mod nftables;
pub mod unbound;
@ -16,6 +17,9 @@ pub enum ApplyError {
#[error(transparent)]
AddrParseError(#[from] ipnet::AddrParseError),
#[error(transparent)]
SerdeError(#[from] serde_json::Error),
#[error("Service Restart Failed")]
ServiceRestartFailed,
}

View file

@ -38,6 +38,7 @@ static APPLY_FUNCTIONS: &'static [fn(
) -> Result<(), super::apply::ApplyError>] = &[
super::apply::networkd::apply_networkd,
super::apply::nftables::apply_nftables,
super::apply::kea::apply_kea,
super::apply::chrony::apply_chrony,
super::apply::unbound::apply_unbound,
];

View file

@ -1,4 +1,3 @@
use core::time;
use macaddr::MacAddr8;
use serde::{Deserialize, Serialize};
use validator::Validate;
@ -15,11 +14,11 @@ pub struct DHCPServer {
pub name: String,
pub interface: String,
pub pool: Vec<String>,
pub lease_time: time::Duration,
pub lease_time: u64,
pub gateway_mode: GatewayMode,
pub dns_server_mode: DNSServerMode,
pub ntp_server_mode: NTPServerMode,
pub reservations: Vec<Reservation>,
// pub reservations: Vec<Reservation>,
pub comment: String,
}