Merge pull request 'doc/pxe_test_setup' (#117) from doc/pxe_test_setup into master
Reviewed-on: https://git.nationtech.io/NationTech/harmony/pulls/117
This commit is contained in:
commit
269f13ae9b
744
Cargo.lock
generated
744
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -14,7 +14,7 @@ members = [
|
||||
"harmony_composer",
|
||||
"harmony_inventory_agent",
|
||||
"harmony_secret_derive",
|
||||
"harmony_secret",
|
||||
"harmony_secret", "adr/agent_discovery/mdns",
|
||||
]
|
||||
|
||||
[workspace.package]
|
||||
|
17
adr/agent_discovery/mdns/Cargo.toml
Normal file
17
adr/agent_discovery/mdns/Cargo.toml
Normal file
@ -0,0 +1,17 @@
|
||||
[package]
|
||||
name = "mdns"
|
||||
edition = "2024"
|
||||
version.workspace = true
|
||||
readme.workspace = true
|
||||
license.workspace = true
|
||||
|
||||
[dependencies]
|
||||
mdns-sd = "0.14"
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
futures = "0.3"
|
||||
dmidecode = "0.2" # For getting the motherboard ID on the agent
|
||||
log.workspace=true
|
||||
env_logger.workspace=true
|
||||
clap = { version = "4.5.46", features = ["derive"] }
|
||||
get_if_addrs = "0.5.3"
|
||||
local-ip-address = "0.6.5"
|
60
adr/agent_discovery/mdns/src/advertise.rs
Normal file
60
adr/agent_discovery/mdns/src/advertise.rs
Normal file
@ -0,0 +1,60 @@
|
||||
// harmony-agent/src/main.rs
|
||||
|
||||
use log::info;
|
||||
use mdns_sd::{ServiceDaemon, ServiceInfo};
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::SERVICE_TYPE;
|
||||
|
||||
// The service we are advertising.
|
||||
const SERVICE_PORT: u16 = 43210; // A port for the service. It needs one, even if unused.
|
||||
|
||||
pub async fn advertise() {
|
||||
info!("Starting Harmony Agent...");
|
||||
|
||||
// Get a unique ID for this machine.
|
||||
let motherboard_id = "some motherboard id";
|
||||
let instance_name = format!("harmony-agent-{}", motherboard_id);
|
||||
info!("This agent's instance name: {}", instance_name);
|
||||
info!("Advertising with ID: {}", motherboard_id);
|
||||
|
||||
// Create a new mDNS daemon.
|
||||
let mdns = ServiceDaemon::new().expect("Failed to create mDNS daemon");
|
||||
|
||||
// Create a TXT record HashMap to hold our metadata.
|
||||
let mut properties = HashMap::new();
|
||||
properties.insert("id".to_string(), motherboard_id.to_string());
|
||||
properties.insert("version".to_string(), "1.0".to_string());
|
||||
|
||||
// Create the service information.
|
||||
// The instance name should be unique on the network.
|
||||
let local_ip = local_ip_address::local_ip().unwrap();
|
||||
let service_info = ServiceInfo::new(
|
||||
SERVICE_TYPE,
|
||||
&instance_name,
|
||||
"harmony-host.local.", // A hostname for the service
|
||||
local_ip,
|
||||
// "0.0.0.0",
|
||||
SERVICE_PORT,
|
||||
Some(properties),
|
||||
)
|
||||
.expect("Failed to create service info");
|
||||
|
||||
// Register our service with the daemon.
|
||||
mdns.register(service_info)
|
||||
.expect("Failed to register service");
|
||||
|
||||
info!(
|
||||
"Service '{}' registered and now being advertised.",
|
||||
instance_name
|
||||
);
|
||||
info!("Agent is running. Press Ctrl+C to exit.");
|
||||
|
||||
for iface in get_if_addrs::get_if_addrs().unwrap() {
|
||||
println!("{:#?}", iface);
|
||||
}
|
||||
|
||||
// Keep the agent running indefinitely.
|
||||
tokio::signal::ctrl_c().await.unwrap();
|
||||
info!("Shutting down agent.");
|
||||
}
|
110
adr/agent_discovery/mdns/src/discover.rs
Normal file
110
adr/agent_discovery/mdns/src/discover.rs
Normal file
@ -0,0 +1,110 @@
|
||||
use log::debug;
|
||||
use mdns_sd::{ServiceDaemon, ServiceEvent};
|
||||
|
||||
use crate::SERVICE_TYPE;
|
||||
|
||||
pub async fn discover() {
|
||||
println!("Starting Harmony Master and browsing for agents...");
|
||||
|
||||
// Create a new mDNS daemon.
|
||||
let mdns = ServiceDaemon::new().expect("Failed to create mDNS daemon");
|
||||
|
||||
// Start browsing for the service type.
|
||||
// The receiver will be a stream of events.
|
||||
let receiver = mdns.browse(SERVICE_TYPE).expect("Failed to browse");
|
||||
|
||||
println!(
|
||||
"Listening for mDNS events for '{}'. Press Ctrl+C to exit.",
|
||||
SERVICE_TYPE
|
||||
);
|
||||
|
||||
std::thread::spawn(move || {
|
||||
while let Ok(event) = receiver.recv() {
|
||||
match event {
|
||||
ServiceEvent::ServiceData(resolved) => {
|
||||
println!("Resolved a new service: {}", resolved.fullname);
|
||||
}
|
||||
other_event => {
|
||||
println!("Received other event: {:?}", &other_event);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Gracefully shutdown the daemon.
|
||||
std::thread::sleep(std::time::Duration::from_secs(1000000));
|
||||
mdns.shutdown().unwrap();
|
||||
|
||||
// Process events as they come in.
|
||||
// while let Ok(event) = receiver.recv_async().await {
|
||||
// debug!("Received event {event:?}");
|
||||
// // match event {
|
||||
// // ServiceEvent::ServiceFound(svc_type, fullname) => {
|
||||
// // println!("\n--- Agent Discovered ---");
|
||||
// // println!(" Service Name: {}", fullname());
|
||||
// // // You can now resolve this service to get its IP, port, and TXT records
|
||||
// // // The resolve operation is a separate network call.
|
||||
// // let receiver = mdns.browse(info.get_fullname()).unwrap();
|
||||
// // if let Ok(resolve_event) = receiver.recv_timeout(Duration::from_secs(2)) {
|
||||
// // if let ServiceEvent::ServiceResolved(info) = resolve_event {
|
||||
// // let ip = info.get_addresses().iter().next().unwrap();
|
||||
// // let port = info.get_port();
|
||||
// // let motherboard_id = info.get_property("id").map_or("N/A", |v| v.val_str());
|
||||
// //
|
||||
// // println!(" IP: {}:{}", ip, port);
|
||||
// // println!(" Motherboard ID: {}", motherboard_id);
|
||||
// // println!("------------------------");
|
||||
// //
|
||||
// // // TODO: Add this agent to your central list of discovered hosts.
|
||||
// // }
|
||||
// // } else {
|
||||
// // println!("Could not resolve service '{}' in time.", info.get_fullname());
|
||||
// // }
|
||||
// // }
|
||||
// // ServiceEvent::ServiceRemoved(info) => {
|
||||
// // println!("\n--- Agent Removed ---");
|
||||
// // println!(" Service Name: {}", info.get_fullname());
|
||||
// // println!("---------------------");
|
||||
// // // TODO: Remove this agent from your list.
|
||||
// // }
|
||||
// // _ => {
|
||||
// // // We don't care about other event types for this example
|
||||
// // }
|
||||
// // }
|
||||
// }
|
||||
}
|
||||
|
||||
async fn discover_example() {
|
||||
use mdns_sd::{ServiceDaemon, ServiceEvent};
|
||||
|
||||
// Create a daemon
|
||||
let mdns = ServiceDaemon::new().expect("Failed to create daemon");
|
||||
|
||||
// Use recently added `ServiceEvent::ServiceData`.
|
||||
mdns.use_service_data(true)
|
||||
.expect("Failed to use ServiceData");
|
||||
|
||||
// Browse for a service type.
|
||||
let service_type = "_mdns-sd-my-test._udp.local.";
|
||||
let receiver = mdns.browse(service_type).expect("Failed to browse");
|
||||
|
||||
// Receive the browse events in sync or async. Here is
|
||||
// an example of using a thread. Users can call `receiver.recv_async().await`
|
||||
// if running in async environment.
|
||||
std::thread::spawn(move || {
|
||||
while let Ok(event) = receiver.recv() {
|
||||
match event {
|
||||
ServiceEvent::ServiceData(resolved) => {
|
||||
println!("Resolved a new service: {}", resolved.fullname);
|
||||
}
|
||||
other_event => {
|
||||
println!("Received other event: {:?}", &other_event);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Gracefully shutdown the daemon.
|
||||
std::thread::sleep(std::time::Duration::from_secs(1));
|
||||
mdns.shutdown().unwrap();
|
||||
}
|
31
adr/agent_discovery/mdns/src/main.rs
Normal file
31
adr/agent_discovery/mdns/src/main.rs
Normal file
@ -0,0 +1,31 @@
|
||||
use clap::{Parser, ValueEnum};
|
||||
|
||||
mod advertise;
|
||||
mod discover;
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(version, about, long_about = None)]
|
||||
struct Args {
|
||||
#[arg(value_enum)]
|
||||
profile: Profiles,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, ValueEnum)]
|
||||
enum Profiles {
|
||||
Advertise,
|
||||
Discover,
|
||||
}
|
||||
|
||||
// The service type we are looking for.
|
||||
const SERVICE_TYPE: &str = "_harmony._tcp.local.";
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
env_logger::init();
|
||||
let args = Args::parse();
|
||||
|
||||
match args.profile {
|
||||
Profiles::Advertise => advertise::advertise().await,
|
||||
Profiles::Discover => discover::discover().await,
|
||||
}
|
||||
}
|
1
check.sh
1
check.sh
@ -1,6 +1,7 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
rustc --version
|
||||
cargo check --all-targets --all-features --keep-going
|
||||
cargo fmt --check
|
||||
cargo clippy
|
||||
|
8
data/pxe/okd/README.md
Normal file
8
data/pxe/okd/README.md
Normal file
@ -0,0 +1,8 @@
|
||||
Here lies all the data files required for an OKD cluster PXE boot setup.
|
||||
|
||||
This inclues ISO files, binary boot files, ipxe, etc.
|
||||
|
||||
TODO as of august 2025 :
|
||||
|
||||
- `harmony_inventory_agent` should be downloaded from official releases, this embedded version is practical for now though
|
||||
- The cluster ssh key should be generated and handled by harmony with the private key saved in a secret store
|
9
data/pxe/okd/http_files/.gitattributes
vendored
Normal file
9
data/pxe/okd/http_files/.gitattributes
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
harmony_inventory_agent filter=lfs diff=lfs merge=lfs -text
|
||||
os filter=lfs diff=lfs merge=lfs -text
|
||||
os/centos-stream-9 filter=lfs diff=lfs merge=lfs -text
|
||||
os/centos-stream-9/images filter=lfs diff=lfs merge=lfs -text
|
||||
os/centos-stream-9/initrd.img filter=lfs diff=lfs merge=lfs -text
|
||||
os/centos-stream-9/vmlinuz filter=lfs diff=lfs merge=lfs -text
|
||||
os/centos-stream-9/images/efiboot.img filter=lfs diff=lfs merge=lfs -text
|
||||
os/centos-stream-9/images/install.img filter=lfs diff=lfs merge=lfs -text
|
||||
os/centos-stream-9/images/pxeboot filter=lfs diff=lfs merge=lfs -text
|
1
data/pxe/okd/http_files/cluster_ssh_key.pub
Normal file
1
data/pxe/okd/http_files/cluster_ssh_key.pub
Normal file
@ -0,0 +1 @@
|
||||
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBx6bDylvC68cVpjKfEFtLQJ/dOFi6PVS2vsIOqPDJIc jeangab@liliane2
|
BIN
data/pxe/okd/http_files/harmony_inventory_agent
(Stored with Git LFS)
Executable file
BIN
data/pxe/okd/http_files/harmony_inventory_agent
(Stored with Git LFS)
Executable file
Binary file not shown.
BIN
data/pxe/okd/http_files/os/centos-stream-9/images/efiboot.img
(Stored with Git LFS)
Normal file
BIN
data/pxe/okd/http_files/os/centos-stream-9/images/efiboot.img
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
data/pxe/okd/http_files/os/centos-stream-9/images/install.img
(Stored with Git LFS)
Normal file
BIN
data/pxe/okd/http_files/os/centos-stream-9/images/install.img
(Stored with Git LFS)
Normal file
Binary file not shown.
Binary file not shown.
BIN
data/pxe/okd/http_files/os/centos-stream-9/images/pxeboot/vmlinuz
Executable file
BIN
data/pxe/okd/http_files/os/centos-stream-9/images/pxeboot/vmlinuz
Executable file
Binary file not shown.
BIN
data/pxe/okd/http_files/os/centos-stream-9/initrd.img
(Stored with Git LFS)
Normal file
BIN
data/pxe/okd/http_files/os/centos-stream-9/initrd.img
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
data/pxe/okd/http_files/os/centos-stream-9/vmlinuz
(Stored with Git LFS)
Executable file
BIN
data/pxe/okd/http_files/os/centos-stream-9/vmlinuz
(Stored with Git LFS)
Executable file
Binary file not shown.
BIN
data/pxe/okd/tftpboot/ipxe.efi
Normal file
BIN
data/pxe/okd/tftpboot/ipxe.efi
Normal file
Binary file not shown.
BIN
data/pxe/okd/tftpboot/undionly.kpxe
Normal file
BIN
data/pxe/okd/tftpboot/undionly.kpxe
Normal file
Binary file not shown.
108
docs/pxe_test/README.md
Normal file
108
docs/pxe_test/README.md
Normal file
@ -0,0 +1,108 @@
|
||||
# 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
|
||||
```
|
191
docs/pxe_test/pxe_vm_lab_setup.sh
Executable file
191
docs/pxe_test/pxe_vm_lab_setup.sh
Executable file
@ -0,0 +1,191 @@
|
||||
#!/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
|
@ -1,6 +1,9 @@
|
||||
use harmony::{
|
||||
inventory::Inventory,
|
||||
modules::dummy::{ErrorScore, PanicScore, SuccessScore},
|
||||
modules::{
|
||||
dummy::{ErrorScore, PanicScore, SuccessScore},
|
||||
inventory::DiscoverInventoryAgentScore,
|
||||
},
|
||||
topology::LocalhostTopology,
|
||||
};
|
||||
|
||||
@ -13,6 +16,9 @@ async fn main() {
|
||||
Box::new(SuccessScore {}),
|
||||
Box::new(ErrorScore {}),
|
||||
Box::new(PanicScore {}),
|
||||
Box::new(DiscoverInventoryAgentScore {
|
||||
discovery_timeout: Some(10),
|
||||
}),
|
||||
],
|
||||
None,
|
||||
)
|
||||
|
@ -125,9 +125,12 @@ 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::new(Url::LocalFolder(
|
||||
let http_score = StaticFilesHttpScore {
|
||||
folder_to_serve: Some(Url::LocalFolder(
|
||||
"./data/watchguard/pxe-http-files".to_string(),
|
||||
));
|
||||
)),
|
||||
files: vec![],
|
||||
};
|
||||
let ipxe_score = IpxeScore::new();
|
||||
|
||||
harmony_tui::run(
|
||||
|
21
examples/okd_pxe/Cargo.toml
Normal file
21
examples/okd_pxe/Cargo.toml
Normal file
@ -0,0 +1,21 @@
|
||||
[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" }
|
||||
harmony_secret = { path = "../../harmony_secret" }
|
||||
harmony_secret_derive = { path = "../../harmony_secret_derive" }
|
||||
cidr = { workspace = true }
|
||||
tokio = { workspace = true }
|
||||
harmony_macros = { path = "../../harmony_macros" }
|
||||
log = { workspace = true }
|
||||
env_logger = { workspace = true }
|
||||
url = { workspace = true }
|
||||
serde.workspace = true
|
24
examples/okd_pxe/src/main.rs
Normal file
24
examples/okd_pxe/src/main.rs
Normal file
@ -0,0 +1,24 @@
|
||||
mod topology;
|
||||
|
||||
use crate::topology::{get_inventory, get_topology};
|
||||
use harmony::modules::okd::ipxe::OkdIpxeScore;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
let inventory = get_inventory();
|
||||
let topology = get_topology().await;
|
||||
|
||||
let kickstart_filename = "inventory.kickstart".to_string();
|
||||
let cluster_pubkey_filename = "cluster_ssh_key.pub".to_string();
|
||||
let harmony_inventory_agent = "harmony_inventory_agent".to_string();
|
||||
|
||||
let ipxe_score = OkdIpxeScore {
|
||||
kickstart_filename,
|
||||
harmony_inventory_agent,
|
||||
cluster_pubkey_filename,
|
||||
};
|
||||
|
||||
harmony_cli::run(inventory, topology, vec![Box::new(ipxe_score)], None)
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
78
examples/okd_pxe/src/topology.rs
Normal file
78
examples/okd_pxe/src/topology.rs
Normal file
@ -0,0 +1,78 @@
|
||||
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};
|
||||
use harmony_secret::{Secret, SecretManager};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{net::IpAddr, sync::Arc};
|
||||
|
||||
#[derive(Secret, Serialize, Deserialize, Debug, PartialEq)]
|
||||
struct OPNSenseFirewallConfig {
|
||||
username: String,
|
||||
password: String,
|
||||
}
|
||||
|
||||
pub async fn get_topology() -> HAClusterTopology {
|
||||
let firewall = harmony::topology::LogicalHost {
|
||||
ip: ip!("192.168.1.1"),
|
||||
name: String::from("opnsense-1"),
|
||||
};
|
||||
|
||||
let config = SecretManager::get::<OPNSenseFirewallConfig>().await;
|
||||
let config = config.unwrap();
|
||||
|
||||
let opnsense = Arc::new(
|
||||
harmony::infra::opnsense::OPNSenseFirewall::new(
|
||||
firewall,
|
||||
None,
|
||||
&config.username,
|
||||
&config.password,
|
||||
)
|
||||
.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![],
|
||||
}
|
||||
}
|
7
examples/okd_pxe/ssh_example_key
Normal file
7
examples/okd_pxe/ssh_example_key
Normal file
@ -0,0 +1,7 @@
|
||||
-----BEGIN OPENSSH PRIVATE KEY-----
|
||||
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
|
||||
QyNTUxOQAAACAcemw8pbwuvHFaYynxBbS0Cf3ThYuj1Utr7CDqjwySHAAAAJikacCNpGnA
|
||||
jQAAAAtzc2gtZWQyNTUxOQAAACAcemw8pbwuvHFaYynxBbS0Cf3ThYuj1Utr7CDqjwySHA
|
||||
AAAECiiKk4V6Q5cVs6axDM4sjAzZn/QCZLQekmYQXS9XbEYxx6bDylvC68cVpjKfEFtLQJ
|
||||
/dOFi6PVS2vsIOqPDJIcAAAAEGplYW5nYWJAbGlsaWFuZTIBAgMEBQ==
|
||||
-----END OPENSSH PRIVATE KEY-----
|
1
examples/okd_pxe/ssh_example_key.pub
Normal file
1
examples/okd_pxe/ssh_example_key.pub
Normal file
@ -0,0 +1 @@
|
||||
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBx6bDylvC68cVpjKfEFtLQJ/dOFi6PVS2vsIOqPDJIc jeangab@liliane2
|
@ -80,9 +80,12 @@ 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::new(Url::LocalFolder(
|
||||
let http_score = StaticFilesHttpScore {
|
||||
folder_to_serve: Some(Url::LocalFolder(
|
||||
"./data/watchguard/pxe-http-files".to_string(),
|
||||
));
|
||||
)),
|
||||
files: vec![],
|
||||
};
|
||||
|
||||
harmony_tui::run(
|
||||
inventory,
|
||||
|
@ -11,8 +11,7 @@ testing = []
|
||||
[dependencies]
|
||||
rand = "0.9"
|
||||
hex = "0.4"
|
||||
libredfish = "0.1.1"
|
||||
reqwest = { version = "0.11", features = ["blocking", "json"] }
|
||||
reqwest = { version = "0.11", features = ["blocking", "json", "rustls-tls"], default-features = false }
|
||||
russh = "0.45.0"
|
||||
rust-ipmi = "0.1.1"
|
||||
semver = "1.0.23"
|
||||
@ -67,7 +66,9 @@ bollard.workspace = true
|
||||
tar.workspace = true
|
||||
base64.workspace = true
|
||||
once_cell = "1.21.3"
|
||||
harmony-secret-derive = { version = "0.1.0", path = "../harmony_secret_derive" }
|
||||
harmony_inventory_agent = { path = "../harmony_inventory_agent" }
|
||||
harmony_secret_derive = { version = "0.1.0", path = "../harmony_secret_derive" }
|
||||
askama = "0.14.0"
|
||||
|
||||
[dev-dependencies]
|
||||
pretty_assertions.workspace = true
|
||||
|
22
harmony/src/domain/data/file.rs
Normal file
22
harmony/src/domain/data/file.rs
Normal file
@ -0,0 +1,22 @@
|
||||
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}")),
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,6 @@
|
||||
mod file;
|
||||
mod id;
|
||||
mod version;
|
||||
pub use file::*;
|
||||
pub use id::*;
|
||||
pub use version::*;
|
||||
|
@ -1,6 +1,5 @@
|
||||
use log::debug;
|
||||
use once_cell::sync::Lazy;
|
||||
use tokio::sync::broadcast;
|
||||
use std::{collections::HashMap, sync::Mutex};
|
||||
|
||||
use crate::modules::application::ApplicationFeatureStatus;
|
||||
|
||||
@ -40,43 +39,46 @@ pub enum HarmonyEvent {
|
||||
},
|
||||
}
|
||||
|
||||
static HARMONY_EVENT_BUS: Lazy<broadcast::Sender<HarmonyEvent>> = Lazy::new(|| {
|
||||
// TODO: Adjust channel capacity
|
||||
let (tx, _rx) = broadcast::channel(100);
|
||||
tx
|
||||
});
|
||||
type Subscriber = Box<dyn Fn(&HarmonyEvent) + Send + Sync>;
|
||||
|
||||
pub fn instrument(event: HarmonyEvent) -> Result<(), &'static str> {
|
||||
if cfg!(any(test, feature = "testing")) {
|
||||
let _ = event; // Suppress the "unused variable" warning for `event`
|
||||
Ok(())
|
||||
} else {
|
||||
match HARMONY_EVENT_BUS.send(event) {
|
||||
Ok(_) => Ok(()),
|
||||
Err(_) => Err("send error: no subscribers"),
|
||||
}
|
||||
}
|
||||
}
|
||||
static SUBSCRIBERS: Lazy<Mutex<HashMap<String, Subscriber>>> =
|
||||
Lazy::new(|| Mutex::new(HashMap::new()));
|
||||
|
||||
pub async fn subscribe<F, Fut>(name: &str, mut handler: F)
|
||||
/// Subscribes a listener to all instrumentation events.
|
||||
///
|
||||
/// Simply provide a unique name and a closure to run when an event happens.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use harmony::instrumentation;
|
||||
/// instrumentation::subscribe("my_logger", |event| {
|
||||
/// println!("Event occurred: {:?}", event);
|
||||
/// });
|
||||
/// ```
|
||||
pub fn subscribe<F>(name: &str, callback: F)
|
||||
where
|
||||
F: FnMut(HarmonyEvent) -> Fut + Send + 'static,
|
||||
Fut: Future<Output = bool> + Send,
|
||||
F: Fn(&HarmonyEvent) + Send + Sync + 'static,
|
||||
{
|
||||
let mut rx = HARMONY_EVENT_BUS.subscribe();
|
||||
debug!("[{name}] Service started. Listening for events...");
|
||||
loop {
|
||||
match rx.recv().await {
|
||||
Ok(event) => {
|
||||
if !handler(event).await {
|
||||
debug!("[{name}] Handler requested exit.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
Err(broadcast::error::RecvError::Lagged(n)) => {
|
||||
debug!("[{name}] Lagged behind by {n} messages.");
|
||||
}
|
||||
Err(_) => break,
|
||||
}
|
||||
}
|
||||
let mut subs = SUBSCRIBERS.lock().unwrap();
|
||||
subs.insert(name.to_string(), Box::new(callback));
|
||||
}
|
||||
|
||||
/// Instruments an event, notifying all subscribers.
|
||||
///
|
||||
/// This will call every closure that was registered with `subscribe`.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use harmony::instrumentation;
|
||||
/// use harmony::instrumentation::HarmonyEvent;
|
||||
/// instrumentation::instrument(HarmonyEvent::HarmonyStarted);
|
||||
/// ```
|
||||
pub fn instrument(event: HarmonyEvent) -> Result<(), &'static str> {
|
||||
let subs = SUBSCRIBERS.lock().unwrap();
|
||||
|
||||
for callback in subs.values() {
|
||||
callback(&event);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -32,6 +32,7 @@ pub enum InterpretName {
|
||||
Lamp,
|
||||
ApplicationMonitoring,
|
||||
K8sPrometheusCrdAlerting,
|
||||
DiscoverInventoryAgent,
|
||||
CephClusterHealth,
|
||||
}
|
||||
|
||||
@ -59,6 +60,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::DiscoverInventoryAgent => f.write_str("DiscoverInventoryAgent"),
|
||||
InterpretName::CephClusterHealth => f.write_str("CephClusterHealth"),
|
||||
}
|
||||
}
|
||||
|
@ -74,6 +74,7 @@ impl<T: Topology> Maestro<T> {
|
||||
|
||||
fn is_topology_initialized(&self) -> bool {
|
||||
self.topology_state.status == TopologyStatus::Success
|
||||
|| self.topology_state.status == TopologyStatus::Noop
|
||||
}
|
||||
|
||||
pub async fn interpret(&self, score: Box<dyn Score<T>>) -> Result<Outcome, InterpretError> {
|
||||
|
@ -1,9 +1,12 @@
|
||||
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;
|
||||
@ -49,9 +52,10 @@ impl Topology for HAClusterTopology {
|
||||
"HAClusterTopology"
|
||||
}
|
||||
async fn ensure_ready(&self) -> Result<PreparationOutcome, PreparationError> {
|
||||
todo!(
|
||||
debug!(
|
||||
"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)
|
||||
}
|
||||
}
|
||||
|
||||
@ -153,12 +157,10 @@ impl DhcpServer for HAClusterTopology {
|
||||
async fn list_static_mappings(&self) -> Vec<(MacAddress, IpAddress)> {
|
||||
self.dhcp_server.list_static_mappings().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
|
||||
async fn set_pxe_options(&self, options: PxeOptions) -> Result<(), ExecutorError> {
|
||||
self.dhcp_server.set_pxe_options(options).await
|
||||
}
|
||||
|
||||
fn get_ip(&self) -> IpAddress {
|
||||
self.dhcp_server.get_ip()
|
||||
}
|
||||
@ -168,16 +170,6 @@ 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]
|
||||
@ -221,17 +213,21 @@ 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 {
|
||||
unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
|
||||
self.http_server.get_ip()
|
||||
}
|
||||
async fn ensure_initialized(&self) -> Result<(), ExecutorError> {
|
||||
unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
|
||||
self.http_server.ensure_initialized().await
|
||||
}
|
||||
async fn commit_config(&self) -> Result<(), ExecutorError> {
|
||||
unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
|
||||
self.http_server.commit_config().await
|
||||
}
|
||||
async fn reload_restart(&self) -> Result<(), ExecutorError> {
|
||||
unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
|
||||
self.http_server.reload_restart().await
|
||||
}
|
||||
}
|
||||
|
||||
@ -299,19 +295,7 @@ impl DhcpServer for DummyInfra {
|
||||
async fn list_static_mappings(&self) -> Vec<(MacAddress, IpAddress)> {
|
||||
unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
|
||||
}
|
||||
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> {
|
||||
async fn set_pxe_options(&self, _options: PxeOptions) -> Result<(), ExecutorError> {
|
||||
unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
|
||||
}
|
||||
fn get_ip(&self) -> IpAddress {
|
||||
@ -381,6 +365,9 @@ 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)
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
use crate::executors::ExecutorError;
|
||||
use crate::{data::FileContent, executors::ExecutorError};
|
||||
use async_trait::async_trait;
|
||||
|
||||
use super::{IpAddress, Url};
|
||||
@ -6,6 +6,7 @@ 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>;
|
||||
|
@ -185,7 +185,10 @@ impl K8sClient {
|
||||
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;
|
||||
stdout
|
||||
.read_to_string(&mut stdout_buf)
|
||||
.await
|
||||
.map_err(|e| format!("Failed to get status stdout {e}"))?;
|
||||
}
|
||||
debug!("Status: {} - {:?}", s, status.details);
|
||||
if s == "Success" {
|
||||
|
@ -46,16 +46,19 @@ 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_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>;
|
||||
async fn set_pxe_options(&self, pxe_options: PxeOptions) -> Result<(), ExecutorError>;
|
||||
fn get_ip(&self) -> IpAddress;
|
||||
fn get_host(&self) -> LogicalHost;
|
||||
async fn commit_config(&self) -> Result<(), ExecutorError>;
|
||||
|
@ -1,10 +1,10 @@
|
||||
use async_trait::async_trait;
|
||||
use harmony_types::net::MacAddress;
|
||||
use log::debug;
|
||||
use log::info;
|
||||
|
||||
use crate::{
|
||||
executors::ExecutorError,
|
||||
topology::{DHCPStaticEntry, DhcpServer, IpAddress, LogicalHost},
|
||||
topology::{DHCPStaticEntry, DhcpServer, IpAddress, LogicalHost, PxeOptions},
|
||||
};
|
||||
|
||||
use super::OPNSenseFirewall;
|
||||
@ -26,7 +26,7 @@ impl DhcpServer for OPNSenseFirewall {
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
debug!("Registered {:?}", entry);
|
||||
info!("Registered {:?}", entry);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -46,57 +46,25 @@ impl DhcpServer for OPNSenseFirewall {
|
||||
self.host.clone()
|
||||
}
|
||||
|
||||
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"),
|
||||
};
|
||||
{
|
||||
async fn set_pxe_options(&self, options: PxeOptions) -> Result<(), ExecutorError> {
|
||||
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(())
|
||||
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}"))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -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 {http_root_path}");
|
||||
info!("Uploading files from url {url} to {OPNSENSE_HTTP_ROOT_PATH}");
|
||||
match url {
|
||||
Url::LocalFolder(path) => {
|
||||
config
|
||||
.upload_files(path, http_root_path)
|
||||
.upload_files(path, OPNSENSE_HTTP_ROOT_PATH)
|
||||
.await
|
||||
.map_err(|e| ExecutorError::UnexpectedError(e.to_string()))?;
|
||||
}
|
||||
@ -27,8 +27,29 @@ 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 {
|
||||
todo!();
|
||||
OPNSenseFirewall::get_ip(self)
|
||||
}
|
||||
|
||||
async fn commit_config(&self) -> Result<(), ExecutorError> {
|
||||
|
@ -28,7 +28,7 @@ impl TftpServer for OPNSenseFirewall {
|
||||
}
|
||||
|
||||
fn get_ip(&self) -> IpAddress {
|
||||
todo!()
|
||||
OPNSenseFirewall::get_ip(self)
|
||||
}
|
||||
|
||||
async fn set_ip(&self, ip: IpAddress) -> Result<(), ExecutorError> {
|
||||
|
@ -7,7 +7,7 @@ use crate::{
|
||||
domain::{data::Version, interpret::InterpretStatus},
|
||||
interpret::{Interpret, InterpretError, InterpretName, Outcome},
|
||||
inventory::Inventory,
|
||||
topology::{DHCPStaticEntry, DhcpServer, HostBinding, IpAddress, Topology},
|
||||
topology::{DHCPStaticEntry, DhcpServer, HostBinding, IpAddress, PxeOptions, Topology},
|
||||
};
|
||||
|
||||
use crate::domain::score::Score;
|
||||
@ -98,69 +98,14 @@ impl DhcpInterpret {
|
||||
_inventory: &Inventory,
|
||||
dhcp_server: &D,
|
||||
) -> Result<Outcome, InterpretError> {
|
||||
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(),
|
||||
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 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());
|
||||
}
|
||||
dhcp_server.set_pxe_options(pxe_options).await?;
|
||||
|
||||
Ok(Outcome::new(
|
||||
InterpretStatus::SUCCESS,
|
||||
|
@ -3,7 +3,7 @@ use derive_new::new;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::{
|
||||
data::{Id, Version},
|
||||
data::{FileContent, Id, Version},
|
||||
interpret::{Interpret, InterpretError, InterpretName, InterpretStatus, Outcome},
|
||||
inventory::Inventory,
|
||||
score::Score,
|
||||
@ -23,7 +23,8 @@ use crate::{
|
||||
/// ```
|
||||
#[derive(Debug, new, Clone, Serialize)]
|
||||
pub struct StaticFilesHttpScore {
|
||||
files_to_serve: Url,
|
||||
pub folder_to_serve: Option<Url>,
|
||||
pub files: Vec<FileContent>,
|
||||
}
|
||||
|
||||
impl<T: Topology + HttpServer> Score<T> for StaticFilesHttpScore {
|
||||
@ -50,12 +51,25 @@ 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?;
|
||||
http_server.serve_files(&self.score.files_to_serve).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.commit_config().await?;
|
||||
http_server.reload_restart().await?;
|
||||
Ok(Outcome::success(format!(
|
||||
"Http Server running and serving files from {}",
|
||||
self.score.files_to_serve
|
||||
"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(",")
|
||||
)))
|
||||
}
|
||||
|
||||
|
72
harmony/src/modules/inventory/mod.rs
Normal file
72
harmony/src/modules/inventory/mod.rs
Normal file
@ -0,0 +1,72 @@
|
||||
use async_trait::async_trait;
|
||||
use harmony_inventory_agent::local_presence::DiscoveryEvent;
|
||||
use log::info;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
data::{Id, Version},
|
||||
interpret::{Interpret, InterpretError, InterpretName, InterpretStatus, Outcome},
|
||||
inventory::Inventory,
|
||||
score::Score,
|
||||
topology::Topology,
|
||||
};
|
||||
|
||||
/// This launches an harmony_inventory_agent discovery process
|
||||
/// This will allow us to register/update hosts running harmony_inventory_agent
|
||||
/// from LAN in the Harmony inventory
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct DiscoverInventoryAgentScore {
|
||||
pub discovery_timeout: Option<u64>,
|
||||
}
|
||||
|
||||
impl<T: Topology> Score<T> for DiscoverInventoryAgentScore {
|
||||
fn name(&self) -> String {
|
||||
"DiscoverInventoryAgentScore".to_string()
|
||||
}
|
||||
|
||||
fn create_interpret(&self) -> Box<dyn Interpret<T>> {
|
||||
Box::new(DiscoverInventoryAgentInterpret {
|
||||
score: self.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct DiscoverInventoryAgentInterpret {
|
||||
score: DiscoverInventoryAgentScore,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<T: Topology> Interpret<T> for DiscoverInventoryAgentInterpret {
|
||||
async fn execute(
|
||||
&self,
|
||||
inventory: &Inventory,
|
||||
topology: &T,
|
||||
) -> Result<Outcome, InterpretError> {
|
||||
harmony_inventory_agent::local_presence::discover_agents(
|
||||
self.score.discovery_timeout,
|
||||
on_discover_event,
|
||||
);
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn get_name(&self) -> InterpretName {
|
||||
InterpretName::DiscoverInventoryAgent
|
||||
}
|
||||
|
||||
fn get_version(&self) -> Version {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn get_status(&self) -> InterpretStatus {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn get_children(&self) -> Vec<Id> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
fn on_discover_event(event: &DiscoveryEvent) {
|
||||
info!("got discovery event {event:?}");
|
||||
}
|
@ -5,6 +5,7 @@ pub mod dns;
|
||||
pub mod dummy;
|
||||
pub mod helm;
|
||||
pub mod http;
|
||||
pub mod inventory;
|
||||
pub mod ipxe;
|
||||
pub mod k3d;
|
||||
pub mod k8s;
|
||||
|
@ -37,18 +37,6 @@ pub struct NtfyInterpret {
|
||||
pub score: NtfyScore,
|
||||
}
|
||||
|
||||
#[derive(Debug, EnumString, Display)]
|
||||
enum NtfyAccessMode {
|
||||
#[strum(serialize = "read-write", serialize = "rw")]
|
||||
ReadWrite,
|
||||
#[strum(serialize = "read-only", serialize = "ro", serialize = "read")]
|
||||
ReadOnly,
|
||||
#[strum(serialize = "write-only", serialize = "wo", serialize = "write")]
|
||||
WriteOnly,
|
||||
#[strum(serialize = "deny", serialize = "none")]
|
||||
Deny,
|
||||
}
|
||||
|
||||
#[derive(Debug, EnumString, Display)]
|
||||
enum NtfyRole {
|
||||
#[strum(serialize = "user")]
|
||||
|
148
harmony/src/modules/okd/ipxe.rs
Normal file
148
harmony/src/modules/okd/ipxe.rs
Normal file
@ -0,0 +1,148 @@
|
||||
use askama::Template;
|
||||
use async_trait::async_trait;
|
||||
use derive_new::new;
|
||||
use serde::Serialize;
|
||||
use std::net::IpAddr;
|
||||
|
||||
use crate::{
|
||||
data::{FileContent, FilePath, Id, Version},
|
||||
interpret::{Interpret, InterpretError, InterpretName, InterpretStatus, Outcome},
|
||||
inventory::Inventory,
|
||||
modules::{dhcp::DhcpScore, http::StaticFilesHttpScore, tftp::TftpScore},
|
||||
score::Score,
|
||||
topology::{DhcpServer, HttpServer, Router, TftpServer, Topology, Url},
|
||||
};
|
||||
|
||||
#[derive(Debug, new, Clone, Serialize)]
|
||||
pub struct OkdIpxeScore {
|
||||
pub kickstart_filename: String,
|
||||
pub harmony_inventory_agent: String,
|
||||
pub cluster_pubkey_filename: String,
|
||||
}
|
||||
|
||||
impl<T: Topology + DhcpServer + TftpServer + HttpServer + Router> Score<T> for OkdIpxeScore {
|
||||
fn create_interpret(&self) -> Box<dyn Interpret<T>> {
|
||||
Box::new(IpxeInterpret::new(self.clone()))
|
||||
}
|
||||
|
||||
fn name(&self) -> String {
|
||||
"OkdIpxeScore".to_string()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, new, Clone)]
|
||||
pub struct IpxeInterpret {
|
||||
score: OkdIpxeScore,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<T: Topology + DhcpServer + TftpServer + HttpServer + Router> Interpret<T> for IpxeInterpret {
|
||||
async fn execute(
|
||||
&self,
|
||||
inventory: &Inventory,
|
||||
topology: &T,
|
||||
) -> Result<Outcome, InterpretError> {
|
||||
let gateway_ip = topology.get_gateway();
|
||||
|
||||
let scores: Vec<Box<dyn Score<T>>> = vec![
|
||||
Box::new(DhcpScore {
|
||||
host_binding: vec![],
|
||||
next_server: Some(topology.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 {
|
||||
// TODO The current russh based copy is way too slow, check for a lib update or use scp
|
||||
// when available
|
||||
//
|
||||
// For now just run :
|
||||
// scp -r data/pxe/okd/http_files/* root@192.168.1.1:/usr/local/http/
|
||||
//
|
||||
folder_to_serve: None,
|
||||
// folder_to_serve: Some(Url::LocalFolder("./data/pxe/okd/http_files/".to_string())),
|
||||
files: vec![
|
||||
FileContent {
|
||||
path: FilePath::Relative("boot.ipxe".to_string()),
|
||||
content: BootIpxeTpl {
|
||||
gateway_ip: &gateway_ip,
|
||||
}
|
||||
.to_string(),
|
||||
},
|
||||
FileContent {
|
||||
path: FilePath::Relative(self.score.kickstart_filename.clone()),
|
||||
content: InventoryKickstartTpl {
|
||||
gateway_ip: &gateway_ip,
|
||||
harmony_inventory_agent: &self.score.harmony_inventory_agent,
|
||||
cluster_pubkey_filename: &self.score.cluster_pubkey_filename,
|
||||
}
|
||||
.to_string(),
|
||||
},
|
||||
FileContent {
|
||||
path: FilePath::Relative("fallback.ipxe".to_string()),
|
||||
content: FallbackIpxeTpl {
|
||||
gateway_ip: &gateway_ip,
|
||||
kickstart_filename: &self.score.kickstart_filename,
|
||||
}
|
||||
.to_string(),
|
||||
},
|
||||
],
|
||||
}),
|
||||
];
|
||||
|
||||
for score in scores {
|
||||
let result = score.interpret(inventory, topology).await;
|
||||
match result {
|
||||
Ok(outcome) => match outcome.status {
|
||||
InterpretStatus::SUCCESS => continue,
|
||||
InterpretStatus::NOOP => continue,
|
||||
_ => return Err(InterpretError::new(outcome.message)),
|
||||
},
|
||||
Err(e) => return Err(e),
|
||||
};
|
||||
}
|
||||
|
||||
Ok(Outcome::success("Ipxe installed".to_string()))
|
||||
}
|
||||
|
||||
fn get_name(&self) -> InterpretName {
|
||||
InterpretName::Ipxe
|
||||
}
|
||||
|
||||
fn get_version(&self) -> Version {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn get_status(&self) -> InterpretStatus {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn get_children(&self) -> Vec<Id> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(path = "boot.ipxe.j2")]
|
||||
struct BootIpxeTpl<'a> {
|
||||
gateway_ip: &'a IpAddr,
|
||||
}
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(path = "fallback.ipxe.j2")]
|
||||
struct FallbackIpxeTpl<'a> {
|
||||
gateway_ip: &'a IpAddr,
|
||||
kickstart_filename: &'a str,
|
||||
}
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(path = "inventory.kickstart.j2")]
|
||||
struct InventoryKickstartTpl<'a> {
|
||||
gateway_ip: &'a IpAddr,
|
||||
cluster_pubkey_filename: &'a str,
|
||||
harmony_inventory_agent: &'a str,
|
||||
}
|
@ -2,5 +2,6 @@ pub mod bootstrap_dhcp;
|
||||
pub mod bootstrap_load_balancer;
|
||||
pub mod dhcp;
|
||||
pub mod dns;
|
||||
pub mod ipxe;
|
||||
pub mod load_balancer;
|
||||
pub mod upgrade;
|
||||
|
@ -1,5 +1,4 @@
|
||||
use std::{
|
||||
process::Command,
|
||||
sync::Arc,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
@ -12,7 +12,7 @@ use crate::{
|
||||
|
||||
#[derive(Debug, new, Clone, Serialize)]
|
||||
pub struct TftpScore {
|
||||
files_to_serve: Url,
|
||||
pub files_to_serve: Url,
|
||||
}
|
||||
|
||||
impl<T: Topology + TftpServer + Router> Score<T> for TftpScore {
|
||||
|
6
harmony/templates/boot.ipxe.j2
Normal file
6
harmony/templates/boot.ipxe.j2
Normal file
@ -0,0 +1,6 @@
|
||||
#!ipxe
|
||||
|
||||
set base-url http://{{ gateway_ip }}:8080
|
||||
set hostfile ${base-url}/byMAC/01-${mac:hexhyp}.ipxe
|
||||
|
||||
chain ${hostfile} || chain ${base-url}/fallback.ipxe
|
40
harmony/templates/fallback.ipxe.j2
Normal file
40
harmony/templates/fallback.ipxe.j2
Normal file
@ -0,0 +1,40 @@
|
||||
#!ipxe
|
||||
|
||||
# =================================================================
|
||||
# Harmony Discovery Agent - Default Boot Script (default.ipxe)
|
||||
# =================================================================
|
||||
#
|
||||
# This script boots the CentOS Stream live environment for the
|
||||
# purpose of hardware inventory. It loads the kernel and initramfs
|
||||
# directly and passes a Kickstart URL for full automation.
|
||||
#
|
||||
|
||||
# --- Configuration
|
||||
# Set the base URL for where the CentOS kernel/initrd are hosted.
|
||||
set os_base_url http://{{gateway_ip}}:8080/os/centos-stream-9
|
||||
# Set the URL for the Kickstart file.
|
||||
set ks_url http://{{ gateway_ip }}:8080/{{ kickstart_filename }}
|
||||
|
||||
# --- Boot Process
|
||||
echo "Harmony: Starting automated node discovery..."
|
||||
echo "Fetching kernel from ${os_base_url}/vmlinuz..."
|
||||
kernel ${os_base_url}/vmlinuz
|
||||
|
||||
echo "Fetching initramfs from ${os_base_url}/initrd.img..."
|
||||
initrd ${os_base_url}/initrd.img
|
||||
|
||||
echo "Configuring kernel boot arguments..."
|
||||
# Kernel Arguments Explained:
|
||||
# - initrd=initrd.img: Specifies the initial ramdisk to use.
|
||||
# - inst.stage2: Points to the OS source. For a live boot, the base URL is sufficient.
|
||||
# - inst.ks: CRITICAL: Points to our Kickstart file for automation.
|
||||
# - ip=dhcp: Ensures the live environment configures its network.
|
||||
# - console=...: Provides boot output on both serial and graphical consoles for debugging.
|
||||
imgargs vmlinuz initrd=initrd.img inst.sshd inst.stage2=${os_base_url} inst.ks=${ks_url} ip=dhcp console=ttyS0,115200 console=tty1
|
||||
|
||||
echo "Booting into CentOS Stream 9 live environment..."
|
||||
boot || goto failed
|
||||
|
||||
:failed
|
||||
echo "Boot failed. Dropping to iPXE shell."
|
||||
shell
|
129
harmony/templates/inventory.kickstart.j2
Normal file
129
harmony/templates/inventory.kickstart.j2
Normal file
@ -0,0 +1,129 @@
|
||||
# --- Pre-Boot Scripting (The Main Goal) ---
|
||||
# This section runs after the live environment has booted into RAM.
|
||||
# It sets up SSH and downloads/runs the harmony-inventory-agent.
|
||||
%pre --log=/root/ks-pre.log
|
||||
|
||||
echo "Harmony Kickstart: Pre-boot script started."
|
||||
|
||||
# 1. Configure SSH Access for Root
|
||||
# Create the .ssh directory and set correct permissions.
|
||||
echo " - Setting up SSH authorized_keys for root..."
|
||||
mkdir -p /root/.ssh
|
||||
chmod 700 /root/.ssh
|
||||
|
||||
# Download the public key from the provisioning server.
|
||||
# The -sS flags make curl silent but show errors. -L follows redirects.
|
||||
curl -vSL "http://{{ gateway_ip }}:8080/{{ cluster_pubkey_filename }}" -o /root/.ssh/authorized_keys
|
||||
if [ $? -ne 0 ]; then
|
||||
echo " - ERROR: Failed to download SSH public key."
|
||||
else
|
||||
echo " - SSH key downloaded successfully."
|
||||
chmod 600 /root/.ssh/authorized_keys
|
||||
fi
|
||||
|
||||
# 2. Download the Harmony Inventory Agent
|
||||
echo " - Downloading harmony-inventory-agent..."
|
||||
curl -vSL "http://{{ gateway_ip }}:8080/{{ harmony_inventory_agent }}" -o /usr/bin/harmony-inventory-agent
|
||||
if [ $? -ne 0 ]; then
|
||||
echo " - ERROR: Failed to download harmony_inventory_agent."
|
||||
else
|
||||
echo " - Agent binary downloaded successfully."
|
||||
chmod +x /usr/bin/harmony-inventory-agent
|
||||
fi
|
||||
|
||||
# 3. Create a systemd service to run the agent persistently.
|
||||
# This is the most robust method to ensure the agent stays running.
|
||||
echo " - Creating systemd service for the agent..."
|
||||
cat > /etc/systemd/system/harmony-agent.service << EOF
|
||||
[Unit]
|
||||
Description=Harmony Inventory Agent
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
ExecStart=/usr/bin/harmony-inventory-agent
|
||||
Restart=on-failure
|
||||
RestartSec=5
|
||||
Environment="RUST_LOG=info"
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
||||
EOF
|
||||
|
||||
# 4. Enable and start the service
|
||||
# The 'systemctl' commands will work correctly within the chroot environment of the %pre script.
|
||||
echo " - Enabling and starting harmony-agent.service..."
|
||||
systemctl daemon-reload
|
||||
systemctl enable --now harmony-agent.service
|
||||
|
||||
# Check if the service started correctly
|
||||
systemctl is-active --quiet harmony-agent.service
|
||||
if [ $? -eq 0 ]; then
|
||||
echo " - Harmony Inventory Agent service is now running."
|
||||
else
|
||||
echo " - ERROR: Harmony Inventory Agent service failed to start."
|
||||
fi
|
||||
|
||||
echo "Harmony Kickstart: Pre-boot script finished. The machine is ready for inventory."
|
||||
|
||||
echo "Running cat - to pause system indefinitely"
|
||||
cat -
|
||||
|
||||
%end
|
||||
|
||||
# =================================================================
|
||||
# Harmony Discovery Agent - Kickstart File (NON-INSTALL, LIVE BOOT)
|
||||
# =================================================================
|
||||
#
|
||||
# This file achieves a fully automated, non-interactive boot into a
|
||||
# live CentOS environment. It does NOT install to disk.
|
||||
#
|
||||
|
||||
# --- Automation and Interaction Control ---
|
||||
# Perform the installation in command-line mode. This is critical for
|
||||
# preventing Anaconda from starting a UI and halting for input.
|
||||
cmdline
|
||||
|
||||
# Accept the End User License Agreement to prevent a prompt.
|
||||
eula --agreed
|
||||
|
||||
# --- Core System Configuration (Required by Anaconda) ---
|
||||
# Set keyboard and language. These are mandatory.
|
||||
keyboard --vckeymap=us --xlayouts='us'
|
||||
lang en_US.UTF-8
|
||||
|
||||
# Configure networking. This is essential for the %post script to work.
|
||||
# The --activate flag ensures this device is brought up in the installer environment.
|
||||
network --bootproto=dhcp --device=link --activate
|
||||
|
||||
# Set a locked root password. This is a mandatory command.
|
||||
rootpw --lock
|
||||
|
||||
# Set the timezone. This is a mandatory command.
|
||||
timezone UTC
|
||||
|
||||
# --- Disable Installation-Specific Features ---
|
||||
# CRITICAL: Do not install a bootloader. The --disabled flag prevents
|
||||
# this step and avoids errors about where to install it.
|
||||
bootloader --disabled
|
||||
|
||||
# CRITICAL: Ignore all disks. This prevents Anaconda from stopping at the
|
||||
# "Installation Destination" screen asking where to install.
|
||||
# ignoredisk --drives /dev/sda
|
||||
|
||||
# Do not run the Initial Setup wizard on first boot.
|
||||
firstboot --disable
|
||||
|
||||
# --- Package Selection ---
|
||||
# We are not installing, so this section can be minimal.
|
||||
# An empty %packages section is valid and ensures no time is wasted
|
||||
# resolving dependencies for an installation that will not happen.
|
||||
%packages
|
||||
%end
|
||||
|
||||
|
||||
# IMPORTANT: Do not include a final action command like 'reboot' or 'poweroff'.
|
||||
# The default action is 'halt', which in cmdline mode will leave the system
|
||||
# running in the live environment with the agent active, which is the desired state.
|
@ -7,19 +7,11 @@ use harmony::{
|
||||
};
|
||||
use log::{error, info, log_enabled};
|
||||
use std::io::Write;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::sync::Mutex;
|
||||
|
||||
pub fn init() -> tokio::task::JoinHandle<()> {
|
||||
pub fn init() {
|
||||
configure_logger();
|
||||
let handle = tokio::spawn(handle_events());
|
||||
|
||||
loop {
|
||||
if instrumentation::instrument(HarmonyEvent::HarmonyStarted).is_ok() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
handle
|
||||
handle_events();
|
||||
}
|
||||
|
||||
fn configure_logger() {
|
||||
@ -86,16 +78,12 @@ fn configure_logger() {
|
||||
.init();
|
||||
}
|
||||
|
||||
async fn handle_events() {
|
||||
let preparing_topology = Arc::new(Mutex::new(false));
|
||||
let current_score: Arc<Mutex<Option<String>>> = Arc::new(Mutex::new(None));
|
||||
fn handle_events() {
|
||||
let preparing_topology = Mutex::new(false);
|
||||
let current_score: Mutex<Option<String>> = Mutex::new(None);
|
||||
|
||||
instrumentation::subscribe("Harmony CLI Logger", {
|
||||
move |event| {
|
||||
let preparing_topology = Arc::clone(&preparing_topology);
|
||||
let current_score = Arc::clone(¤t_score);
|
||||
|
||||
async move {
|
||||
let mut preparing_topology = preparing_topology.lock().unwrap();
|
||||
let mut current_score = current_score.lock().unwrap();
|
||||
|
||||
@ -104,7 +92,6 @@ async fn handle_events() {
|
||||
HarmonyEvent::HarmonyFinished => {
|
||||
let emoji = crate::theme::EMOJI_HARMONY.to_string();
|
||||
info!(emoji = emoji.as_str(); "Harmony completed");
|
||||
return false;
|
||||
}
|
||||
HarmonyEvent::TopologyStateChanged {
|
||||
topology,
|
||||
@ -113,7 +100,10 @@ async fn handle_events() {
|
||||
} => match status {
|
||||
TopologyStatus::Queued => {}
|
||||
TopologyStatus::Preparing => {
|
||||
let emoji = format!("{}", style(crate::theme::EMOJI_TOPOLOGY.to_string()).yellow());
|
||||
let emoji = format!(
|
||||
"{}",
|
||||
style(crate::theme::EMOJI_TOPOLOGY.to_string()).yellow()
|
||||
);
|
||||
info!(emoji = emoji.as_str(); "Preparing environment: {topology}...");
|
||||
(*preparing_topology) = true;
|
||||
}
|
||||
@ -158,7 +148,7 @@ async fn handle_events() {
|
||||
score,
|
||||
outcome,
|
||||
} => {
|
||||
if current_score.is_some() && current_score.clone().unwrap() == score {
|
||||
if current_score.is_some() && ¤t_score.clone().unwrap() == score {
|
||||
(*current_score) = None;
|
||||
}
|
||||
|
||||
@ -175,7 +165,7 @@ async fn handle_events() {
|
||||
}
|
||||
},
|
||||
Err(err) => {
|
||||
error!(status = "failed"; "{}", err);
|
||||
error!(status = "failed"; "{err}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -186,19 +176,16 @@ async fn handle_events() {
|
||||
status,
|
||||
} => match status {
|
||||
ApplicationFeatureStatus::Installing => {
|
||||
info!("Installing feature '{}' for '{}'...", feature, application);
|
||||
info!("Installing feature '{feature}' for '{application}'...");
|
||||
}
|
||||
ApplicationFeatureStatus::Installed => {
|
||||
info!(status = "finished"; "Feature '{}' installed", feature);
|
||||
info!(status = "finished"; "Feature '{feature}' installed");
|
||||
}
|
||||
ApplicationFeatureStatus::Failed { details } => {
|
||||
error!(status = "failed"; "Feature '{}' installation failed: {}", feature, details);
|
||||
error!(status = "failed"; "Feature '{feature}' installation failed: {details}");
|
||||
}
|
||||
},
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
})
|
||||
.await;
|
||||
});
|
||||
}
|
||||
|
@ -115,7 +115,7 @@ pub async fn run_cli<T: Topology + Send + Sync + 'static>(
|
||||
scores: Vec<Box<dyn Score<T>>>,
|
||||
args: Args,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let cli_logger_handle = cli_logger::init();
|
||||
cli_logger::init();
|
||||
|
||||
let mut maestro = Maestro::initialize(inventory, topology).await.unwrap();
|
||||
maestro.register_all(scores);
|
||||
@ -123,7 +123,6 @@ pub async fn run_cli<T: Topology + Send + Sync + 'static>(
|
||||
let result = init(maestro, args).await;
|
||||
|
||||
instrumentation::instrument(instrumentation::HarmonyEvent::HarmonyFinished).unwrap();
|
||||
let _ = tokio::try_join!(cli_logger_handle);
|
||||
result
|
||||
}
|
||||
|
||||
|
@ -1,39 +1,26 @@
|
||||
use harmony_cli::progress::{IndicatifProgressTracker, ProgressTracker};
|
||||
use indicatif::MultiProgress;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::instrumentation::{self, HarmonyComposerEvent};
|
||||
|
||||
pub fn init() -> tokio::task::JoinHandle<()> {
|
||||
pub fn init() {
|
||||
configure_logger();
|
||||
let handle = tokio::spawn(handle_events());
|
||||
|
||||
loop {
|
||||
if instrumentation::instrument(HarmonyComposerEvent::HarmonyComposerStarted).is_ok() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
handle
|
||||
handle_events();
|
||||
}
|
||||
|
||||
fn configure_logger() {
|
||||
env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info")).build();
|
||||
}
|
||||
|
||||
pub async fn handle_events() {
|
||||
let progress_tracker = Arc::new(IndicatifProgressTracker::new(MultiProgress::new()));
|
||||
pub fn handle_events() {
|
||||
let progress_tracker = IndicatifProgressTracker::new(MultiProgress::new());
|
||||
|
||||
const SETUP_SECTION: &str = "project-initialization";
|
||||
const COMPILTATION_TASK: &str = "compilation";
|
||||
const PROGRESS_DEPLOYMENT: &str = "deployment";
|
||||
|
||||
instrumentation::subscribe("Harmony Composer Logger", {
|
||||
move |event| {
|
||||
let progress_tracker = Arc::clone(&progress_tracker);
|
||||
|
||||
async move {
|
||||
match event {
|
||||
move |event| match event {
|
||||
HarmonyComposerEvent::HarmonyComposerStarted => {}
|
||||
HarmonyComposerEvent::ProjectInitializationStarted => {
|
||||
progress_tracker.add_section(
|
||||
@ -46,13 +33,16 @@ pub async fn handle_events() {
|
||||
}
|
||||
HarmonyComposerEvent::ProjectInitialized => {}
|
||||
HarmonyComposerEvent::ProjectCompilationStarted { details } => {
|
||||
progress_tracker.add_task(SETUP_SECTION, COMPILTATION_TASK, &details);
|
||||
progress_tracker.add_task(SETUP_SECTION, COMPILTATION_TASK, details);
|
||||
}
|
||||
HarmonyComposerEvent::ProjectCompiled => {
|
||||
progress_tracker.finish_task(COMPILTATION_TASK, "project compiled");
|
||||
}
|
||||
HarmonyComposerEvent::ProjectCompilationFailed { details } => {
|
||||
progress_tracker.fail_task(COMPILTATION_TASK, &format!("failed to compile project:\n{details}"));
|
||||
progress_tracker.fail_task(
|
||||
COMPILTATION_TASK,
|
||||
&format!("failed to compile project:\n{details}"),
|
||||
);
|
||||
}
|
||||
HarmonyComposerEvent::DeploymentStarted { target, profile } => {
|
||||
progress_tracker.add_section(
|
||||
@ -68,15 +58,9 @@ pub async fn handle_events() {
|
||||
}
|
||||
HarmonyComposerEvent::DeploymentFailed { details } => {
|
||||
progress_tracker.add_task(PROGRESS_DEPLOYMENT, "deployment-failed", "");
|
||||
progress_tracker.fail_task("deployment-failed", &details);
|
||||
},
|
||||
HarmonyComposerEvent::Shutdown => {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
true
|
||||
progress_tracker.fail_task("deployment-failed", details);
|
||||
}
|
||||
HarmonyComposerEvent::Shutdown => {}
|
||||
}
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
use log::debug;
|
||||
use once_cell::sync::Lazy;
|
||||
use tokio::sync::broadcast;
|
||||
use std::{collections::HashMap, sync::Mutex};
|
||||
|
||||
use crate::{HarmonyProfile, HarmonyTarget};
|
||||
|
||||
@ -27,48 +26,43 @@ pub enum HarmonyComposerEvent {
|
||||
Shutdown,
|
||||
}
|
||||
|
||||
static HARMONY_COMPOSER_EVENT_BUS: Lazy<broadcast::Sender<HarmonyComposerEvent>> =
|
||||
Lazy::new(|| {
|
||||
// TODO: Adjust channel capacity
|
||||
let (tx, _rx) = broadcast::channel(16);
|
||||
tx
|
||||
});
|
||||
type Subscriber = Box<dyn Fn(&HarmonyComposerEvent) + Send + Sync>;
|
||||
|
||||
pub fn instrument(event: HarmonyComposerEvent) -> Result<(), &'static str> {
|
||||
#[cfg(not(test))]
|
||||
{
|
||||
match HARMONY_COMPOSER_EVENT_BUS.send(event) {
|
||||
Ok(_) => Ok(()),
|
||||
Err(_) => Err("send error: no subscribers"),
|
||||
}
|
||||
}
|
||||
static SUBSCRIBERS: Lazy<Mutex<HashMap<String, Subscriber>>> =
|
||||
Lazy::new(|| Mutex::new(HashMap::new()));
|
||||
|
||||
#[cfg(test)]
|
||||
{
|
||||
let _ = event; // Suppress the "unused variable" warning for `event`
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn subscribe<F, Fut>(name: &str, mut handler: F)
|
||||
/// Subscribes a listener to all instrumentation events.
|
||||
///
|
||||
/// Simply provide a unique name and a closure to run when an event happens.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// instrumentation::subscribe("my_logger", |event| {
|
||||
/// println!("Event occurred: {:?}", event);
|
||||
/// });
|
||||
/// ```
|
||||
pub fn subscribe<F>(name: &str, callback: F)
|
||||
where
|
||||
F: FnMut(HarmonyComposerEvent) -> Fut + Send + 'static,
|
||||
Fut: Future<Output = bool> + Send,
|
||||
F: Fn(&HarmonyComposerEvent) + Send + Sync + 'static,
|
||||
{
|
||||
let mut rx = HARMONY_COMPOSER_EVENT_BUS.subscribe();
|
||||
debug!("[{name}] Service started. Listening for events...");
|
||||
loop {
|
||||
match rx.recv().await {
|
||||
Ok(event) => {
|
||||
if !handler(event).await {
|
||||
debug!("[{name}] Handler requested exit.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
Err(broadcast::error::RecvError::Lagged(n)) => {
|
||||
debug!("[{name}] Lagged behind by {n} messages.");
|
||||
}
|
||||
Err(_) => break,
|
||||
}
|
||||
}
|
||||
let mut subs = SUBSCRIBERS.lock().unwrap();
|
||||
subs.insert(name.to_string(), Box::new(callback));
|
||||
}
|
||||
|
||||
/// Instruments an event, notifying all subscribers.
|
||||
///
|
||||
/// This will call every closure that was registered with `subscribe`.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// instrumentation::instrument(HarmonyEvent::HarmonyStarted);
|
||||
/// ```
|
||||
pub fn instrument(event: HarmonyComposerEvent) -> Result<(), &'static str> {
|
||||
let subs = SUBSCRIBERS.lock().unwrap();
|
||||
|
||||
for callback in subs.values() {
|
||||
callback(&event);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -99,7 +99,7 @@ impl std::fmt::Display for HarmonyProfile {
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
let hc_logger_handle = harmony_composer_logger::init();
|
||||
harmony_composer_logger::init();
|
||||
let cli_args = GlobalArgs::parse();
|
||||
|
||||
let harmony_path = Path::new(&cli_args.harmony_path)
|
||||
@ -199,8 +199,6 @@ async fn main() {
|
||||
}
|
||||
|
||||
instrumentation::instrument(HarmonyComposerEvent::Shutdown).unwrap();
|
||||
|
||||
let _ = tokio::try_join!(hc_logger_handle);
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, clap::ValueEnum)]
|
||||
|
@ -10,3 +10,8 @@ serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
log.workspace = true
|
||||
env_logger.workspace = true
|
||||
tokio.workspace = true
|
||||
thiserror.workspace = true
|
||||
# mdns-sd = "0.14.1"
|
||||
mdns-sd = { git = "https://github.com/jggc/mdns-sd.git", branch = "patch-1" }
|
||||
local-ip-address = "0.6.5"
|
||||
|
22
harmony_inventory_agent/README.md
Normal file
22
harmony_inventory_agent/README.md
Normal file
@ -0,0 +1,22 @@
|
||||
## Compiling :
|
||||
|
||||
```bash
|
||||
cargo build -p harmony_inventory_agent --release --target x86_64-unknown-linux-musl
|
||||
```
|
||||
|
||||
This will create a statically linked binary that can run on pretty much any x86_64 system.
|
||||
|
||||
This requires installation of the target
|
||||
|
||||
```
|
||||
rustup target add x86_64-unknown-linux-musl
|
||||
```
|
||||
|
||||
And installation of the musl tools too.
|
||||
|
||||
On Archlinux, they can be installed with :
|
||||
|
||||
```
|
||||
pacman -S musl
|
||||
```
|
||||
|
@ -1,4 +1,4 @@
|
||||
use log::debug;
|
||||
use log::{debug, warn};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
use std::fs;
|
||||
@ -104,48 +104,58 @@ impl PhysicalHost {
|
||||
|
||||
fn all_tools_available() -> Result<(), String> {
|
||||
let required_tools = [
|
||||
("lsblk", "--version"),
|
||||
("lspci", "--version"),
|
||||
("lsmod", "--version"),
|
||||
("dmidecode", "--version"),
|
||||
("smartctl", "--version"),
|
||||
("ip", "route"), // No version flag available
|
||||
("lsblk", Some("--version")),
|
||||
("lspci", Some("--version")),
|
||||
("lsmod", None),
|
||||
("dmidecode", Some("--version")),
|
||||
("smartctl", Some("--version")),
|
||||
("ip", Some("route")), // No version flag available
|
||||
];
|
||||
|
||||
let mut missing_tools = Vec::new();
|
||||
|
||||
debug!("Looking for required_tools {required_tools:?}");
|
||||
for (tool, tool_arg) in required_tools.iter() {
|
||||
// First check if tool exists in PATH using which(1)
|
||||
let exists = if let Ok(output) = Command::new("which").arg(tool).output() {
|
||||
let mut exists = if let Ok(output) = Command::new("which").arg(tool).output() {
|
||||
output.status.success()
|
||||
} else {
|
||||
// Fallback: manual PATH search if which(1) is unavailable
|
||||
if let Ok(path_var) = std::env::var("PATH") {
|
||||
path_var.split(':').any(|dir| {
|
||||
let tool_path = std::path::Path::new(dir).join(tool);
|
||||
tool_path.exists() && Self::is_executable(&tool_path)
|
||||
})
|
||||
} else {
|
||||
false
|
||||
}
|
||||
};
|
||||
|
||||
if !exists {
|
||||
// Fallback: manual PATH search if which(1) is unavailable
|
||||
debug!("Looking for {tool} in path");
|
||||
if let Ok(path_var) = std::env::var("PATH") {
|
||||
debug!("PATH is {path_var}");
|
||||
exists = path_var.split(':').any(|dir| {
|
||||
let tool_path = std::path::Path::new(dir).join(tool);
|
||||
tool_path.exists() && Self::is_executable(&tool_path)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if !exists {
|
||||
warn!("Unable to find tool {tool} from PATH");
|
||||
missing_tools.push(*tool);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Verify tool is functional by checking version/help output
|
||||
let mut cmd = Command::new(tool);
|
||||
if let Some(tool_arg) = tool_arg {
|
||||
cmd.arg(tool_arg);
|
||||
}
|
||||
cmd.stdout(std::process::Stdio::null());
|
||||
cmd.stderr(std::process::Stdio::null());
|
||||
|
||||
if let Ok(status) = cmd.status() {
|
||||
if !status.success() {
|
||||
warn!("Unable to test {tool} status failed");
|
||||
missing_tools.push(*tool);
|
||||
}
|
||||
} else {
|
||||
warn!("Unable to test {tool}");
|
||||
missing_tools.push(*tool);
|
||||
}
|
||||
}
|
||||
@ -167,6 +177,7 @@ impl PhysicalHost {
|
||||
|
||||
#[cfg(unix)]
|
||||
fn is_executable(path: &std::path::Path) -> bool {
|
||||
debug!("Checking if {} is executable", path.to_string_lossy());
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
|
||||
match std::fs::metadata(path) {
|
||||
@ -285,11 +296,11 @@ impl PhysicalHost {
|
||||
if device_path.exists() {
|
||||
if drive.model.is_empty() {
|
||||
drive.model = Self::read_sysfs_string(&device_path.join("device/model"))
|
||||
.map_err(|e| format!("Failed to read model for {}: {}", name, e))?;
|
||||
.unwrap_or(format!("Failed to read model for {}", name));
|
||||
}
|
||||
if drive.serial.is_empty() {
|
||||
drive.serial = Self::read_sysfs_string(&device_path.join("device/serial"))
|
||||
.map_err(|e| format!("Failed to read serial for {}: {}", name, e))?;
|
||||
.unwrap_or(format!("Failed to read serial for {}", name));
|
||||
}
|
||||
}
|
||||
|
||||
@ -655,6 +666,10 @@ impl PhysicalHost {
|
||||
Ok("IDE".to_string())
|
||||
} else if device_name.starts_with("vd") {
|
||||
Ok("VirtIO".to_string())
|
||||
} else if device_name.starts_with("sr") {
|
||||
Ok("CDROM".to_string())
|
||||
} else if device_name.starts_with("zram") {
|
||||
Ok("Ramdisk".to_string())
|
||||
} else {
|
||||
// Try to determine from device path
|
||||
let subsystem = Self::read_sysfs_string(&device_path.join("device/subsystem"))?;
|
||||
|
2
harmony_inventory_agent/src/lib.rs
Normal file
2
harmony_inventory_agent/src/lib.rs
Normal file
@ -0,0 +1,2 @@
|
||||
mod hwinfo;
|
||||
pub mod local_presence;
|
90
harmony_inventory_agent/src/local_presence/advertise.rs
Normal file
90
harmony_inventory_agent/src/local_presence/advertise.rs
Normal file
@ -0,0 +1,90 @@
|
||||
use log::{debug, error, info, warn};
|
||||
use mdns_sd::{ServiceDaemon, ServiceInfo};
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::{
|
||||
hwinfo::PhysicalHost,
|
||||
local_presence::{PresenceError, SERVICE_NAME, VERSION},
|
||||
};
|
||||
|
||||
/// Advertises the agent's presence on the local network.
|
||||
///
|
||||
/// This function is synchronous and non-blocking. It spawns a background Tokio task
|
||||
/// to handle the mDNS advertisement for the lifetime of the application.
|
||||
pub fn advertise(service_port: u16) -> Result<(), PresenceError> {
|
||||
let host_id = match PhysicalHost::gather() {
|
||||
Ok(host) => Some(host.host_uuid),
|
||||
Err(e) => {
|
||||
error!("Could not build physical host, harmony presence id will be unavailable : {e}");
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
let instance_name = format!(
|
||||
"inventory-agent-{}",
|
||||
host_id.clone().unwrap_or("unknown".to_string())
|
||||
);
|
||||
|
||||
let spawned_msg = format!("Spawned local presence advertisement task for '{instance_name}'.");
|
||||
|
||||
tokio::spawn(async move {
|
||||
info!(
|
||||
"Local presence task started. Advertising as '{}'.",
|
||||
instance_name
|
||||
);
|
||||
|
||||
// The ServiceDaemon must live for the entire duration of the advertisement.
|
||||
// If it's dropped, the advertisement stops.
|
||||
let mdns = match ServiceDaemon::new() {
|
||||
Ok(daemon) => daemon,
|
||||
Err(e) => {
|
||||
warn!("Failed to create mDNS daemon: {}. Task shutting down.", e);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let mut props = HashMap::new();
|
||||
if let Some(host_id) = host_id {
|
||||
props.insert("id".to_string(), host_id);
|
||||
}
|
||||
props.insert("version".to_string(), VERSION.to_string());
|
||||
|
||||
let local_ip: Box<dyn mdns_sd::AsIpAddrs> = match local_ip_address::local_ip() {
|
||||
Ok(ip) => Box::new(ip),
|
||||
Err(e) => {
|
||||
error!(
|
||||
"Could not figure out local ip, mdns will have to try to figure it out by itself : {e}"
|
||||
);
|
||||
Box::new(())
|
||||
}
|
||||
};
|
||||
|
||||
debug!("Using local ip {:?}", local_ip.as_ip_addrs());
|
||||
|
||||
let service_info = ServiceInfo::new(
|
||||
SERVICE_NAME,
|
||||
&instance_name,
|
||||
&format!("{}.local.", instance_name),
|
||||
local_ip,
|
||||
service_port,
|
||||
Some(props),
|
||||
)
|
||||
.expect("ServiceInfo creation should not fail with valid inputs");
|
||||
|
||||
// The registration handle must also be kept alive.
|
||||
let _registration_handle = match mdns.register(service_info) {
|
||||
Ok(handle) => {
|
||||
info!("Service successfully registered on the local network.");
|
||||
handle
|
||||
}
|
||||
Err(e) => {
|
||||
warn!("Failed to register service: {}. Task shutting down.", e);
|
||||
return;
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
info!("{spawned_msg}");
|
||||
|
||||
Ok(())
|
||||
}
|
34
harmony_inventory_agent/src/local_presence/discover.rs
Normal file
34
harmony_inventory_agent/src/local_presence/discover.rs
Normal file
@ -0,0 +1,34 @@
|
||||
use mdns_sd::{ServiceDaemon, ServiceEvent};
|
||||
|
||||
use crate::local_presence::SERVICE_NAME;
|
||||
|
||||
pub type DiscoveryEvent = ServiceEvent;
|
||||
|
||||
pub fn discover_agents(timeout: Option<u64>, on_event: fn(&DiscoveryEvent)) {
|
||||
// Create a new mDNS daemon.
|
||||
let mdns = ServiceDaemon::new().expect("Failed to create mDNS daemon");
|
||||
|
||||
// Start browsing for the service type.
|
||||
// The receiver will be a stream of events.
|
||||
let receiver = mdns.browse(SERVICE_NAME).expect("Failed to browse");
|
||||
|
||||
std::thread::spawn(move || {
|
||||
while let Ok(event) = receiver.recv() {
|
||||
on_event(&event);
|
||||
match event {
|
||||
ServiceEvent::ServiceResolved(resolved) => {
|
||||
println!("Resolved a new service: {}", resolved.fullname);
|
||||
}
|
||||
other_event => {
|
||||
println!("Received other event: {:?}", &other_event);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if let Some(timeout) = timeout {
|
||||
// Gracefully shutdown the daemon.
|
||||
std::thread::sleep(std::time::Duration::from_secs(timeout));
|
||||
mdns.shutdown().unwrap();
|
||||
}
|
||||
}
|
16
harmony_inventory_agent/src/local_presence/mod.rs
Normal file
16
harmony_inventory_agent/src/local_presence/mod.rs
Normal file
@ -0,0 +1,16 @@
|
||||
mod discover;
|
||||
pub use discover::*;
|
||||
mod advertise;
|
||||
pub use advertise::*;
|
||||
|
||||
pub const SERVICE_NAME: &str = "_harmony._tcp.local.";
|
||||
const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
|
||||
// A specific error type for our module enhances clarity and usability.
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum PresenceError {
|
||||
#[error("Failed to create mDNS daemon")]
|
||||
DaemonCreationFailed(#[from] mdns_sd::Error),
|
||||
#[error("The shutdown signal has already been sent")]
|
||||
ShutdownFailed,
|
||||
}
|
@ -1,9 +1,12 @@
|
||||
// src/main.rs
|
||||
use actix_web::{App, HttpServer, Responder, get};
|
||||
use hwinfo::PhysicalHost;
|
||||
use log::error;
|
||||
use std::env;
|
||||
|
||||
use crate::hwinfo::PhysicalHost;
|
||||
|
||||
mod hwinfo;
|
||||
mod local_presence;
|
||||
|
||||
#[get("/inventory")]
|
||||
async fn inventory() -> impl Responder {
|
||||
@ -26,10 +29,17 @@ async fn main() -> std::io::Result<()> {
|
||||
env_logger::init();
|
||||
|
||||
let port = env::var("HARMONY_INVENTORY_AGENT_PORT").unwrap_or_else(|_| "8080".to_string());
|
||||
let port = port
|
||||
.parse::<u16>()
|
||||
.expect(&format!("Invalid port number, cannot parse to u16 {port}"));
|
||||
let bind_addr = format!("0.0.0.0:{}", port);
|
||||
|
||||
log::info!("Starting inventory agent on {}", bind_addr);
|
||||
|
||||
if let Err(e) = local_presence::advertise(port) {
|
||||
error!("Could not start advertise local presence : {e}");
|
||||
}
|
||||
|
||||
HttpServer::new(|| App::new().service(inventory))
|
||||
.bind(&bind_addr)?
|
||||
.run()
|
||||
|
@ -1,19 +1,20 @@
|
||||
[package]
|
||||
name = "harmony-secret"
|
||||
name = "harmony_secret"
|
||||
edition = "2024"
|
||||
version.workspace = true
|
||||
readme.workspace = true
|
||||
license.workspace = true
|
||||
|
||||
[dependencies]
|
||||
harmony-secret-derive = { version = "0.1.0", path = "../harmony_secret_derive" }
|
||||
harmony_secret_derive = { version = "0.1.0", path = "../harmony_secret_derive" }
|
||||
serde = { version = "1.0.209", features = ["derive", "rc"] }
|
||||
serde_json = "1.0.127"
|
||||
thiserror.workspace = true
|
||||
lazy_static.workspace = true
|
||||
directories.workspace = true
|
||||
log.workspace = true
|
||||
infisical = "0.0.2"
|
||||
# infisical = "0.0.2"
|
||||
infisical = { git = "https://github.com/jggc/rust-sdk.git", branch = "patch-1" }
|
||||
tokio.workspace = true
|
||||
async-trait.workspace = true
|
||||
http.workspace = true
|
||||
|
@ -126,6 +126,7 @@ impl SecretManager {
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
#[cfg(secrete2etest)]
|
||||
use pretty_assertions::assert_eq;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
|
@ -70,7 +70,6 @@ mod tests {
|
||||
#[tokio::test]
|
||||
async fn test_set_and_get_raw_successfully() {
|
||||
let dir = tempdir().unwrap();
|
||||
let store = LocalFileSecretStore::default();
|
||||
let ns = "test-ns";
|
||||
let key = "test-key";
|
||||
let value = b"{\"data\":\"test-value\"}";
|
||||
|
@ -1,5 +1,5 @@
|
||||
[package]
|
||||
name = "harmony-secret-derive"
|
||||
name = "harmony_secret_derive"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
|
@ -10,10 +10,10 @@ pub fn derive_secret(input: TokenStream) -> TokenStream {
|
||||
|
||||
// The key for the secret will be the stringified name of the struct itself.
|
||||
// e.g., `struct OKDClusterSecret` becomes key `"OKDClusterSecret"`.
|
||||
let key = struct_ident.to_string();
|
||||
let key = struct_ident.to_string(); // TODO: Utiliser path complet de la struct
|
||||
|
||||
// Find the path to the `harmony_secret` crate.
|
||||
let secret_crate_path = match crate_name("harmony-secret") {
|
||||
let secret_crate_path = match crate_name("harmony_secret") {
|
||||
Ok(FoundCrate::Itself) => quote!(crate),
|
||||
Ok(FoundCrate::Name(name)) => {
|
||||
let ident = Ident::new(&name, proc_macro2::Span::call_site());
|
||||
|
@ -94,13 +94,9 @@ async fn init_instrumentation() -> tokio::task::JoinHandle<()> {
|
||||
}
|
||||
|
||||
async fn handle_harmony_events() {
|
||||
instrumentation::subscribe("Harmony TUI Logger", async |event| {
|
||||
if let HarmonyEvent::HarmonyFinished = event {
|
||||
return false;
|
||||
};
|
||||
true
|
||||
})
|
||||
.await;
|
||||
instrumentation::subscribe("Harmony TUI Logger", |_| {
|
||||
// TODO: Display events in the TUI
|
||||
});
|
||||
}
|
||||
|
||||
pub struct HarmonyTUI<T: Topology> {
|
||||
|
@ -11,7 +11,7 @@ async-trait = { workspace = true }
|
||||
tokio = { workspace = true }
|
||||
octocrab = "0.44.0"
|
||||
regex = "1.11.1"
|
||||
reqwest = { version = "0.12", features = ["stream"] }
|
||||
reqwest = { version = "0.12", features = ["stream", "rustls-tls", "http2"], default-features = false }
|
||||
url.workspace = true
|
||||
sha2 = "0.10.8"
|
||||
futures-util = "0.3.31"
|
||||
|
@ -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: MaybeString,
|
||||
pub tls_dns_secret_api_key: Option<MaybeString>,
|
||||
#[yaserde(rename = "TlsDnsOptionalField1")]
|
||||
pub tls_dns_optional_field1: MaybeString,
|
||||
pub tls_dns_optional_field1: Option<MaybeString>,
|
||||
#[yaserde(rename = "TlsDnsOptionalField2")]
|
||||
pub tls_dns_optional_field2: MaybeString,
|
||||
pub tls_dns_optional_field2: Option<MaybeString>,
|
||||
#[yaserde(rename = "TlsDnsOptionalField3")]
|
||||
pub tls_dns_optional_field3: MaybeString,
|
||||
pub tls_dns_optional_field3: Option<MaybeString>,
|
||||
#[yaserde(rename = "TlsDnsOptionalField4")]
|
||||
pub tls_dns_optional_field4: MaybeString,
|
||||
pub tls_dns_optional_field4: Option<MaybeString>,
|
||||
#[yaserde(rename = "TlsDnsPropagationTimeout")]
|
||||
pub tls_dns_propagation_timeout: Option<MaybeString>,
|
||||
#[yaserde(rename = "TlsDnsPropagationTimeoutPeriod")]
|
||||
@ -47,6 +47,8 @@ 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: Option<MaybeString>,
|
||||
pub accesslist: MaybeString,
|
||||
#[yaserde(rename = "DisableSuperuser")]
|
||||
pub disable_superuser: Option<i32>,
|
||||
@ -56,6 +58,10 @@ 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")]
|
||||
|
113
opnsense-config-xml/src/data/dnsmasq.rs
Normal file
113
opnsense-config-xml/src/data/dnsmasq.rs
Normal file
@ -0,0 +1,113 @@
|
||||
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: Option<String>,
|
||||
pub interface: Option<String>,
|
||||
pub set_tag: Option<MaybeString>,
|
||||
pub start_addr: Option<String>,
|
||||
pub end_addr: Option<String>,
|
||||
pub subnet_mask: Option<MaybeString>,
|
||||
pub constructor: Option<MaybeString>,
|
||||
pub mode: Option<MaybeString>,
|
||||
pub prefix_len: Option<MaybeString>,
|
||||
pub lease_time: Option<MaybeString>,
|
||||
pub domain_type: Option<String>,
|
||||
pub domain: Option<MaybeString>,
|
||||
pub nosync: Option<u8>,
|
||||
pub ra_mode: Option<MaybeString>,
|
||||
pub ra_priority: Option<MaybeString>,
|
||||
pub ra_mtu: Option<MaybeString>,
|
||||
pub ra_interval: Option<MaybeString>,
|
||||
pub ra_router_lifetime: Option<MaybeString>,
|
||||
pub description: Option<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: Option<u8>,
|
||||
pub description: MaybeString,
|
||||
}
|
@ -8,10 +8,12 @@ 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")]
|
||||
|
@ -1,5 +1,6 @@
|
||||
mod caddy;
|
||||
mod dhcpd;
|
||||
pub mod dnsmasq;
|
||||
mod haproxy;
|
||||
mod interfaces;
|
||||
mod opnsense;
|
||||
|
@ -1,3 +1,4 @@
|
||||
use crate::dnsmasq::DnsMasq;
|
||||
use crate::HAProxy;
|
||||
use crate::{data::dhcpd::DhcpInterface, xml_utils::to_xml_str};
|
||||
use log::error;
|
||||
@ -22,7 +23,7 @@ pub struct OPNsense {
|
||||
pub load_balancer: Option<LoadBalancer>,
|
||||
pub rrd: Option<RawXml>,
|
||||
pub ntpd: Ntpd,
|
||||
pub widgets: Widgets,
|
||||
pub widgets: Option<Widgets>,
|
||||
pub revision: Revision,
|
||||
#[yaserde(rename = "OPNsense")]
|
||||
pub opnsense: OPNsenseXmlSection,
|
||||
@ -45,7 +46,7 @@ pub struct OPNsense {
|
||||
#[yaserde(rename = "Pischem")]
|
||||
pub pischem: Option<Pischem>,
|
||||
pub ifgroups: Ifgroups,
|
||||
pub dnsmasq: Option<RawXml>,
|
||||
pub dnsmasq: Option<DnsMasq>,
|
||||
}
|
||||
|
||||
impl From<String> for OPNsense {
|
||||
@ -165,9 +166,9 @@ pub struct Sysctl {
|
||||
|
||||
#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)]
|
||||
pub struct SysctlItem {
|
||||
pub descr: MaybeString,
|
||||
pub tunable: String,
|
||||
pub value: MaybeString,
|
||||
pub descr: Option<MaybeString>,
|
||||
pub tunable: Option<String>,
|
||||
pub value: Option<MaybeString>,
|
||||
}
|
||||
|
||||
#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)]
|
||||
@ -182,8 +183,8 @@ pub struct System {
|
||||
pub domain: String,
|
||||
pub group: Vec<Group>,
|
||||
pub user: Vec<User>,
|
||||
pub nextuid: u32,
|
||||
pub nextgid: u32,
|
||||
pub nextuid: Option<u32>,
|
||||
pub nextgid: Option<u32>,
|
||||
pub timezone: String,
|
||||
pub timeservers: String,
|
||||
pub webgui: WebGui,
|
||||
@ -242,6 +243,7 @@ pub struct Ssh {
|
||||
pub passwordauth: u8,
|
||||
pub keysig: MaybeString,
|
||||
pub permitrootlogin: u8,
|
||||
pub rekeylimit: Option<MaybeString>,
|
||||
}
|
||||
|
||||
#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)]
|
||||
@ -271,6 +273,7 @@ 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)]
|
||||
@ -1506,7 +1509,7 @@ pub struct Vlans {
|
||||
|
||||
#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)]
|
||||
pub struct Bridges {
|
||||
pub bridged: MaybeString,
|
||||
pub bridged: Option<MaybeString>,
|
||||
}
|
||||
|
||||
#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)]
|
||||
|
@ -20,6 +20,7 @@ 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
|
||||
|
@ -4,8 +4,8 @@ use crate::{
|
||||
config::{SshConfigManager, SshCredentials, SshOPNSenseShell},
|
||||
error::Error,
|
||||
modules::{
|
||||
caddy::CaddyConfig, dhcp::DhcpConfig, dns::DnsConfig, load_balancer::LoadBalancerConfig,
|
||||
tftp::TftpConfig,
|
||||
caddy::CaddyConfig, dhcp_legacy::DhcpConfigLegacyISC, dns::DnsConfig,
|
||||
dnsmasq::DhcpConfigDnsMasq, load_balancer::LoadBalancerConfig, tftp::TftpConfig,
|
||||
},
|
||||
};
|
||||
use log::{debug, info, trace, warn};
|
||||
@ -43,23 +43,27 @@ impl Config {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn dhcp(&mut self) -> DhcpConfig {
|
||||
DhcpConfig::new(&mut self.opnsense, self.shell.clone())
|
||||
pub fn dhcp_legacy_isc(&mut self) -> DhcpConfigLegacyISC<'_> {
|
||||
DhcpConfigLegacyISC::new(&mut self.opnsense, self.shell.clone())
|
||||
}
|
||||
|
||||
pub fn dns(&mut self) -> DnsConfig {
|
||||
pub fn dhcp(&mut self) -> DhcpConfigDnsMasq<'_> {
|
||||
DhcpConfigDnsMasq::new(&mut self.opnsense, self.shell.clone())
|
||||
}
|
||||
|
||||
pub fn dns(&mut self) -> DnsConfig<'_> {
|
||||
DnsConfig::new(&mut self.opnsense)
|
||||
}
|
||||
|
||||
pub fn tftp(&mut self) -> TftpConfig {
|
||||
pub fn tftp(&mut self) -> TftpConfig<'_> {
|
||||
TftpConfig::new(&mut self.opnsense, self.shell.clone())
|
||||
}
|
||||
|
||||
pub fn caddy(&mut self) -> CaddyConfig {
|
||||
pub fn caddy(&mut self) -> CaddyConfig<'_> {
|
||||
CaddyConfig::new(&mut self.opnsense, self.shell.clone())
|
||||
}
|
||||
|
||||
pub fn load_balancer(&mut self) -> LoadBalancerConfig {
|
||||
pub fn load_balancer(&mut self) -> LoadBalancerConfig<'_> {
|
||||
LoadBalancerConfig::new(&mut self.opnsense, self.shell.clone())
|
||||
}
|
||||
|
||||
@ -67,6 +71,10 @@ 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.
|
||||
///
|
||||
@ -200,7 +208,7 @@ impl Config {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::config::{DummyOPNSenseShell, LocalFileConfigManager};
|
||||
use crate::modules::dhcp::DhcpConfig;
|
||||
use crate::modules::dhcp_legacy::DhcpConfigLegacyISC;
|
||||
use std::fs;
|
||||
use std::net::Ipv4Addr;
|
||||
|
||||
@ -215,6 +223,9 @@ 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);
|
||||
@ -257,7 +268,7 @@ mod tests {
|
||||
|
||||
println!("Config {:?}", config);
|
||||
|
||||
let mut dhcp_config = DhcpConfig::new(&mut config.opnsense, shell);
|
||||
let mut dhcp_config = DhcpConfigLegacyISC::new(&mut config.opnsense, shell);
|
||||
dhcp_config
|
||||
.add_static_mapping(
|
||||
"00:00:00:00:00:00",
|
||||
|
@ -9,6 +9,7 @@ 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>;
|
||||
}
|
||||
|
||||
@ -25,6 +26,14 @@ 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");
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
use std::{
|
||||
net::IpAddr,
|
||||
path::Path,
|
||||
sync::Arc,
|
||||
time::{SystemTime, UNIX_EPOCH},
|
||||
};
|
||||
@ -44,6 +45,10 @@ 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> {
|
||||
let channel = self.get_ssh_channel().await?;
|
||||
channel
|
||||
.request_subsystem(true, "sftp")
|
||||
@ -53,10 +58,18 @@ impl OPNsenseShell for SshOPNSenseShell {
|
||||
.await
|
||||
.expect("Should acquire sftp subsystem");
|
||||
|
||||
let mut file = sftp.create(&temp_filename).await.unwrap();
|
||||
if let Some(parent) = Path::new(filename).parent() {
|
||||
if let Some(parent_str) = parent.to_str() {
|
||||
if !parent_str.is_empty() {
|
||||
self.ensure_remote_dir_exists(&sftp, parent_str).await?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut file = sftp.create(filename).await.unwrap();
|
||||
file.write_all(content.as_bytes()).await?;
|
||||
|
||||
Ok(temp_filename)
|
||||
Ok(filename.to_string())
|
||||
}
|
||||
|
||||
async fn upload_folder(&self, source: &str, destination: &str) -> Result<String, Error> {
|
||||
@ -69,10 +82,7 @@ impl OPNsenseShell for SshOPNSenseShell {
|
||||
.await
|
||||
.expect("Should acquire sftp subsystem");
|
||||
|
||||
if !sftp.try_exists(destination).await? {
|
||||
info!("Creating remote directory {destination}");
|
||||
sftp.create_dir(destination).await?;
|
||||
}
|
||||
self.ensure_remote_dir_exists(&sftp, destination).await?;
|
||||
|
||||
info!("Reading local directory {source}");
|
||||
let mut entries = read_dir(source).await?;
|
||||
@ -149,6 +159,14 @@ impl SshOPNSenseShell {
|
||||
wait_for_completion(&mut channel).await
|
||||
}
|
||||
|
||||
async fn ensure_remote_dir_exists(&self, sftp: &SftpSession, path: &str) -> Result<(), Error> {
|
||||
if !sftp.try_exists(path).await? {
|
||||
info!("Creating remote directory {path}");
|
||||
sftp.create_dir(path).await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn new(host: (IpAddr, u16), credentials: SshCredentials, ssh_config: Arc<Config>) -> Self {
|
||||
info!("Initializing SshOPNSenseShell on host {host:?}");
|
||||
Self {
|
||||
|
@ -1,19 +1,3 @@
|
||||
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),
|
||||
@ -21,6 +5,7 @@ pub enum DhcpError {
|
||||
IpAddressAlreadyMapped(String),
|
||||
MacAddressAlreadyMapped(String),
|
||||
IpAddressOutOfRange(String),
|
||||
Configuration(String),
|
||||
}
|
||||
|
||||
impl std::fmt::Display for DhcpError {
|
||||
@ -37,158 +22,9 @@ 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());
|
||||
}
|
||||
}
|
||||
|
166
opnsense-config/src/modules/dhcp_legacy.rs
Normal file
166
opnsense-config/src/modules/dhcp_legacy.rs
Normal file
@ -0,0 +1,166 @@
|
||||
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());
|
||||
}
|
||||
}
|
187
opnsense-config/src/modules/dnsmasq.rs
Normal file
187
opnsense-config/src/modules/dnsmasq.rs
Normal file
@ -0,0 +1,187 @@
|
||||
// dnsmasq.rs
|
||||
use crate::modules::dhcp::DhcpError;
|
||||
use log::{debug, info};
|
||||
use opnsense_config_xml::{MaybeString, StaticMap};
|
||||
use std::net::Ipv4Addr;
|
||||
use std::sync::Arc;
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
/// 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(())
|
||||
}
|
||||
}
|
@ -1,5 +1,7 @@
|
||||
pub mod caddy;
|
||||
pub mod dhcp;
|
||||
pub mod dhcp_legacy;
|
||||
pub mod dns;
|
||||
pub mod dnsmasq;
|
||||
pub mod load_balancer;
|
||||
pub mod tftp;
|
||||
|
@ -0,0 +1,896 @@
|
||||
<?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>
|
@ -0,0 +1,867 @@
|
||||
<?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>
|
826
opnsense-config/src/tests/data/config-full-25.7.xml
Normal file
826
opnsense-config/src/tests/data/config-full-25.7.xml
Normal file
@ -0,0 +1,826 @@
|
||||
<?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>
|
2572
opnsense-config/src/tests/data/config-full-ncd0.xml
Normal file
2572
opnsense-config/src/tests/data/config-full-ncd0.xml
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user