Compare commits

..

11 Commits

Author SHA1 Message Date
a9fe4ab267 fix: cargo fmt
All checks were successful
Run Check Script / check (pull_request) Successful in 1m0s
2025-08-25 13:33:36 -04:00
65cc9befeb mod.rs
Some checks failed
Run Check Script / check (pull_request) Failing after 20s
2025-08-25 13:31:39 -04:00
d456a1f9ee feat: score to validate whether the ceph cluster is healthy 2025-08-25 13:30:32 -04:00
d36c574590 Merge pull request 'feat/inventory_agent' (#119) from feat/inventory_agent into master
Some checks failed
Run Check Script / check (push) Failing after 38s
Compile and package harmony_composer / package_harmony_composer (push) Successful in 5m48s
Reviewed-on: #119
2025-08-22 01:55:52 +00:00
bfca9cf163 Merge pull request 'feat/ceph-osd-score' (#116) from feat/ceph-osd-score into master
Some checks failed
Run Check Script / check (push) Failing after 36s
Compile and package harmony_composer / package_harmony_composer (push) Successful in 15m5s
Reviewed-on: #116
Reviewed-by: johnride <jg@nationtech.io>
2025-08-20 18:19:42 +00:00
cd3ea6fc10 fix: added check to ensure that rook-ceph-tools is available in the designated namespace
All checks were successful
Run Check Script / check (pull_request) Successful in 1m16s
2025-08-20 12:54:19 -04:00
89eb88d10e feat: socre to remove an osd from the ceph osd tree using K8sClient to interact with rook-ceph-toolbox pod 2025-08-20 12:09:55 -04:00
72fb05b5cc fix(inventory_agent) : Agent now retreives correct dmidecode fields, fixed uuid generation which is unacceptable, fixed storage drive parsing, much better error handling, much more strict behavior which also leads to more complete output as missing fields will raise errors unless explicitely optional 2025-08-19 17:56:06 -04:00
6685b05cc5 wip(inventory_agent): Refactoring for better error handling in progress 2025-08-19 17:05:23 -04:00
d1a274b705 fix: checks deployment status ready replicas rather than pod name since the pod name is not necessarily matching the deployment name and often has a random generated number in it 2025-08-15 15:44:06 -04:00
b43ca7c740 feat: score for preparing rook ceph cluster to remove drive based on rook-ceph-osd deployment name added functions to K8sclient to be able to scale deployment to a desired replicaset number and get pod based on name and namespace 2025-08-15 14:51:16 -04:00
51 changed files with 1650 additions and 6602 deletions

17
Cargo.lock generated
View File

@@ -1655,21 +1655,6 @@ dependencies = [
"url",
]
[[package]]
name = "example-pxe"
version = "0.1.0"
dependencies = [
"cidr",
"env_logger",
"harmony",
"harmony_cli",
"harmony_macros",
"harmony_types",
"log",
"tokio",
"url",
]
[[package]]
name = "example-rust"
version = "0.1.0"
@@ -2193,7 +2178,6 @@ dependencies = [
"serde",
"serde_json",
"sysinfo",
"uuid",
]
[[package]]
@@ -3596,7 +3580,6 @@ dependencies = [
"tokio",
"tokio-stream",
"tokio-util",
"uuid",
]
[[package]]

View File

@@ -1,3 +0,0 @@
Here lies all the data files required for an OKD cluster PXE boot setup.
This inclues ISO files, binary boot files, ipxe, etc.

Binary file not shown.

Binary file not shown.

View File

@@ -1,108 +0,0 @@
# OPNsense PXE Lab Environment
This project contains a script to automatically set up a virtual lab environment for testing PXE boot services managed by an OPNsense firewall.
## Overview
The `pxe_vm_lab_setup.sh` script will create the following resources using libvirt/KVM:
1. **A Virtual Network**: An isolated network named `harmonylan` (`virbr1`) for the lab.
2. **Two Virtual Machines**:
* `opnsense-pxe`: A firewall VM that will act as the gateway and PXE server.
* `pxe-node-1`: A client VM configured to boot from the network.
## Prerequisites
Ensure you have the following software installed on your Arch Linux host:
* `libvirt`
* `qemu`
* `virt-install` (from the `virt-install` package)
* `curl`
* `bzip2`
## Usage
### 1. Create the Environment
Run the `up` command to download the necessary images and create the network and VMs.
```bash
sudo ./pxe_vm_lab_setup.sh up
```
### 2. Install and Configure OPNsense
The OPNsense VM is created but the OS needs to be installed manually via the console.
1. **Connect to the VM console**:
```bash
sudo virsh console opnsense-pxe
```
2. **Log in as the installer**:
* Username: `installer`
* Password: `opnsense`
3. **Follow the on-screen installation wizard**. When prompted to assign network interfaces (`WAN` and `LAN`):
* Find the MAC address for the `harmonylan` interface by running this command in another terminal:
```bash
virsh domiflist opnsense-pxe
# Example output:
# Interface Type Source Model MAC
# ---------------------------------------------------------
# vnet18 network default virtio 52:54:00:b5:c4:6d
# vnet19 network harmonylan virtio 52:54:00:21:f9:ba
```
* Assign the interface connected to `harmonylan` (e.g., `vtnet1` with MAC `52:54:00:21:f9:ba`) as your **LAN**.
* Assign the other interface as your **WAN**.
4. After the installation is complete, **shut down** the VM from the console menu.
5. **Detach the installation media** by editing the VM's configuration:
```bash
sudo virsh edit opnsense-pxe
```
Find and **delete** the entire `<disk>` block corresponding to the `.img` file (the one with `<target ... bus='usb'/>`).
6. **Start the VM** to boot into the newly installed system:
```bash
sudo virsh start opnsense-pxe
```
### 3. Connect to OPNsense from Your Host
To configure OPNsense, you need to connect your host to the `harmonylan` network.
1. By default, OPNsense configures its LAN interface with the IP `192.168.1.1`.
2. Assign a compatible IP address to your host's `virbr1` bridge interface:
```bash
sudo ip addr add 192.168.1.5/24 dev virbr1
```
3. You can now access the OPNsense VM from your host:
* **SSH**: `ssh root@192.168.1.1` (password: `opnsense`)
* **Web UI**: `https://192.168.1.1`
### 4. Configure PXE Services with Harmony
With connectivity established, you can now use Harmony to configure the OPNsense firewall for PXE booting. Point your Harmony OPNsense scores to the firewall using these details:
* **Hostname/IP**: `192.168.1.1`
* **Credentials**: `root` / `opnsense`
### 5. Boot the PXE Client
Once your Harmony configuration has been applied and OPNsense is serving DHCP/TFTP, start the client VM. It will automatically attempt to boot from the network.
```bash
sudo virsh start pxe-node-1
sudo virsh console pxe-node-1
```
## Cleanup
To destroy all VMs and networks created by the script, run the `clean` command:
```bash
sudo ./pxe_vm_lab_setup.sh clean
```

View File

@@ -1,191 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
# --- Configuration ---
LAB_DIR="/var/lib/harmony_pxe_test"
IMG_DIR="${LAB_DIR}/images"
STATE_DIR="${LAB_DIR}/state"
VM_OPN="opnsense-pxe"
VM_PXE="pxe-node-1"
NET_HARMONYLAN="harmonylan"
# Network settings for the isolated LAN
VLAN_CIDR="192.168.150.0/24"
VLAN_GW="192.168.150.1"
VLAN_MASK="255.255.255.0"
# VM Specifications
RAM_OPN="2048"
VCPUS_OPN="2"
DISK_OPN_GB="10"
OS_VARIANT_OPN="freebsd14.0" # Updated to a more recent FreeBSD variant
RAM_PXE="4096"
VCPUS_PXE="2"
DISK_PXE_GB="40"
OS_VARIANT_LINUX="centos-stream9"
OPN_IMG_URL="https://mirror.ams1.nl.leaseweb.net/opnsense/releases/25.7/OPNsense-25.7-serial-amd64.img.bz2"
OPN_IMG_PATH="${IMG_DIR}/OPNsense-25.7-serial-amd64.img"
CENTOS_ISO_URL="https://mirror.stream.centos.org/9-stream/BaseOS/x86_64/os/images/boot.iso"
CENTOS_ISO_PATH="${IMG_DIR}/CentOS-Stream-9-latest-boot.iso"
CONNECT_URI="qemu:///system"
download_if_missing() {
local url="$1"
local dest="$2"
if [[ ! -f "$dest" ]]; then
echo "Downloading $url to $dest"
mkdir -p "$(dirname "$dest")"
local tmp
tmp="$(mktemp)"
curl -L --progress-bar "$url" -o "$tmp"
case "$url" in
*.bz2) bunzip2 -c "$tmp" > "$dest" && rm -f "$tmp" ;;
*) mv "$tmp" "$dest" ;;
esac
else
echo "Already present: $dest"
fi
}
# Ensures a libvirt network is defined and active
ensure_network() {
local net_name="$1"
local net_xml_path="$2"
if virsh --connect "${CONNECT_URI}" net-info "${net_name}" >/dev/null 2>&1; then
echo "Network ${net_name} already exists."
else
echo "Defining network ${net_name} from ${net_xml_path}"
virsh --connect "${CONNECT_URI}" net-define "${net_xml_path}"
fi
if ! virsh --connect "${CONNECT_URI}" net-info "${net_name}" | grep "Active: *yes"; then
echo "Starting network ${net_name}..."
virsh --connect "${CONNECT_URI}" net-start "${net_name}"
virsh --connect "${CONNECT_URI}" net-autostart "${net_name}"
fi
}
# Destroys a VM completely
destroy_vm() {
local vm_name="$1"
if virsh --connect "${CONNECT_URI}" dominfo "$vm_name" >/dev/null 2>&1; then
echo "Destroying and undefining VM: ${vm_name}"
virsh --connect "${CONNECT_URI}" destroy "$vm_name" || true
virsh --connect "${CONNECT_URI}" undefine "$vm_name" --nvram
fi
}
# Destroys a libvirt network
destroy_network() {
local net_name="$1"
if virsh --connect "${CONNECT_URI}" net-info "$net_name" >/dev/null 2>&1; then
echo "Destroying and undefining network: ${net_name}"
virsh --connect "${CONNECT_URI}" net-destroy "$net_name" || true
virsh --connect "${CONNECT_URI}" net-undefine "$net_name"
fi
}
# --- Main Logic ---
create_lab_environment() {
# Create network definition files
cat > "${STATE_DIR}/default.xml" <<EOF
<network>
<name>default</name>
<forward mode='nat'/>
<bridge name='virbr0' stp='on' delay='0'/>
<ip address='192.168.122.1' netmask='255.255.255.0'>
<dhcp>
<range start='192.168.122.100' end='192.168.122.200'/>
</dhcp>
</ip>
</network>
EOF
cat > "${STATE_DIR}/${NET_HARMONYLAN}.xml" <<EOF
<network>
<name>${NET_HARMONYLAN}</name>
<bridge name='virbr1' stp='on' delay='0'/>
</network>
EOF
# Ensure both networks exist and are active
ensure_network "default" "${STATE_DIR}/default.xml"
ensure_network "${NET_HARMONYLAN}" "${STATE_DIR}/${NET_HARMONYLAN}.xml"
# --- Create OPNsense VM (MODIFIED SECTION) ---
local disk_opn="${IMG_DIR}/${VM_OPN}.qcow2"
if [[ ! -f "$disk_opn" ]]; then
qemu-img create -f qcow2 "$disk_opn" "${DISK_OPN_GB}G"
fi
echo "Creating OPNsense VM using serial image..."
virt-install \
--connect "${CONNECT_URI}" \
--name "${VM_OPN}" \
--ram "${RAM_OPN}" \
--vcpus "${VCPUS_OPN}" \
--cpu host-passthrough \
--os-variant "${OS_VARIANT_OPN}" \
--graphics none \
--noautoconsole \
--disk path="${disk_opn}",device=disk,bus=virtio,boot.order=1 \
--disk path="${OPN_IMG_PATH}",device=disk,bus=usb,readonly=on,boot.order=2 \
--network network=default,model=virtio \
--network network="${NET_HARMONYLAN}",model=virtio \
--boot uefi,menu=on
echo "OPNsense VM created. Connect with: sudo virsh console ${VM_OPN}"
echo "The VM will boot from the serial installation image."
echo "Login with user 'installer' and password 'opnsense' to start the installation."
echo "Install onto the VirtIO disk (vtbd0)."
echo "After installation, shutdown the VM, then run 'sudo virsh edit ${VM_OPN}' and remove the USB disk block to boot from the installed system."
# --- Create PXE Client VM ---
local disk_pxe="${IMG_DIR}/${VM_PXE}.qcow2"
if [[ ! -f "$disk_pxe" ]]; then
qemu-img create -f qcow2 "$disk_pxe" "${DISK_PXE_GB}G"
fi
echo "Creating PXE client VM..."
virt-install \
--connect "${CONNECT_URI}" \
--name "${VM_PXE}" \
--ram "${RAM_PXE}" \
--vcpus "${VCPUS_PXE}" \
--cpu host-passthrough \
--os-variant "${OS_VARIANT_LINUX}" \
--graphics none \
--noautoconsole \
--disk path="${disk_pxe}",format=qcow2,bus=virtio \
--network network="${NET_HARMONYLAN}",model=virtio \
--pxe \
--boot uefi,menu=on
echo "PXE VM created. It will attempt to netboot on ${NET_HARMONYLAN}."
}
# --- Script Entrypoint ---
case "${1:-}" in
up)
mkdir -p "${IMG_DIR}" "${STATE_DIR}"
download_if_missing "$OPN_IMG_URL" "$OPN_IMG_PATH"
download_if_missing "$CENTOS_ISO_URL" "$CENTOS_ISO_PATH"
create_lab_environment
echo "Lab setup complete. Use 'sudo virsh list --all' to see VMs."
;;
clean)
destroy_vm "${VM_PXE}"
destroy_vm "${VM_OPN}"
destroy_network "${NET_HARMONYLAN}"
# Optionally destroy the default network if you want a full reset
# destroy_network "default"
echo "Cleanup complete."
;;
*)
echo "Usage: sudo $0 {up|clean}"
exit 1
;;
esac

View File

