From af15844ed501d61aec4f60e1d7776e43b1cba52f Mon Sep 17 00:00:00 2001 From: Dominik Polakovics Date: Fri, 6 Jun 2025 22:57:17 +0200 Subject: [PATCH 1/3] feat: add rules and guidelines for Cloonar Assistant LLM --- .roo/rules/rules.md | 157 ++++++++++++++++++++++++++++++++++++++++++++ scripts/run-vm | 59 ++++++++++++----- 2 files changed, 201 insertions(+), 15 deletions(-) create mode 100644 .roo/rules/rules.md diff --git a/.roo/rules/rules.md b/.roo/rules/rules.md new file mode 100644 index 0000000..7f4c55e --- /dev/null +++ b/.roo/rules/rules.md @@ -0,0 +1,157 @@ +# Cloonar Assistant LLM Rules + +This document defines the rules and guidelines for an LLM working with the Cloonar Assistant NixOS module project. + +## 1. Project Understanding + +### 1.1 Core Components +- Network Infrastructure (VLANs, DHCP, DNS, Firewall) +- Security Services (WireGuard VPN, SSL/ACME) +- Home Automation (Home Assistant) +- System Services (Dynamic DNS, Container Management) +- Development Tools (ISO Builder, VM Testing) + +### 1.2 Module Architecture +```mermaid +graph TD + A[Cloonar Assistant] --> B[Network Management] + A --> C[Security] + A --> D[Services] + A --> E[Development Tools] + + B --> B1[VLANs] + B --> B2[DHCP/Kea] + B --> B3[DNS/Unbound] + B --> B4[Firewall/nftables] + + C --> C1[WireGuard VPN] + C --> C2[SSL/ACME] + C --> C3[SOPS Integration] + + D --> D1[Home Assistant] + D --> D2[Dynamic DNS] + D --> D3[Container Services] + + E --> E1[ISO Builder] + E --> E2[VM Testing] +``` + +## 2. NixOS Integration (REQUIRED) + +### 2.1 Package and Option Verification +- ALWAYS use the NixOS MCP server to verify packages and options before suggesting them +- Query format: `use_mcp_tool` with server "nixos" for all NixOS-related lookups +- Verify package availability in the project's current NixOS version +- Validate option compatibility and deprecation status + +### 2.2 Configuration Guidelines +- All NixOS configurations must be validated through MCP before suggestion +- Use proper module imports and option declarations +- Follow NixOS naming conventions and type declarations +- Consider module dependencies and conflicts + +## 3. Development Guidelines + +### 3.1 Code Structure +- Maintain modular organization in `modules/cloonar-assistant/` +- Follow existing patterns for option declarations +- Use descriptive names for options and properties +- Keep related functionality grouped in appropriate submodules + +### 3.2 Testing Protocol +- Utilize the VM testing scripts in `scripts/` +- Test configuration changes with `test-configuration` +- Verify VLAN and network functionality in VM environment +- Ensure proper service container operation + +## 4. Configuration Support + +### 4.1 Network Configuration +- Define appropriate VLANs based on network requirements +- Configure firewall rules using nftables syntax +- Set up proper DHCP and DNS services +- Implement correct routing between VLANs + +### 4.2 Service Configuration +- Configure Home Assistant container with proper isolation +- Set up SSL certificates via ACME +- Manage WireGuard VPN peers and configurations +- Configure dynamic DNS updates + +## 5. Security Best Practices + +### 5.1 General Security +- Never expose sensitive information in configurations +- Use SOPS for secrets management +- Implement proper network segmentation +- Follow principle of least privilege + +### 5.2 Network Security +- Verify firewall rule correctness +- Implement proper VLAN isolation +- Secure VPN configurations +- Validate SSL certificate management + +## 6. Troubleshooting Framework + +### 6.1 Diagnostic Approach +```mermaid +flowchart LR + A[Issue Reported] --> B{Category?} + B --> C[Network] + B --> D[Service] + B --> E[Build] + + C --> C1[Check VLANs] + C --> C2[Verify Firewall] + C --> C3[Test DNS] + + D --> D1[Container Status] + D --> D2[Service Logs] + D --> D3[Dependencies] + + E --> E1[Nix Errors] + E --> E2[Option Issues] + E --> E3[Build Logs] +``` + +### 6.2 Common Issues +- VLAN connectivity problems +- Container networking issues +- SSL certificate renewal failures +- Build and configuration errors + +## 7. Self-Maintenance Rules + +### 7.1 Rule Update Triggers +Monitor and update rules.md when: +- Major NixOS version changes affect module functionality +- New security considerations emerge +- Core module features are added/modified +- Breaking changes in dependencies occur + +### 7.2 Update Protocol +1. Identify breaking changes or important updates +2. Document impact on existing configurations +3. Update relevant rule sections +4. Add new guidelines if needed +5. Update version compatibility information + +### 7.3 Documentation Sync +- Keep rules aligned with current codebase +- Update mermaid diagrams for architectural changes +- Maintain accurate NixOS version compatibility info +- Document new features and deprecations + +### 7.4 Change Validation +Before updating rules: +- Verify changes against current codebase +- Test impact on existing configurations +- Check NixOS MCP for option/package changes +- Validate security implications + +## 8. Version Information + +- Last Updated: 2025-06-06 +- Compatible NixOS Versions: 23.05, 23.11, 24.05 +- Project Version: Current Master \ No newline at end of file diff --git a/scripts/run-vm b/scripts/run-vm index f3d1850..35a7c26 100755 --- a/scripts/run-vm +++ b/scripts/run-vm @@ -2,12 +2,11 @@ set -Euo pipefail # ----------------------------------------------------------------------------- -# This script sets up and launches a QEMU virtual machine with OVMF (UEFI). -# It checks for the necessary files, creates directories/images as needed, -# and provides clear, user-friendly output along the way. +# This script sets up and launches (or stops) a QEMU virtual machine with OVMF. +# # Usage: -# ./run-vm.sh [install] -# - Pass "install" to attach the ISO as a CD-ROM for installation. +# ./run-vm.sh [install] # starts (and backgrounds) the VM; use "install" to attach the ISO +# ./run-vm.sh stop # kills the running QEMU VM (reads PID from .vm/qemu.pid) # ----------------------------------------------------------------------------- # Paths to OVMF firmware (pflash) @@ -17,9 +16,33 @@ OVMF_VARS_DEFAULT="/run/libvirt/nix-ovmf/OVMF_VARS.fd" # Determine where this script lives and compute related paths SCRIPT_DIR=$(dirname "$(readlink -f "$0")") TARGET_DIR=$(readlink -f "$SCRIPT_DIR/../.vm") -OVMF_VARS_PATH=$(readlink -f "$SCRIPT_DIR/../.vm/OVMF_VARS-myvm.fd") -IMG_PATH=$(readlink -f "$SCRIPT_DIR/../.vm/disk.img") +OVMF_VARS_PATH="$TARGET_DIR/OVMF_VARS-myvm.fd" +IMG_PATH="$TARGET_DIR/disk.img" ISO_DIR=$(readlink -f "$SCRIPT_DIR/../iso/result/iso") +PID_FILE="$TARGET_DIR/qemu.pid" + +# If first argument is "stop", then kill the running VM and exit: +if [ "${1-}" = "stop" ]; then + if [ -f "$PID_FILE" ]; then + VM_PID=$(<"$PID_FILE") + if kill -0 "$VM_PID" 2>/dev/null; then + echo "Killing QEMU (PID $VM_PID)..." + kill "$VM_PID" + # Optionally wait for it to die: + wait "$VM_PID" 2>/dev/null || true + echo "✅ VM stopped." + rm -f "$PID_FILE" + exit 0 + else + echo "⚠️ No running QEMU process with PID $VM_PID. Removing stale PID file." + rm -f "$PID_FILE" + exit 1 + fi + else + echo "⚠️ No PID file found at $PID_FILE. Is the VM running?" + exit 1 + fi +fi echo echo "============================================================" @@ -92,7 +115,8 @@ fi if [ "$INSTALL_MODE" -eq 1 ]; then echo "[5/6] Install mode enabled: CD-ROM will be attached" - CDROM_OPTS="-drive file=\"$ISO_FILE\",format=raw,if=none,media=cdrom,id=cd1,readonly=on -device ahci,id=ahci0 -device ide-cd,bus=ahci0.0,drive=cd1,bootindex=1" + CDROM_OPTS="-drive file=\"$ISO_FILE\",format=raw,if=none,media=cdrom,id=cd1,readonly=on \ +-device ahci,id=ahci0 -device ide-cd,bus=ahci0.0,drive=cd1,bootindex=1" else echo "[5/6] Normal boot mode: No CD-ROM attached" CDROM_OPTS="" @@ -100,9 +124,9 @@ fi echo # ----------------------------------------------------------------------------- -# 6. Launch QEMU +# 6. Launch QEMU (in the background) # ----------------------------------------------------------------------------- -echo "[6/6] Launching QEMU VM now..." +echo "[6/6] Launching QEMU VM now (in background)..." echo "------------------------------------------------------------" echo " • Machine: q35, KVM acceleration" echo " • Memory: 4096 MB" @@ -122,7 +146,7 @@ echo # Construct network options NET_OPTS="-netdev user,id=net0,hostfwd=tcp::2222-:22 -device e1000,netdev=net0" -# Run QEMU using eval to allow variable expansion in CDROM_OPTS +# Run QEMU in the background and store its PID eval qemu-system-x86_64 \ -machine type=q35,accel=kvm \ -m 4096 \ @@ -137,9 +161,14 @@ eval qemu-system-x86_64 \ $CDROM_OPTS \ \ $NET_OPTS \ - -vga virtio + -vga virtio \ + & +VM_PID=$! +echo "$VM_PID" > "$PID_FILE" +echo "✅ QEMU started with PID $VM_PID. PID file: $PID_FILE" echo -echo "============================================================" -echo " QEMU VM has exited" -echo "============================================================" +echo "To stop the VM at any time, run:" +echo " $0 stop" +echo +exit 0 From bda71a27da955935ba8bfbb1bf799f651599a9c5 Mon Sep 17 00:00:00 2001 From: Dominik Polakovics Date: Fri, 6 Jun 2025 22:58:20 +0200 Subject: [PATCH 2/3] feat: add .gitignore to exclude specific directories and files --- .gitignore | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a4d834a --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +# Ignore everything in .vm directory +.vm/* +# But keep .gitkeep +!.vm/.gitkeep + +# Ignore result in iso directory +iso/result \ No newline at end of file From f032c4abbd8d320956695b1c14feb7d00096018b Mon Sep 17 00:00:00 2001 From: Dominik Polakovics Date: Fri, 6 Jun 2025 23:24:38 +0200 Subject: [PATCH 3/3] feat: implement dnsmasq for DHCP and DNS management, remove unbound and dhcp configurations --- candle-sensor | 12 ++ .../cloonar-assistant/networking/default.nix | 3 +- modules/cloonar-assistant/networking/dhcp.nix | 187 ------------------ .../cloonar-assistant/networking/dnsmasq.nix | 98 +++++++++ .../cloonar-assistant/networking/unbound.nix | 138 ------------- 5 files changed, 111 insertions(+), 327 deletions(-) create mode 100644 candle-sensor delete mode 100644 modules/cloonar-assistant/networking/dhcp.nix create mode 100644 modules/cloonar-assistant/networking/dnsmasq.nix delete mode 100644 modules/cloonar-assistant/networking/unbound.nix diff --git a/candle-sensor b/candle-sensor new file mode 100644 index 0000000..4f6b364 --- /dev/null +++ b/candle-sensor @@ -0,0 +1,12 @@ +it does not work if the sensor is underneath + +i could build a device which you can put on the table and looks to the candles +it has 80cm of recognizing a flame. + +it would use the following components +Adafruit ESP32 Feather V2 +ANGEEK KY-026 Flame Sensor Module +2000 - 3000 mah LiPo + +battery life would be 20 Days, cost would be around 35€ just plain devices cost without work time +3d printed housing would be needed diff --git a/modules/cloonar-assistant/networking/default.nix b/modules/cloonar-assistant/networking/default.nix index 94eff10..a317a1c 100644 --- a/modules/cloonar-assistant/networking/default.nix +++ b/modules/cloonar-assistant/networking/default.nix @@ -1,9 +1,8 @@ { ... }: { imports = [ ./interfaces.nix - ./dhcp.nix ./firewall.nix - ./unbound.nix + ./dnsmasq.nix # New dnsmasq configuration ./wireguard.nix ]; } diff --git a/modules/cloonar-assistant/networking/dhcp.nix b/modules/cloonar-assistant/networking/dhcp.nix deleted file mode 100644 index 164acd0..0000000 --- a/modules/cloonar-assistant/networking/dhcp.nix +++ /dev/null @@ -1,187 +0,0 @@ -{ config, lib, ... }: -{ - services.kea.dhcp4 = lib.mkIf config.cloonar-assistant.firewall.enable { - enable = true; - settings = { - interfaces-config = { - interfaces = [ - "lan" - "server" - "infrastructure" - "multimedia" - "smart" - "guest" - ]; - }; - lease-database = { - name = "/var/lib/kea/dhcp4.leases"; - persist = true; - type = "memfile"; - }; - rebind-timer = 2000; - renew-timer = 1000; - subnet4 = [ - { - id = 96; - pools = [ - { - pool = "${config.cloonar-assistant.networkPrefix}.96.100 - ${config.cloonar-assistant.networkPrefix}.96.240"; - } - ]; - subnet = "${config.cloonar-assistant.networkPrefix}.96.0/24"; - interface = "lan"; - option-data = [ - { - name = "routers"; - data = "${config.cloonar-assistant.networkPrefix}.96.1"; - } - { - name = "domain-name"; - data = config.cloonar-assistant.domain; - } - { - name = "domain-search"; - data = config.cloonar-assistant.domain; - } - { - name = "domain-name-servers"; - data = "${config.cloonar-assistant.networkPrefix}.96.1"; - } - ]; - reservations = [ - ]; - } - { - id = 97; - pools = [ - { - pool = "${config.cloonar-assistant.networkPrefix}.97.100 - ${config.cloonar-assistant.networkPrefix}.97.240"; - } - ]; - subnet = "${config.cloonar-assistant.networkPrefix}.97.0/24"; - interface = "server"; - option-data = [ - { - name = "routers"; - data = "${config.cloonar-assistant.networkPrefix}.97.1"; - } - { - name = "domain-name"; - data = config.cloonar-assistant.domain; - } - { - name = "domain-name-servers"; - data = "${config.cloonar-assistant.networkPrefix}.97.1"; - } - ]; - reservations = [ - ]; - } - { - id = 101; - pools = [ - { - pool = "${config.cloonar-assistant.networkPrefix}.101.100 - ${config.cloonar-assistant.networkPrefix}.101.240"; - } - ]; - subnet = "${config.cloonar-assistant.networkPrefix}.101.0/24"; - interface = "infrastructure"; - option-data = [ - { - name = "routers"; - data = "${config.cloonar-assistant.networkPrefix}.101.1"; - } - { - name = "domain-name"; - data = config.cloonar-assistant.domain; - } - { - name = "domain-name-servers"; - data = "${config.cloonar-assistant.networkPrefix}.101.1"; - } - { - name = "capwap-ac-v4"; - code = 138; - data = "${config.cloonar-assistant.networkPrefix}.97.2"; - } - ]; - reservations = [ - ]; - } - { - id = 99; - pools = [ - { - pool = "${config.cloonar-assistant.networkPrefix}.99.100 - ${config.cloonar-assistant.networkPrefix}.99.240"; - } - ]; - subnet = "${config.cloonar-assistant.networkPrefix}.99.0/24"; - interface = "multimedia"; - option-data = [ - { - name = "routers"; - data = "${config.cloonar-assistant.networkPrefix}.99.1"; - } - { - name = "domain-name"; - data = config.cloonar-assistant.domain; - } - { - name = "domain-name-servers"; - data = "${config.cloonar-assistant.networkPrefix}.99.1"; - } - ]; - reservations = [ - ]; - } - { - id = 254; - pools = [ - { - pool = "${config.cloonar-assistant.networkPrefix}.254.10 - ${config.cloonar-assistant.networkPrefix}.254.254"; - } - ]; - subnet = "${config.cloonar-assistant.networkPrefix}.254.0/24"; - interface = "guest"; - option-data = [ - { - name = "routers"; - data = "${config.cloonar-assistant.networkPrefix}.254.1"; - } - { - name = "domain-name-servers"; - data = "9.9.9.9"; - } - ]; - } - { - id = 100; - pools = [ - { - pool = "${config.cloonar-assistant.networkPrefix}.100.100 - ${config.cloonar-assistant.networkPrefix}.100.240"; - } - ]; - subnet = "${config.cloonar-assistant.networkPrefix}.100.0/24"; - interface = "smart"; - option-data = [ - { - name = "routers"; - data = "${config.cloonar-assistant.networkPrefix}.100.1"; - } - { - name = "domain-name"; - data = config.cloonar-assistant.domain; - } - { - name = "domain-name-servers"; - data = "${config.cloonar-assistant.networkPrefix}.100.1"; - } - ]; - reservations = [ - ]; - } - ]; - valid-lifetime = 4000; - }; - }; -} diff --git a/modules/cloonar-assistant/networking/dnsmasq.nix b/modules/cloonar-assistant/networking/dnsmasq.nix new file mode 100644 index 0000000..9ae354f --- /dev/null +++ b/modules/cloonar-assistant/networking/dnsmasq.nix @@ -0,0 +1,98 @@ +{ config, lib, ... }: + +{ + # Disable systemd-resolved (same as current unbound.nix) + services.resolved.enable = false; + + # Main dnsmasq service with preserved conditional enablement + services.dnsmasq = lib.mkIf config.cloonar-assistant.firewall.enable { + enable = true; + resolveLocalQueries = false; # We handle DNS manually + + settings = { + # Interface binding + interface = [ + "lan" + "server" + "infrastructure" + "multimedia" + "smart" + "guest" + ]; + + # DHCP ranges per VLAN + dhcp-range = [ + "${config.cloonar-assistant.networkPrefix}.96.100,${config.cloonar-assistant.networkPrefix}.96.240,24h" + "${config.cloonar-assistant.networkPrefix}.97.100,${config.cloonar-assistant.networkPrefix}.97.240,24h" + "${config.cloonar-assistant.networkPrefix}.101.100,${config.cloonar-assistant.networkPrefix}.101.240,24h" + "${config.cloonar-assistant.networkPrefix}.99.100,${config.cloonar-assistant.networkPrefix}.99.240,24h" + "${config.cloonar-assistant.networkPrefix}.100.100,${config.cloonar-assistant.networkPrefix}.100.240,24h" + "${config.cloonar-assistant.networkPrefix}.254.10,${config.cloonar-assistant.networkPrefix}.254.254,24h" + ]; + + # DHCP options with VLAN tagging + dhcp-option = [ + # LAN VLAN (.96) + "tag:lan,option:router,${config.cloonar-assistant.networkPrefix}.96.1" + "tag:lan,option:dns-server,${config.cloonar-assistant.networkPrefix}.96.1" + "tag:lan,option:domain-name,${config.cloonar-assistant.domain}" + + # Server VLAN (.97) + "tag:server,option:router,${config.cloonar-assistant.networkPrefix}.97.1" + "tag:server,option:dns-server,${config.cloonar-assistant.networkPrefix}.97.1" + "tag:server,option:domain-name,${config.cloonar-assistant.domain}" + + # Infrastructure VLAN (.101) with CAPWAP option + "tag:infrastructure,option:router,${config.cloonar-assistant.networkPrefix}.101.1" + "tag:infrastructure,option:dns-server,${config.cloonar-assistant.networkPrefix}.101.1" + "tag:infrastructure,option:domain-name,${config.cloonar-assistant.domain}" + "tag:infrastructure,138,${config.cloonar-assistant.networkPrefix}.97.2" # CAPWAP + + # Multimedia VLAN (.99) + "tag:multimedia,option:router,${config.cloonar-assistant.networkPrefix}.99.1" + "tag:multimedia,option:dns-server,${config.cloonar-assistant.networkPrefix}.99.1" + "tag:multimedia,option:domain-name,${config.cloonar-assistant.domain}" + + # Smart VLAN (.100) + "tag:smart,option:router,${config.cloonar-assistant.networkPrefix}.100.1" + "tag:smart,option:dns-server,${config.cloonar-assistant.networkPrefix}.100.1" + "tag:smart,option:domain-name,${config.cloonar-assistant.domain}" + + # Guest VLAN (.254) - DNS isolation + "tag:guest,option:router,${config.cloonar-assistant.networkPrefix}.254.1" + "tag:guest,option:dns-server,9.9.9.9" # External DNS only + ]; + + # Static DNS records + address = [ + "/fw.${config.cloonar-assistant.domain}/${config.cloonar-assistant.networkPrefix}.97.1" + "/fw/${config.cloonar-assistant.networkPrefix}.97.1" + "/home-assistant.${config.cloonar-assistant.domain}/${config.cloonar-assistant.networkPrefix}.97.20" + "/mopidy.${config.cloonar-assistant.domain}/${config.cloonar-assistant.networkPrefix}.97.21" + "/snapcast.${config.cloonar-assistant.domain}/${config.cloonar-assistant.networkPrefix}.97.21" + "/localhost/127.0.0.1" + "/localhost.${config.cloonar-assistant.domain}/127.0.0.1" + ]; + + # Domain configuration + domain = "${config.cloonar-assistant.domain}"; + expand-hosts = true; + + # Upstream DNS servers (plain DNS, no DoT support in dnsmasq) + server = [ + "9.9.9.9" + "149.112.112.11" + ]; + + # Performance and security + cache-size = 1000; + neg-ttl = 60; + domain-needed = true; # Don't forward plain names + bogus-priv = true; # Don't forward RFC1918 reverse lookups + bind-interfaces = true; # Only bind to specified interfaces + }; + }; + + # Firewall configuration (preserve existing) + networking.firewall.allowedUDPPorts = [ 53 5353 ]; +} \ No newline at end of file diff --git a/modules/cloonar-assistant/networking/unbound.nix b/modules/cloonar-assistant/networking/unbound.nix deleted file mode 100644 index 4745b26..0000000 --- a/modules/cloonar-assistant/networking/unbound.nix +++ /dev/null @@ -1,138 +0,0 @@ -{ config, lib, pkgs, ... }: -let - cfg = { - remote-control.control-enable = true; - server = { - interface = [ "0.0.0.0" "::0" ]; - interface-automatic = "yes"; - access-control = [ - "127.0.0.0/8 allow" - "${config.cloonar-assistant.networkPrefix}.96.0/24 allow" - "${config.cloonar-assistant.networkPrefix}.97.0/24 allow" - "${config.cloonar-assistant.networkPrefix}.98.0/24 allow" - "${config.cloonar-assistant.networkPrefix}.99.0/24 allow" - "${config.cloonar-assistant.networkPrefix}.101.0/24 allow" - "0.0.0.0/0 allow" - ]; - tls-cert-bundle = "/etc/ssl/certs/ca-certificates.crt"; - local-zone = "\"${config.cloonar-assistant.domain}\" transparent"; - local-data = [ - "\"localhost A 127.0.0.1\"" - "\"localhost.${config.cloonar-assistant.domain} A 127.0.0.1\"" - "\"localhost AAAA ::1\"" - "\"localhost.${config.cloonar-assistant.domain} AAAA ::1\"" - "\"fw.${config.cloonar-assistant.domain} A ${config.cloonar-assistant.networkPrefix}.97.1\"" - "\"fw A ${config.cloonar-assistant.networkPrefix}.97.1\"" - - "\"mopidy.${config.cloonar-assistant.domain} IN A ${config.cloonar-assistant.networkPrefix}.97.21\"" - "\"snapcast.${config.cloonar-assistant.domain} IN A ${config.cloonar-assistant.networkPrefix}.97.21\"" - "\"home-assistant.${config.cloonar-assistant.domain} IN A ${config.cloonar-assistant.networkPrefix}.97.20\"" - ]; - local-data-ptr = [ - "\"127.0.0.1 localhost\"" - "\"::1 localhost\"" - "\"${config.cloonar-assistant.networkPrefix}.97.1 fw.${config.cloonar-assistant.domain}\"" - "\"${config.cloonar-assistant.networkPrefix}.97.20 home-assistant.${config.cloonar-assistant.domain}\"" - "\"${config.cloonar-assistant.networkPrefix}.97.21 snapcast.${config.cloonar-assistant.domain}\"" - ]; - # ssl-upstream = "yes"; - }; - forward-zone = [ - { - name = "."; - forward-tls-upstream = "yes"; - forward-first = "no"; - forward-addr = [ - "9.9.9.9@853#dns9.quad9.net" - "149.112.112.11@853#dns11.quad9.net" - ]; - } - ]; - }; -in { - users.users.unbound = { - group = "unbound"; - isSystemUser = true; - extraGroups = [ "ssl-users" ]; - }; - users.groups.unbound = { }; - - services.resolved.enable = false; - - services.unbound = { - enable = true; - settings = cfg; - }; - - systemd.services.unbound-sync = lib.mkIf config.cloonar-assistant.firewall.enable { - enable = true; - path = with pkgs; [ unbound inotify-tools ]; - script = '' - function readFile() { - if [[ "''\$2" == "A" ]] ; then - cat "''\$1" | tail -n +2 | while IFS=, read -r address hwaddr client_id valid_lifetime expire subnet_id fqdn_fwd fqdn_rev hostname state user_context - do - echo "''\${address},''\${hostname}" - done - else - cat "''\$1" | tail -n +2 | while IFS=, read -r address duid valid_lifetime expire subnet_id pref_lifetime lease_type iaid prefix_len fqdn_fwd fqdn_rev hostname hwaddr state user_context hwtype hwaddr_source - do - echo "''\${address},''\${hostname}" - done - fi - } - - function readFileUnique() { - readFile "''\$1" ''\$2 | uniq | while IFS=, read -r address hostname - do - if echo "''\${1}" | grep -Eq '.*\.(${config.cloonar-assistant.domain})'; then - echo ''\${hostname} ''\$2 ''\${address} - unbound-control local_data ''\${hostname} ''\$2 ''\${address} > /dev/null 2>&1 - if [[ "''\$2" == "A" ]] ; then - echo ''\${address} | while IFS=. read -r ip0 ip1 ip2 ip3 - do - unbound-control local_data ''\${ip3}.''\${ip2}.''\${ip1}.''\${ip0}.ip4.arpa. PTR ''\${hostname} > /dev/null 2>&1 - unbound-control local_data ''\${ip3}.''\${ip2}.''\${ip1}.''\${ip0}.in-addr.arpa. PTR ''\${hostname} > /dev/null 2>&1 - done - fi - else - if [[ "''\$2" == "A" ]] ; then - echo ''\${address} | while IFS=. read -r ip0 ip1 ip2 ip3 - do - if [[ "''\${hostname}" != "" ]]; then - domain=${config.cloonar-assistant.domain} - if [[ "''\${hostname}" != *. ]]; then - unbound-control local_data ''\${hostname}.''\${domain} ''\$2 ''\${address} > /dev/null 2>&1 - else - unbound-control local_data ''\${hostname}''\${domain} ''\$2 ''\${address} > /dev/null 2>&1 - fi - - fi - unbound-control local_data ''\${ip3}.''\${ip2}.''\${ip1}.''\${ip0}.ip4.arpa. PTR ''\${hostname} > /dev/null 2>&1 - unbound-control local_data ''\${ip3}.''\${ip2}.''\${ip1}.''\${ip0}.in-addr.arpa. PTR ''\${hostname} > /dev/null 2>&1 - done - fi - fi - done - } - - function syncFile() { - # readFileUnique "''\$1" "''\$2" - while true; do - readFileUnique "''\$1" "''\$2" - sleep 10 - done - } - - syncFile "/var/lib/kea/dhcp4.leases" A & - # syncFile "/var/lib/kea/dhcp6.leases" AAAA & - wait - ''; - wants = [ "network-online.target" "unbound.service" ]; - after = [ "network-online.target" "unbound.service" ]; - partOf = [ "unbound.service" ]; - wantedBy = [ "multi-user.target" ]; - }; - - networking.firewall.allowedUDPPorts = [ 53 5353 ]; -}