@@ -125,10 +125,9 @@ async fn main() {
harmony::modules::okd::load_balancer::OKDLoadBalancerScore::new(&topology);
let tftp_score = TftpScore::new(Url::LocalFolder("./data/watchguard/tftpboot".to_string()));
let http_score = StaticFilesHttpScore {
folder_to_serve: Some(Url::LocalFolder("./data/watchguard/pxe-http-files".to_string())),
files: vec![],
};
let http_score = StaticFilesHttpScore::new(Url::LocalFolder(
"./data/watchguard/pxe-http-files".to_string(),
));
let ipxe_score = IpxeScore::new();
harmony_tui::run(

View File

@@ -1,18 +0,0 @@
[package]
name = "example-pxe"
edition = "2024"
version.workspace = true
readme.workspace = true
license.workspace = true
publish = false
[dependencies]
harmony = { path = "../../harmony" }
harmony_cli = { path = "../../harmony_cli" }
harmony_types = { path = "../../harmony_types" }
cidr = { workspace = true }
tokio = { workspace = true }
harmony_macros = { path = "../../harmony_macros" }
log = { workspace = true }
env_logger = { workspace = true }
url = { workspace = true }

View File

@@ -1,50 +0,0 @@
mod topology;
use harmony::{
data::{FileContent, FilePath},
modules::{dhcp::DhcpScore, http::StaticFilesHttpScore, tftp::TftpScore},
score::Score,
topology::{HAClusterTopology, Url},
};
use crate::topology::{get_inventory, get_topology};
#[tokio::main]
async fn main() {
let inventory = get_inventory();
let topology = get_topology().await;
let gateway_ip = topology.router.get_gateway();
// TODO this should be a single IPXEScore instead of having the user do this step by step
let scores: Vec<Box<dyn Score<HAClusterTopology>>> = vec![
Box::new(DhcpScore {
host_binding: vec![],
next_server: Some(topology.router.get_gateway()),
boot_filename: None,
filename: Some("undionly.kpxe".to_string()),
filename64: Some("ipxe.efi".to_string()),
filenameipxe: Some(format!("http://{gateway_ip}:8080/boot.ipxe").to_string()),
}),
Box::new(TftpScore {
files_to_serve: Url::LocalFolder("./data/pxe/okd/tftpboot/".to_string()),
}),
Box::new(StaticFilesHttpScore {
folder_to_serve: None,
files: vec![FileContent {
path: FilePath::Relative("boot.ipxe".to_string()),
content: format!(
"#!ipxe
set base-url http://{gateway_ip}:8080
set hostfile ${{base-url}}/byMAC/01-${{mac:hexhyp}}.ipxe
chain ${{hostfile}} || chain ${{base-url}}/default.ipxe"
),
}],
}),
];
harmony_cli::run(inventory, topology, scores, None)
.await
.unwrap();
}

View File

@@ -1,65 +0,0 @@
use std::{
net::{IpAddr, Ipv4Addr},
sync::Arc,
};
use cidr::Ipv4Cidr;
use harmony::{
hardware::{FirewallGroup, HostCategory, Location, PhysicalHost, SwitchGroup},
infra::opnsense::OPNSenseManagementInterface,
inventory::Inventory,
topology::{HAClusterTopology, LogicalHost, UnmanagedRouter},
};
use harmony_macros::{ip, ipv4};
pub async fn get_topology() -> HAClusterTopology {
let firewall = harmony::topology::LogicalHost {
ip: ip!("192.168.1.1"),
name: String::from("opnsense-1"),
};
let opnsense = Arc::new(
harmony::infra::opnsense::OPNSenseFirewall::new(firewall, None, "root", "opnsense").await,
);
let lan_subnet = ipv4!("192.168.1.0");
let gateway_ipv4 = ipv4!("192.168.1.1");
let gateway_ip = IpAddr::V4(gateway_ipv4);
harmony::topology::HAClusterTopology {
domain_name: "demo.harmony.mcd".to_string(),
router: Arc::new(UnmanagedRouter::new(
gateway_ip,
Ipv4Cidr::new(lan_subnet, 24).unwrap(),
)),
load_balancer: opnsense.clone(),
firewall: opnsense.clone(),
tftp_server: opnsense.clone(),
http_server: opnsense.clone(),
dhcp_server: opnsense.clone(),
dns_server: opnsense.clone(),
control_plane: vec![LogicalHost {
ip: ip!("10.100.8.20"),
name: "cp0".to_string(),
}],
bootstrap_host: LogicalHost {
ip: ip!("10.100.8.20"),
name: "cp0".to_string(),
},
workers: vec![],
switch: vec![],
}
}
pub fn get_inventory() -> Inventory {
Inventory {
location: Location::new(
"Some virtual machine or maybe a physical machine if you're cool".to_string(),
"testopnsense".to_string(),
),
switch: SwitchGroup::from([]),
firewall: FirewallGroup::from([PhysicalHost::empty(HostCategory::Firewall)
.management(Arc::new(OPNSenseManagementInterface::new()))]),
storage_host: vec![],
worker_host: vec![],
control_plane_host: vec![],
}
}

View File

@@ -80,12 +80,9 @@ async fn main() {
let load_balancer_score = OKDLoadBalancerScore::new(&topology);
let tftp_score = TftpScore::new(Url::LocalFolder("./data/watchguard/tftpboot".to_string()));
let http_score = StaticFilesHttpScore {
folder_to_serve: Some(Url::LocalFolder(
"./data/watchguard/pxe-http-files".to_string(),
)),
files: vec![],
};
let http_score = StaticFilesHttpScore::new(Url::LocalFolder(
"./data/watchguard/pxe-http-files".to_string(),
));
harmony_tui::run(
inventory,

View File

@@ -0,0 +1,11 @@
[package]
name = "example_validate_ceph_cluster_health"
edition = "2024"
version.workspace = true
readme.workspace = true
license.workspace = true
[dependencies]
harmony = { version = "0.1.0", path = "../../harmony" }
harmony_cli = { version = "0.1.0", path = "../../harmony_cli" }
tokio.workspace = true

View File

@@ -0,0 +1,18 @@
use harmony::{
inventory::Inventory,
modules::storage::ceph::ceph_validate_health_score::CephVerifyClusterHealth,
topology::K8sAnywhereTopology,
};
#[tokio::main]
async fn main() {
let ceph_health_score = CephVerifyClusterHealth {
rook_ceph_namespace: "rook-ceph".to_string(),
};
let topology = K8sAnywhereTopology::from_env();
let inventory = Inventory::autoload();
harmony_cli::run(inventory, topology, vec![Box::new(ceph_health_score)], None)
.await
.unwrap();
}

View File

@@ -1,22 +0,0 @@
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FileContent {
pub path: FilePath,
pub content: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum FilePath {
Relative(String),
Absolute(String),
}
impl std::fmt::Display for FilePath {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
FilePath::Relative(path) => f.write_fmt(format_args!("./{path}")),
FilePath::Absolute(path) => f.write_fmt(format_args!("/{path}")),
}
}
}

View File

@@ -1,6 +1,4 @@
mod id;
mod version;
mod file;
pub use id::*;
pub use version::*;
pub use file::*;

View File

@@ -32,6 +32,7 @@ pub enum InterpretName {
Lamp,
ApplicationMonitoring,
K8sPrometheusCrdAlerting,
CephClusterHealth,
}
impl std::fmt::Display for InterpretName {
@@ -58,6 +59,7 @@ impl std::fmt::Display for InterpretName {
InterpretName::Lamp => f.write_str("LAMP"),
InterpretName::ApplicationMonitoring => f.write_str("ApplicationMonitoring"),
InterpretName::K8sPrometheusCrdAlerting => f.write_str("K8sPrometheusCrdAlerting"),
InterpretName::CephClusterHealth => f.write_str("CephClusterHealth"),
}
}
}

View File

@@ -1,12 +1,9 @@
use async_trait::async_trait;
use harmony_macros::ip;
use harmony_types::net::MacAddress;
use log::debug;
use log::info;
use crate::data::FileContent;
use crate::executors::ExecutorError;
use crate::topology::PxeOptions;
use super::DHCPStaticEntry;
use super::DhcpServer;
@@ -52,10 +49,9 @@ impl Topology for HAClusterTopology {
"HAClusterTopology"
}
async fn ensure_ready(&self) -> Result<PreparationOutcome, PreparationError> {
debug!(
todo!(
"ensure_ready, not entirely sure what it should do here, probably something like verify that the hosts are reachable and all services are up and ready."
);
Ok(PreparationOutcome::Noop)
)
}
}
@@ -157,10 +153,12 @@ impl DhcpServer for HAClusterTopology {
async fn list_static_mappings(&self) -> Vec<(MacAddress, IpAddress)> {
self.dhcp_server.list_static_mappings().await
}
async fn set_pxe_options(&self, options: PxeOptions) -> Result<(), ExecutorError> {
self.dhcp_server.set_pxe_options(options).await
async fn set_next_server(&self, ip: IpAddress) -> Result<(), ExecutorError> {
self.dhcp_server.set_next_server(ip).await
}
async fn set_boot_filename(&self, boot_filename: &str) -> Result<(), ExecutorError> {
self.dhcp_server.set_boot_filename(boot_filename).await
}
fn get_ip(&self) -> IpAddress {
self.dhcp_server.get_ip()
}
@@ -170,6 +168,16 @@ impl DhcpServer for HAClusterTopology {
async fn commit_config(&self) -> Result<(), ExecutorError> {
self.dhcp_server.commit_config().await
}
async fn set_filename(&self, filename: &str) -> Result<(), ExecutorError> {
self.dhcp_server.set_filename(filename).await
}
async fn set_filename64(&self, filename64: &str) -> Result<(), ExecutorError> {
self.dhcp_server.set_filename64(filename64).await
}
async fn set_filenameipxe(&self, filenameipxe: &str) -> Result<(), ExecutorError> {
self.dhcp_server.set_filenameipxe(filenameipxe).await
}
}
#[async_trait]
@@ -213,21 +221,17 @@ impl HttpServer for HAClusterTopology {
self.http_server.serve_files(url).await
}
async fn serve_file_content(&self, file: &FileContent) -> Result<(), ExecutorError> {
self.http_server.serve_file_content(file).await
}
fn get_ip(&self) -> IpAddress {
self.http_server.get_ip()
unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
}
async fn ensure_initialized(&self) -> Result<(), ExecutorError> {
self.http_server.ensure_initialized().await
unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
}
async fn commit_config(&self) -> Result<(), ExecutorError> {
self.http_server.commit_config().await
unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
}
async fn reload_restart(&self) -> Result<(), ExecutorError> {
self.http_server.reload_restart().await
unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
}
}
@@ -295,7 +299,19 @@ impl DhcpServer for DummyInfra {
async fn list_static_mappings(&self) -> Vec<(MacAddress, IpAddress)> {
unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
}
async fn set_pxe_options(&self, _options: PxeOptions) -> Result<(), ExecutorError> {
async fn set_next_server(&self, _ip: IpAddress) -> Result<(), ExecutorError> {
unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
}
async fn set_boot_filename(&self, _boot_filename: &str) -> Result<(), ExecutorError> {
unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
}
async fn set_filename(&self, _filename: &str) -> Result<(), ExecutorError> {
unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
}
async fn set_filename64(&self, _filename: &str) -> Result<(), ExecutorError> {
unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
}
async fn set_filenameipxe(&self, _filenameipxe: &str) -> Result<(), ExecutorError> {
unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
}
fn get_ip(&self) -> IpAddress {
@@ -365,9 +381,6 @@ impl HttpServer for DummyInfra {
async fn serve_files(&self, _url: &Url) -> Result<(), ExecutorError> {
unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
}
async fn serve_file_content(&self, _file: &FileContent) -> Result<(), ExecutorError> {
unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
}
fn get_ip(&self) -> IpAddress {
unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
}

View File

@@ -1,4 +1,4 @@
use crate::{data::FileContent, executors::ExecutorError};
use crate::executors::ExecutorError;
use async_trait::async_trait;
use super::{IpAddress, Url};
@@ -6,7 +6,6 @@ use super::{IpAddress, Url};
#[async_trait]
pub trait HttpServer: Send + Sync {
async fn serve_files(&self, url: &Url) -> Result<(), ExecutorError>;
async fn serve_file_content(&self, file: &FileContent) -> Result<(), ExecutorError>;
fn get_ip(&self) -> IpAddress;
// async fn set_ip(&self, ip: IpAddress) -> Result<(), ExecutorError>;

View File

@@ -5,7 +5,7 @@ use k8s_openapi::{
};
use kube::{
Client, Config, Error, Resource,
api::{Api, AttachParams, ListParams, Patch, PatchParams, ResourceExt},
api::{Api, AttachParams, DeleteParams, ListParams, Patch, PatchParams, ResourceExt},
config::{KubeConfigOptions, Kubeconfig},
core::ErrorResponse,
runtime::reflector::Lookup,
@@ -17,7 +17,9 @@ use kube::{
};
use log::{debug, error, trace};
use serde::{Serialize, de::DeserializeOwned};
use serde_json::json;
use similar::TextDiff;
use tokio::io::AsyncReadExt;
#[derive(new, Clone)]
pub struct K8sClient {
@@ -51,6 +53,66 @@ impl K8sClient {
})
}
pub async fn get_deployment(
&self,
name: &str,
namespace: Option<&str>,
) -> Result<Option<Deployment>, Error> {
let deps: Api<Deployment> = if let Some(ns) = namespace {
Api::namespaced(self.client.clone(), ns)
} else {
Api::default_namespaced(self.client.clone())
};
Ok(deps.get_opt(name).await?)
}
pub async fn get_pod(&self, name: &str, namespace: Option<&str>) -> Result<Option<Pod>, Error> {
let pods: Api<Pod> = if let Some(ns) = namespace {
Api::namespaced(self.client.clone(), ns)
} else {
Api::default_namespaced(self.client.clone())
};
Ok(pods.get_opt(name).await?)
}
pub async fn scale_deployment(
&self,
name: &str,
namespace: Option<&str>,
replicas: u32,
) -> Result<(), Error> {
let deployments: Api<Deployment> = if let Some(ns) = namespace {
Api::namespaced(self.client.clone(), ns)
} else {
Api::default_namespaced(self.client.clone())
};
let patch = json!({
"spec": {
"replicas": replicas
}
});
let pp = PatchParams::default();
let scale = Patch::Apply(&patch);
deployments.patch_scale(name, &pp, &scale).await?;
Ok(())
}
pub async fn delete_deployment(
&self,
name: &str,
namespace: Option<&str>,
) -> Result<(), Error> {
let deployments: Api<Deployment> = if let Some(ns) = namespace {
Api::namespaced(self.client.clone(), ns)
} else {
Api::default_namespaced(self.client.clone())
};
let delete_params = DeleteParams::default();
deployments.delete(name, &delete_params).await?;
Ok(())
}
pub async fn wait_until_deployment_ready(
&self,
name: String,
@@ -76,6 +138,68 @@ impl K8sClient {
}
}
/// Will execute a commond in the first pod found that matches the specified label
/// '{label}={name}'
pub async fn exec_app_capture_output(
&self,
name: String,
label: String,
namespace: Option<&str>,
command: Vec<&str>,
) -> Result<String, String> {
let api: Api<Pod>;
if let Some(ns) = namespace {
api = Api::namespaced(self.client.clone(), ns);
} else {
api = Api::default_namespaced(self.client.clone());
}
let pod_list = api
.list(&ListParams::default().labels(format!("{label}={name}").as_str()))
.await
.expect("couldn't get list of pods");
let res = api
.exec(
pod_list
.items
.first()
.expect("couldn't get pod")
.name()
.expect("couldn't get pod name")
.into_owned()
.as_str(),
command,
&AttachParams::default().stdout(true).stderr(true),
)
.await;
match res {
Err(e) => Err(e.to_string()),
Ok(mut process) => {
let status = process
.take_status()
.expect("Couldn't get status")
.await
.expect("Couldn't unwrap status");
if let Some(s) = status.status {
let mut stdout_buf = String::new();
if let Some(mut stdout) = process.stdout().take() {
stdout.read_to_string(&mut stdout_buf).await;
}
debug!("Status: {} - {:?}", s, status.details);
if s == "Success" {
Ok(stdout_buf)
} else {
Err(s)
}
} else {
Err("Couldn't get inner status of pod exec".to_string())
}
}
}
}
/// Will execute a command in the first pod found that matches the label `app.kubernetes.io/name={name}`
pub async fn exec_app(
&self,

View File

@@ -46,19 +46,16 @@ pub trait K8sclient: Send + Sync {
async fn k8s_client(&self) -> Result<Arc<K8sClient>, String>;
}
pub struct PxeOptions {
pub ipxe_filename: String,
pub bios_filename: String,
pub efi_filename: String,
pub tftp_ip: Option<IpAddress>,
}
#[async_trait]
pub trait DhcpServer: Send + Sync + std::fmt::Debug {
async fn add_static_mapping(&self, entry: &DHCPStaticEntry) -> Result<(), ExecutorError>;
async fn remove_static_mapping(&self, mac: &MacAddress) -> Result<(), ExecutorError>;
async fn list_static_mappings(&self) -> Vec<(MacAddress, IpAddress)>;
async fn set_pxe_options(&self, pxe_options: PxeOptions) -> Result<(), ExecutorError>;
async fn set_next_server(&self, ip: IpAddress) -> Result<(), ExecutorError>;
async fn set_boot_filename(&self, boot_filename: &str) -> Result<(), ExecutorError>;
async fn set_filename(&self, filename: &str) -> Result<(), ExecutorError>;
async fn set_filename64(&self, filename64: &str) -> Result<(), ExecutorError>;
async fn set_filenameipxe(&self, filenameipxe: &str) -> Result<(), ExecutorError>;
fn get_ip(&self) -> IpAddress;
fn get_host(&self) -> LogicalHost;
async fn commit_config(&self) -> Result<(), ExecutorError>;

View File

@@ -1,10 +1,10 @@
use async_trait::async_trait;
use harmony_types::net::MacAddress;
use log::{debug, info};
use log::debug;
use crate::{
executors::ExecutorError,
topology::{DHCPStaticEntry, DhcpServer, IpAddress, LogicalHost, PxeOptions},
topology::{DHCPStaticEntry, DhcpServer, IpAddress, LogicalHost},
};
use super::OPNSenseFirewall;
@@ -26,7 +26,7 @@ impl DhcpServer for OPNSenseFirewall {
.unwrap();
}
info!("Registered {:?}", entry);
debug!("Registered {:?}", entry);
Ok(())
}
@@ -46,25 +46,57 @@ impl DhcpServer for OPNSenseFirewall {
self.host.clone()
}
async fn set_pxe_options(&self, options: PxeOptions) -> Result<(), ExecutorError> {
let mut writable_opnsense = self.opnsense_config.write().await;
let PxeOptions {
ipxe_filename,
bios_filename,
efi_filename,
tftp_ip,
} = options;
writable_opnsense
.dhcp()
.set_pxe_options(
tftp_ip.map(|i| i.to_string()),
bios_filename,
efi_filename,
ipxe_filename,
)
.await
.map_err(|dhcp_error| {
ExecutorError::UnexpectedError(format!("Failed to set_pxe_options : {dhcp_error}"))
})
async fn set_next_server(&self, ip: IpAddress) -> Result<(), ExecutorError> {
let ipv4 = match ip {
std::net::IpAddr::V4(ipv4_addr) => ipv4_addr,
std::net::IpAddr::V6(_) => todo!("ipv6 not supported yet"),
};
{
let mut writable_opnsense = self.opnsense_config.write().await;
writable_opnsense.dhcp().set_next_server(ipv4);
debug!("OPNsense dhcp server set next server {ipv4}");
}
Ok(())
}
async fn set_boot_filename(&self, boot_filename: &str) -> Result<(), ExecutorError> {
{
let mut writable_opnsense = self.opnsense_config.write().await;
writable_opnsense.dhcp().set_boot_filename(boot_filename);
debug!("OPNsense dhcp server set boot filename {boot_filename}");
}
Ok(())
}
async fn set_filename(&self, filename: &str) -> Result<(), ExecutorError> {
{
let mut writable_opnsense = self.opnsense_config.write().await;
writable_opnsense.dhcp().set_filename(filename);
debug!("OPNsense dhcp server set filename {filename}");
}
Ok(())
}
async fn set_filename64(&self, filename: &str) -> Result<(), ExecutorError> {
{
let mut writable_opnsense = self.opnsense_config.write().await;
writable_opnsense.dhcp().set_filename64(filename);
debug!("OPNsense dhcp server set filename {filename}");
}
Ok(())
}
async fn set_filenameipxe(&self, filenameipxe: &str) -> Result<(), ExecutorError> {
{
let mut writable_opnsense = self.opnsense_config.write().await;
writable_opnsense.dhcp().set_filenameipxe(filenameipxe);
debug!("OPNsense dhcp server set filenameipxe {filenameipxe}");
}
Ok(())
}
}

View File

@@ -2,23 +2,23 @@ use async_trait::async_trait;
use log::info;
use crate::{
data::FileContent,
executors::ExecutorError,
topology::{HttpServer, IpAddress, Url},
};
use super::OPNSenseFirewall;
const OPNSENSE_HTTP_ROOT_PATH: &str = "/usr/local/http";
#[async_trait]
impl HttpServer for OPNSenseFirewall {
async fn serve_files(&self, url: &Url) -> Result<(), ExecutorError> {
let http_root_path = "/usr/local/http";
let config = self.opnsense_config.read().await;
info!("Uploading files from url {url} to {OPNSENSE_HTTP_ROOT_PATH}");
info!("Uploading files from url {url} to {http_root_path}");
match url {
Url::LocalFolder(path) => {
config
.upload_files(path, OPNSENSE_HTTP_ROOT_PATH)
.upload_files(path, http_root_path)
.await
.map_err(|e| ExecutorError::UnexpectedError(e.to_string()))?;
}
@@ -27,29 +27,8 @@ impl HttpServer for OPNSenseFirewall {
Ok(())
}
async fn serve_file_content(&self, file: &FileContent) -> Result<(), ExecutorError> {
let path = match &file.path {
crate::data::FilePath::Relative(path) => {
format!("{OPNSENSE_HTTP_ROOT_PATH}/{}", path.to_string())
}
crate::data::FilePath::Absolute(path) => {
return Err(ExecutorError::ConfigurationError(format!(
"Cannot serve file from http server with absolute path : {path}"
)));
}
};
let config = self.opnsense_config.read().await;
info!("Uploading file content to {}", path);
config
.upload_file_content(&path, &file.content)
.await
.map_err(|e| ExecutorError::UnexpectedError(e.to_string()))?;
Ok(())
}
fn get_ip(&self) -> IpAddress {
OPNSenseFirewall::get_ip(self)
todo!();
}
async fn commit_config(&self) -> Result<(), ExecutorError> {

View File

@@ -28,7 +28,7 @@ impl TftpServer for OPNSenseFirewall {
}
fn get_ip(&self) -> IpAddress {
OPNSenseFirewall::get_ip(self)
todo!()
}
async fn set_ip(&self, ip: IpAddress) -> Result<(), ExecutorError> {

View File

@@ -7,7 +7,7 @@ use crate::{
domain::{data::Version, interpret::InterpretStatus},
interpret::{Interpret, InterpretError, InterpretName, Outcome},
inventory::Inventory,
topology::{DHCPStaticEntry, DhcpServer, HostBinding, IpAddress, PxeOptions, Topology},
topology::{DHCPStaticEntry, DhcpServer, HostBinding, IpAddress, Topology},
};
use crate::domain::score::Score;
@@ -98,14 +98,69 @@ impl DhcpInterpret {
_inventory: &Inventory,
dhcp_server: &D,
) -> Result<Outcome, InterpretError> {
let pxe_options = PxeOptions {
ipxe_filename: self.score.filenameipxe.clone().unwrap_or_default(),
bios_filename: self.score.filename.clone().unwrap_or_default(),
efi_filename: self.score.filename64.clone().unwrap_or_default(),
tftp_ip: self.score.next_server,
let next_server_outcome = match self.score.next_server {
Some(next_server) => {
dhcp_server.set_next_server(next_server).await?;
Outcome::new(
InterpretStatus::SUCCESS,
format!("Dhcp Interpret Set next boot to {next_server}"),
)
}
None => Outcome::noop(),
};
dhcp_server.set_pxe_options(pxe_options).await?;
let boot_filename_outcome = match &self.score.boot_filename {
Some(boot_filename) => {
dhcp_server.set_boot_filename(boot_filename).await?;
Outcome::new(
InterpretStatus::SUCCESS,
format!("Dhcp Interpret Set boot filename to {boot_filename}"),
)
}
None => Outcome::noop(),
};
let filename_outcome = match &self.score.filename {
Some(filename) => {
dhcp_server.set_filename(filename).await?;
Outcome::new(
InterpretStatus::SUCCESS,
format!("Dhcp Interpret Set filename to {filename}"),
)
}
None => Outcome::noop(),
};
let filename64_outcome = match &self.score.filename64 {
Some(filename64) => {
dhcp_server.set_filename64(filename64).await?;
Outcome::new(
InterpretStatus::SUCCESS,
format!("Dhcp Interpret Set filename64 to {filename64}"),
)
}
None => Outcome::noop(),
};
let filenameipxe_outcome = match &self.score.filenameipxe {
Some(filenameipxe) => {
dhcp_server.set_filenameipxe(filenameipxe).await?;
Outcome::new(
InterpretStatus::SUCCESS,
format!("Dhcp Interpret Set filenameipxe to {filenameipxe}"),
)
}
None => Outcome::noop(),
};
if next_server_outcome.status == InterpretStatus::NOOP
&& boot_filename_outcome.status == InterpretStatus::NOOP
&& filename_outcome.status == InterpretStatus::NOOP
&& filename64_outcome.status == InterpretStatus::NOOP
&& filenameipxe_outcome.status == InterpretStatus::NOOP
{
return Ok(Outcome::noop());
}
Ok(Outcome::new(
InterpretStatus::SUCCESS,

View File

@@ -3,7 +3,7 @@ use derive_new::new;
use serde::Serialize;
use crate::{
data::{FileContent, Id, Version},
data::{Id, Version},
interpret::{Interpret, InterpretError, InterpretName, InterpretStatus, Outcome},
inventory::Inventory,
score::Score,
@@ -23,8 +23,7 @@ use crate::{
/// ```
#[derive(Debug, new, Clone, Serialize)]
pub struct StaticFilesHttpScore {
pub folder_to_serve: Option<Url>,
pub files: Vec<FileContent>,
files_to_serve: Url,
}
impl<T: Topology + HttpServer> Score<T> for StaticFilesHttpScore {
@@ -51,20 +50,12 @@ impl<T: Topology + HttpServer> Interpret<T> for StaticFilesHttpInterpret {
) -> Result<Outcome, InterpretError> {
http_server.ensure_initialized().await?;
// http_server.set_ip(topology.router.get_gateway()).await?;
if let Some(folder) = self.score.folder_to_serve.as_ref() {
http_server.serve_files(folder).await?;
}
for f in self.score.files.iter() {
http_server.serve_file_content(&f).await?
}
http_server.serve_files(&self.score.files_to_serve).await?;
http_server.commit_config().await?;
http_server.reload_restart().await?;
Ok(Outcome::success(format!(
"Http Server running and serving files from folder {:?} and content for {}",
self.score.folder_to_serve,
self.score.files.iter().map(|f| f.path.to_string()).collect::<Vec<String>>().join(",")
"Http Server running and serving files from {}",
self.score.files_to_serve
)))
}

View File

@@ -14,5 +14,6 @@ pub mod monitoring;
pub mod okd;
pub mod opnsense;
pub mod prometheus;
pub mod storage;
pub mod tenant;
pub mod tftp;

View File

@@ -0,0 +1,419 @@
use std::{
process::Command,
sync::Arc,
time::{Duration, Instant},
};
use async_trait::async_trait;
use log::{info, warn};
use serde::{Deserialize, Serialize};
use tokio::time::sleep;
use crate::{
data::{Id, Version},
interpret::{Interpret, InterpretError, InterpretName, InterpretStatus, Outcome},
inventory::Inventory,
score::Score,
topology::{K8sclient, Topology, k8s::K8sClient},
};
#[derive(Debug, Clone, Serialize)]
pub struct CephRemoveOsd {
osd_deployment_name: String,
rook_ceph_namespace: String,
}
impl<T: Topology + K8sclient> Score<T> for CephRemoveOsd {
fn name(&self) -> String {
format!("CephRemoveOsdScore")
}
#[doc(hidden)]
fn create_interpret(&self) -> Box<dyn Interpret<T>> {
Box::new(CephRemoveOsdInterpret {
score: self.clone(),
})
}
}
#[derive(Debug, Clone)]
pub struct CephRemoveOsdInterpret {
score: CephRemoveOsd,
}
#[async_trait]
impl<T: Topology + K8sclient> Interpret<T> for CephRemoveOsdInterpret {
async fn execute(
&self,
_inventory: &Inventory,
topology: &T,
) -> Result<Outcome, InterpretError> {
let client = topology.k8s_client().await.unwrap();
self.verify_ceph_toolbox_exists(client.clone()).await?;
self.scale_deployment(client.clone()).await?;
self.verify_deployment_scaled(client.clone()).await?;
self.delete_deployment(client.clone()).await?;
self.verify_deployment_deleted(client.clone()).await?;
let osd_id_full = self.get_ceph_osd_id().unwrap();
self.purge_ceph_osd(client.clone(), &osd_id_full).await?;
self.verify_ceph_osd_removal(client.clone(), &osd_id_full)
.await?;
Ok(Outcome::success(format!(
"Successfully removed OSD {} from rook-ceph cluster by deleting deployment {}",
osd_id_full, self.score.osd_deployment_name
)))
}
fn get_name(&self) -> InterpretName {
todo!()
}
fn get_version(&self) -> Version {
todo!()
}
fn get_status(&self) -> InterpretStatus {
todo!()
}
fn get_children(&self) -> Vec<Id> {
todo!()
}
}
impl CephRemoveOsdInterpret {
pub fn get_ceph_osd_id(&self) -> Result<String, InterpretError> {
let osd_id_numeric = self
.score
.osd_deployment_name
.split('-')
.nth(3)
.ok_or_else(|| {
InterpretError::new(format!(
"Could not parse OSD id from deployment name {}",
self.score.osd_deployment_name
))
})?;
let osd_id_full = format!("osd.{}", osd_id_numeric);
info!(
"Targeting Ceph OSD: {} (parsed from deployment {})",
osd_id_full, self.score.osd_deployment_name
);
Ok(osd_id_full)
}
pub async fn verify_ceph_toolbox_exists(
&self,
client: Arc<K8sClient>,
) -> Result<Outcome, InterpretError> {
let toolbox_dep = "rook-ceph-tools".to_string();
match client
.get_deployment(&toolbox_dep, Some(&self.score.rook_ceph_namespace))
.await
{
Ok(Some(deployment)) => {
if let Some(status) = deployment.status {
let ready_count = status.ready_replicas.unwrap_or(0);
if ready_count >= 1 {
return Ok(Outcome::success(format!(
"'{}' is ready with {} replica(s).",
&toolbox_dep, ready_count
)));
} else {
return Err(InterpretError::new(
"ceph-tool-box not ready in cluster".to_string(),
));
}
} else {
Err(InterpretError::new(format!(
"failed to get deployment status {}",
&toolbox_dep
)))
}
}
Ok(None) => Err(InterpretError::new(format!(
"Deployment '{}' not found in namespace '{}'.",
&toolbox_dep, self.score.rook_ceph_namespace
))),
Err(e) => Err(InterpretError::new(format!(
"Failed to query for deployment '{}': {}",
&toolbox_dep, e
))),
}
}
pub async fn scale_deployment(
&self,
client: Arc<K8sClient>,
) -> Result<Outcome, InterpretError> {
info!(
"Scaling down OSD deployment: {}",
self.score.osd_deployment_name
);
client
.scale_deployment(
&self.score.osd_deployment_name,
Some(&self.score.rook_ceph_namespace),
0,
)
.await?;
Ok(Outcome::success(format!(
"Scaled down deployment {}",
self.score.osd_deployment_name
)))
}
pub async fn verify_deployment_scaled(
&self,
client: Arc<K8sClient>,
) -> Result<Outcome, InterpretError> {
let (timeout, interval, start) = self.build_timer();
info!("Waiting for OSD deployment to scale down to 0 replicas");
loop {
let dep = client
.get_deployment(
&self.score.osd_deployment_name,
Some(&self.score.rook_ceph_namespace),
)
.await?;
if let Some(deployment) = dep {
if let Some(status) = deployment.status {
if status.replicas.unwrap_or(1) == 0 && status.ready_replicas.unwrap_or(1) == 0
{
return Ok(Outcome::success(
"Deployment successfully scaled down.".to_string(),
));
}
}
}
if start.elapsed() > timeout {
return Err(InterpretError::new(format!(
"Timed out waiting for deployment {} to scale down",
self.score.osd_deployment_name
)));
}
sleep(interval).await;
}
}
fn build_timer(&self) -> (Duration, Duration, Instant) {
let timeout = Duration::from_secs(120);
let interval = Duration::from_secs(5);
let start = Instant::now();
(timeout, interval, start)
}
pub async fn delete_deployment(
&self,
client: Arc<K8sClient>,
) -> Result<Outcome, InterpretError> {
info!(
"Deleting OSD deployment: {}",
self.score.osd_deployment_name
);
client
.delete_deployment(
&self.score.osd_deployment_name,
Some(&self.score.rook_ceph_namespace),
)
.await?;
Ok(Outcome::success(format!(
"deployment {} deleted",
self.score.osd_deployment_name
)))
}
pub async fn verify_deployment_deleted(
&self,
client: Arc<K8sClient>,
) -> Result<Outcome, InterpretError> {
let (timeout, interval, start) = self.build_timer();
info!("Waiting for OSD deployment to scale down to 0 replicas");
loop {
let dep = client
.get_deployment(
&self.score.osd_deployment_name,
Some(&self.score.rook_ceph_namespace),
)
.await?;
if dep.is_none() {
info!(
"Deployment {} successfully deleted.",
self.score.osd_deployment_name
);
return Ok(Outcome::success(format!(
"Deployment {} deleted.",
self.score.osd_deployment_name
)));
}
if start.elapsed() > timeout {
return Err(InterpretError::new(format!(
"Timed out waiting for deployment {} to be deleted",
self.score.osd_deployment_name
)));
}
sleep(interval).await;
}
}
fn get_osd_tree(&self, json: serde_json::Value) -> Result<CephOsdTree, InterpretError> {
let nodes = json.get("nodes").ok_or_else(|| {
InterpretError::new("Missing 'nodes' field in ceph osd tree JSON".to_string())
})?;
let tree: CephOsdTree = CephOsdTree {
nodes: serde_json::from_value(nodes.clone()).map_err(|e| {
InterpretError::new(format!("Failed to parse ceph osd tree JSON: {}", e))
})?,
};
Ok(tree)
}
pub async fn purge_ceph_osd(
&self,
client: Arc<K8sClient>,
osd_id_full: &str,
) -> Result<Outcome, InterpretError> {
info!(
"Purging OSD {} from Ceph cluster and removing its auth key",
osd_id_full
);
client
.exec_app_capture_output(
"rook-ceph-tools".to_string(),
"app".to_string(),
Some(&self.score.rook_ceph_namespace),
vec![
format!("ceph osd purge {osd_id_full} --yes-i-really-mean-it").as_str(),
format!("ceph auth del osd.{osd_id_full}").as_str(),
],
)
.await?;
Ok(Outcome::success(format!(
"osd id {} removed from osd tree",
osd_id_full
)))
}
pub async fn verify_ceph_osd_removal(
&self,
client: Arc<K8sClient>,
osd_id_full: &str,
) -> Result<Outcome, InterpretError> {
let (timeout, interval, start) = self.build_timer();
info!(
"Verifying OSD {} has been removed from the Ceph tree...",
osd_id_full
);
loop {
let output = client
.exec_app_capture_output(
"rook-ceph-tools".to_string(),
"app".to_string(),
Some(&self.score.rook_ceph_namespace),
vec!["ceph osd tree -f json"],
)
.await?;
let tree =
self.get_osd_tree(serde_json::from_str(&output).expect("could not extract json"));
let osd_found = tree
.unwrap()
.nodes
.iter()
.any(|node| node.name == osd_id_full);
if !osd_found {
return Ok(Outcome::success(format!(
"Successfully verified that OSD {} is removed from the Ceph cluster.",
osd_id_full,
)));
}
if start.elapsed() > timeout {
return Err(InterpretError::new(format!(
"Timed out waiting for OSD {} to be removed from Ceph tree",
osd_id_full
)));
}
warn!(
"OSD {} still found in Ceph tree, retrying in {:?}...",
osd_id_full, interval
);
sleep(interval).await;
}
}
}
#[derive(Debug, Deserialize, PartialEq)]
pub struct CephOsdTree {
pub nodes: Vec<CephNode>,
}
#[derive(Debug, Deserialize, PartialEq)]
pub struct CephNode {
pub id: i32,
pub name: String,
#[serde(rename = "type")]
pub node_type: String,
pub type_id: Option<i32>,
pub children: Option<Vec<i32>>,
pub exists: Option<i32>,
pub status: Option<String>,
}
#[cfg(test)]
mod tests {
use serde_json::json;
use super::*;
#[test]
fn test_get_osd_tree() {
let json_data = json!({
"nodes": [
{"id": 1, "name": "osd.1", "type": "osd", "primary_affinity":"1"},
{"id": 2, "name": "osd.2", "type": "osd", "crush_weight": 1.22344}
]
});
let interpret = CephRemoveOsdInterpret {
score: CephRemoveOsd {
osd_deployment_name: "osd-1".to_string(),
rook_ceph_namespace: "dummy_ns".to_string(),
},
};
let json = interpret.get_osd_tree(json_data).unwrap();
let expected = CephOsdTree {
nodes: vec![
CephNode {
id: 1,
name: "osd.1".to_string(),
node_type: "osd".to_string(),
type_id: None,
children: None,
exists: None,
status: None,
},
CephNode {
id: 2,
name: "osd.2".to_string(),
node_type: "osd".to_string(),
type_id: None,
children: None,
exists: None,
status: None,
},
],
};
assert_eq!(json, expected);
}
}

View File

@@ -0,0 +1,136 @@
use std::{sync::Arc, time::Duration};
use async_trait::async_trait;
use log::debug;
use serde::Serialize;
use tokio::time::Instant;
use crate::{
data::{Id, Version},
interpret::{Interpret, InterpretError, InterpretName, InterpretStatus, Outcome},
inventory::Inventory,
score::Score,
topology::{K8sclient, Topology, k8s::K8sClient},
};
#[derive(Clone, Debug, Serialize)]
pub struct CephVerifyClusterHealth {
pub rook_ceph_namespace: String,
}
impl<T: Topology + K8sclient> Score<T> for CephVerifyClusterHealth {
fn name(&self) -> String {
format!("CephValidateClusterHealth")
}
fn create_interpret(&self) -> Box<dyn Interpret<T>> {
Box::new(CephVerifyClusterHealthInterpret {
score: self.clone(),
})
}
}
#[derive(Clone, Debug)]
pub struct CephVerifyClusterHealthInterpret {
score: CephVerifyClusterHealth,
}
#[async_trait]
impl<T: Topology + K8sclient> Interpret<T> for CephVerifyClusterHealthInterpret {
async fn execute(
&self,
_inventory: &Inventory,
topology: &T,
) -> Result<Outcome, InterpretError> {
let client = topology.k8s_client().await.unwrap();
self.verify_ceph_toolbox_exists(client.clone()).await?;
self.validate_ceph_cluster_health(client.clone()).await?;
Ok(Outcome::success("Ceph cluster healthy".to_string()))
}
fn get_name(&self) -> InterpretName {
InterpretName::CephClusterHealth
}
fn get_version(&self) -> Version {
todo!()
}
fn get_status(&self) -> InterpretStatus {
todo!()
}
fn get_children(&self) -> Vec<Id> {
todo!()
}
}
impl CephVerifyClusterHealthInterpret {
pub async fn verify_ceph_toolbox_exists(
&self,
client: Arc<K8sClient>,
) -> Result<Outcome, InterpretError> {
let toolbox_dep = "rook-ceph-tools".to_string();
match client
.get_deployment(&toolbox_dep, Some(&self.score.rook_ceph_namespace))
.await
{
Ok(Some(deployment)) => {
if let Some(status) = deployment.status {
let ready_count = status.ready_replicas.unwrap_or(0);
if ready_count >= 1 {
return Ok(Outcome::success(format!(
"'{}' is ready with {} replica(s).",
&toolbox_dep, ready_count
)));
} else {
return Err(InterpretError::new(
"ceph-tool-box not ready in cluster".to_string(),
));
}
} else {
Err(InterpretError::new(format!(
"failed to get deployment status {}",
&toolbox_dep
)))
}
}
Ok(None) => Err(InterpretError::new(format!(
"Deployment '{}' not found in namespace '{}'.",
&toolbox_dep, self.score.rook_ceph_namespace
))),
Err(e) => Err(InterpretError::new(format!(
"Failed to query for deployment '{}': {}",
&toolbox_dep, e
))),
}
}
pub async fn validate_ceph_cluster_health(
&self,
client: Arc<K8sClient>,
) -> Result<Outcome, InterpretError> {
debug!("Verifying ceph cluster is in healthy state");
let health = client
.exec_app_capture_output(
"rook-ceph-tools".to_string(),
"app".to_string(),
Some(&self.score.rook_ceph_namespace),
vec!["sh", "-c", "ceph health"],
)
.await?;
if health.contains("HEALTH_OK") {
return Ok(Outcome::success(
"Ceph Cluster in healthy state".to_string(),
));
} else {
Err(InterpretError::new(format!(
"Ceph cluster unhealthy {}",
health
)))
}
}
}

View File

@@ -0,0 +1,2 @@
pub mod ceph_osd_replacement_score;
pub mod ceph_validate_health_score;

View File

@@ -0,0 +1 @@
pub mod ceph;

View File

@@ -12,7 +12,7 @@ use crate::{
#[derive(Debug, new, Clone, Serialize)]
pub struct TftpScore {
pub files_to_serve: Url,
files_to_serve: Url,
}
impl<T: Topology + TftpServer + Router> Score<T> for TftpScore {

View File

@@ -10,4 +10,3 @@ serde.workspace = true
serde_json.workspace = true
log.workspace = true
env_logger.workspace = true
uuid.workspace = true

File diff suppressed because it is too large Load Diff

View File

@@ -9,8 +9,16 @@ mod hwinfo;
async fn inventory() -> impl Responder {
log::info!("Received inventory request");
let host = PhysicalHost::gather();
log::info!("Inventory data gathered successfully");
actix_web::HttpResponse::Ok().json(host)
match host {
Ok(host) => {
log::info!("Inventory data gathered successfully");
actix_web::HttpResponse::Ok().json(host)
}
Err(error) => {
log::error!("Inventory data gathering FAILED");
actix_web::HttpResponse::InternalServerError().json(error)
}
}
}
#[actix_web::main]

View File

@@ -30,15 +30,15 @@ pub struct CaddyGeneral {
#[yaserde(rename = "TlsDnsApiKey")]
pub tls_dns_api_key: MaybeString,
#[yaserde(rename = "TlsDnsSecretApiKey")]
pub tls_dns_secret_api_key: Option<MaybeString>,
pub tls_dns_secret_api_key: MaybeString,
#[yaserde(rename = "TlsDnsOptionalField1")]
pub tls_dns_optional_field1: Option<MaybeString>,
pub tls_dns_optional_field1: MaybeString,
#[yaserde(rename = "TlsDnsOptionalField2")]
pub tls_dns_optional_field2: Option<MaybeString>,
pub tls_dns_optional_field2: MaybeString,
#[yaserde(rename = "TlsDnsOptionalField3")]
pub tls_dns_optional_field3: Option<MaybeString>,
pub tls_dns_optional_field3: MaybeString,
#[yaserde(rename = "TlsDnsOptionalField4")]
pub tls_dns_optional_field4: Option<MaybeString>,
pub tls_dns_optional_field4: MaybeString,
#[yaserde(rename = "TlsDnsPropagationTimeout")]
pub tls_dns_propagation_timeout: Option<MaybeString>,
#[yaserde(rename = "TlsDnsPropagationTimeoutPeriod")]
@@ -47,8 +47,6 @@ pub struct CaddyGeneral {
pub tls_dns_propagation_delay: Option<MaybeString>,
#[yaserde(rename = "TlsDnsPropagationResolvers")]
pub tls_dns_propagation_resolvers: MaybeString,
#[yaserde(rename = "TlsDnsEchDomain")]
pub tls_dns_ech_domain: MaybeString,
pub accesslist: MaybeString,
#[yaserde(rename = "DisableSuperuser")]
pub disable_superuser: Option<i32>,
@@ -58,10 +56,6 @@ pub struct CaddyGeneral {
pub http_version: Option<MaybeString>,
#[yaserde(rename = "HttpVersions")]
pub http_versions: Option<MaybeString>,
pub timeout_read_body: Option<MaybeString>,
pub timeout_read_header: Option<MaybeString>,
pub timeout_write: Option<MaybeString>,
pub timeout_idle: Option<MaybeString>,
#[yaserde(rename = "LogCredentials")]
pub log_credentials: MaybeString,
#[yaserde(rename = "LogAccessPlain")]

View File

@@ -1,113 +0,0 @@
use yaserde::{MaybeString, RawXml};
use yaserde_derive::{YaDeserialize, YaSerialize};
// This is the top-level struct that represents the entire <dnsmasq> element.
#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)]
pub struct DnsMasq {
#[yaserde(attribute = true)]
pub version: String,
#[yaserde(attribute = true)]
pub persisted_at: Option<String>,
pub enable: u8,
pub regdhcp: u8,
pub regdhcpstatic: u8,
pub dhcpfirst: u8,
pub strict_order: u8,
pub domain_needed: u8,
pub no_private_reverse: u8,
pub no_resolv: Option<u8>,
pub log_queries: u8,
pub no_hosts: u8,
pub strictbind: u8,
pub dnssec: u8,
pub regdhcpdomain: MaybeString,
pub interface: Option<String>,
pub port: Option<u32>,
pub dns_forward_max: MaybeString,
pub cache_size: MaybeString,
pub local_ttl: MaybeString,
pub add_mac: Option<MaybeString>,
pub add_subnet: Option<u8>,
pub strip_subnet: Option<u8>,
pub no_ident: Option<u8>,
pub dhcp: Option<Dhcp>,
pub dhcp_ranges: Vec<DhcpRange>,
pub dhcp_options: Vec<DhcpOptions>,
pub dhcp_boot: Vec<DhcpBoot>,
pub dhcp_tags: Vec<RawXml>,
}
// Represents the <dhcp> element and its nested fields.
#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)]
#[yaserde(rename = "dhcp")]
pub struct Dhcp {
pub no_interface: MaybeString,
pub fqdn: u8,
pub domain: MaybeString,
pub local: Option<MaybeString>,
pub lease_max: MaybeString,
pub authoritative: u8,
pub default_fw_rules: u8,
pub reply_delay: MaybeString,
pub enable_ra: u8,
pub nosync: u8,
}
// Represents a single <dhcp_ranges> element.
#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)]
#[yaserde(rename = "dhcp_ranges")]
pub struct DhcpRange {
#[yaserde(attribute = true)]
pub uuid: String,
pub interface: String,
pub set_tag: MaybeString,
pub start_addr: String,
pub end_addr: String,
pub subnet_mask: MaybeString,
pub constructor: MaybeString,
pub mode: MaybeString,
pub prefix_len: MaybeString,
pub lease_time: MaybeString,
pub domain_type: String,
pub domain: MaybeString,
pub nosync: u8,
pub ra_mode: MaybeString,
pub ra_priority: MaybeString,
pub ra_mtu: MaybeString,
pub ra_interval: MaybeString,
pub ra_router_lifetime: MaybeString,
pub description: MaybeString,
}
// Represents a single <dhcp_boot> element.
#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)]
#[yaserde(rename = "dhcp_boot")]
pub struct DhcpBoot {
#[yaserde(attribute = true)]
pub uuid: String,
pub interface: MaybeString,
pub tag: MaybeString,
pub filename: Option<String>,
pub servername: String,
pub address: String,
pub description: Option<String>,
}
// Represents a single <dhcp_options> element.
#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)]
#[yaserde(rename = "dhcp_options")]
pub struct DhcpOptions {
#[yaserde(attribute = true)]
pub uuid: String,
#[yaserde(rename = "type")]
pub _type: String,
pub option: MaybeString,
pub option6: MaybeString,
pub interface: MaybeString,
pub tag: MaybeString,
pub set_tag: MaybeString,
pub value: String,
pub force: u8,
pub description: MaybeString,
}

View File

@@ -8,12 +8,10 @@ pub struct Interface {
#[yaserde(rename = "if")]
pub physical_interface_name: String,
pub descr: Option<MaybeString>,
pub mtu: Option<MaybeString>,
pub enable: MaybeString,
pub lock: Option<MaybeString>,
#[yaserde(rename = "spoofmac")]
pub spoof_mac: Option<MaybeString>,
pub mss: Option<MaybeString>,
pub ipaddr: Option<MaybeString>,
pub dhcphostname: Option<MaybeString>,
#[yaserde(rename = "alias-address")]

View File

@@ -3,7 +3,6 @@ mod dhcpd;
mod haproxy;
mod interfaces;
mod opnsense;
pub mod dnsmasq;
pub use caddy::*;
pub use dhcpd::*;
pub use haproxy::*;

View File

@@ -1,4 +1,3 @@
use crate::dnsmasq::DnsMasq;
use crate::HAProxy;
use crate::{data::dhcpd::DhcpInterface, xml_utils::to_xml_str};
use log::error;
@@ -23,7 +22,7 @@ pub struct OPNsense {
pub load_balancer: Option<LoadBalancer>,
pub rrd: Option<RawXml>,
pub ntpd: Ntpd,
pub widgets: Option<Widgets>,
pub widgets: Widgets,
pub revision: Revision,
#[yaserde(rename = "OPNsense")]
pub opnsense: OPNsenseXmlSection,
@@ -46,7 +45,7 @@ pub struct OPNsense {
#[yaserde(rename = "Pischem")]
pub pischem: Option<Pischem>,
pub ifgroups: Ifgroups,
pub dnsmasq: Option<DnsMasq>,
pub dnsmasq: Option<RawXml>,
}
impl From<String> for OPNsense {
@@ -166,9 +165,9 @@ pub struct Sysctl {
#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)]
pub struct SysctlItem {
pub descr: Option<MaybeString>,
pub tunable: Option<String>,
pub value: Option<MaybeString>,
pub descr: MaybeString,
pub tunable: String,
pub value: MaybeString,
}
#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)]
@@ -183,8 +182,8 @@ pub struct System {
pub domain: String,
pub group: Vec<Group>,
pub user: Vec<User>,
pub nextuid: Option<u32>,
pub nextgid: Option<u32>,
pub nextuid: u32,
pub nextgid: u32,
pub timezone: String,
pub timeservers: String,
pub webgui: WebGui,
@@ -243,7 +242,6 @@ pub struct Ssh {
pub passwordauth: u8,
pub keysig: MaybeString,
pub permitrootlogin: u8,
pub rekeylimit: Option<MaybeString>,
}
#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)]
@@ -273,7 +271,6 @@ pub struct Group {
pub member: Vec<u32>,
#[yaserde(rename = "priv")]
pub priv_field: String,
pub source_networks: Option<MaybeString>,
}
#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)]
@@ -1509,7 +1506,7 @@ pub struct Vlans {
#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)]
pub struct Bridges {
pub bridged: Option<MaybeString>,
pub bridged: MaybeString,
}
#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)]

View File

@@ -20,7 +20,6 @@ russh-sftp = "2.0.6"
serde_json = "1.0.133"
tokio-util = { version = "0.7.13", features = ["codec"] }
tokio-stream = "0.1.17"
uuid.workspace = true
[dev-dependencies]
pretty_assertions.workspace = true

View File

@@ -4,7 +4,8 @@ use crate::{
config::{SshConfigManager, SshCredentials, SshOPNSenseShell},
error::Error,
modules::{
caddy::CaddyConfig, dhcp_legacy::DhcpConfigLegacyISC, dns::DnsConfig, dnsmasq::DhcpConfigDnsMasq, load_balancer::LoadBalancerConfig, tftp::TftpConfig
caddy::CaddyConfig, dhcp::DhcpConfig, dns::DnsConfig, load_balancer::LoadBalancerConfig,
tftp::TftpConfig,
},
};
use log::{debug, info, trace, warn};
@@ -42,12 +43,8 @@ impl Config {
})
}
pub fn dhcp_legacy_isc(&mut self) -> DhcpConfigLegacyISC {
DhcpConfigLegacyISC::new(&mut self.opnsense, self.shell.clone())
}
pub fn dhcp(&mut self) -> DhcpConfigDnsMasq {
DhcpConfigDnsMasq::new(&mut self.opnsense, self.shell.clone())
pub fn dhcp(&mut self) -> DhcpConfig {
DhcpConfig::new(&mut self.opnsense, self.shell.clone())
}
pub fn dns(&mut self) -> DnsConfig {
@@ -70,10 +67,6 @@ impl Config {
self.shell.upload_folder(source, destination).await
}
pub async fn upload_file_content(&self, path: &str, content: &str) -> Result<String, Error> {
self.shell.write_content_to_file(content, path).await
}
/// Checks in config file if system.firmware.plugins csv field contains the specified package
/// name.
///
@@ -207,7 +200,7 @@ impl Config {
#[cfg(test)]
mod tests {
use crate::config::{DummyOPNSenseShell, LocalFileConfigManager};
use crate::modules::dhcp_legacy::DhcpConfigLegacyISC;
use crate::modules::dhcp::DhcpConfig;
use std::fs;
use std::net::Ipv4Addr;
@@ -222,9 +215,6 @@ mod tests {
"src/tests/data/config-vm-test.xml",
"src/tests/data/config-structure.xml",
"src/tests/data/config-full-1.xml",
"src/tests/data/config-full-ncd0.xml",
"src/tests/data/config-full-25.7.xml",
"src/tests/data/config-full-25.7-dummy-dnsmasq-options.xml",
] {
let mut test_file_path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
test_file_path.push(path);
@@ -267,7 +257,7 @@ mod tests {
println!("Config {:?}", config);
let mut dhcp_config = DhcpConfigLegacyISC::new(&mut config.opnsense, shell);
let mut dhcp_config = DhcpConfig::new(&mut config.opnsense, shell);
dhcp_config
.add_static_mapping(
"00:00:00:00:00:00",

View File

@@ -9,7 +9,6 @@ use crate::Error;
pub trait OPNsenseShell: std::fmt::Debug + Send + Sync {
async fn exec(&self, command: &str) -> Result<String, Error>;
async fn write_content_to_temp_file(&self, content: &str) -> Result<String, Error>;
async fn write_content_to_file(&self, content: &str, filename: &str) -> Result<String, Error>;
async fn upload_folder(&self, source: &str, destination: &str) -> Result<String, Error>;
}
@@ -26,14 +25,6 @@ impl OPNsenseShell for DummyOPNSenseShell {
async fn write_content_to_temp_file(&self, _content: &str) -> Result<String, Error> {
unimplemented!("This is a dummy implementation");
}
async fn write_content_to_file(
&self,
_content: &str,
_filename: &str,
) -> Result<String, Error> {
unimplemented!("This is a dummy implementation");
}
async fn upload_folder(&self, _source: &str, _destination: &str) -> Result<String, Error> {
unimplemented!("This is a dummy implementation");
}

View File

@@ -44,11 +44,6 @@ impl OPNsenseShell for SshOPNSenseShell {
.unwrap()
.as_millis()
);
self.write_content_to_file(content, &temp_filename).await
}
async fn write_content_to_file(&self, content: &str, filename: &str) -> Result<String, Error> {
// TODO fix this to create the directory before uploading the file
let channel = self.get_ssh_channel().await?;
channel
.request_subsystem(true, "sftp")
@@ -58,10 +53,10 @@ impl OPNsenseShell for SshOPNSenseShell {
.await
.expect("Should acquire sftp subsystem");
let mut file = sftp.create(filename).await.unwrap();
let mut file = sftp.create(&temp_filename).await.unwrap();
file.write_all(content.as_bytes()).await?;
Ok(filename.to_string())
Ok(temp_filename)
}
async fn upload_folder(&self, source: &str, destination: &str) -> Result<String, Error> {

View File

@@ -1,3 +1,19 @@
use log::info;
use opnsense_config_xml::MaybeString;
use opnsense_config_xml::StaticMap;
use std::net::Ipv4Addr;
use std::sync::Arc;
use opnsense_config_xml::OPNsense;
use crate::config::OPNsenseShell;
use crate::Error;
pub struct DhcpConfig<'a> {
opnsense: &'a mut OPNsense,
opnsense_shell: Arc<dyn OPNsenseShell>,
}
#[derive(Debug)]
pub enum DhcpError {
InvalidMacAddress(String),
@@ -5,7 +21,6 @@ pub enum DhcpError {
IpAddressAlreadyMapped(String),
MacAddressAlreadyMapped(String),
IpAddressOutOfRange(String),
Configuration(String),
}
impl std::fmt::Display for DhcpError {
@@ -22,9 +37,158 @@ impl std::fmt::Display for DhcpError {
DhcpError::IpAddressOutOfRange(ip) => {
write!(f, "IP address {} is out of interface range", ip)
}
DhcpError::Configuration(msg) => f.write_str(&msg),
}
}
}
impl std::error::Error for DhcpError {}
impl<'a> DhcpConfig<'a> {
pub fn new(opnsense: &'a mut OPNsense, opnsense_shell: Arc<dyn OPNsenseShell>) -> Self {
Self {
opnsense,
opnsense_shell,
}
}
pub fn remove_static_mapping(&mut self, mac: &str) {
let lan_dhcpd = self.get_lan_dhcpd();
lan_dhcpd
.staticmaps
.retain(|static_entry| static_entry.mac != mac);
}
fn get_lan_dhcpd(&mut self) -> &mut opnsense_config_xml::DhcpInterface {
&mut self
.opnsense
.dhcpd
.elements
.iter_mut()
.find(|(name, _config)| name == "lan")
.expect("Interface lan should have dhcpd activated")
.1
}
pub fn add_static_mapping(
&mut self,
mac: &str,
ipaddr: Ipv4Addr,
hostname: &str,
) -> Result<(), DhcpError> {
let mac = mac.to_string();
let hostname = hostname.to_string();
let lan_dhcpd = self.get_lan_dhcpd();
let existing_mappings: &mut Vec<StaticMap> = &mut lan_dhcpd.staticmaps;
if !Self::is_valid_mac(&mac) {
return Err(DhcpError::InvalidMacAddress(mac));
}
// TODO validate that address is in subnet range
if existing_mappings.iter().any(|m| {
m.ipaddr
.parse::<Ipv4Addr>()
.expect("Mapping contains invalid ipv4")
== ipaddr
&& m.mac == mac
}) {
info!("Mapping already exists for {} [{}], skipping", ipaddr, mac);
return Ok(());
}
if existing_mappings.iter().any(|m| {
m.ipaddr
.parse::<Ipv4Addr>()
.expect("Mapping contains invalid ipv4")
== ipaddr
}) {
return Err(DhcpError::IpAddressAlreadyMapped(ipaddr.to_string()));
}
if existing_mappings.iter().any(|m| m.mac == mac) {
return Err(DhcpError::MacAddressAlreadyMapped(mac));
}
let static_map = StaticMap {
mac,
ipaddr: ipaddr.to_string(),
hostname,
descr: Default::default(),
winsserver: Default::default(),
dnsserver: Default::default(),
ntpserver: Default::default(),
};
existing_mappings.push(static_map);
Ok(())
}
fn is_valid_mac(mac: &str) -> bool {
let parts: Vec<&str> = mac.split(':').collect();
if parts.len() != 6 {
return false;
}
parts
.iter()
.all(|part| part.len() <= 2 && part.chars().all(|c| c.is_ascii_hexdigit()))
}
pub async fn get_static_mappings(&self) -> Result<Vec<StaticMap>, Error> {
let list_static_output = self
.opnsense_shell
.exec("configctl dhcpd list static")
.await?;
let value: serde_json::Value = serde_json::from_str(&list_static_output)
.unwrap_or_else(|_| panic!("Got invalid json from configctl {list_static_output}"));
let static_maps = value["dhcpd"]
.as_array()
.ok_or(Error::Command(format!(
"Invalid DHCP data from configctl command, got {list_static_output}"
)))?
.iter()
.map(|entry| StaticMap {
mac: entry["mac"].as_str().unwrap_or_default().to_string(),
ipaddr: entry["ipaddr"].as_str().unwrap_or_default().to_string(),
hostname: entry["hostname"].as_str().unwrap_or_default().to_string(),
descr: entry["descr"].as_str().map(MaybeString::from),
winsserver: MaybeString::default(),
dnsserver: MaybeString::default(),
ntpserver: MaybeString::default(),
})
.collect();
Ok(static_maps)
}
pub fn enable_netboot(&mut self) {
self.get_lan_dhcpd().netboot = Some(1);
}
pub fn set_next_server(&mut self, ip: Ipv4Addr) {
self.enable_netboot();
self.get_lan_dhcpd().nextserver = Some(ip.to_string());
self.get_lan_dhcpd().tftp = Some(ip.to_string());
}
pub fn set_boot_filename(&mut self, boot_filename: &str) {
self.enable_netboot();
self.get_lan_dhcpd().bootfilename = Some(boot_filename.to_string());
}
pub fn set_filename(&mut self, filename: &str) {
self.enable_netboot();
self.get_lan_dhcpd().filename = Some(filename.to_string());
}
pub fn set_filename64(&mut self, filename64: &str) {
self.enable_netboot();
self.get_lan_dhcpd().filename64 = Some(filename64.to_string());
}
pub fn set_filenameipxe(&mut self, filenameipxe: &str) {
self.enable_netboot();
self.get_lan_dhcpd().filenameipxe = Some(filenameipxe.to_string());
}
}

View File

@@ -1,166 +0,0 @@
use crate::modules::dhcp::DhcpError;
use log::info;
use opnsense_config_xml::MaybeString;
use opnsense_config_xml::StaticMap;
use std::net::Ipv4Addr;
use std::sync::Arc;
use opnsense_config_xml::OPNsense;
use crate::config::OPNsenseShell;
use crate::Error;
pub struct DhcpConfigLegacyISC<'a> {
opnsense: &'a mut OPNsense,
opnsense_shell: Arc<dyn OPNsenseShell>,
}
impl<'a> DhcpConfigLegacyISC<'a> {
pub fn new(opnsense: &'a mut OPNsense, opnsense_shell: Arc<dyn OPNsenseShell>) -> Self {
Self {
opnsense,
opnsense_shell,
}
}
pub fn remove_static_mapping(&mut self, mac: &str) {
let lan_dhcpd = self.get_lan_dhcpd();
lan_dhcpd
.staticmaps
.retain(|static_entry| static_entry.mac != mac);
}
fn get_lan_dhcpd(&mut self) -> &mut opnsense_config_xml::DhcpInterface {
&mut self
.opnsense
.dhcpd
.elements
.iter_mut()
.find(|(name, _config)| name == "lan")
.expect("Interface lan should have dhcpd activated")
.1
}
pub fn add_static_mapping(
&mut self,
mac: &str,
ipaddr: Ipv4Addr,
hostname: &str,
) -> Result<(), DhcpError> {
let mac = mac.to_string();
let hostname = hostname.to_string();
let lan_dhcpd = self.get_lan_dhcpd();
let existing_mappings: &mut Vec<StaticMap> = &mut lan_dhcpd.staticmaps;
if !Self::is_valid_mac(&mac) {
return Err(DhcpError::InvalidMacAddress(mac));
}
// TODO validate that address is in subnet range
if existing_mappings.iter().any(|m| {
m.ipaddr
.parse::<Ipv4Addr>()
.expect("Mapping contains invalid ipv4")
== ipaddr
&& m.mac == mac
}) {
info!("Mapping already exists for {} [{}], skipping", ipaddr, mac);
return Ok(());
}
if existing_mappings.iter().any(|m| {
m.ipaddr
.parse::<Ipv4Addr>()
.expect("Mapping contains invalid ipv4")
== ipaddr
}) {
return Err(DhcpError::IpAddressAlreadyMapped(ipaddr.to_string()));
}
if existing_mappings.iter().any(|m| m.mac == mac) {
return Err(DhcpError::MacAddressAlreadyMapped(mac));
}
let static_map = StaticMap {
mac,
ipaddr: ipaddr.to_string(),
hostname,
descr: Default::default(),
winsserver: Default::default(),
dnsserver: Default::default(),
ntpserver: Default::default(),
};
existing_mappings.push(static_map);
Ok(())
}
fn is_valid_mac(mac: &str) -> bool {
let parts: Vec<&str> = mac.split(':').collect();
if parts.len() != 6 {
return false;
}
parts
.iter()
.all(|part| part.len() <= 2 && part.chars().all(|c| c.is_ascii_hexdigit()))
}
pub async fn get_static_mappings(&self) -> Result<Vec<StaticMap>, Error> {
let list_static_output = self
.opnsense_shell
.exec("configctl dhcpd list static")
.await?;
let value: serde_json::Value = serde_json::from_str(&list_static_output)
.unwrap_or_else(|_| panic!("Got invalid json from configctl {list_static_output}"));
let static_maps = value["dhcpd"]
.as_array()
.ok_or(Error::Command(format!(
"Invalid DHCP data from configctl command, got {list_static_output}"
)))?
.iter()
.map(|entry| StaticMap {
mac: entry["mac"].as_str().unwrap_or_default().to_string(),
ipaddr: entry["ipaddr"].as_str().unwrap_or_default().to_string(),
hostname: entry["hostname"].as_str().unwrap_or_default().to_string(),
descr: entry["descr"].as_str().map(MaybeString::from),
winsserver: MaybeString::default(),
dnsserver: MaybeString::default(),
ntpserver: MaybeString::default(),
})
.collect();
Ok(static_maps)
}
pub fn enable_netboot(&mut self) {
self.get_lan_dhcpd().netboot = Some(1);
}
pub fn set_next_server(&mut self, ip: Ipv4Addr) {
self.enable_netboot();
self.get_lan_dhcpd().nextserver = Some(ip.to_string());
self.get_lan_dhcpd().tftp = Some(ip.to_string());
}
pub fn set_boot_filename(&mut self, boot_filename: &str) {
self.enable_netboot();
self.get_lan_dhcpd().bootfilename = Some(boot_filename.to_string());
}
pub fn set_filename(&mut self, filename: &str) {
self.enable_netboot();
self.get_lan_dhcpd().filename = Some(filename.to_string());
}
pub fn set_filename64(&mut self, filename64: &str) {
self.enable_netboot();
self.get_lan_dhcpd().filename64 = Some(filename64.to_string());
}
pub fn set_filenameipxe(&mut self, filenameipxe: &str) {
self.enable_netboot();
self.get_lan_dhcpd().filenameipxe = Some(filenameipxe.to_string());
}
}

View File

@@ -1,198 +0,0 @@
// dnsmasq.rs
use crate::modules::dhcp::DhcpError;
use log::{debug, info};
use opnsense_config_xml::dnsmasq::{DhcpBoot, DhcpOptions, DnsMasq};
use opnsense_config_xml::{MaybeString, StaticMap};
use std::net::Ipv4Addr;
use std::sync::Arc;
use uuid::Uuid;
use opnsense_config_xml::OPNsense;
use crate::config::OPNsenseShell;
use crate::Error;
pub struct DhcpConfigDnsMasq<'a> {
opnsense: &'a mut OPNsense,
opnsense_shell: Arc<dyn OPNsenseShell>,
}
const DNS_MASQ_PXE_CONFIG_FILE: &str = "/usr/local/etc/dnsmasq.conf.d/pxe.conf";
impl<'a> DhcpConfigDnsMasq<'a> {
pub fn new(opnsense: &'a mut OPNsense, opnsense_shell: Arc<dyn OPNsenseShell>) -> Self {
Self {
opnsense,
opnsense_shell,
}
}
/// Removes a static mapping by its MAC address.
/// Static mappings are stored in the <dhcpd> section of the config, shared with the ISC module.
pub fn remove_static_mapping(&mut self, mac: &str) {
let lan_dhcpd = self.get_lan_dhcpd();
lan_dhcpd
.staticmaps
.retain(|static_entry| static_entry.mac != mac);
}
/// Retrieves a mutable reference to the LAN interface's DHCP configuration.
/// This is located in the shared <dhcpd> section of the config.
fn get_lan_dhcpd(&mut self) -> &mut opnsense_config_xml::DhcpInterface {
&mut self
.opnsense
.dhcpd
.elements
.iter_mut()
.find(|(name, _config)| name == "lan")
.expect("Interface lan should have dhcpd activated")
.1
}
fn dnsmasq(&mut self) -> &mut DnsMasq {
self.opnsense
.dnsmasq
.as_mut()
.expect("Dnsmasq config should exist. Maybe it is not installed yet")
}
/// Adds a new static DHCP mapping.
/// Validates the MAC address and checks for existing mappings to prevent conflicts.
pub fn add_static_mapping(
&mut self,
mac: &str,
ipaddr: Ipv4Addr,
hostname: &str,
) -> Result<(), DhcpError> {
let mac = mac.to_string();
let hostname = hostname.to_string();
let lan_dhcpd = self.get_lan_dhcpd();
let existing_mappings: &mut Vec<StaticMap> = &mut lan_dhcpd.staticmaps;
if !Self::is_valid_mac(&mac) {
return Err(DhcpError::InvalidMacAddress(mac));
}
// TODO: Validate that the IP address is within a configured DHCP range.
if existing_mappings
.iter()
.any(|m| m.ipaddr == ipaddr.to_string() && m.mac == mac)
{
info!("Mapping already exists for {} [{}], skipping", ipaddr, mac);
return Ok(());
}
if existing_mappings
.iter()
.any(|m| m.ipaddr == ipaddr.to_string())
{
return Err(DhcpError::IpAddressAlreadyMapped(ipaddr.to_string()));
}
if existing_mappings.iter().any(|m| m.mac == mac) {
return Err(DhcpError::MacAddressAlreadyMapped(mac));
}
let static_map = StaticMap {
mac,
ipaddr: ipaddr.to_string(),
hostname: hostname,
..Default::default()
};
existing_mappings.push(static_map);
Ok(())
}
/// Helper function to validate a MAC address format.
fn is_valid_mac(mac: &str) -> bool {
let parts: Vec<&str> = mac.split(':').collect();
if parts.len() != 6 {
return false;
}
parts
.iter()
.all(|part| part.len() <= 2 && part.chars().all(|c| c.is_ascii_hexdigit()))
}
/// Retrieves the list of current static mappings by shelling out to `configctl`.
/// This provides the real-time state from the running system.
pub async fn get_static_mappings(&self) -> Result<Vec<StaticMap>, Error> {
let list_static_output = self
.opnsense_shell
.exec("configctl dhcpd list static")
.await?;
let value: serde_json::Value = serde_json::from_str(&list_static_output)
.unwrap_or_else(|_| panic!("Got invalid json from configctl {list_static_output}"));
let static_maps = value["dhcpd"]
.as_array()
.ok_or(Error::Command(format!(
"Invalid DHCP data from configctl command, got {list_static_output}"
)))?
.iter()
.map(|entry| StaticMap {
mac: entry["mac"].as_str().unwrap_or_default().to_string(),
ipaddr: entry["ipaddr"].as_str().unwrap_or_default().to_string(),
hostname: entry["hostname"].as_str().unwrap_or_default().to_string(),
descr: entry["descr"].as_str().map(MaybeString::from),
..Default::default()
})
.collect();
Ok(static_maps)
}
pub async fn set_pxe_options(
&self,
tftp_ip: Option<String>,
bios_filename: String,
efi_filename: String,
ipxe_filename: String,
) -> Result<(), DhcpError> {
// As of writing this opnsense does not support negative tags, and the dnsmasq config is a
// bit complicated anyways. So we are writing directly a dnsmasq config file to
// /usr/local/etc/dnsmasq.conf.d
let tftp_str = tftp_ip.map_or(String::new(), |i| format!(",{i},{i}"));
let config = format!(
"
# Add tag ipxe to dhcp requests with user class (77) = iPXE
dhcp-match=set:ipxe,77,iPXE
# Add tag bios to dhcp requests with arch (93) = 0
dhcp-match=set:bios,93,0
# Add tag efi to dhcp requests with arch (93) = 7
dhcp-match=set:efi,93,7
# Provide ipxe efi file to uefi but NOT ipxe clients
dhcp-boot=tag:efi,tag:!ipxe,{efi_filename}{tftp_str}
# Provide ipxe boot script to ipxe clients
dhcp-boot=tag:ipxe,{ipxe_filename}{tftp_str}
# Provide undionly to legacy bios clients
dhcp-boot=tag:bios,{bios_filename}{tftp_str}
"
);
info!("Writing configuration file to {DNS_MASQ_PXE_CONFIG_FILE}");
debug!("Content:\n{config}");
self.opnsense_shell
.write_content_to_file(&config, DNS_MASQ_PXE_CONFIG_FILE)
.await
.map_err(|e| {
DhcpError::Configuration(format!(
"Could not configure pxe for dhcp because of : {e}"
))
})?;
info!("Restarting dnsmasq to apply changes");
self.opnsense_shell.exec("configctl dnsmasq restart").await
.map_err(|e| {
DhcpError::Configuration(format!(
"Restarting dnsmasq failed : {e}"
))
})?;
Ok(())
}
}

View File

@@ -1,7 +1,5 @@
pub mod caddy;
pub mod dhcp_legacy;
pub mod dhcp;
pub mod dns;
pub mod load_balancer;
pub mod tftp;
pub mod dhcp;
pub mod dnsmasq;

View File

@@ -1,896 +0,0 @@
<?xml version="1.0"?>
<opnsense>
<theme>opnsense</theme>
<sysctl>
<item/>
</sysctl>
<system>
<serialspeed>115200</serialspeed>
<primaryconsole>serial</primaryconsole>
<optimization>normal</optimization>
<hostname>OPNsense</hostname>
<domain>testpxe.harmony.mcd</domain>
<group>
<name>admins</name>
<description>System Administrators</description>
<scope>system</scope>
<gid>1999</gid>
<member>0</member>
<priv>page-all</priv>
<source_networks/>
</group>
<user>
<name>root</name>
<descr>System Administrator</descr>
<scope>system</scope>
<password>$2y$10$YRVoF4SgskIsrXOvOQjGieB9XqHPRra9R7d80B3BZdbY/j21TwBfS</password>
<pwd_changed_at/>
<uid>0</uid>
<disabled>0</disabled>
<landing_page/>
<comment/>
<email/>
<apikeys/>
<priv/>
<language/>
<expires/>
<authorizedkeys/>
<dashboard/>
<otp_seed/>
<shell/>
</user>
<timezone>Etc/UTC</timezone>
<timeservers>0.opnsense.pool.ntp.org 1.opnsense.pool.ntp.org 2.opnsense.pool.ntp.org 3.opnsense.pool.ntp.org</timeservers>
<webgui>
<protocol>https</protocol>
<ssl-certref>68a72b6f7f776</ssl-certref>
<port/>
<ssl-ciphers/>
<interfaces/>
<compression/>
</webgui>
<usevirtualterminal>1</usevirtualterminal>
<disablenatreflection>yes</disablenatreflection>
<disableconsolemenu>1</disableconsolemenu>
<disablevlanhwfilter>1</disablevlanhwfilter>
<disablechecksumoffloading>1</disablechecksumoffloading>
<disablesegmentationoffloading>1</disablesegmentationoffloading>
<disablelargereceiveoffloading>1</disablelargereceiveoffloading>
<ipv6allow>1</ipv6allow>
<powerd_ac_mode>hadp</powerd_ac_mode>
<powerd_battery_mode>hadp</powerd_battery_mode>
<powerd_normal_mode>hadp</powerd_normal_mode>
<bogons>
<interval>monthly</interval>
</bogons>
<pf_share_forward>1</pf_share_forward>
<lb_use_sticky>1</lb_use_sticky>
<ssh>
<group>admins</group>
<noauto>1</noauto>
<interfaces/>
<kex/>
<ciphers/>
<macs/>
<keys/>
<enabled>enabled</enabled>
<passwordauth>1</passwordauth>
<keysig/>
<permitrootlogin>1</permitrootlogin>
<rekeylimit/>
</ssh>
<rrdbackup>-1</rrdbackup>
<netflowbackup>-1</netflowbackup>
<firmware version="1.0.1">
<mirror/>
<flavour/>
<plugins>os-tftp</plugins>
<type/>
<subscription/>
<reboot>0</reboot>
</firmware>
<language>en_US</language>
<dnsserver/>
<dnsallowoverride>1</dnsallowoverride>
<dnsallowoverride_exclude/>
</system>
<interfaces>
<wan>
<if>vtnet0</if>
<mtu/>
<enable>1</enable>
<spoofmac/>
<mss/>
<ipaddr>dhcp</ipaddr>
<dhcphostname/>
<blockpriv>0</blockpriv>
<blockbogons>1</blockbogons>
<subnet/>
<ipaddrv6>dhcp6</ipaddrv6>
<dhcp6-ia-pd-len>0</dhcp6-ia-pd-len>
<gateway/>
<media/>
<mediaopt/>
</wan>
<lan>
<if>vtnet1</if>
<enable>1</enable>
<ipaddr>192.168.1.1</ipaddr>
<subnet>24</subnet>
<ipaddrv6>track6</ipaddrv6>
<subnetv6>64</subnetv6>
<media/>
<mediaopt/>
<track6-interface>wan</track6-interface>
<track6-prefix-id>0</track6-prefix-id>
</lan>
<lo0>
<internal_dynamic>1</internal_dynamic>
<if>lo0</if>
<descr>Loopback</descr>
<enable>1</enable>
<ipaddr>127.0.0.1</ipaddr>
<type>none</type>
<virtual>1</virtual>
<subnet>8</subnet>
<ipaddrv6>::1</ipaddrv6>
<subnetv6>128</subnetv6>
</lo0>
</interfaces>
<dhcpd/>
<snmpd>
<syslocation/>
<syscontact/>
<rocommunity>public</rocommunity>
</snmpd>
<syslog/>
<nat>
<outbound>
<mode>automatic</mode>
</outbound>
</nat>
<filter>
<rule>
<type>pass</type>
<interface>lan</interface>
<ipprotocol>inet</ipprotocol>
<descr>Default allow LAN to any rule</descr>
<source>
<network>lan</network>
</source>
<destination>
<any/>
</destination>
</rule>
<rule>
<type>pass</type>
<interface>lan</interface>
<ipprotocol>inet6</ipprotocol>
<descr>Default allow LAN IPv6 to any rule</descr>
<source>
<network>lan</network>
</source>
<destination>
<any/>
</destination>
</rule>
</filter>
<rrd>
<enable/>
</rrd>
<ntpd>
<prefer>0.opnsense.pool.ntp.org</prefer>
</ntpd>
<revision>
<username>root@192.168.1.5</username>
<description>/api/dnsmasq/settings/set made changes</description>
<time>1755800176.40</time>
</revision>
<OPNsense>
<captiveportal version="1.0.4">
<zones/>
<templates/>
</captiveportal>
<cron version="1.0.4">
<jobs/>
</cron>
<Netflow version="1.0.1">
<capture>
<interfaces/>
<egress_only/>
<version>v9</version>
<targets/>
</capture>
<collect>
<enable>0</enable>
</collect>
<activeTimeout>1800</activeTimeout>
<inactiveTimeout>15</inactiveTimeout>
</Netflow>
<Firewall>
<Lvtemplate version="0.0.1">
<templates/>
</Lvtemplate>
<Category version="1.0.0">
<categories/>
</Category>
<Filter version="1.0.4">
<rules/>
<snatrules/>
<npt/>
<onetoone/>
</Filter>
<Alias version="1.0.1">
<geoip>
<url/>
</geoip>
<aliases/>
</Alias>
</Firewall>
<IDS version="1.1.0">
<rules/>
<policies/>
<userDefinedRules/>
<files/>
<fileTags/>
<general>
<enabled>0</enabled>
<ips>0</ips>
<promisc>0</promisc>
<interfaces>wan</interfaces>
<homenet>192.168.0.0/16,10.0.0.0/8,172.16.0.0/12</homenet>
<defaultPacketSize/>
<UpdateCron/>
<AlertLogrotate>W0D23</AlertLogrotate>
<AlertSaveLogs>4</AlertSaveLogs>
<MPMAlgo/>
<detect>
<Profile/>
<toclient_groups/>
<toserver_groups/>
</detect>
<syslog>0</syslog>
<syslog_eve>0</syslog_eve>
<LogPayload>0</LogPayload>
<verbosity/>
<eveLog>
<http>
<enable>0</enable>
<extended>0</extended>
<dumpAllHeaders/>
</http>
<tls>
<enable>0</enable>
<extended>0</extended>
<sessionResumption>0</sessionResumption>
<custom/>
</tls>
</eveLog>
</general>
</IDS>
<IPsec version="1.0.4">
<general>
<enabled/>
<preferred_oldsa>0</preferred_oldsa>
<disablevpnrules>0</disablevpnrules>
<passthrough_networks/>
<user_source/>
<local_group/>
</general>
<keyPairs/>
<preSharedKeys/>
<charon>
<max_ikev1_exchanges/>
<threads>16</threads>
<ikesa_table_size>32</ikesa_table_size>
<ikesa_table_segments>4</ikesa_table_segments>
<init_limit_half_open>1000</init_limit_half_open>
<ignore_acquire_ts>1</ignore_acquire_ts>
<install_routes>0</install_routes>
<cisco_unity>0</cisco_unity>
<make_before_break>0</make_before_break>
<retransmit_tries/>
<retransmit_timeout/>
<retransmit_base/>
<retransmit_jitter/>
<retransmit_limit/>
<syslog>
<daemon>
<ike_name>1</ike_name>
<log_level>0</log_level>
<app>1</app>
<asn>1</asn>
<cfg>1</cfg>
<chd>1</chd>
<dmn>1</dmn>
<enc>1</enc>
<esp>1</esp>
<ike>1</ike>
<imc>1</imc>
<imv>1</imv>
<job>1</job>
<knl>1</knl>
<lib>1</lib>
<mgr>1</mgr>
<net>1</net>
<pts>1</pts>
<tls>1</tls>
<tnc>1</tnc>
</daemon>
</syslog>
<plugins>
<attr>
<subnet/>
<split-include/>
<x_28674/>
<x_28675/>
<x_28672/>
<x_28673>0</x_28673>
<x_28679/>
<dns/>
<nbns/>
</attr>
<eap-radius>
<servers/>
<accounting>0</accounting>
<class_group>0</class_group>
</eap-radius>
<xauth-pam>
<pam_service>ipsec</pam_service>
<session>0</session>
<trim_email>1</trim_email>
</xauth-pam>
</plugins>
</charon>
</IPsec>
<Interfaces>
<vxlans version="1.0.2"/>
<loopbacks version="1.0.0"/>
<neighbors version="1.0.0"/>
</Interfaces>
<Kea>
<dhcp4 version="1.0.4" persisted_at="1755786069.95">
<general>
<enabled>0</enabled>
<manual_config>0</manual_config>
<interfaces/>
<valid_lifetime>4000</valid_lifetime>
<fwrules>1</fwrules>
<dhcp_socket_type>raw</dhcp_socket_type>
</general>
<ha>
<enabled>0</enabled>
<this_server_name/>
<max_unacked_clients>2</max_unacked_clients>
</ha>
<subnets/>
<reservations/>
<ha_peers/>
</dhcp4>
<ctrl_agent version="0.0.1" persisted_at="1755786069.95">
<general>
<enabled>0</enabled>
<http_host>127.0.0.1</http_host>
<http_port>8000</http_port>
</general>
</ctrl_agent>
<dhcp6 version="1.0.0" persisted_at="1755786069.95">
<general>
<enabled>0</enabled>
<manual_config>0</manual_config>
<interfaces/>
<valid_lifetime>4000</valid_lifetime>
<fwrules>1</fwrules>
</general>
<ha>
<enabled>0</enabled>
<this_server_name/>
<max_unacked_clients>2</max_unacked_clients>
</ha>
<subnets/>
<reservations/>
<pd_pools/>
<ha_peers/>
</dhcp6>
</Kea>
<monit version="1.0.13">
<general>
<enabled>0</enabled>
<interval>120</interval>
<startdelay>120</startdelay>
<mailserver>127.0.0.1</mailserver>
<port>25</port>
<username/>
<password/>
<ssl>0</ssl>
<sslversion>auto</sslversion>
<sslverify>1</sslverify>
<logfile/>
<statefile/>
<eventqueuePath/>
<eventqueueSlots/>
<httpdEnabled>0</httpdEnabled>
<httpdUsername>root</httpdUsername>
<httpdPassword/>
<httpdPort>2812</httpdPort>
<httpdAllow/>
<mmonitUrl/>
<mmonitTimeout>5</mmonitTimeout>
<mmonitRegisterCredentials>1</mmonitRegisterCredentials>
</general>
<alert uuid="ce8ca7d9-66ab-41d5-acea-598f4803e8ba">
<enabled>0</enabled>
<recipient>root@localhost.local</recipient>
<noton>0</noton>
<events/>
<format/>
<reminder/>
<description/>
</alert>
<service uuid="dc3b9298-4a56-4c45-bd61-be2fdb103383">
<enabled>1</enabled>
<name>$HOST</name>
<description/>
<type>system</type>
<pidfile/>
<match/>
<path/>
<timeout>300</timeout>
<starttimeout>30</starttimeout>
<address/>
<interface/>
<start/>
<stop/>
<tests>cfed35dc-f74b-417d-9ed9-682c5de96495,f961277a-07f1-49a4-90ee-bb15738d9ebb,30b2cce2-f650-4e44-a3e2-ee53886cda3f,3c86136f-35a4-4126-865b-82732c6542d9</tests>
<depends/>
<polltime/>
</service>
<service uuid="b4d5bdb4-206d-4afe-8d86-377ffbbdb2ec">
<enabled>1</enabled>
<name>RootFs</name>
<description/>
<type>filesystem</type>
<pidfile/>
<match/>
<path>/</path>
<timeout>300</timeout>
<starttimeout>30</starttimeout>
<address/>
<interface/>
<start/>
<stop/>
<tests>fbb8dfe2-b9ad-4730-a0f3-41d7ecda6289</tests>
<depends/>
<polltime/>
</service>
<service uuid="f96e3cbb-6c98-4d20-8337-bab717d4ab54">
<enabled>0</enabled>
<name>carp_status_change</name>
<description/>
<type>custom</type>
<pidfile/>
<match/>
<path>/usr/local/opnsense/scripts/OPNsense/Monit/carp_status</path>
<timeout>300</timeout>
<starttimeout>30</starttimeout>
<address/>
<interface/>
<start/>
<stop/>
<tests>11ceca8a-dff8-45e0-9dc5-ed80dc4b3947</tests>
<depends/>
<polltime/>
</service>
<service uuid="69bbd4d5-3a50-42a7-ab64-050450504038">
<enabled>0</enabled>
<name>gateway_alert</name>
<description/>
<type>custom</type>
<pidfile/>
<match/>
<path>/usr/local/opnsense/scripts/OPNsense/Monit/gateway_alert</path>
<timeout>300</timeout>
<starttimeout>30</starttimeout>
<address/>
<interface/>
<start/>
<stop/>
<tests>fad1f465-4a92-4b93-be66-59d7059b8779</tests>
<depends/>
<polltime/>
</service>
<test uuid="2bd5d8c0-6a4a-430b-b953-34214a107ccf">
<name>Ping</name>
<type>NetworkPing</type>
<condition>failed ping</condition>
<action>alert</action>
<path/>
</test>
<test uuid="0f06ffff-9bfa-463d-b75e-f7195cd8dcab">
<name>NetworkLink</name>
<type>NetworkInterface</type>
<condition>failed link</condition>
<action>alert</action>
<path/>
</test>
<test uuid="79b119ce-10e0-4a6a-bd1a-b0be371d0fd7">
<name>NetworkSaturation</name>
<type>NetworkInterface</type>
<condition>saturation is greater than 75%</condition>
<action>alert</action>
<path/>
</test>
<test uuid="cfed35dc-f74b-417d-9ed9-682c5de96495">
<name>MemoryUsage</name>
<type>SystemResource</type>
<condition>memory usage is greater than 75%</condition>
<action>alert</action>
<path/>
</test>
<test uuid="f961277a-07f1-49a4-90ee-bb15738d9ebb">
<name>CPUUsage</name>
<type>SystemResource</type>
<condition>cpu usage is greater than 75%</condition>
<action>alert</action>
<path/>
</test>
<test uuid="30b2cce2-f650-4e44-a3e2-ee53886cda3f">
<name>LoadAvg1</name>
<type>SystemResource</type>
<condition>loadavg (1min) is greater than 4</condition>
<action>alert</action>
<path/>
</test>
<test uuid="3c86136f-35a4-4126-865b-82732c6542d9">
<name>LoadAvg5</name>
<type>SystemResource</type>
<condition>loadavg (5min) is greater than 3</condition>
<action>alert</action>
<path/>
</test>
<test uuid="27e91c6f-3e8e-4570-bb3a-27f46dd301a7">
<name>LoadAvg15</name>
<type>SystemResource</type>
<condition>loadavg (15min) is greater than 2</condition>
<action>alert</action>
<path/>
</test>
<test uuid="fbb8dfe2-b9ad-4730-a0f3-41d7ecda6289">
<name>SpaceUsage</name>
<type>SpaceUsage</type>
<condition>space usage is greater than 75%</condition>
<action>alert</action>
<path/>
</test>
<test uuid="11ceca8a-dff8-45e0-9dc5-ed80dc4b3947">
<name>ChangedStatus</name>
<type>ProgramStatus</type>
<condition>changed status</condition>
<action>alert</action>
<path/>
</test>
<test uuid="fad1f465-4a92-4b93-be66-59d7059b8779">
<name>NonZeroStatus</name>
<type>ProgramStatus</type>
<condition>status != 0</condition>
<action>alert</action>
<path/>
</test>
</monit>
<OpenVPNExport version="0.0.1">
<servers/>
</OpenVPNExport>
<Syslog version="1.0.2">
<general>
<enabled>1</enabled>
<loglocal>1</loglocal>
<maxpreserve>31</maxpreserve>
<maxfilesize/>
</general>
<destinations/>
</Syslog>
<TrafficShaper version="1.0.3" persisted_at="1755786069.77">
<pipes/>
<queues/>
<rules/>
</TrafficShaper>
<unboundplus version="1.0.12">
<general>
<enabled>1</enabled>
<port>53</port>
<stats>0</stats>
<active_interface/>
<dnssec>0</dnssec>
<dns64>0</dns64>
<dns64prefix/>
<noarecords>0</noarecords>
<regdhcp>0</regdhcp>
<regdhcpdomain/>
<regdhcpstatic>0</regdhcpstatic>
<noreglladdr6>0</noreglladdr6>
<noregrecords>0</noregrecords>
<txtsupport>0</txtsupport>
<cacheflush>0</cacheflush>
<local_zone_type>transparent</local_zone_type>
<outgoing_interface/>
<enable_wpad>0</enable_wpad>
</general>
<advanced>
<hideidentity>0</hideidentity>
<hideversion>0</hideversion>
<prefetch>0</prefetch>
<prefetchkey>0</prefetchkey>
<dnssecstripped>0</dnssecstripped>
<aggressivensec>1</aggressivensec>
<serveexpired>0</serveexpired>
<serveexpiredreplyttl/>
<serveexpiredttl/>
<serveexpiredttlreset>0</serveexpiredttlreset>
<serveexpiredclienttimeout/>
<qnameminstrict>0</qnameminstrict>
<extendedstatistics>0</extendedstatistics>
<logqueries>0</logqueries>
<logreplies>0</logreplies>
<logtagqueryreply>0</logtagqueryreply>
<logservfail>0</logservfail>
<loglocalactions>0</loglocalactions>
<logverbosity>1</logverbosity>
<valloglevel>0</valloglevel>
<privatedomain/>
<privateaddress>0.0.0.0/8,10.0.0.0/8,100.64.0.0/10,169.254.0.0/16,172.16.0.0/12,192.0.2.0/24,192.168.0.0/16,198.18.0.0/15,198.51.100.0/24,203.0.113.0/24,233.252.0.0/24,::1/128,2001:db8::/32,fc00::/8,fd00::/8,fe80::/10</privateaddress>
<insecuredomain/>
<msgcachesize/>
<rrsetcachesize/>
<outgoingnumtcp/>
<incomingnumtcp/>
<numqueriesperthread/>
<outgoingrange/>
<jostletimeout/>
<discardtimeout/>
<cachemaxttl/>
<cachemaxnegativettl/>
<cacheminttl/>
<infrahostttl/>
<infrakeepprobing>0</infrakeepprobing>
<infracachenumhosts/>
<unwantedreplythreshold/>
</advanced>
<acls>
<default_action>allow</default_action>
</acls>
<dnsbl>
<enabled>0</enabled>
<safesearch>0</safesearch>
<type/>
<lists/>
<whitelists/>
<blocklists/>
<wildcards/>
<address/>
<nxdomain>0</nxdomain>
</dnsbl>
<forwarding>
<enabled>0</enabled>
</forwarding>
<dots/>
<hosts/>
<aliases/>
</unboundplus>
<DHCRelay version="1.0.1" persisted_at="1755786069.97"/>
<trust>
<general version="1.0.1" persisted_at="1755786070.08">
<store_intermediate_certs>0</store_intermediate_certs>
<install_crls>0</install_crls>
<fetch_crls>0</fetch_crls>
<enable_legacy_sect>1</enable_legacy_sect>
<enable_config_constraints>0</enable_config_constraints>
<CipherString/>
<Ciphersuites/>
<SignatureAlgorithms/>
<groups/>
<MinProtocol/>
<MinProtocol_DTLS/>
</general>
</trust>
<tftp>
<general version="0.0.1">
<enabled>1</enabled>
<listen>192.168.1.1</listen>
</general>
</tftp>
<wireguard>
<general version="0.0.1">
<enabled>0</enabled>
</general>
<server version="1.0.0">
<servers/>
</server>
<client version="1.0.0">
<clients/>
</client>
</wireguard>
<Swanctl version="1.0.0">
<Connections/>
<locals/>
<remotes/>
<children/>
<Pools/>
<VTIs/>
<SPDs/>
</Swanctl>
<OpenVPN version="1.0.1">
<Overwrites/>
<Instances/>
<StaticKeys/>
</OpenVPN>
<Gateways version="1.0.0" persisted_at="1755786217.76"/>
</OPNsense>
<staticroutes version="1.0.0"/>
<ca/>
<cert>
<refid>68a72b6f7f776</refid>
<descr>Web GUI TLS certificate</descr>
<crt>LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUhFakNDQlBxZ0F3SUJBZ0lVUkZqWUQ0Z1U0bzRNZGdiN2pIc29KNU9GVGFnd0RRWUpLb1pJaHZjTkFRRUwKQlFBd2dZWXhHakFZQmdOVkJBTU1FVTlRVG5ObGJuTmxMbWx1ZEdWeWJtRnNNUXN3Q1FZRFZRUUdFd0pPVERFVgpNQk1HQTFVRUNBd01XblZwWkMxSWIyeHNZVzVrTVJVd0V3WURWUVFIREF4TmFXUmtaV3hvWVhKdWFYTXhMVEFyCkJnTlZCQW9NSkU5UVRuTmxibk5sSUhObGJHWXRjMmxuYm1Wa0lIZGxZaUJqWlhKMGFXWnBZMkYwWlRBZUZ3MHkKTlRBNE1qRXhOREl4TXpaYUZ3MHlOakE1TWpJeE5ESXhNelphTUlHR01Sb3dHQVlEVlFRRERCRlBVRTV6Wlc1egpaUzVwYm5SbGNtNWhiREVMTUFrR0ExVUVCaE1DVGt3eEZUQVRCZ05WQkFnTURGcDFhV1F0U0c5c2JHRnVaREVWCk1CTUdBMVVFQnd3TVRXbGtaR1ZzYUdGeWJtbHpNUzB3S3dZRFZRUUtEQ1JQVUU1elpXNXpaU0J6Wld4bUxYTnAKWjI1bFpDQjNaV0lnWTJWeWRHbG1hV05oZEdVd2dnSWlNQTBHQ1NxR1NJYjNEUUVCQVFVQUE0SUNEd0F3Z2dJSwpBb0lDQVFDbENkeFJ3ZWJQQkxvYlVORnYvL2t3TEdKWExweDl6OFFHV2lyWTNpamVDeUxDQ0FwczBLaE1adTNRClhkczMranppbDRnSE96L0hvUEo5Z0xxMy9FYnR4cE9ENWkvQzZwbXc3SGM1M2tTQ3JCK2tlWUFnVWZ1aDU3MzAKZyt3cGc5RDQzaHFBNzF1L3F0ZC95eitnTVJnTWdZMndEK3ZWQWRrdGxVSWlmN2piTmR1RDRGMmdkL0gwbzljWApEUm5zMzNNQVptTkZwajN4QWFwQi9RWnhKV1JMZ1J5K1A5MWcyZEZFNzhNaWY4ZTRNSCtrU29ndzIwVG1JbmpzCitKdEVTc0xQZmx2eUZUa0lkTVdFbURWOG1HUk5hNXVoYXlEbVNEUU9xV0NUTlZHV3ZVWjZTRnJRZ1Q1MDBEdXgKWnRtYlhGdEVqRzlIaGd5SW5QT0dKbWYzTWVzS3dYclVNMW1BenVCRVBFR0lwOTc3UTY2SitpTDYzWTUvTTB3aAphMGVVNGppNTVRQnJOQjlaWjJsa080bGU2TXdmZm50c29JakMrVDh5RW5tbW5nQTlTdWNPRW9CcFFhd3cvRGhOCmtSNGk4TUptR1JNdmpLazlHVzZ3Z2VNVThJVDhKZDRjTmJOVzdFSGpzV08xN1luTVhIMEUxOVZqa2d1R3dIODAKZ3ZROGtzTmV4WVA3WWo0b0VycnRKbWVhWU8wbFVkV0tGektNdS8va0UvNG5HK0h4emlRUnA5QmdyTURNYks4ZgpkM29mY2tqZFZTTW9Vc1FJaWlmdTFMK1I4V1Y3K3hsTzdTWS80dGk3Y28zcjNXRTYyVlE4Vk9QMVphcStWRFpvClNIMVRCa0lTSU5paVJFRzhZSDQvRHJwNWZ2dHBPcERBRGN1TGdDNDJHcExmS1pwVEtRSURBUUFCbzRJQmREQ0MKQVhBd0NRWURWUjBUQkFJd0FEQVJCZ2xnaGtnQmh2aENBUUVFQkFNQ0JrQXdOQVlKWUlaSUFZYjRRZ0VOQkNjVwpKVTlRVG5ObGJuTmxJRWRsYm1WeVlYUmxaQ0JUWlhKMlpYSWdRMlZ5ZEdsbWFXTmhkR1V3SFFZRFZSME9CQllFCkZIdUVQK05yYlorZWdMdWZVSUFKaUo2M1c4SDFNSUd3QmdOVkhTTUVnYWd3Z2FXaGdZeWtnWWt3Z1lZeEdqQVkKQmdOVkJBTU1FVTlRVG5ObGJuTmxMbWx1ZEdWeWJtRnNNUXN3Q1FZRFZRUUdFd0pPVERFVk1CTUdBMVVFQ0F3TQpXblZwWkMxSWIyeHNZVzVrTVJVd0V3WURWUVFIREF4TmFXUmtaV3hvWVhKdWFYTXhMVEFyQmdOVkJBb01KRTlRClRuTmxibk5sSUhObGJHWXRjMmxuYm1Wa0lIZGxZaUJqWlhKMGFXWnBZMkYwWllJVVJGallENGdVNG80TWRnYjcKakhzb0o1T0ZUYWd3SFFZRFZSMGxCQll3RkFZSUt3WUJCUVVIQXdFR0NDc0dBUVVGQ0FJQ01Bc0dBMVVkRHdRRQpBd0lGb0RBY0JnTlZIUkVFRlRBVGdoRlBVRTV6Wlc1elpTNXBiblJsY201aGJEQU5CZ2txaGtpRzl3MEJBUXNGCkFBT0NBZ0VBV2JzM2MwSXYwcEd3Y0wvUmRlbnBiZVJHQ3FsODY0V1ZITEtMZzJIR3BkKytJdmRFcHJEZkZ3SCsKdHdOd2VrZTlXUEtYa20vUkZDWE5DQmVLNjkxeURVWCtCNUJOMjMvSks5N1lzRVdtMURIV3FvSDE1WmdqelZ0QQp2d2JmbnRQdlhCWU1wV2ZQY0Zua0hjN3pxUjI3RzBEZHFUeGg2TjhFenV1S3JRWXFtaWhJUXFkNU9HRVhteW9ZCmdPVjdoZ0lWSUR6a1Z0QkRiS3dFV3VFN2pKYzViMXR4Mk1FUFRsVklEZGo0Zm5vdURWemdkczA2RER4aFM4eXAKbXJOSXhxb045ekEzYXVtTnRNZ2haSHVZRHdjbm5GSnBNZHlJSEdHZ1dlNnZZNHFtdEFSVDd3a0x6MTZnUG9LMAo5bFhVU0RmV3YwUDJGUXFHZTJjaXQ3VVE2ZGtsUWsrVGVtUEFwNnhEV09HR3oxRkdmUUoxN040b3AvOGtlOUo2Cm96RVp3QTh1aDVYTUl2N3loM2dobjV1d1R6RDUyZ1BBZFdaekEyaHVWV3p5cVM0WVc0N3ZkaGV6TTFTUndabVEKUmYzNDk0UVFydWd0bzdycWdMUlRTSXN4WEtkU21MaHZjT0hsSlhISW1XNTRzeFlXNm9NUStpRExFT29ZVVdpcgp1aUJvT1RsNEJaOG5Xcm9pV0JtWlFLaVRPYlFRczVWTkIwYnVybmRISTJVdmtIRTE3QTM0bFYySjY5Q0dNNzJ2CjQ5aE9TN3B2Tzg4cEVKZm90d01YYlRhdkR2WTBHazZxbERFMVBud1U2Wm8ySEprcFdUTUxOSzh1alZ1RkhlMGkKR2JvZi9va08vZW4rUi9PUXNyd1JYbzFwVTRiWnlyWGVQeUdqSSsrdFYzemhjd0IwWjNJPQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==</crt>
<prv>LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUpRUUlCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQ1Nzd2dna25BZ0VBQW9JQ0FRQ2xDZHhSd2ViUEJMb2IKVU5Gdi8va3dMR0pYTHB4OXo4UUdXaXJZM2lqZUN5TENDQXBzMEtoTVp1M1FYZHMzK2p6aWw0Z0hPei9Ib1BKOQpnTHEzL0VidHhwT0Q1aS9DNnBtdzdIYzUza1NDckIra2VZQWdVZnVoNTczMGcrd3BnOUQ0M2hxQTcxdS9xdGQvCnl6K2dNUmdNZ1kyd0QrdlZBZGt0bFVJaWY3amJOZHVENEYyZ2QvSDBvOWNYRFJuczMzTUFabU5GcGozeEFhcEIKL1FaeEpXUkxnUnkrUDkxZzJkRkU3OE1pZjhlNE1IK2tTb2d3MjBUbUluanMrSnRFU3NMUGZsdnlGVGtJZE1XRQptRFY4bUdSTmE1dWhheURtU0RRT3FXQ1ROVkdXdlVaNlNGclFnVDUwMER1eFp0bWJYRnRFakc5SGhneUluUE9HCkptZjNNZXNLd1hyVU0xbUF6dUJFUEVHSXA5NzdRNjZKK2lMNjNZNS9NMHdoYTBlVTRqaTU1UUJyTkI5WloybGsKTzRsZTZNd2ZmbnRzb0lqQytUOHlFbm1tbmdBOVN1Y09Fb0JwUWF3dy9EaE5rUjRpOE1KbUdSTXZqS2s5R1c2dwpnZU1VOElUOEpkNGNOYk5XN0VIanNXTzE3WW5NWEgwRTE5VmprZ3VHd0g4MGd2UThrc05leFlQN1lqNG9FcnJ0CkptZWFZTzBsVWRXS0Z6S011Ly9rRS80bkcrSHh6aVFScDlCZ3JNRE1iSzhmZDNvZmNramRWU01vVXNRSWlpZnUKMUwrUjhXVjcreGxPN1NZLzR0aTdjbzNyM1dFNjJWUThWT1AxWmFxK1ZEWm9TSDFUQmtJU0lOaWlSRUc4WUg0LwpEcnA1ZnZ0cE9wREFEY3VMZ0M0MkdwTGZLWnBUS1FJREFRQUJBb0lDQUFTSHc4Tit4aDR5ckFVcDc4WGFTZlhYCmtnK0FtUTBmRWV0MnVDeGgxTTlia09Xd29OQ2gzYXpUT24zNHhaYkF5TUVUbGNsVkNBZ3IwOXc4RjJRTGljcm4KSTQrQVZ4bExwVkprKzFUY1ZCY2VNSFFzWGFjRmVSblZxYkkzbU5qKzVGS2dqaXV4NWx2WmpiYlZWbmJJUWplOQpxcTBGa3R5ekEwb3NDYmUydDlWVW9pVDVtTGhaOG90Ym9BRGkvQzR6YUEyL3djUGNyMkNaUWhvem51U21PUjJWCmVydUNOMHA4VURGTFA1a0gxdXlvY0NpTFh6ZXdIVEVRQ3krK0YwMEZuRmxqeDVSYW5za3JvMnhqWFR5QlZtZUYKcDYwRHF0Q0hkTjVlS2VlQWxDL0dIRlFvL2swdzd3ejMxbHVsVGgza3FDQzJsaXRwYzVpZ2JsTGxaUDgxSUpXTQp0bkhlczNsTXk1RGNDWUx3L3huZFdmVDZFMTB4WlhFNWI0QTdxYjF4Yjhsd1FoNHFJckhDZ2p1NDVPYXNCMERJClBYZ3E2eWkwL2FKWXV6SU5kcjRTeFRibExGUkp6MXlQaGZTZDVGbjdWQVBYU1JNTDlDbzJrL0M1SDlwdG1HMjYKZHBLQVNib1ZMcStrbXg3anVKYXc0a1JNNHZmYndHZGNMZEhqMXByZ08xNkd1ckpQOVRRQ0x5YzhaR0xOekcvaApIMzBpU2FlclJOUmtDRlhmeTEzWWJJZTZHTE12KzVmODlYSENGNmZrZ1JkZjVrbTA3cEc3SCtMZytmZFdtd2lZCm0waExNSFVZeHJ3WkFma2tvZjhlSllscEVQVmQ3ZytCVjd2eTZhYW0yQituUTdHYk84WUprSnlJME04amlSaDEKeGdjRmFZaGZlT21RZGtVbi9BcUJBb0lCQVFEU1JZbDl0SnJyQk5UOXlZN0twWTJiOGVURFJqdDByU1NQRUJvNgppeWoyVWR5S1ZSbHFFdzRma2IrejV3WWt2bnBpMW1uS3NjNFlLZmoyaDVSdXVDbzVzTUNLbmpDUXBpbll4bWRFCk45Z3l6SWRYMmlzRUh6dXNSZkZiajBVSWU1dHc0TE9pL3cyVzFYWGJUc3liOFdhTmhkbVQ4TGxDNjQ5WkNNUWQKeDZkeTdOWS9uYnVWVVQ0KzM3WmV0VlR1eDg1ekl5OTdnMWp4dFZhaXZrd2hQVWtLcWpXWDYzaUZidjFuY1FVdgpiQURrWkFoOXRWYWV2UGZ2NURDeDZITldiVFlObjVRZWd3OTRyVndoSjhYb1V5ZDRqWFB0VmdXU2VkN0tWd2U5CmNkNW9CZWFBOVhDdnJxdkNIRjI4QXg2OUI2YWQrQlk1S0dVcGU2LythQnlKdlQwUkFvSUJBUURJN2c3c0dMc3AKVWJ4dGhJQm9yRzF5MDRKWWlnaE5VMlF4YjdzSkxocnRTc2NtRkxSZU5DYy8zOTBCV3ZrbVFIazFnZkpxV3hDLwp2R0VMT0Iwd3U5VFBBRWFsS25IZ2RhNkFTMURuM3NTWTltcFBRRjYvbEY2cm00cDlNUU1TTFo1V3ZsL0ZNRklHCjUvaXVSVjJaOVdkeTV4QVFWNG5uZmdMOWJDNzhPa2k3VnFPTDJDZk0vcEJEMHdzRUZmOGZiejFSZXo4dEFRZ2QKVXY4cEpFTWdDTCtQeEdkdG5DYUcxYm5obXhEUUxiWmQ4TTFOQkpKOWZLZFgzVWtLcTlDdmFJVXBIZlduSFBWVAprVWNwMUVTYnEzOFVhTzFSS1NBNUtQd1ZiNkVPVGJBSGtlaEN4ZVhpN2F3YkZwYXlTelpIaWl4Y05QQjk1YUtSCkpJQ0J5ekFwQTVTWkFvSUJBRlZKYXlrWGxqWjVNVUwyKy9ucUNIUVdPeW1SVlJCUUlpSDg4QWFLNTBSeGs3aHcKSit6RWFkZ1lMOTl5ZHlWME5RUGQzKzhkQzNEMXBVdXBWbVZLUWFaQXNQZ0lqYjQrQjM4cmlqczdRMi9uVVlZcQpzWVBzZnpHeTlPQ2tUZVhRN1ExdHRxOElNS1RiVkFCdUI4UEF1RTN5Mm51TkNqZkFmOVluSGhUT0pIY1M1UnZNCmlJZForcHRaOWdpWUdDajUxaDBSU25NWXBYejBobjFnSGxUbEhMazhySnhBSUJSUEhtMVVoRHZsM0w3R2JFTkEKeUM5K2lqbzlIaHNySTQwTW92NEhtZlorUmtvMlZzWUQ4ZHYzem15eFF6SWkwQVBIZHJ3dmJLNUVmMmRGN1dhbApKdDI3UldOb1NnUzJaME5ZMVJZQnlGSEt0cTJLdzZtMjVNeGhlMkVDZ2dFQVhSNFdSRXhoMEpCVXB0eVZOZTFTCis3Z1IzRDU4QW5uM0lRSUt5QUpaOEVhTGJKYUQwSFNUREFNUFJTV0grYlkvZGhDMjY1c3djK3MxZmlHUFJacUcKMFRmcmhYZmFOby9UUXhta2NSRElRNnRQTVZNL2xjR0k3amF6UTdtSEZ0R1ZZOVh1UkZCVWMyYmwxTDNJMXlUbgp3RlJkR1hXNEwxUXl4b2R3YnV3RDhPNEI5VGxEbUxrUTJwM2ZxUkVZbnRUS3NneFFCdWRIZjIrTFdPRzVTZ3RECjI3akZ4Z0pyeUdrY0wvWFJJT2xPYnRLK0VrZGdMRStzcmdlYlpocWlKK2hrYmQyNGpxM1k4OVdNQ1ZLYVNScDkKVmxRYVIxYXIzRkdtSWJrT0JyYnlNVS9wTjZqSEZSZllmdVZGQ1hQWnYrWEZFU1pubmJEaVdpbDBkTEpacTJoQgpZUUtDQVFBOVlTcE1wS3dhVGwrTmhTZlovMXU0NjZiMkpZcmJPYlRJM2VCZUowMnFwNXdQTjZYUHJ5aVZaZ1FXClh5cG04a3M5MEJIblBPNUczNFJnKzhLRFlONU1Ed1hBclJubDdSeTEySG5oV3lSaHNKYmdZOEh1c2d4SEROMU8KMEcwSFBpVWtIbTYydlRNYll6bkhPeE5sS1hFdFhBcTlZM3dQYkFjb01rRXZ0MzEwdEdLSUNtdTdEWkpXRlVvTAp1Y3RVS3Boc0V5VWdHbHIwRjJKekVoQWdMRXplczB0S1JpRWdlaFdnbXdlMEhlTEhCdW5oRFBTMmFJY2lCME1pCjY2SGc3cVZyMDlneXpFeGxrY3RLRzhsSm9WbU8vdlhucWQrWDB5M21YTUVZbkFIWHpIeG1Pd2JCNnF3Y3VWTlEKZytqRXliUWF3d3A2OC9id0JncFREQUhORGxrRQotLS0tLUVORCBQUklWQVRFIEtFWS0tLS0tCg==</prv>
</cert>
<dhcpdv6/>
<virtualip version="1.0.1">
<vip/>
</virtualip>
<openvpn/>
<ppps>
<ppp/>
</ppps>
<vlans version="1.0.0">
<vlan/>
</vlans>
<bridges>
<bridged/>
</bridges>
<gifs version="1.0.0">
<gif/>
</gifs>
<gres version="1.0.0">
<gre/>
</gres>
<laggs version="1.0.0">
<lagg/>
</laggs>
<wireless>
<clone/>
</wireless>
<hasync version="1.0.2">
<pfsyncinterface/>
<synchronizetoip/>
<verifypeer>0</verifypeer>
<username/>
<password/>
<disablepreempt>0</disablepreempt>
<disconnectppps>0</disconnectppps>
<pfsyncpeerip/>
<pfsyncversion>1400</pfsyncversion>
<syncitems/>
</hasync>
<ifgroups version="1.0.0"/>
<dnsmasq version="1.0.7" persisted_at="1755800176.40">
<enable>1</enable>
<regdhcp>0</regdhcp>
<regdhcpstatic>0</regdhcpstatic>
<dhcpfirst>0</dhcpfirst>
<strict_order>0</strict_order>
<domain_needed>0</domain_needed>
<no_private_reverse>0</no_private_reverse>
<no_resolv>0</no_resolv>
<log_queries>0</log_queries>
<no_hosts>0</no_hosts>
<strictbind>0</strictbind>
<dnssec>0</dnssec>
<regdhcpdomain/>
<interface>lan</interface>
<port>0</port>
<dns_forward_max/>
<cache_size/>
<local_ttl/>
<add_mac/>
<add_subnet>0</add_subnet>
<strip_subnet>0</strip_subnet>
<dhcp>
<no_interface/>
<fqdn>1</fqdn>
<domain/>
<lease_max/>
<authoritative>0</authoritative>
<default_fw_rules>1</default_fw_rules>
<reply_delay/>
<enable_ra>0</enable_ra>
<nosync>0</nosync>
</dhcp>
<no_ident>1</no_ident>
<dhcp_tags uuid="8d190cf3-8d2d-47db-ab9b-fa21016b533e">
<tag>ipxe</tag>
</dhcp_tags>
<dhcp_tags uuid="0b2982da-198c-4ca4-9a3e-95813667047c">
<tag>pxeEfi</tag>
</dhcp_tags>
<dhcp_tags uuid="993e079f-09b9-4a0f-a70f-8898872b9983">
<tag>pxeBios</tag>
</dhcp_tags>
<dhcp_ranges uuid="843574fc-4c3f-4f81-9e86-56be45f4ba49">
<interface>lan</interface>
<set_tag/>
<start_addr>192.168.1.41</start_addr>
<end_addr>192.168.1.245</end_addr>
<subnet_mask/>
<constructor/>
<mode/>
<prefix_len/>
<lease_time/>
<domain_type>range</domain_type>
<domain/>
<nosync>0</nosync>
<ra_mode/>
<ra_priority/>
<ra_mtu/>
<ra_interval/>
<ra_router_lifetime/>
<description/>
</dhcp_ranges>
<dhcp_options uuid="1e8d6f0f-7c2c-4873-8960-95a5c9447318">
<type>match</type>
<option>77</option>
<option6/>
<interface/>
<tag/>
<set_tag>8d190cf3-8d2d-47db-ab9b-fa21016b533e</set_tag>
<value>iPXE</value>
<force/>
<description/>
</dhcp_options>
<dhcp_options uuid="9b54181b-aa68-4fd6-9d59-10a77c291fcb">
<type>match</type>
<option>93</option>
<option6/>
<interface/>
<tag/>
<set_tag>993e079f-09b9-4a0f-a70f-8898872b9983</set_tag>
<value>0</value>
<force/>
<description/>
</dhcp_options>
<dhcp_options uuid="26402b93-91bb-48a9-92da-b567a91ed4d8">
<type>match</type>
<option>93</option>
<option6/>
<interface/>
<tag/>
<set_tag>0b2982da-198c-4ca4-9a3e-95813667047c</set_tag>
<value>7</value>
<force/>
<description/>
</dhcp_options>
<dhcp_boot uuid="57436655-6f95-4590-bcdc-dfe542347560">
<interface/>
<tag>0b2982da-198c-4ca4-9a3e-95813667047c</tag>
<filename>ipxe.efi</filename>
<servername>192.168.1.1</servername>
<address>192.168.1.1</address>
<description/>
</dhcp_boot>
<dhcp_boot uuid="dcc00bf2-5148-40ef-9f79-1f17bf572f6c">
<interface/>
<tag>8d190cf3-8d2d-47db-ab9b-fa21016b533e</tag>
<filename>http://192.168.1.1:8080/boot.ipxe</filename>
<servername>192.168.1.1</servername>
<address>192.168.1.1</address>
<description/>
</dhcp_boot>
<dhcp_boot uuid="8b9263a4-d242-4c41-8ff2-6f1af5001c41">
<interface/>
<tag>993e079f-09b9-4a0f-a70f-8898872b9983</tag>
<filename>undionly.kpxe</filename>
<servername>192.168.1.1</servername>
<address>192.168.1.1</address>
<description/>
</dhcp_boot>
</dnsmasq>
</opnsense>

View File

@@ -1,867 +0,0 @@
<?xml version="1.0"?>
<opnsense>
<theme>opnsense</theme>
<sysctl version="1.0.1" persisted_at="1755708111.39">
<item/>
</sysctl>
<system>
<serialspeed>115200</serialspeed>
<primaryconsole>serial</primaryconsole>
<optimization>normal</optimization>
<hostname>OPNsense</hostname>
<domain>internal</domain>
<dnsallowoverride>1</dnsallowoverride>
<dnsallowoverride_exclude/>
<group uuid="67305f6f-7f7a-454d-8a4e-65cb8f072d81">
<gid>1999</gid>
<name>admins</name>
<scope>system</scope>
<description>System Administrators</description>
<priv>page-all</priv>
<member>0</member>
<source_networks/>
</group>
<user uuid="1d2ed537-5d1a-4772-9600-37b93f9f798b">
<uid>0</uid>
<name>root</name>
<disabled>0</disabled>
<scope>system</scope>
<expires/>
<authorizedkeys/>
<otp_seed/>
<shell/>
<password>$2y$10$YRVoF4SgskIsrXOvOQjGieB9XqHPRra9R7d80B3BZdbY/j21TwBfS</password>
<pwd_changed_at/>
<landing_page/>
<comment/>
<email/>
<apikeys/>
<priv/>
<language/>
<descr>System Administrator</descr>
<dashboard/>
</user>
<timezone>Etc/UTC</timezone>
<timeservers>0.opnsense.pool.ntp.org 1.opnsense.pool.ntp.org 2.opnsense.pool.ntp.org 3.opnsense.pool.ntp.org</timeservers>
<webgui>
<protocol>https</protocol>
<ssl-certref>68a5faf1685db</ssl-certref>
<port/>
<ssl-ciphers/>
<interfaces/>
<compression/>
</webgui>
<disablenatreflection>yes</disablenatreflection>
<usevirtualterminal>1</usevirtualterminal>
<disableconsolemenu>1</disableconsolemenu>
<disablevlanhwfilter>1</disablevlanhwfilter>
<disablechecksumoffloading>1</disablechecksumoffloading>
<disablesegmentationoffloading>1</disablesegmentationoffloading>
<disablelargereceiveoffloading>1</disablelargereceiveoffloading>
<ipv6allow>1</ipv6allow>
<powerd_ac_mode>hadp</powerd_ac_mode>
<powerd_battery_mode>hadp</powerd_battery_mode>
<powerd_normal_mode>hadp</powerd_normal_mode>
<bogons>
<interval>monthly</interval>
</bogons>
<pf_share_forward>1</pf_share_forward>
<lb_use_sticky>1</lb_use_sticky>
<ssh>
<group>admins</group>
<noauto>1</noauto>
<interfaces/>
<kex/>
<ciphers/>
<macs/>
<keys/>
<keysig/>
<rekeylimit/>
<enabled>enabled</enabled>
<passwordauth>1</passwordauth>
<permitrootlogin>1</permitrootlogin>
</ssh>
<rrdbackup>-1</rrdbackup>
<netflowbackup>-1</netflowbackup>
<firmware version="1.0.1" persisted_at="1755708111.32">
<mirror/>
<flavour/>
<plugins/>
<type/>
<subscription/>
<reboot>0</reboot>
</firmware>
<dnsserver/>
<language>en_US</language>
</system>
<interfaces>
<wan>
<enable>1</enable>
<if>vtnet0</if>
<mtu/>
<ipaddr>dhcp</ipaddr>
<ipaddrv6>dhcp6</ipaddrv6>
<subnet/>
<gateway/>
<blockpriv>0</blockpriv>
<blockbogons>1</blockbogons>
<media/>
<mediaopt/>
<dhcp6-ia-pd-len>0</dhcp6-ia-pd-len>
<dhcphostname/>
<spoofmac/>
<mss/>
</wan>
<lan>
<enable>1</enable>
<if>vtnet1</if>
<ipaddr>192.168.1.1</ipaddr>
<subnet>24</subnet>
<ipaddrv6>track6</ipaddrv6>
<subnetv6>64</subnetv6>
<media/>
<mediaopt/>
<track6-interface>wan</track6-interface>
<track6-prefix-id>0</track6-prefix-id>
</lan>
<lo0>
<internal_dynamic>1</internal_dynamic>
<descr>Loopback</descr>
<enable>1</enable>
<if>lo0</if>
<ipaddr>127.0.0.1</ipaddr>
<ipaddrv6>::1</ipaddrv6>
<subnet>8</subnet>
<subnetv6>128</subnetv6>
<type>none</type>
<virtual>1</virtual>
</lo0>
</interfaces>
<dnsmasq version="1.0.7" persisted_at="1755723263.06">
<enable>1</enable>
<regdhcp>0</regdhcp>
<regdhcpstatic>0</regdhcpstatic>
<dhcpfirst>0</dhcpfirst>
<strict_order>0</strict_order>
<domain_needed>0</domain_needed>
<no_private_reverse>0</no_private_reverse>
<no_resolv>0</no_resolv>
<log_queries>0</log_queries>
<no_hosts>0</no_hosts>
<strictbind>0</strictbind>
<dnssec>0</dnssec>
<regdhcpdomain/>
<interface>lan</interface>
<port>0</port>
<dns_forward_max/>
<cache_size/>
<local_ttl/>
<add_mac/>
<add_subnet>0</add_subnet>
<strip_subnet>0</strip_subnet>
<dhcp>
<no_interface/>
<fqdn>1</fqdn>
<domain/>
<lease_max/>
<authoritative>0</authoritative>
<default_fw_rules>1</default_fw_rules>
<reply_delay/>
<enable_ra>0</enable_ra>
<nosync>0</nosync>
</dhcp>
<no_ident>1</no_ident>
<dhcp_ranges uuid="78b5c4a4-565d-4cd7-af10-29050f29e494">
<interface>lan</interface>
<set_tag/>
<start_addr>192.168.1.41</start_addr>
<end_addr>192.168.1.245</end_addr>
<subnet_mask/>
<constructor/>
<mode/>
<prefix_len/>
<lease_time/>
<domain_type>range</domain_type>
<domain/>
<nosync>0</nosync>
<ra_mode/>
<ra_priority/>
<ra_mtu/>
<ra_interval/>
<ra_router_lifetime/>
<description/>
</dhcp_ranges>
<dhcp_options uuid="b05f2b57-bad8-4072-8f51-d051671832fe">
<type>set</type>
<option>67</option>
<option6/>
<interface>lan</interface>
<tag/>
<set_tag/>
<value>test/boot/filename</value>
<force>0</force>
<description/>
</dhcp_options>
<dhcp_options uuid="5a5a7854-ec44-4631-95a9-85b4897588d5">
<type>set</type>
<option>128</option>
<option6/>
<interface>lan</interface>
<tag/>
<set_tag/>
<value>test some pxe setting vendor specific 128</value>
<force>0</force>
<description/>
</dhcp_options>
<dhcp_options uuid="7706861d-1851-420f-9b50-891ad413663e">
<type>set</type>
<option>208</option>
<option6/>
<interface/>
<tag/>
<set_tag/>
<value>pxelinux magic what is this (on any interface)</value>
<force>0</force>
<description/>
</dhcp_options>
<dhcp_boot uuid="748c0bd2-ef58-4cba-8486-bbf142f53859">
<interface/>
<tag/>
<filename>boot options filename</filename>
<servername>boot servername</servername>
<address>boot server address</address>
<description>boot description</description>
</dhcp_boot>
</dnsmasq>
<snmpd>
<syslocation/>
<syscontact/>
<rocommunity>public</rocommunity>
</snmpd>
<nat>
<outbound>
<mode>automatic</mode>
</outbound>
</nat>
<filter>
<rule>
<type>pass</type>
<ipprotocol>inet</ipprotocol>
<descr>Default allow LAN to any rule</descr>
<interface>lan</interface>
<source>
<network>lan</network>
</source>
<destination>
<any/>
</destination>
</rule>
<rule>
<type>pass</type>
<ipprotocol>inet6</ipprotocol>
<descr>Default allow LAN IPv6 to any rule</descr>
<interface>lan</interface>
<source>
<network>lan</network>
</source>
<destination>
<any/>
</destination>
</rule>
</filter>
<rrd>
<enable/>
</rrd>
<ntpd>
<prefer>0.opnsense.pool.ntp.org</prefer>
</ntpd>
<revision>
<username>root@192.168.1.5</username>
<description>/api/dnsmasq/settings/set made changes</description>
<time>1755723263.06</time>
</revision>
<OPNsense>
<wireguard>
<client version="1.0.0" persisted_at="1755708111.04">
<clients/>
</client>
<general version="0.0.1" persisted_at="1755708111.05">
<enabled>0</enabled>
</general>
<server version="1.0.0" persisted_at="1755708111.05">
<servers/>
</server>
</wireguard>
<IPsec version="1.0.4" persisted_at="1755708111.06">
<general>
<enabled/>
<preferred_oldsa>0</preferred_oldsa>
<disablevpnrules>0</disablevpnrules>
<passthrough_networks/>
<user_source/>
<local_group/>
</general>
<charon>
<max_ikev1_exchanges/>
<threads>16</threads>
<ikesa_table_size>32</ikesa_table_size>
<ikesa_table_segments>4</ikesa_table_segments>
<init_limit_half_open>1000</init_limit_half_open>
<ignore_acquire_ts>1</ignore_acquire_ts>
<install_routes>0</install_routes>
<cisco_unity>0</cisco_unity>
<make_before_break>0</make_before_break>
<retransmit_tries/>
<retransmit_timeout/>
<retransmit_base/>
<retransmit_jitter/>
<retransmit_limit/>
<syslog>
<daemon>
<ike_name>1</ike_name>
<log_level>0</log_level>
<app>1</app>
<asn>1</asn>
<cfg>1</cfg>
<chd>1</chd>
<dmn>1</dmn>
<enc>1</enc>
<esp>1</esp>
<ike>1</ike>
<imc>1</imc>
<imv>1</imv>
<job>1</job>
<knl>1</knl>
<lib>1</lib>
<mgr>1</mgr>
<net>1</net>
<pts>1</pts>
<tls>1</tls>
<tnc>1</tnc>
</daemon>
</syslog>
<plugins>
<attr>
<subnet/>
<split-include/>
<x_28674/>
<x_28675/>
<x_28672/>
<x_28673>0</x_28673>
<x_28679/>
<dns/>
<nbns/>
</attr>
<eap-radius>
<servers/>
<accounting>0</accounting>
<class_group>0</class_group>
</eap-radius>
<xauth-pam>
<pam_service>ipsec</pam_service>
<session>0</session>
<trim_email>1</trim_email>
</xauth-pam>
</plugins>
</charon>
<keyPairs/>
<preSharedKeys/>
</IPsec>
<Swanctl version="1.0.0" persisted_at="1755708111.08">
<Connections/>
<locals/>
<remotes/>
<children/>
<Pools/>
<VTIs/>
<SPDs/>
</Swanctl>
<OpenVPNExport version="0.0.1" persisted_at="1755708111.40">
<servers/>
</OpenVPNExport>
<OpenVPN version="1.0.1" persisted_at="1755708111.40">
<Overwrites/>
<Instances/>
<StaticKeys/>
</OpenVPN>
<captiveportal version="1.0.4" persisted_at="1755708111.41">
<zones/>
<templates/>
</captiveportal>
<cron version="1.0.4" persisted_at="1755708111.43">
<jobs/>
</cron>
<DHCRelay version="1.0.1" persisted_at="1755708111.43"/>
<Firewall>
<Lvtemplate version="0.0.1" persisted_at="1755708111.45">
<templates/>
</Lvtemplate>
<Alias version="1.0.1" persisted_at="1755708111.65">
<geoip>
<url/>
</geoip>
<aliases/>
</Alias>
<Category version="1.0.0" persisted_at="1755708111.65">
<categories/>
</Category>
<Filter version="1.0.4" persisted_at="1755708111.70">
<rules/>
<snatrules/>
<npt/>
<onetoone/>
</Filter>
</Firewall>
<Netflow version="1.0.1" persisted_at="1755708111.45">
<capture>
<interfaces/>
<egress_only/>
<version>v9</version>
<targets/>
</capture>
<collect>
<enable>0</enable>
</collect>
<activeTimeout>1800</activeTimeout>
<inactiveTimeout>15</inactiveTimeout>
</Netflow>
<IDS version="1.1.0" persisted_at="1755708111.90">
<rules/>
<policies/>
<userDefinedRules/>
<files/>
<fileTags/>
<general>
<enabled>0</enabled>
<ips>0</ips>
<promisc>0</promisc>
<interfaces>wan</interfaces>
<homenet>192.168.0.0/16,10.0.0.0/8,172.16.0.0/12</homenet>
<defaultPacketSize/>
<UpdateCron/>
<AlertLogrotate>W0D23</AlertLogrotate>
<AlertSaveLogs>4</AlertSaveLogs>
<MPMAlgo/>
<detect>
<Profile/>
<toclient_groups/>
<toserver_groups/>
</detect>
<syslog>0</syslog>
<syslog_eve>0</syslog_eve>
<LogPayload>0</LogPayload>
<verbosity/>
<eveLog>
<http>
<enable>0</enable>
<extended>0</extended>
<dumpAllHeaders/>
</http>
<tls>
<enable>0</enable>
<extended>0</extended>
<sessionResumption>0</sessionResumption>
<custom/>
</tls>
</eveLog>
</general>
</IDS>
<Interfaces>
<loopbacks version="1.0.0" persisted_at="1755708111.95"/>
<neighbors version="1.0.0" persisted_at="1755708111.96"/>
<vxlans version="1.0.2" persisted_at="1755708111.99"/>
</Interfaces>
<Kea>
<ctrl_agent version="0.0.1" persisted_at="1755708111.99">
<general>
<enabled>0</enabled>
<http_host>127.0.0.1</http_host>
<http_port>8000</http_port>
</general>
</ctrl_agent>
<dhcp4 version="1.0.4" persisted_at="1755708112.00">
<general>
<enabled>0</enabled>
<manual_config>0</manual_config>
<interfaces/>
<valid_lifetime>4000</valid_lifetime>
<fwrules>1</fwrules>
<dhcp_socket_type>raw</dhcp_socket_type>
</general>
<ha>
<enabled>0</enabled>
<this_server_name/>
<max_unacked_clients>2</max_unacked_clients>
</ha>
<subnets/>
<reservations/>
<ha_peers/>
</dhcp4>
<dhcp6 version="1.0.0" persisted_at="1755708112.00">
<general>
<enabled>0</enabled>
<manual_config>0</manual_config>
<interfaces/>
<valid_lifetime>4000</valid_lifetime>
<fwrules>1</fwrules>
</general>
<ha>
<enabled>0</enabled>
<this_server_name/>
<max_unacked_clients>2</max_unacked_clients>
</ha>
<subnets/>
<reservations/>
<pd_pools/>
<ha_peers/>
</dhcp6>
</Kea>
<monit version="1.0.13" persisted_at="1755708112.02">
<general>
<enabled>0</enabled>
<interval>120</interval>
<startdelay>120</startdelay>
<mailserver>127.0.0.1</mailserver>
<port>25</port>
<username/>
<password/>
<ssl>0</ssl>
<sslversion>auto</sslversion>
<sslverify>1</sslverify>
<logfile/>
<statefile/>
<eventqueuePath/>
<eventqueueSlots/>
<httpdEnabled>0</httpdEnabled>
<httpdUsername>root</httpdUsername>
<httpdPassword/>
<httpdPort>2812</httpdPort>
<httpdAllow/>
<mmonitUrl/>
<mmonitTimeout>5</mmonitTimeout>
<mmonitRegisterCredentials>1</mmonitRegisterCredentials>
</general>
<alert uuid="76cd5195-a487-4f4f-8ef5-4f9815bf19e5">
<enabled>0</enabled>
<recipient>root@localhost.local</recipient>
<noton>0</noton>
<events/>
<format/>
<reminder/>
<description/>
</alert>
<service uuid="11610907-f700-4f0a-9926-c25a47709493">
<enabled>1</enabled>
<name>$HOST</name>
<description/>
<type>system</type>
<pidfile/>
<match/>
<path/>
<timeout>300</timeout>
<starttimeout>30</starttimeout>
<address/>
<interface/>
<start/>
<stop/>
<tests>02014be3-fc31-4af3-a0d5-061eaa67d28a,ebfd0d97-ae21-45d5-8b42-5220c75ce46f,d37f25f0-89e3-44b6-8ad2-280ac83a8904,37afd0d9-990c-4f03-a817-45691461e3d0</tests>
<depends/>
<polltime/>
</service>
<service uuid="dcc7e42f-a878-49a3-8b90-67faf21d67bd">
<enabled>1</enabled>
<name>RootFs</name>
<description/>
<type>filesystem</type>
<pidfile/>
<match/>
<path>/</path>
<timeout>300</timeout>
<starttimeout>30</starttimeout>
<address/>
<interface/>
<start/>
<stop/>
<tests>b44b859c-bc72-4c2e-82c9-4f56d84a5497</tests>
<depends/>
<polltime/>
</service>
<service uuid="5ca544d4-846a-4f61-9d6f-c191eafcd9fb">
<enabled>0</enabled>
<name>carp_status_change</name>
<description/>
<type>custom</type>
<pidfile/>
<match/>
<path>/usr/local/opnsense/scripts/OPNsense/Monit/carp_status</path>
<timeout>300</timeout>
<starttimeout>30</starttimeout>
<address/>
<interface/>
<start/>
<stop/>
<tests>0909801b-cd11-41c8-afeb-369396247308</tests>
<depends/>
<polltime/>
</service>
<service uuid="6e16e26e-e732-4b9b-ac92-2086b84f3158">
<enabled>0</enabled>
<name>gateway_alert</name>
<description/>
<type>custom</type>
<pidfile/>
<match/>
<path>/usr/local/opnsense/scripts/OPNsense/Monit/gateway_alert</path>
<timeout>300</timeout>
<starttimeout>30</starttimeout>
<address/>
<interface/>
<start/>
<stop/>
<tests>56e67d76-cef6-4167-a51e-2c69a921ebc9</tests>
<depends/>
<polltime/>
</service>
<test uuid="b7671673-5cf1-4123-a58c-37f57a8e9d59">
<name>Ping</name>
<type>NetworkPing</type>
<condition>failed ping</condition>
<action>alert</action>
<path/>
</test>
<test uuid="30cf3664-5c6f-4ac5-a52a-36023270c6fb">
<name>NetworkLink</name>
<type>NetworkInterface</type>
<condition>failed link</condition>
<action>alert</action>
<path/>
</test>
<test uuid="a7c0836a-b102-4f37-a73b-2a50903ffcc8">
<name>NetworkSaturation</name>
<type>NetworkInterface</type>
<condition>saturation is greater than 75%</condition>
<action>alert</action>
<path/>
</test>
<test uuid="02014be3-fc31-4af3-a0d5-061eaa67d28a">
<name>MemoryUsage</name>
<type>SystemResource</type>
<condition>memory usage is greater than 75%</condition>
<action>alert</action>
<path/>
</test>
<test uuid="ebfd0d97-ae21-45d5-8b42-5220c75ce46f">
<name>CPUUsage</name>
<type>SystemResource</type>
<condition>cpu usage is greater than 75%</condition>
<action>alert</action>
<path/>
</test>
<test uuid="d37f25f0-89e3-44b6-8ad2-280ac83a8904">
<name>LoadAvg1</name>
<type>SystemResource</type>
<condition>loadavg (1min) is greater than 4</condition>
<action>alert</action>
<path/>
</test>
<test uuid="37afd0d9-990c-4f03-a817-45691461e3d0">
<name>LoadAvg5</name>
<type>SystemResource</type>
<condition>loadavg (5min) is greater than 3</condition>
<action>alert</action>
<path/>
</test>
<test uuid="608d2888-9df5-486d-bbfb-bf17fad75a7e">
<name>LoadAvg15</name>
<type>SystemResource</type>
<condition>loadavg (15min) is greater than 2</condition>
<action>alert</action>
<path/>
</test>
<test uuid="b44b859c-bc72-4c2e-82c9-4f56d84a5497">
<name>SpaceUsage</name>
<type>SpaceUsage</type>
<condition>space usage is greater than 75%</condition>
<action>alert</action>
<path/>
</test>
<test uuid="0909801b-cd11-41c8-afeb-369396247308">
<name>ChangedStatus</name>
<type>ProgramStatus</type>
<condition>changed status</condition>
<action>alert</action>
<path/>
</test>
<test uuid="56e67d76-cef6-4167-a51e-2c69a921ebc9">
<name>NonZeroStatus</name>
<type>ProgramStatus</type>
<condition>status != 0</condition>
<action>alert</action>
<path/>
</test>
</monit>
<Gateways version="1.0.0" persisted_at="1755721633.53"/>
<Syslog version="1.0.2" persisted_at="1755708112.05">
<general>
<enabled>1</enabled>
<loglocal>1</loglocal>
<maxpreserve>31</maxpreserve>
<maxfilesize/>
</general>
<destinations/>
</Syslog>
<TrafficShaper version="1.0.3" persisted_at="1755708112.06">
<pipes/>
<queues/>
<rules/>
</TrafficShaper>
<trust>
<general version="1.0.1" persisted_at="1755708112.22">
<store_intermediate_certs>0</store_intermediate_certs>
<install_crls>0</install_crls>
<fetch_crls>0</fetch_crls>
<enable_legacy_sect>1</enable_legacy_sect>
<enable_config_constraints>0</enable_config_constraints>
<CipherString/>
<Ciphersuites/>
<SignatureAlgorithms/>
<groups/>
<MinProtocol/>
<MinProtocol_DTLS/>
</general>
</trust>
<unboundplus version="1.0.12" persisted_at="1755708112.29">
<general>
<enabled>1</enabled>
<port>53</port>
<stats>0</stats>
<active_interface/>
<dnssec>0</dnssec>
<dns64>0</dns64>
<dns64prefix/>
<noarecords>0</noarecords>
<regdhcp>0</regdhcp>
<regdhcpdomain/>
<regdhcpstatic>0</regdhcpstatic>
<noreglladdr6>0</noreglladdr6>
<noregrecords>0</noregrecords>
<txtsupport>0</txtsupport>
<cacheflush>0</cacheflush>
<local_zone_type>transparent</local_zone_type>
<outgoing_interface/>
<enable_wpad>0</enable_wpad>
</general>
<advanced>
<hideidentity>0</hideidentity>
<hideversion>0</hideversion>
<prefetch>0</prefetch>
<prefetchkey>0</prefetchkey>
<dnssecstripped>0</dnssecstripped>
<aggressivensec>1</aggressivensec>
<serveexpired>0</serveexpired>
<serveexpiredreplyttl/>
<serveexpiredttl/>
<serveexpiredttlreset>0</serveexpiredttlreset>
<serveexpiredclienttimeout/>
<qnameminstrict>0</qnameminstrict>
<extendedstatistics>0</extendedstatistics>
<logqueries>0</logqueries>
<logreplies>0</logreplies>
<logtagqueryreply>0</logtagqueryreply>
<logservfail>0</logservfail>
<loglocalactions>0</loglocalactions>
<logverbosity>1</logverbosity>
<valloglevel>0</valloglevel>
<privatedomain/>
<privateaddress>0.0.0.0/8,10.0.0.0/8,100.64.0.0/10,169.254.0.0/16,172.16.0.0/12,192.0.2.0/24,192.168.0.0/16,198.18.0.0/15,198.51.100.0/24,203.0.113.0/24,233.252.0.0/24,::1/128,2001:db8::/32,fc00::/8,fd00::/8,fe80::/10</privateaddress>
<insecuredomain/>
<msgcachesize/>
<rrsetcachesize/>
<outgoingnumtcp/>
<incomingnumtcp/>
<numqueriesperthread/>
<outgoingrange/>
<jostletimeout/>
<discardtimeout/>
<cachemaxttl/>
<cachemaxnegativettl/>
<cacheminttl/>
<infrahostttl/>
<infrakeepprobing>0</infrakeepprobing>
<infracachenumhosts/>
<unwantedreplythreshold/>
</advanced>
<acls>
<default_action>allow</default_action>
</acls>
<dnsbl>
<enabled>0</enabled>
<safesearch>0</safesearch>
<type/>
<lists/>
<whitelists/>
<blocklists/>
<wildcards/>
<address/>
<nxdomain>0</nxdomain>
</dnsbl>
<forwarding>
<enabled>0</enabled>
</forwarding>
<dots/>
<hosts/>
<aliases/>
</unboundplus>
</OPNsense>
<hasync version="1.0.2" persisted_at="1755708111.35">
<disablepreempt>0</disablepreempt>
<disconnectppps>0</disconnectppps>
<pfsyncinterface/>
<pfsyncpeerip/>
<pfsyncversion>1400</pfsyncversion>
<synchronizetoip/>
<verifypeer>0</verifypeer>
<username/>
<password/>
<syncitems/>
</hasync>
<openvpn/>
<ifgroups version="1.0.0" persisted_at="1755708111.71"/>
<bridges version="1.0.0" persisted_at="1755708111.91">
<bridged/>
</bridges>
<gifs version="1.0.0" persisted_at="1755708111.92">
<gif/>
</gifs>
<gres version="1.0.0" persisted_at="1755708111.93">
<gre/>
</gres>
<laggs version="1.0.0" persisted_at="1755708111.95">
<lagg/>
</laggs>
<virtualip version="1.0.1" persisted_at="1755708111.96">
<vip/>
</virtualip>
<vlans version="1.0.0" persisted_at="1755708111.98">
<vlan/>
</vlans>
<staticroutes version="1.0.0" persisted_at="1755708112.03"/>
<ppps>
<ppp/>
</ppps>
<wireless>
<clone/>
</wireless>
<ca/>
<dhcpd/>
<dhcpdv6/>
<cert uuid="d60972af-642c-411f-abb6-43e0d680fefd">
<refid>68a5faf1685db</refid>
<descr>Web GUI TLS certificate</descr>
<caref/>
<crt>LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUhFakNDQlBxZ0F3SUJBZ0lVQlpQYjUwMXNaM3hhTTZzSDVUM1ZNRG9mcnlNd0RRWUpLb1pJaHZjTkFRRUwKQlFBd2dZWXhHakFZQmdOVkJBTU1FVTlRVG5ObGJuTmxMbWx1ZEdWeWJtRnNNUXN3Q1FZRFZRUUdFd0pPVERFVgpNQk1HQTFVRUNBd01XblZwWkMxSWIyeHNZVzVrTVJVd0V3WURWUVFIREF4TmFXUmtaV3hvWVhKdWFYTXhMVEFyCkJnTlZCQW9NSkU5UVRuTmxibk5sSUhObGJHWXRjMmxuYm1Wa0lIZGxZaUJqWlhKMGFXWnBZMkYwWlRBZUZ3MHkKTlRBNE1qQXhOalF5TWpaYUZ3MHlOakE1TWpFeE5qUXlNalphTUlHR01Sb3dHQVlEVlFRRERCRlBVRTV6Wlc1egpaUzVwYm5SbGNtNWhiREVMTUFrR0ExVUVCaE1DVGt3eEZUQVRCZ05WQkFnTURGcDFhV1F0U0c5c2JHRnVaREVWCk1CTUdBMVVFQnd3TVRXbGtaR1ZzYUdGeWJtbHpNUzB3S3dZRFZRUUtEQ1JQVUU1elpXNXpaU0J6Wld4bUxYTnAKWjI1bFpDQjNaV0lnWTJWeWRHbG1hV05oZEdVd2dnSWlNQTBHQ1NxR1NJYjNEUUVCQVFVQUE0SUNEd0F3Z2dJSwpBb0lDQVFEQVozZk1lakdOckZYVFhZd24vTUp5YXBYMDdVOTVWZ0JERzZmWU1wZkZEU1NXeFRValRLRlNvR3JRCkpDb0ZyQ0NRQ3BsWnlua0ZjVFZvWVAraGszcndWWmxHZVFyL0RyR2ZiM1lPc2RGbEtublo5YzRTOGVrSkc2WTIKYWI0eFJTcnBja0hoTWQ4MHNENUFRdks1Skc2MS9UMEhNRXVMcGlraUF3MFpCempWbjlVUUpSRTJiS29kOW9IdgpEbG5DVGNTV1FUNWYrV3A0Sll0anVBVHBZMUlRVW4wbkxuWDBHUC9JRGtsWjFrdEZOczRPOGcrQmRVWFU0MUdvCjNGTFpCc1hQVm90WFVrRVl2R0ZldjJRMlBwSnNib25aWEpoR255amREUlZkZFZGOGhacGJua0wwU2xJTVFNeFQKSTRXK051ZmUvUTZGRDdmZnFRa0toemZ3SlJ2N0dGM2RTRlEwWldPUGNTZVZXY3lWTlVVMTMvcnBteUpvNXZhWQpYR000THcxb1d2c1FjUWxaUllnSkxhSWMzM0dnMGQySHhvZTIvdytIeFRXOEw4ZldqbzgxK250YWJXTlZhV0IwCnd6TXNFNGRBOWtxR2dmcWZjbm96ckovamJtNEFBTTB6QTlVZFh0SUJtRGdpeFNkVzB4eHNQNlZWRTdMdnlURTgKWnBJTjRoL1FCYURyc2hhRXJ6TXhUd01IZXJ1RlV4bFpxZEdSa3ErQXRJU1VwRE05VXB0YWZZMk5ydzgxVDFhSwoycFduVFlFQktCUnVwdk00TzFHNXN5NU5GZm13NFRTc0pqRUhtMFEvVTZ4ZU45bVg0OFhIYUZFNnhvQTJEZEMrCjJHS2lKWFlrYi9nZm13cmp1Y3NGWGpBS2tDNWF6ZXFqaERXeGxDbmJNS1YwSTQxOWp3SURBUUFCbzRJQmREQ0MKQVhBd0NRWURWUjBUQkFJd0FEQVJCZ2xnaGtnQmh2aENBUUVFQkFNQ0JrQXdOQVlKWUlaSUFZYjRRZ0VOQkNjVwpKVTlRVG5ObGJuTmxJRWRsYm1WeVlYUmxaQ0JUWlhKMlpYSWdRMlZ5ZEdsbWFXTmhkR1V3SFFZRFZSME9CQllFCkZMK2YzU0tCM0tMSi9nWStBUTJIZDBhTzVPRU1NSUd3QmdOVkhTTUVnYWd3Z2FXaGdZeWtnWWt3Z1lZeEdqQVkKQmdOVkJBTU1FVTlRVG5ObGJuTmxMbWx1ZEdWeWJtRnNNUXN3Q1FZRFZRUUdFd0pPVERFVk1CTUdBMVVFQ0F3TQpXblZwWkMxSWIyeHNZVzVrTVJVd0V3WURWUVFIREF4TmFXUmtaV3hvWVhKdWFYTXhMVEFyQmdOVkJBb01KRTlRClRuTmxibk5sSUhObGJHWXRjMmxuYm1Wa0lIZGxZaUJqWlhKMGFXWnBZMkYwWllJVUJaUGI1MDFzWjN4YU02c0gKNVQzVk1Eb2ZyeU13SFFZRFZSMGxCQll3RkFZSUt3WUJCUVVIQXdFR0NDc0dBUVVGQ0FJQ01Bc0dBMVVkRHdRRQpBd0lGb0RBY0JnTlZIUkVFRlRBVGdoRlBVRTV6Wlc1elpTNXBiblJsY201aGJEQU5CZ2txaGtpRzl3MEJBUXNGCkFBT0NBZ0VBY3F3VEN0RmVwWDlFOEFMYXRaWEE4dkN2b1YwZkxuaElPM0lWT2tZNEhYMTRRRU9NUGo4KzVXMXoKc3hyUGNscEJQVU5tbXJRb0hWUHBuWTRFM2hFanlYY1JWSzZtTEFrSkpRUDJPOEE2NFFpL3FNSkhCYUlEU0MzKwpqMHdkMGYyODRvcEppQ0F2UnF0SGh1bDd3akd4QzNZUXRxWTFMODNWUHBOWGRjOVVsZExTUGZ6WWluMlBPekMrClFDWU9qN3VQUDlCVGExTURudkdPdkdrOHdQeUJGaFZQWVFBWjYwb1ZaS2psOUI2R1piRzF1SG1ON3p3a3k0eEIKNk1RSlF3cHFscDdXQ09QSHJlZTFiVGZaMkJMZFQrZzJHakVMT0xNRGJMTHAzNUw0blBDUmNGRkJZNEszMHBncQpVWjdncEtNTmhuR3huQ29lR3dHUk54ZHFoZnNlT3FqaURVM0hBVDEya3FReU1vOEZNT1g1bG9EaVpXSGZTS0JrClVRaG5Tc1BTMG0vZ2dSRVgwNVQzYWM2NUxNOVVXMEs2MXppZUs5WFhTcjNXWlQ1TkhNV2JmU0VScUR4SGRtZ0YKeGt3YXkxZWpCZTFoZzdGMGpicTVDMGo1ZXB5ZDNOc1BTVUtDY2FDNi9aWTNkUHVYWVZZL0J2dnFsZHZJSzRBeAo0R1BLQ0xzaStGRjliSXZRWG4zbTV2KzJoQWF2WlpxcmJOTFUzVFQ0aDd2QllBOVRNcXpwd3lEaW5BR3RhaEE3CnhDSW5IU01kZXpQcnNkZDJrTW5TckhPdWtGeGdKb2lNZ3krVlJmdTdvZk9NMjhYQ1FySWF6djBFYmFhTU1ZMTcKRzlXOFd3SXZUb2lYY1ZWNTk3K1NUNHRsSTVIM3lDZFBtSk1kRm5GcDg1K2JzbW42MFQwPQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==</crt>
<csr/>
<prv>LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUpRZ0lCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQ1N3d2dna29BZ0VBQW9JQ0FRREFaM2ZNZWpHTnJGWFQKWFl3bi9NSnlhcFgwN1U5NVZnQkRHNmZZTXBmRkRTU1d4VFVqVEtGU29HclFKQ29GckNDUUNwbFp5bmtGY1RWbwpZUCtoazNyd1ZabEdlUXIvRHJHZmIzWU9zZEZsS25uWjljNFM4ZWtKRzZZMmFiNHhSU3JwY2tIaE1kODBzRDVBClF2SzVKRzYxL1QwSE1FdUxwaWtpQXcwWkJ6alZuOVVRSlJFMmJLb2Q5b0h2RGxuQ1RjU1dRVDVmK1dwNEpZdGoKdUFUcFkxSVFVbjBuTG5YMEdQL0lEa2xaMWt0Rk5zNE84ZytCZFVYVTQxR28zRkxaQnNYUFZvdFhVa0VZdkdGZQp2MlEyUHBKc2JvblpYSmhHbnlqZERSVmRkVkY4aFpwYm5rTDBTbElNUU14VEk0VytOdWZlL1E2RkQ3ZmZxUWtLCmh6ZndKUnY3R0YzZFNGUTBaV09QY1NlVldjeVZOVVUxMy9ycG15Sm81dmFZWEdNNEx3MW9XdnNRY1FsWlJZZ0oKTGFJYzMzR2cwZDJIeG9lMi93K0h4VFc4TDhmV2pvODErbnRhYldOVmFXQjB3ek1zRTRkQTlrcUdnZnFmY25vegpySi9qYm00QUFNMHpBOVVkWHRJQm1EZ2l4U2RXMHh4c1A2VlZFN0x2eVRFOFpwSU40aC9RQmFEcnNoYUVyek14ClR3TUhlcnVGVXhsWnFkR1JrcStBdElTVXBETTlVcHRhZlkyTnJ3ODFUMWFLMnBXblRZRUJLQlJ1cHZNNE8xRzUKc3k1TkZmbXc0VFNzSmpFSG0wUS9VNnhlTjltWDQ4WEhhRkU2eG9BMkRkQysyR0tpSlhZa2IvZ2Ztd3JqdWNzRgpYakFLa0M1YXplcWpoRFd4bENuYk1LVjBJNDE5andJREFRQUJBb0lDQUNVVkRBTE4zalVXN09leTFPdDBES251Cm52NDRxcU9SRHJYZ1k2WUlnalhKUmE4RlRTdURmbWdsWU5EQzE1S0dUVFJWeHA2R3BuS0ZFaTBPM05Yd1RiWjYKV1BNN0t3SmplNXBsNmhRRTgzMlRCUzhiNzk2NDN4Z1JTeVNibHJ0NlFENEQ5bXlIcHlSSmY0WDFJVURMbzhiUgppdXlTdzB5ajlyT0djUVRNM29oVnFNUFcwUTF6UGdwT1UxYVdwbmdMY3dNZWlmNEhYUnpRNTUrTmZPemFacHVjCnVtQk4xUS81clhxS1BscmhNVnFpcUc0Nit3QVJjU2NKdE5oZHRsMzdyeTQ1Mk5zNGtERkxSVnowZUVUNEpGSmYKcjVQRUE5bEFuYWlVOS9RdVEwbERtcTlqdmpYRkNURXhYKy82SGJHK2RVd0Y2OEY3ZVEzVFQxbkhHK0hkMVJsbgpOWm1JM0p2d0Z1cG9JeU9VdlpJb3VGVmo2ak8ra0JLejkza1BHWmdMbnNmUUw5WDhRbTU3cjh4K3Z1eFNudGI1CjV4WVBxRkdrOWQrbDUwbTlQakdkekxGT3UwYnJ5TmQ4MFVMS2tuUlFtUVpvTngxck5GTUxpSjNlZENWUS9lclUKT1BDQ0Z0WEJMemJGTjR2ZzVWRjZMUkhvZGxqcEgxRzJOSXNoSzJhc1FuWS9RWDFpUUNLSk1tWERSUndMTWVsNQp3MUF4T2FqYVkzbWx2ZlRVd2xqdkE3a0tFUDBvZzRPeXZldDA2WTVRWk1EQXc1V00yT0pZVDVxcmFlYjZDbTdMCjlNckk4bG50TGp3WFVSZG4yU3U2RCtCWXNpcC9KK3BvOFNqYlJBaGJIc0lJbkJ1QWJnbGxqdTB2QXRXZmFkQlQKOTg4YnUwK3VUb1Q2T1Jkbk84Y1JBb0lCQVFEcStWYkVUQWVpSHN6K29jZnFWV3VwVHJrc2FNNm1wUUMwb0tqZApwb1FzWGVuTmNiNThHQ3FhWHkvdTJHWmNKUnR1QXRHamEyUVpEUUFUSjQyVTFmaTFhWm90Y053eXhPdmlud1NjCmVLZyt0ZGcwdW9LeGs2aXJKRFptaDBIK3Ewblg2RFJYK25RNDVmWVNmRkRFK0ZLd1lac0dQMkhYV3dKaVZ6OE0KU2NkL2pETTFRTWV2OXIzZWx1dS9DWFlvZ1N0N00wMklyczVoNjRuNjFmdVZjNHI4YmUwdFkrUTVsUnlwWk9NVwpkQ2VkWGFOV3RaNjF2bEFxamNiWkpkdXFBUjJjNzAyR3NML201TXA4Zmd3YmY2aG51TXJLaVlpQjlZalZxalc2CmYyUW1PclZtMUk0MFJBMC9OaFBTR2NXejBkNXZrdXY0VHUra2JFbERZTCsxaHY1M0FvSUJBUURSbnZaTmJaa1UKTXpmUTRLWEdML3dLUXJEbjNvL0RENWVBR1ZDTGcwTUkyYlAxYWpubHNVTjE4NCs1UWF6cVVOaWlZT3laODczeQpQYkw0cTBOZWFDYXdxby9WbjJMSkVIUFVTTVhUWjB4ckxTa1hPUjFuMDUwT2tDWXhVbFpOUXFvZU1xcHJGNXZLCm1NNlJxalN4NS8ydU9IUlR1SDRVV2RETEpwTDVUN2RpUCtXcFUwSDlSUWhrNDdkQUJaUjZEZjNxaDJEYmVxUWoKdWcxY0hWUVNqaldhUGpVZGlLR2dHemdvdlE2UkdNZDA1UVUzdkRMdzBCSkNPQ25XV2x0VXkvMW1jMUpPUHR2ZQp4UGltV2tRNmlkRHZ4RGZFRGg5U05zY1FPMnBTVjZxNnhCTWlqVGgvTldGN2NsOU1LYUhJWGxzTmt0RFVXWHZyCmNKRlM4eE1TcDhlcEFvSUJBUUNtWktVTjRxMHhIOUNZckdYT1Nta3Yvc0JnYzJPTFhLTXdSZWp1OVFENkRoTUgKMmZsREZUWHVGV1B6SmlqdUxaVE1CWkVBd1lhanVySUgzbVdETlRhbStMNG1XWnFGRlMvWlRqUk12YUNlcjlVSQpHZDk4OG94cGpQNDlBcUU0UDRIT00vQUZNU1ZtT1dwVTB0VzdkZ0hRUjM0cElXOGV1cUxva3RIaDJNaytTRURuCkFCV29SUGxWaTlncmN2N0tWaFk5YXlvSGxZb3VpMFl0YTZSNXc5VnpSa0REZU01Zi9Iak1kOVhieTZ0VjQ3NU0KSTliYzZvVUliVmVYNUJnMnZnMkRXVzZ6NTZ3dFRHMGJWWU1yWWU0V2JTU2w0bGpaZHM5TVJ2ay9OUUR0bFh0cAo4ekUwVDlCMXA4ekhabHE3S08zMFlyMVpIRVRWVVoxYjZrSTN3UDJuQW9JQkFFZ1VGKzlCMjF4RnpGQ0hucGtLClVPa2FTNGcvVUVHcmI5VzlYcVBLUzllVVBEd0wvY0tNZEh6dmRpRW1neFhESE9xZzExcU1wR2pTYkdMelNPUUMKZmlOTFV0QUswVVgvNFVSQ2pidUdqcEZmNHZ3NFNITTJJWkFyWXVhY3dFNHF1U0pQRzZoZFl0V0VPNnQ4MGtmRwpWTVYrWmdtUHE5TEZtM1R2VzZSY2s5czF5M3V3eEVVWllxeUdYTEduK1lrS25KL3pVd3ZGSFFHbjdRWWFrNWtaCnl6YXhZMFEzZ2hQeXFCbmlBRXRHTVBkeDlKeFltMCtRekdaMnQzUWNkOEV0cjRGMTcvdzF3eGJUdGdoRmk2WngKVXlYTzI3b1BmUmVnL0V3SmtpS2tRSEdlRUZKV0t2SWE0ZDAzMDZyMXVjcVRIMDRJaU1RcnpOK0ZRb002VC9tZgpOWmtDZ2dFQUsrRVJNVVdJZTE3V1k3VDIycy9lOEplN0xxSXlUcU9mSGovaWFUUjhHbXhqcU1HNEdod1RpVXJsCkh0Skhud3BMVGFjVmdYUjV3UmtYcEhRT2JqSUFzeVNBUGxwSzBvZUkyK2kvS0cyQjZ2U0cza0V2b1VZY0RlRk4KdzhHd0oxNDNTd21LQXM4eUtWMmd1RjhmRXNNVitEQzNzVHFlZXJmMy82bFprMUVCVFF0QTZqVHdqK0picXgwVgpaalZJUXBwUE8vc1VHdi9LZVE3MW5ockJpT0lXclJ0dDRTUDJ2aWx2em9DUTQxVjFqZ09wS3VwU3E1Y2J3VDRxCmp1bkJIMkx5VnNQaUc4M0Vha1JSUEhDK0craTk1MFJxckxVRUJOeVdHNGlMNTdUdU9xYVJuSmRnN2ZFb2lVLzMKNld4TjlvR2VRWjV0NjZkdTJEL01WSUZ4ZzJ1cXRBPT0KLS0tLS1FTkQgUFJJVkFURSBLRVktLS0tLQo=</prv>
</cert>
<syslog/>
</opnsense>

View File

@@ -1,826 +0,0 @@
<?xml version="1.0"?>
<opnsense>
<theme>opnsense</theme>
<sysctl version="1.0.1" persisted_at="1755708111.39">
<item/>
</sysctl>
<system>
<serialspeed>115200</serialspeed>
<primaryconsole>serial</primaryconsole>
<optimization>normal</optimization>
<hostname>OPNsense</hostname>
<domain>internal</domain>
<dnsallowoverride>1</dnsallowoverride>
<dnsallowoverride_exclude/>
<group uuid="67305f6f-7f7a-454d-8a4e-65cb8f072d81">
<gid>1999</gid>
<name>admins</name>
<scope>system</scope>
<description>System Administrators</description>
<priv>page-all</priv>
<member>0</member>
<source_networks/>
</group>
<user uuid="1d2ed537-5d1a-4772-9600-37b93f9f798b">
<uid>0</uid>
<name>root</name>
<disabled>0</disabled>
<scope>system</scope>
<expires/>
<authorizedkeys/>
<otp_seed/>
<shell/>
<password>$2y$10$YRVoF4SgskIsrXOvOQjGieB9XqHPRra9R7d80B3BZdbY/j21TwBfS</password>
<pwd_changed_at/>
<landing_page/>
<comment/>
<email/>
<apikeys/>
<priv/>
<language/>
<descr>System Administrator</descr>
<dashboard/>
</user>
<timezone>Etc/UTC</timezone>
<timeservers>0.opnsense.pool.ntp.org 1.opnsense.pool.ntp.org 2.opnsense.pool.ntp.org 3.opnsense.pool.ntp.org</timeservers>
<webgui>
<protocol>https</protocol>
<ssl-certref>68a5faf1685db</ssl-certref>
<port/>
<ssl-ciphers/>
<interfaces/>
<compression/>
</webgui>
<disablenatreflection>yes</disablenatreflection>
<usevirtualterminal>1</usevirtualterminal>
<disableconsolemenu>1</disableconsolemenu>
<disablevlanhwfilter>1</disablevlanhwfilter>
<disablechecksumoffloading>1</disablechecksumoffloading>
<disablesegmentationoffloading>1</disablesegmentationoffloading>
<disablelargereceiveoffloading>1</disablelargereceiveoffloading>
<ipv6allow>1</ipv6allow>
<powerd_ac_mode>hadp</powerd_ac_mode>
<powerd_battery_mode>hadp</powerd_battery_mode>
<powerd_normal_mode>hadp</powerd_normal_mode>
<bogons>
<interval>monthly</interval>
</bogons>
<pf_share_forward>1</pf_share_forward>
<lb_use_sticky>1</lb_use_sticky>
<ssh>
<group>admins</group>
<noauto>1</noauto>
<interfaces/>
<kex/>
<ciphers/>
<macs/>
<keys/>
<keysig/>
<rekeylimit/>
<enabled>enabled</enabled>
<passwordauth>1</passwordauth>
<permitrootlogin>1</permitrootlogin>
</ssh>
<rrdbackup>-1</rrdbackup>
<netflowbackup>-1</netflowbackup>
<firmware version="1.0.1" persisted_at="1755708111.32">
<mirror/>
<flavour/>
<plugins/>
<type/>
<subscription/>
<reboot>0</reboot>
</firmware>
<dnsserver/>
<language>en_US</language>
</system>
<interfaces>
<wan>
<enable>1</enable>
<if>vtnet0</if>
<mtu/>
<ipaddr>dhcp</ipaddr>
<ipaddrv6>dhcp6</ipaddrv6>
<subnet/>
<gateway/>
<blockpriv>0</blockpriv>
<blockbogons>1</blockbogons>
<media/>
<mediaopt/>
<dhcp6-ia-pd-len>0</dhcp6-ia-pd-len>
<dhcphostname/>
<spoofmac/>
<mss/>
</wan>
<lan>
<enable>1</enable>
<if>vtnet1</if>
<ipaddr>192.168.1.1</ipaddr>
<subnet>24</subnet>
<ipaddrv6>track6</ipaddrv6>
<subnetv6>64</subnetv6>
<media/>
<mediaopt/>
<track6-interface>wan</track6-interface>
<track6-prefix-id>0</track6-prefix-id>
</lan>
<lo0>
<internal_dynamic>1</internal_dynamic>
<descr>Loopback</descr>
<enable>1</enable>
<if>lo0</if>
<ipaddr>127.0.0.1</ipaddr>
<ipaddrv6>::1</ipaddrv6>
<subnet>8</subnet>
<subnetv6>128</subnetv6>
<type>none</type>
<virtual>1</virtual>
</lo0>
</interfaces>
<dnsmasq version="1.0.7" persisted_at="1755721633.54">
<enable>1</enable>
<regdhcp>0</regdhcp>
<regdhcpstatic>0</regdhcpstatic>
<dhcpfirst>0</dhcpfirst>
<strict_order>0</strict_order>
<domain_needed>0</domain_needed>
<no_private_reverse>0</no_private_reverse>
<no_resolv>0</no_resolv>
<log_queries>0</log_queries>
<no_hosts>0</no_hosts>
<strictbind>0</strictbind>
<dnssec>0</dnssec>
<regdhcpdomain/>
<interface>lan</interface>
<port>0</port>
<dns_forward_max/>
<cache_size/>
<local_ttl/>
<add_mac/>
<add_subnet>0</add_subnet>
<strip_subnet>0</strip_subnet>
<dhcp>
<no_interface/>
<fqdn>1</fqdn>
<domain/>
<lease_max/>
<authoritative>0</authoritative>
<default_fw_rules>1</default_fw_rules>
<reply_delay/>
<enable_ra>0</enable_ra>
<nosync>0</nosync>
</dhcp>
<no_ident>1</no_ident>
<dhcp_ranges uuid="78b5c4a4-565d-4cd7-af10-29050f29e494">
<interface>lan</interface>
<set_tag/>
<start_addr>192.168.1.41</start_addr>
<end_addr>192.168.1.245</end_addr>
<subnet_mask/>
<constructor/>
<mode/>
<prefix_len/>
<lease_time/>
<domain_type>range</domain_type>
<domain/>
<nosync>0</nosync>
<ra_mode/>
<ra_priority/>
<ra_mtu/>
<ra_interval/>
<ra_router_lifetime/>
<description/>
</dhcp_ranges>
</dnsmasq>
<snmpd>
<syslocation/>
<syscontact/>
<rocommunity>public</rocommunity>
</snmpd>
<nat>
<outbound>
<mode>automatic</mode>
</outbound>
</nat>
<filter>
<rule>
<type>pass</type>
<ipprotocol>inet</ipprotocol>
<descr>Default allow LAN to any rule</descr>
<interface>lan</interface>
<source>
<network>lan</network>
</source>
<destination>
<any/>
</destination>
</rule>
<rule>
<type>pass</type>
<ipprotocol>inet6</ipprotocol>
<descr>Default allow LAN IPv6 to any rule</descr>
<interface>lan</interface>
<source>
<network>lan</network>
</source>
<destination>
<any/>
</destination>
</rule>
</filter>
<rrd>
<enable/>
</rrd>
<ntpd>
<prefer>0.opnsense.pool.ntp.org</prefer>
</ntpd>
<revision>
<username>root@192.168.1.5</username>
<description>/system_advanced_admin.php made changes</description>
<time>1755721653.06</time>
</revision>
<OPNsense>
<wireguard>
<client version="1.0.0" persisted_at="1755708111.04">
<clients/>
</client>
<general version="0.0.1" persisted_at="1755708111.05">
<enabled>0</enabled>
</general>
<server version="1.0.0" persisted_at="1755708111.05">
<servers/>
</server>
</wireguard>
<IPsec version="1.0.4" persisted_at="1755708111.06">
<general>
<enabled/>
<preferred_oldsa>0</preferred_oldsa>
<disablevpnrules>0</disablevpnrules>
<passthrough_networks/>
<user_source/>
<local_group/>
</general>
<charon>
<max_ikev1_exchanges/>
<threads>16</threads>
<ikesa_table_size>32</ikesa_table_size>
<ikesa_table_segments>4</ikesa_table_segments>
<init_limit_half_open>1000</init_limit_half_open>
<ignore_acquire_ts>1</ignore_acquire_ts>
<install_routes>0</install_routes>
<cisco_unity>0</cisco_unity>
<make_before_break>0</make_before_break>
<retransmit_tries/>
<retransmit_timeout/>
<retransmit_base/>
<retransmit_jitter/>
<retransmit_limit/>
<syslog>
<daemon>
<ike_name>1</ike_name>
<log_level>0</log_level>
<app>1</app>
<asn>1</asn>
<cfg>1</cfg>
<chd>1</chd>
<dmn>1</dmn>
<enc>1</enc>
<esp>1</esp>
<ike>1</ike>
<imc>1</imc>
<imv>1</imv>
<job>1</job>
<knl>1</knl>
<lib>1</lib>
<mgr>1</mgr>
<net>1</net>
<pts>1</pts>
<tls>1</tls>
<tnc>1</tnc>
</daemon>
</syslog>
<plugins>
<attr>
<subnet/>
<split-include/>
<x_28674/>
<x_28675/>
<x_28672/>
<x_28673>0</x_28673>
<x_28679/>
<dns/>
<nbns/>
</attr>
<eap-radius>
<servers/>
<accounting>0</accounting>
<class_group>0</class_group>
</eap-radius>
<xauth-pam>
<pam_service>ipsec</pam_service>
<session>0</session>
<trim_email>1</trim_email>
</xauth-pam>
</plugins>
</charon>
<keyPairs/>
<preSharedKeys/>
</IPsec>
<Swanctl version="1.0.0" persisted_at="1755708111.08">
<Connections/>
<locals/>
<remotes/>
<children/>
<Pools/>
<VTIs/>
<SPDs/>
</Swanctl>
<OpenVPNExport version="0.0.1" persisted_at="1755708111.40">
<servers/>
</OpenVPNExport>
<OpenVPN version="1.0.1" persisted_at="1755708111.40">
<Overwrites/>
<Instances/>
<StaticKeys/>
</OpenVPN>
<captiveportal version="1.0.4" persisted_at="1755708111.41">
<zones/>
<templates/>
</captiveportal>
<cron version="1.0.4" persisted_at="1755708111.43">
<jobs/>
</cron>
<DHCRelay version="1.0.1" persisted_at="1755708111.43"/>
<Firewall>
<Lvtemplate version="0.0.1" persisted_at="1755708111.45">
<templates/>
</Lvtemplate>
<Alias version="1.0.1" persisted_at="1755708111.65">
<geoip>
<url/>
</geoip>
<aliases/>
</Alias>
<Category version="1.0.0" persisted_at="1755708111.65">
<categories/>
</Category>
<Filter version="1.0.4" persisted_at="1755708111.70">
<rules/>
<snatrules/>
<npt/>
<onetoone/>
</Filter>
</Firewall>
<Netflow version="1.0.1" persisted_at="1755708111.45">
<capture>
<interfaces/>
<egress_only/>
<version>v9</version>
<targets/>
</capture>
<collect>
<enable>0</enable>
</collect>
<activeTimeout>1800</activeTimeout>
<inactiveTimeout>15</inactiveTimeout>
</Netflow>
<IDS version="1.1.0" persisted_at="1755708111.90">
<rules/>
<policies/>
<userDefinedRules/>
<files/>
<fileTags/>
<general>
<enabled>0</enabled>
<ips>0</ips>
<promisc>0</promisc>
<interfaces>wan</interfaces>
<homenet>192.168.0.0/16,10.0.0.0/8,172.16.0.0/12</homenet>
<defaultPacketSize/>
<UpdateCron/>
<AlertLogrotate>W0D23</AlertLogrotate>
<AlertSaveLogs>4</AlertSaveLogs>
<MPMAlgo/>
<detect>
<Profile/>
<toclient_groups/>
<toserver_groups/>
</detect>
<syslog>0</syslog>
<syslog_eve>0</syslog_eve>
<LogPayload>0</LogPayload>
<verbosity/>
<eveLog>
<http>
<enable>0</enable>
<extended>0</extended>
<dumpAllHeaders/>
</http>
<tls>
<enable>0</enable>
<extended>0</extended>
<sessionResumption>0</sessionResumption>
<custom/>
</tls>
</eveLog>
</general>
</IDS>
<Interfaces>
<loopbacks version="1.0.0" persisted_at="1755708111.95"/>
<neighbors version="1.0.0" persisted_at="1755708111.96"/>
<vxlans version="1.0.2" persisted_at="1755708111.99"/>
</Interfaces>
<Kea>
<ctrl_agent version="0.0.1" persisted_at="1755708111.99">
<general>
<enabled>0</enabled>
<http_host>127.0.0.1</http_host>
<http_port>8000</http_port>
</general>
</ctrl_agent>
<dhcp4 version="1.0.4" persisted_at="1755708112.00">
<general>
<enabled>0</enabled>
<manual_config>0</manual_config>
<interfaces/>
<valid_lifetime>4000</valid_lifetime>
<fwrules>1</fwrules>
<dhcp_socket_type>raw</dhcp_socket_type>
</general>
<ha>
<enabled>0</enabled>
<this_server_name/>
<max_unacked_clients>2</max_unacked_clients>
</ha>
<subnets/>
<reservations/>
<ha_peers/>
</dhcp4>
<dhcp6 version="1.0.0" persisted_at="1755708112.00">
<general>
<enabled>0</enabled>
<manual_config>0</manual_config>
<interfaces/>
<valid_lifetime>4000</valid_lifetime>
<fwrules>1</fwrules>
</general>
<ha>
<enabled>0</enabled>
<this_server_name/>
<max_unacked_clients>2</max_unacked_clients>
</ha>
<subnets/>
<reservations/>
<pd_pools/>
<ha_peers/>
</dhcp6>
</Kea>
<monit version="1.0.13" persisted_at="1755708112.02">
<general>
<enabled>0</enabled>
<interval>120</interval>
<startdelay>120</startdelay>
<mailserver>127.0.0.1</mailserver>
<port>25</port>
<username/>
<password/>
<ssl>0</ssl>
<sslversion>auto</sslversion>
<sslverify>1</sslverify>
<logfile/>
<statefile/>
<eventqueuePath/>
<eventqueueSlots/>
<httpdEnabled>0</httpdEnabled>
<httpdUsername>root</httpdUsername>
<httpdPassword/>
<httpdPort>2812</httpdPort>
<httpdAllow/>
<mmonitUrl/>
<mmonitTimeout>5</mmonitTimeout>
<mmonitRegisterCredentials>1</mmonitRegisterCredentials>
</general>
<alert uuid="76cd5195-a487-4f4f-8ef5-4f9815bf19e5">
<enabled>0</enabled>
<recipient>root@localhost.local</recipient>
<noton>0</noton>
<events/>
<format/>
<reminder/>
<description/>
</alert>
<service uuid="11610907-f700-4f0a-9926-c25a47709493">
<enabled>1</enabled>
<name>$HOST</name>
<description/>
<type>system</type>
<pidfile/>
<match/>
<path/>
<timeout>300</timeout>
<starttimeout>30</starttimeout>
<address/>
<interface/>
<start/>
<stop/>
<tests>02014be3-fc31-4af3-a0d5-061eaa67d28a,ebfd0d97-ae21-45d5-8b42-5220c75ce46f,d37f25f0-89e3-44b6-8ad2-280ac83a8904,37afd0d9-990c-4f03-a817-45691461e3d0</tests>
<depends/>
<polltime/>
</service>
<service uuid="dcc7e42f-a878-49a3-8b90-67faf21d67bd">
<enabled>1</enabled>
<name>RootFs</name>
<description/>
<type>filesystem</type>
<pidfile/>
<match/>
<path>/</path>
<timeout>300</timeout>
<starttimeout>30</starttimeout>
<address/>
<interface/>
<start/>
<stop/>
<tests>b44b859c-bc72-4c2e-82c9-4f56d84a5497</tests>
<depends/>
<polltime/>
</service>
<service uuid="5ca544d4-846a-4f61-9d6f-c191eafcd9fb">
<enabled>0</enabled>
<name>carp_status_change</name>
<description/>
<type>custom</type>
<pidfile/>
<match/>
<path>/usr/local/opnsense/scripts/OPNsense/Monit/carp_status</path>
<timeout>300</timeout>
<starttimeout>30</starttimeout>
<address/>
<interface/>
<start/>
<stop/>
<tests>0909801b-cd11-41c8-afeb-369396247308</tests>
<depends/>
<polltime/>
</service>
<service uuid="6e16e26e-e732-4b9b-ac92-2086b84f3158">
<enabled>0</enabled>
<name>gateway_alert</name>
<description/>
<type>custom</type>
<pidfile/>
<match/>
<path>/usr/local/opnsense/scripts/OPNsense/Monit/gateway_alert</path>
<timeout>300</timeout>
<starttimeout>30</starttimeout>
<address/>
<interface/>
<start/>
<stop/>
<tests>56e67d76-cef6-4167-a51e-2c69a921ebc9</tests>
<depends/>
<polltime/>
</service>
<test uuid="b7671673-5cf1-4123-a58c-37f57a8e9d59">
<name>Ping</name>
<type>NetworkPing</type>
<condition>failed ping</condition>
<action>alert</action>
<path/>
</test>
<test uuid="30cf3664-5c6f-4ac5-a52a-36023270c6fb">
<name>NetworkLink</name>
<type>NetworkInterface</type>
<condition>failed link</condition>
<action>alert</action>
<path/>
</test>
<test uuid="a7c0836a-b102-4f37-a73b-2a50903ffcc8">
<name>NetworkSaturation</name>
<type>NetworkInterface</type>
<condition>saturation is greater than 75%</condition>
<action>alert</action>
<path/>
</test>
<test uuid="02014be3-fc31-4af3-a0d5-061eaa67d28a">
<name>MemoryUsage</name>
<type>SystemResource</type>
<condition>memory usage is greater than 75%</condition>
<action>alert</action>
<path/>
</test>
<test uuid="ebfd0d97-ae21-45d5-8b42-5220c75ce46f">
<name>CPUUsage</name>
<type>SystemResource</type>
<condition>cpu usage is greater than 75%</condition>
<action>alert</action>
<path/>
</test>
<test uuid="d37f25f0-89e3-44b6-8ad2-280ac83a8904">
<name>LoadAvg1</name>
<type>SystemResource</type>
<condition>loadavg (1min) is greater than 4</condition>
<action>alert</action>
<path/>
</test>
<test uuid="37afd0d9-990c-4f03-a817-45691461e3d0">
<name>LoadAvg5</name>
<type>SystemResource</type>
<condition>loadavg (5min) is greater than 3</condition>
<action>alert</action>
<path/>
</test>
<test uuid="608d2888-9df5-486d-bbfb-bf17fad75a7e">
<name>LoadAvg15</name>
<type>SystemResource</type>
<condition>loadavg (15min) is greater than 2</condition>
<action>alert</action>
<path/>
</test>
<test uuid="b44b859c-bc72-4c2e-82c9-4f56d84a5497">
<name>SpaceUsage</name>
<type>SpaceUsage</type>
<condition>space usage is greater than 75%</condition>
<action>alert</action>
<path/>
</test>
<test uuid="0909801b-cd11-41c8-afeb-369396247308">
<name>ChangedStatus</name>
<type>ProgramStatus</type>
<condition>changed status</condition>
<action>alert</action>
<path/>
</test>
<test uuid="56e67d76-cef6-4167-a51e-2c69a921ebc9">
<name>NonZeroStatus</name>
<type>ProgramStatus</type>
<condition>status != 0</condition>
<action>alert</action>
<path/>
</test>
</monit>
<Gateways version="1.0.0" persisted_at="1755721633.53"/>
<Syslog version="1.0.2" persisted_at="1755708112.05">
<general>
<enabled>1</enabled>
<loglocal>1</loglocal>
<maxpreserve>31</maxpreserve>
<maxfilesize/>
</general>
<destinations/>
</Syslog>
<TrafficShaper version="1.0.3" persisted_at="1755708112.06">
<pipes/>
<queues/>
<rules/>
</TrafficShaper>
<trust>
<general version="1.0.1" persisted_at="1755708112.22">
<store_intermediate_certs>0</store_intermediate_certs>
<install_crls>0</install_crls>
<fetch_crls>0</fetch_crls>
<enable_legacy_sect>1</enable_legacy_sect>
<enable_config_constraints>0</enable_config_constraints>
<CipherString/>
<Ciphersuites/>
<SignatureAlgorithms/>
<groups/>
<MinProtocol/>
<MinProtocol_DTLS/>
</general>
</trust>
<unboundplus version="1.0.12" persisted_at="1755708112.29">
<general>
<enabled>1</enabled>
<port>53</port>
<stats>0</stats>
<active_interface/>
<dnssec>0</dnssec>
<dns64>0</dns64>
<dns64prefix/>
<noarecords>0</noarecords>
<regdhcp>0</regdhcp>
<regdhcpdomain/>
<regdhcpstatic>0</regdhcpstatic>
<noreglladdr6>0</noreglladdr6>
<noregrecords>0</noregrecords>
<txtsupport>0</txtsupport>
<cacheflush>0</cacheflush>
<local_zone_type>transparent</local_zone_type>
<outgoing_interface/>
<enable_wpad>0</enable_wpad>
</general>
<advanced>
<hideidentity>0</hideidentity>
<hideversion>0</hideversion>
<prefetch>0</prefetch>
<prefetchkey>0</prefetchkey>
<dnssecstripped>0</dnssecstripped>
<aggressivensec>1</aggressivensec>
<serveexpired>0</serveexpired>
<serveexpiredreplyttl/>
<serveexpiredttl/>
<serveexpiredttlreset>0</serveexpiredttlreset>
<serveexpiredclienttimeout/>
<qnameminstrict>0</qnameminstrict>
<extendedstatistics>0</extendedstatistics>
<logqueries>0</logqueries>
<logreplies>0</logreplies>
<logtagqueryreply>0</logtagqueryreply>
<logservfail>0</logservfail>
<loglocalactions>0</loglocalactions>
<logverbosity>1</logverbosity>
<valloglevel>0</valloglevel>
<privatedomain/>
<privateaddress>0.0.0.0/8,10.0.0.0/8,100.64.0.0/10,169.254.0.0/16,172.16.0.0/12,192.0.2.0/24,192.168.0.0/16,198.18.0.0/15,198.51.100.0/24,203.0.113.0/24,233.252.0.0/24,::1/128,2001:db8::/32,fc00::/8,fd00::/8,fe80::/10</privateaddress>
<insecuredomain/>
<msgcachesize/>
<rrsetcachesize/>
<outgoingnumtcp/>
<incomingnumtcp/>
<numqueriesperthread/>
<outgoingrange/>
<jostletimeout/>
<discardtimeout/>
<cachemaxttl/>
<cachemaxnegativettl/>
<cacheminttl/>
<infrahostttl/>
<infrakeepprobing>0</infrakeepprobing>
<infracachenumhosts/>
<unwantedreplythreshold/>
</advanced>
<acls>
<default_action>allow</default_action>
</acls>
<dnsbl>
<enabled>0</enabled>
<safesearch>0</safesearch>
<type/>
<lists/>
<whitelists/>
<blocklists/>
<wildcards/>
<address/>
<nxdomain>0</nxdomain>
</dnsbl>
<forwarding>
<enabled>0</enabled>
</forwarding>
<dots/>
<hosts/>
<aliases/>
</unboundplus>
</OPNsense>
<hasync version="1.0.2" persisted_at="1755708111.35">
<disablepreempt>0</disablepreempt>
<disconnectppps>0</disconnectppps>
<pfsyncinterface/>
<pfsyncpeerip/>
<pfsyncversion>1400</pfsyncversion>
<synchronizetoip/>
<verifypeer>0</verifypeer>
<username/>
<password/>
<syncitems/>
</hasync>
<openvpn/>
<ifgroups version="1.0.0" persisted_at="1755708111.71"/>
<bridges version="1.0.0" persisted_at="1755708111.91">
<bridged/>
</bridges>
<gifs version="1.0.0" persisted_at="1755708111.92">
<gif/>
</gifs>
<gres version="1.0.0" persisted_at="1755708111.93">
<gre/>
</gres>
<laggs version="1.0.0" persisted_at="1755708111.95">
<lagg/>
</laggs>
<virtualip version="1.0.1" persisted_at="1755708111.96">
<vip/>
</virtualip>
<vlans version="1.0.0" persisted_at="1755708111.98">
<vlan/>
</vlans>
<staticroutes version="1.0.0" persisted_at="1755708112.03"/>
<ppps>
<ppp/>
</ppps>
<wireless>
<clone/>
</wireless>
<ca/>
<dhcpd/>
<dhcpdv6/>
<cert uuid="d60972af-642c-411f-abb6-43e0d680fefd">
<refid>68a5faf1685db</refid>
<descr>Web GUI TLS certificate</descr>
<caref/>
<crt>LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUhFakNDQlBxZ0F3SUJBZ0lVQlpQYjUwMXNaM3hhTTZzSDVUM1ZNRG9mcnlNd0RRWUpLb1pJaHZjTkFRRUwKQlFBd2dZWXhHakFZQmdOVkJBTU1FVTlRVG5ObGJuTmxMbWx1ZEdWeWJtRnNNUXN3Q1FZRFZRUUdFd0pPVERFVgpNQk1HQTFVRUNBd01XblZwWkMxSWIyeHNZVzVrTVJVd0V3WURWUVFIREF4TmFXUmtaV3hvWVhKdWFYTXhMVEFyCkJnTlZCQW9NSkU5UVRuTmxibk5sSUhObGJHWXRjMmxuYm1Wa0lIZGxZaUJqWlhKMGFXWnBZMkYwWlRBZUZ3MHkKTlRBNE1qQXhOalF5TWpaYUZ3MHlOakE1TWpFeE5qUXlNalphTUlHR01Sb3dHQVlEVlFRRERCRlBVRTV6Wlc1egpaUzVwYm5SbGNtNWhiREVMTUFrR0ExVUVCaE1DVGt3eEZUQVRCZ05WQkFnTURGcDFhV1F0U0c5c2JHRnVaREVWCk1CTUdBMVVFQnd3TVRXbGtaR1ZzYUdGeWJtbHpNUzB3S3dZRFZRUUtEQ1JQVUU1elpXNXpaU0J6Wld4bUxYTnAKWjI1bFpDQjNaV0lnWTJWeWRHbG1hV05oZEdVd2dnSWlNQTBHQ1NxR1NJYjNEUUVCQVFVQUE0SUNEd0F3Z2dJSwpBb0lDQVFEQVozZk1lakdOckZYVFhZd24vTUp5YXBYMDdVOTVWZ0JERzZmWU1wZkZEU1NXeFRValRLRlNvR3JRCkpDb0ZyQ0NRQ3BsWnlua0ZjVFZvWVAraGszcndWWmxHZVFyL0RyR2ZiM1lPc2RGbEtublo5YzRTOGVrSkc2WTIKYWI0eFJTcnBja0hoTWQ4MHNENUFRdks1Skc2MS9UMEhNRXVMcGlraUF3MFpCempWbjlVUUpSRTJiS29kOW9IdgpEbG5DVGNTV1FUNWYrV3A0Sll0anVBVHBZMUlRVW4wbkxuWDBHUC9JRGtsWjFrdEZOczRPOGcrQmRVWFU0MUdvCjNGTFpCc1hQVm90WFVrRVl2R0ZldjJRMlBwSnNib25aWEpoR255amREUlZkZFZGOGhacGJua0wwU2xJTVFNeFQKSTRXK051ZmUvUTZGRDdmZnFRa0toemZ3SlJ2N0dGM2RTRlEwWldPUGNTZVZXY3lWTlVVMTMvcnBteUpvNXZhWQpYR000THcxb1d2c1FjUWxaUllnSkxhSWMzM0dnMGQySHhvZTIvdytIeFRXOEw4ZldqbzgxK250YWJXTlZhV0IwCnd6TXNFNGRBOWtxR2dmcWZjbm96ckovamJtNEFBTTB6QTlVZFh0SUJtRGdpeFNkVzB4eHNQNlZWRTdMdnlURTgKWnBJTjRoL1FCYURyc2hhRXJ6TXhUd01IZXJ1RlV4bFpxZEdSa3ErQXRJU1VwRE05VXB0YWZZMk5ydzgxVDFhSwoycFduVFlFQktCUnVwdk00TzFHNXN5NU5GZm13NFRTc0pqRUhtMFEvVTZ4ZU45bVg0OFhIYUZFNnhvQTJEZEMrCjJHS2lKWFlrYi9nZm13cmp1Y3NGWGpBS2tDNWF6ZXFqaERXeGxDbmJNS1YwSTQxOWp3SURBUUFCbzRJQmREQ0MKQVhBd0NRWURWUjBUQkFJd0FEQVJCZ2xnaGtnQmh2aENBUUVFQkFNQ0JrQXdOQVlKWUlaSUFZYjRRZ0VOQkNjVwpKVTlRVG5ObGJuTmxJRWRsYm1WeVlYUmxaQ0JUWlhKMlpYSWdRMlZ5ZEdsbWFXTmhkR1V3SFFZRFZSME9CQllFCkZMK2YzU0tCM0tMSi9nWStBUTJIZDBhTzVPRU1NSUd3QmdOVkhTTUVnYWd3Z2FXaGdZeWtnWWt3Z1lZeEdqQVkKQmdOVkJBTU1FVTlRVG5ObGJuTmxMbWx1ZEdWeWJtRnNNUXN3Q1FZRFZRUUdFd0pPVERFVk1CTUdBMVVFQ0F3TQpXblZwWkMxSWIyeHNZVzVrTVJVd0V3WURWUVFIREF4TmFXUmtaV3hvWVhKdWFYTXhMVEFyQmdOVkJBb01KRTlRClRuTmxibk5sSUhObGJHWXRjMmxuYm1Wa0lIZGxZaUJqWlhKMGFXWnBZMkYwWllJVUJaUGI1MDFzWjN4YU02c0gKNVQzVk1Eb2ZyeU13SFFZRFZSMGxCQll3RkFZSUt3WUJCUVVIQXdFR0NDc0dBUVVGQ0FJQ01Bc0dBMVVkRHdRRQpBd0lGb0RBY0JnTlZIUkVFRlRBVGdoRlBVRTV6Wlc1elpTNXBiblJsY201aGJEQU5CZ2txaGtpRzl3MEJBUXNGCkFBT0NBZ0VBY3F3VEN0RmVwWDlFOEFMYXRaWEE4dkN2b1YwZkxuaElPM0lWT2tZNEhYMTRRRU9NUGo4KzVXMXoKc3hyUGNscEJQVU5tbXJRb0hWUHBuWTRFM2hFanlYY1JWSzZtTEFrSkpRUDJPOEE2NFFpL3FNSkhCYUlEU0MzKwpqMHdkMGYyODRvcEppQ0F2UnF0SGh1bDd3akd4QzNZUXRxWTFMODNWUHBOWGRjOVVsZExTUGZ6WWluMlBPekMrClFDWU9qN3VQUDlCVGExTURudkdPdkdrOHdQeUJGaFZQWVFBWjYwb1ZaS2psOUI2R1piRzF1SG1ON3p3a3k0eEIKNk1RSlF3cHFscDdXQ09QSHJlZTFiVGZaMkJMZFQrZzJHakVMT0xNRGJMTHAzNUw0blBDUmNGRkJZNEszMHBncQpVWjdncEtNTmhuR3huQ29lR3dHUk54ZHFoZnNlT3FqaURVM0hBVDEya3FReU1vOEZNT1g1bG9EaVpXSGZTS0JrClVRaG5Tc1BTMG0vZ2dSRVgwNVQzYWM2NUxNOVVXMEs2MXppZUs5WFhTcjNXWlQ1TkhNV2JmU0VScUR4SGRtZ0YKeGt3YXkxZWpCZTFoZzdGMGpicTVDMGo1ZXB5ZDNOc1BTVUtDY2FDNi9aWTNkUHVYWVZZL0J2dnFsZHZJSzRBeAo0R1BLQ0xzaStGRjliSXZRWG4zbTV2KzJoQWF2WlpxcmJOTFUzVFQ0aDd2QllBOVRNcXpwd3lEaW5BR3RhaEE3CnhDSW5IU01kZXpQcnNkZDJrTW5TckhPdWtGeGdKb2lNZ3krVlJmdTdvZk9NMjhYQ1FySWF6djBFYmFhTU1ZMTcKRzlXOFd3SXZUb2lYY1ZWNTk3K1NUNHRsSTVIM3lDZFBtSk1kRm5GcDg1K2JzbW42MFQwPQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==</crt>
<csr/>
<prv>LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUpRZ0lCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQ1N3d2dna29BZ0VBQW9JQ0FRREFaM2ZNZWpHTnJGWFQKWFl3bi9NSnlhcFgwN1U5NVZnQkRHNmZZTXBmRkRTU1d4VFVqVEtGU29HclFKQ29GckNDUUNwbFp5bmtGY1RWbwpZUCtoazNyd1ZabEdlUXIvRHJHZmIzWU9zZEZsS25uWjljNFM4ZWtKRzZZMmFiNHhSU3JwY2tIaE1kODBzRDVBClF2SzVKRzYxL1QwSE1FdUxwaWtpQXcwWkJ6alZuOVVRSlJFMmJLb2Q5b0h2RGxuQ1RjU1dRVDVmK1dwNEpZdGoKdUFUcFkxSVFVbjBuTG5YMEdQL0lEa2xaMWt0Rk5zNE84ZytCZFVYVTQxR28zRkxaQnNYUFZvdFhVa0VZdkdGZQp2MlEyUHBKc2JvblpYSmhHbnlqZERSVmRkVkY4aFpwYm5rTDBTbElNUU14VEk0VytOdWZlL1E2RkQ3ZmZxUWtLCmh6ZndKUnY3R0YzZFNGUTBaV09QY1NlVldjeVZOVVUxMy9ycG15Sm81dmFZWEdNNEx3MW9XdnNRY1FsWlJZZ0oKTGFJYzMzR2cwZDJIeG9lMi93K0h4VFc4TDhmV2pvODErbnRhYldOVmFXQjB3ek1zRTRkQTlrcUdnZnFmY25vegpySi9qYm00QUFNMHpBOVVkWHRJQm1EZ2l4U2RXMHh4c1A2VlZFN0x2eVRFOFpwSU40aC9RQmFEcnNoYUVyek14ClR3TUhlcnVGVXhsWnFkR1JrcStBdElTVXBETTlVcHRhZlkyTnJ3ODFUMWFLMnBXblRZRUJLQlJ1cHZNNE8xRzUKc3k1TkZmbXc0VFNzSmpFSG0wUS9VNnhlTjltWDQ4WEhhRkU2eG9BMkRkQysyR0tpSlhZa2IvZ2Ztd3JqdWNzRgpYakFLa0M1YXplcWpoRFd4bENuYk1LVjBJNDE5andJREFRQUJBb0lDQUNVVkRBTE4zalVXN09leTFPdDBES251Cm52NDRxcU9SRHJYZ1k2WUlnalhKUmE4RlRTdURmbWdsWU5EQzE1S0dUVFJWeHA2R3BuS0ZFaTBPM05Yd1RiWjYKV1BNN0t3SmplNXBsNmhRRTgzMlRCUzhiNzk2NDN4Z1JTeVNibHJ0NlFENEQ5bXlIcHlSSmY0WDFJVURMbzhiUgppdXlTdzB5ajlyT0djUVRNM29oVnFNUFcwUTF6UGdwT1UxYVdwbmdMY3dNZWlmNEhYUnpRNTUrTmZPemFacHVjCnVtQk4xUS81clhxS1BscmhNVnFpcUc0Nit3QVJjU2NKdE5oZHRsMzdyeTQ1Mk5zNGtERkxSVnowZUVUNEpGSmYKcjVQRUE5bEFuYWlVOS9RdVEwbERtcTlqdmpYRkNURXhYKy82SGJHK2RVd0Y2OEY3ZVEzVFQxbkhHK0hkMVJsbgpOWm1JM0p2d0Z1cG9JeU9VdlpJb3VGVmo2ak8ra0JLejkza1BHWmdMbnNmUUw5WDhRbTU3cjh4K3Z1eFNudGI1CjV4WVBxRkdrOWQrbDUwbTlQakdkekxGT3UwYnJ5TmQ4MFVMS2tuUlFtUVpvTngxck5GTUxpSjNlZENWUS9lclUKT1BDQ0Z0WEJMemJGTjR2ZzVWRjZMUkhvZGxqcEgxRzJOSXNoSzJhc1FuWS9RWDFpUUNLSk1tWERSUndMTWVsNQp3MUF4T2FqYVkzbWx2ZlRVd2xqdkE3a0tFUDBvZzRPeXZldDA2WTVRWk1EQXc1V00yT0pZVDVxcmFlYjZDbTdMCjlNckk4bG50TGp3WFVSZG4yU3U2RCtCWXNpcC9KK3BvOFNqYlJBaGJIc0lJbkJ1QWJnbGxqdTB2QXRXZmFkQlQKOTg4YnUwK3VUb1Q2T1Jkbk84Y1JBb0lCQVFEcStWYkVUQWVpSHN6K29jZnFWV3VwVHJrc2FNNm1wUUMwb0tqZApwb1FzWGVuTmNiNThHQ3FhWHkvdTJHWmNKUnR1QXRHamEyUVpEUUFUSjQyVTFmaTFhWm90Y053eXhPdmlud1NjCmVLZyt0ZGcwdW9LeGs2aXJKRFptaDBIK3Ewblg2RFJYK25RNDVmWVNmRkRFK0ZLd1lac0dQMkhYV3dKaVZ6OE0KU2NkL2pETTFRTWV2OXIzZWx1dS9DWFlvZ1N0N00wMklyczVoNjRuNjFmdVZjNHI4YmUwdFkrUTVsUnlwWk9NVwpkQ2VkWGFOV3RaNjF2bEFxamNiWkpkdXFBUjJjNzAyR3NML201TXA4Zmd3YmY2aG51TXJLaVlpQjlZalZxalc2CmYyUW1PclZtMUk0MFJBMC9OaFBTR2NXejBkNXZrdXY0VHUra2JFbERZTCsxaHY1M0FvSUJBUURSbnZaTmJaa1UKTXpmUTRLWEdML3dLUXJEbjNvL0RENWVBR1ZDTGcwTUkyYlAxYWpubHNVTjE4NCs1UWF6cVVOaWlZT3laODczeQpQYkw0cTBOZWFDYXdxby9WbjJMSkVIUFVTTVhUWjB4ckxTa1hPUjFuMDUwT2tDWXhVbFpOUXFvZU1xcHJGNXZLCm1NNlJxalN4NS8ydU9IUlR1SDRVV2RETEpwTDVUN2RpUCtXcFUwSDlSUWhrNDdkQUJaUjZEZjNxaDJEYmVxUWoKdWcxY0hWUVNqaldhUGpVZGlLR2dHemdvdlE2UkdNZDA1UVUzdkRMdzBCSkNPQ25XV2x0VXkvMW1jMUpPUHR2ZQp4UGltV2tRNmlkRHZ4RGZFRGg5U05zY1FPMnBTVjZxNnhCTWlqVGgvTldGN2NsOU1LYUhJWGxzTmt0RFVXWHZyCmNKRlM4eE1TcDhlcEFvSUJBUUNtWktVTjRxMHhIOUNZckdYT1Nta3Yvc0JnYzJPTFhLTXdSZWp1OVFENkRoTUgKMmZsREZUWHVGV1B6SmlqdUxaVE1CWkVBd1lhanVySUgzbVdETlRhbStMNG1XWnFGRlMvWlRqUk12YUNlcjlVSQpHZDk4OG94cGpQNDlBcUU0UDRIT00vQUZNU1ZtT1dwVTB0VzdkZ0hRUjM0cElXOGV1cUxva3RIaDJNaytTRURuCkFCV29SUGxWaTlncmN2N0tWaFk5YXlvSGxZb3VpMFl0YTZSNXc5VnpSa0REZU01Zi9Iak1kOVhieTZ0VjQ3NU0KSTliYzZvVUliVmVYNUJnMnZnMkRXVzZ6NTZ3dFRHMGJWWU1yWWU0V2JTU2w0bGpaZHM5TVJ2ay9OUUR0bFh0cAo4ekUwVDlCMXA4ekhabHE3S08zMFlyMVpIRVRWVVoxYjZrSTN3UDJuQW9JQkFFZ1VGKzlCMjF4RnpGQ0hucGtLClVPa2FTNGcvVUVHcmI5VzlYcVBLUzllVVBEd0wvY0tNZEh6dmRpRW1neFhESE9xZzExcU1wR2pTYkdMelNPUUMKZmlOTFV0QUswVVgvNFVSQ2pidUdqcEZmNHZ3NFNITTJJWkFyWXVhY3dFNHF1U0pQRzZoZFl0V0VPNnQ4MGtmRwpWTVYrWmdtUHE5TEZtM1R2VzZSY2s5czF5M3V3eEVVWllxeUdYTEduK1lrS25KL3pVd3ZGSFFHbjdRWWFrNWtaCnl6YXhZMFEzZ2hQeXFCbmlBRXRHTVBkeDlKeFltMCtRekdaMnQzUWNkOEV0cjRGMTcvdzF3eGJUdGdoRmk2WngKVXlYTzI3b1BmUmVnL0V3SmtpS2tRSEdlRUZKV0t2SWE0ZDAzMDZyMXVjcVRIMDRJaU1RcnpOK0ZRb002VC9tZgpOWmtDZ2dFQUsrRVJNVVdJZTE3V1k3VDIycy9lOEplN0xxSXlUcU9mSGovaWFUUjhHbXhqcU1HNEdod1RpVXJsCkh0Skhud3BMVGFjVmdYUjV3UmtYcEhRT2JqSUFzeVNBUGxwSzBvZUkyK2kvS0cyQjZ2U0cza0V2b1VZY0RlRk4KdzhHd0oxNDNTd21LQXM4eUtWMmd1RjhmRXNNVitEQzNzVHFlZXJmMy82bFprMUVCVFF0QTZqVHdqK0picXgwVgpaalZJUXBwUE8vc1VHdi9LZVE3MW5ockJpT0lXclJ0dDRTUDJ2aWx2em9DUTQxVjFqZ09wS3VwU3E1Y2J3VDRxCmp1bkJIMkx5VnNQaUc4M0Vha1JSUEhDK0craTk1MFJxckxVRUJOeVdHNGlMNTdUdU9xYVJuSmRnN2ZFb2lVLzMKNld4TjlvR2VRWjV0NjZkdTJEL01WSUZ4ZzJ1cXRBPT0KLS0tLS1FTkQgUFJJVkFURSBLRVktLS0tLQo=</prv>
</cert>
<syslog/>
</opnsense>

File diff suppressed because it is too large Load Diff