From 8bdbbcd21cda10fd839e102f4c07d20293d78d39 Mon Sep 17 00:00:00 2001 From: Dominik Polakovics Date: Mon, 28 Apr 2025 22:47:03 +0200 Subject: [PATCH] feat: add iso and readme --- README.md | 172 +++++++++++++++++++++++++++++++++ iso/configuration.nix | 52 ++++++++++ iso/default.nix | 86 +++++++++++++++++ iso/hardware-configuration.nix | 26 +++++ 4 files changed, 336 insertions(+) create mode 100644 README.md create mode 100644 iso/configuration.nix create mode 100644 iso/default.nix create mode 100644 iso/hardware-configuration.nix diff --git a/README.md b/README.md new file mode 100644 index 0000000..c9a8331 --- /dev/null +++ b/README.md @@ -0,0 +1,172 @@ +# Cloonar Assistant + +This repository contains a NixOS module (`cloonar-assistant`) designed to help configure a home network appliance or router with various integrated services. + +## Features + +* **VLAN-based Network Segmentation:** Creates multiple VLANs (`lan`, `server`, `infrastructure`, `multimedia`, `smart`, `guest`) for network isolation. +* **DHCP Server:** Uses Kea DHCP to provide IP addresses for each configured VLAN. +* **DNS Resolver:** Configures Unbound DNS for local hostname resolution (integrating with Kea DHCP leases) and secure DNS-over-TLS forwarding for external queries. +* **Firewall:** Implements a stateful firewall using `nftables` with granular rules for inter-VLAN traffic, WAN access, and NAT. +* **WireGuard VPN:** Sets up a WireGuard VPN server for secure remote access. +* **Dynamic DNS:** Includes a client (`updns-client`) to update a specified domain name with the system's current public IP address via `updns.cloonar.com`. +* **Home Assistant:** Runs Home Assistant in a systemd container with a MariaDB backend and an Nginx reverse proxy. +* **SSL Certificates:** Automatically obtains and renews SSL certificates for the specified domain using ACME (Let's Encrypt). +* **ISO Build Utility:** Provides configuration to build a bootable ISO for automated minimal NixOS installation (useful for bootstrapping). + +## Prerequisites + +* A machine running NixOS where you want to apply this configuration. + +## Configuration (Module Usage) + +The primary way to use `cloonar-assistant` is by importing its module into your own NixOS `configuration.nix`. + +1. **Import the Module:** Fetch the module source using `builtins.fetchTarball` and add it to your `imports`. +2. **Configure Options:** Enable and configure the desired features under the `cloonar-assistant` attribute set. + +```nix +# /etc/nixos/configuration.nix +{ config, pkgs, ... }: +let + # Fetch the source code for the cloonar-assistant module + cloonar-assistant-src = builtins.fetchTarball "https://git.cloonar.com/Cloonar/cloonar-assistant/archive/master.tar.gz"; +in +{ + imports = [ + # Import the main module + "${cloonar-assistant-src}/modules/cloonar-assistant" + + # Include your hardware-configuration.nix and other custom modules + ./hardware-configuration.nix + # ... + ]; + + # --- Configure Cloonar Assistant Options --- + cloonar-assistant = { + # Required: Define the first two octets for your internal networks + networkPrefix = "10.42"; # Example: Results in 10.42.96.0/24, 10.42.97.0/24, etc. + + # Required: Define the domain name for local services and DDNS + domain = "home.example.com"; # Example + + # Required: Define the network interface connected to the WAN/Internet + firewall.interfaces.wan = "eth0"; # Example + + # Required: Define the network interface for internal VLANs + # Set to null if you only have one interface (WAN) + firewall.interfaces.internal = "eth1"; # Example + + # Enable VPN Server + vpn.enable = true; + vpn.privateKeyFile = "/path/to/your/wireguard_private_key"; # Store securely! + vpn.clients = [ + { name = "myphone"; publicKey = "..."; allowedIPs = [ "${config.cloonar-assistant.networkPrefix}.98.2/32" ]; } + # ... more clients + ]; + + # Enable Dynamic DNS Updates + updns.enable = true; + updns.key = "your-updns-key"; # Key provided by updns-client.cloonar.com + updns.secretFile = "/path/to/your/updns_secret"; # Store securely! + + # Enable setup mode (allows WAN access for initial setup - disable for production) + setup = false; + + # ... other options can be configured as needed. + }; + + # --- Other System Configuration --- + networking.hostName = "myrouter"; # Example hostname + + # Ensure necessary packages for fetching are available if not using flakes + environment.systemPackages = [ pkgs.nix ]; + + system.stateVersion = "23.11"; # Set to your NixOS version +} +``` + +## Module Options + +The main configuration options are available under the `cloonar-assistant` attribute set in your `configuration.nix`. Refer to `modules/cloonar-assistant/default.nix` within the fetched source for a detailed definition of all available options and their defaults. Key areas include: + +* `cloonar-assistant.networkPrefix` +* `cloonar-assistant.domain` +* `cloonar-assistant.firewall.*` (including `interfaces.wan`, `interfaces.internal`) +* `cloonar-assistant.vpn.*` +* `cloonar-assistant.updns.*` +* `cloonar-assistant.setup` + +## ISO Build Utility (Optional) + +This repository also includes configuration in the `iso/` directory to build a bootable `.iso` file. + +**Purpose:** This ISO performs a fully automated, minimal NixOS installation on a target machine. It partitions the disk (creating a boot partition and a LUKS-encrypted root partition using `/dev/zero` as the keyfile - **WARNING: This is insecure and intended only for bootstrapping**), formats filesystems, and installs a base NixOS system defined in `iso/configuration.nix`. It does **not** install the `cloonar-assistant` configuration itself. It's primarily useful for quickly bootstrapping a bare-metal machine before applying your custom NixOS configuration (which imports the `cloonar-assistant` module). + +**Build Command (Non-Flake):** + +```bash +nix-build ./iso/default.nix +``` + +This command should be run from the root of this repository checkout. The resulting ISO will be in the `result/` directory. + +**ISO Installation Flow:** + +```mermaid +graph TD + subgraph "ISO Build & Install Utility" + A["Run nix-build command"] --> B("Generates ISO file"); + B --> C{"Boot target machine from ISO"}; + C --> D["Automated Install Script runs"]; + D --> E{"Partitions Disk (BOOT + LUKS)"}; + E --> F{"Formats Filesystems (FAT32 + EXT4)"}; + F --> G{"Mounts Filesystems"}; + G --> H["Installs base NixOS using iso/configuration.nix"]; + H --> I("System reboots into minimal NixOS"); + I --> J{"User creates custom config
importing cloonar-assistant module"}; + J --> K["Run nixos-rebuild switch"]; + K --> L("System running full configuration"); + end +``` + +## Network Architecture + +The module sets up the following logical network structure when the firewall and VLANs are enabled: + +```mermaid +graph LR + subgraph "Network Architecture" + Internet -- WAN --> R("Router/Firewall"); + subgraph "Internal Network (VLANs on configured internal interface)" + R -- "VLAN 96" --> LAN["LAN Subnet
${config.cloonar-assistant.networkPrefix}.96.0/24"]; + R -- "VLAN 97" --> SRV["Server Subnet
${config.cloonar-assistant.networkPrefix}.97.0/24"]; + R -- "VLAN 101" --> INF["Infrastructure Subnet
${config.cloonar-assistant.networkPrefix}.101.0/24"]; + R -- "VLAN 99" --> MM["Multimedia Subnet
${config.cloonar-assistant.networkPrefix}.99.0/24"]; + R -- "VLAN 100" --> SMT["Smart Home Subnet
${config.cloonar-assistant.networkPrefix}.100.0/24"]; + R -- "VLAN 254" --> GST["Guest Subnet
${config.cloonar-assistant.networkPrefix}.254.0/24"]; + end + subgraph "VPN" + VPNClient -- "UDP 51820" --> R; + R -- "wg_cloonar" --> VPNNet["VPN Subnet
${config.cloonar-assistant.networkPrefix}.98.0/24"]; + end + subgraph "Services on Router" + R --> DHCP("Kea DHCP"); + R --> DNS("Unbound DNS"); + R --> Firewall("NFTables"); + R --> DynDNS("UpDNS Client"); + R --> HAProxy("Nginx Proxy"); + HAProxy --> HACont[("Container: Home Assistant")]; + end + SRV --> HACont; + end +``` + +## Module Overview + +* `modules/cloonar-assistant/`: Contains the main NixOS module definition (`default.nix`) and imports submodules. +* `modules/cloonar-assistant/networking/`: Configures network interfaces, VLANs, DHCP (Kea), DNS (Unbound), firewall (`nftables`), and WireGuard. +* `modules/cloonar-assistant/updns/`: Configures the dynamic DNS client service. +* `modules/cloonar-assistant/home-assistant/`: Configures the Home Assistant systemd container, MariaDB database, Nginx reverse proxy, and ACME SSL certificates. +* `modules/cloonar-assistant/multiroom-audio/`: Currently unused, planned for future multiroom audio features. +* `iso/`: Contains configuration files (`default.nix`, `configuration.nix`) for building the optional automated installation ISO. diff --git a/iso/configuration.nix b/iso/configuration.nix new file mode 100644 index 0000000..755b64d --- /dev/null +++ b/iso/configuration.nix @@ -0,0 +1,52 @@ +{ config, lib, pkgs, ... }: { + imports = [ + + + ]; + + nixpkgs.config.allowUnfree = true; + + zramSwap.enable = true; + + security.sudo.wheelNeedsPassword = false; + + networking.hostName = "install"; + + services.openssh.enable = true; + services.openssh.settings.PermitRootLogin = "yes"; + + users.mutableUsers = false; + users.users.root = { + # Password is "linux" + hashedPassword = lib.mkForce "$6$7IKExnDde920x.YH$ggegnnKJYdmg1Wt33fxuPpM.MmIaX32LXVyjL8ed7ohT385lKotFGzRpitncQ3pd9Lci1QCFGRn2tVJGxkFAm0"; + }; + + services.avahi = { + enable = true; + ipv4 = true; + ipv6 = true; + nssmdns = true; + publish = { enable = true; domain = true; addresses = true; }; + }; + + environment.systemPackages = with pkgs; [ + coreutils + curl + file + git + htop + lsof + nano + openssl + pciutils + pv + tmux + tree + unar + vim_configurable + wget + zip + ]; + + system.stateVersion = "23.05"; # Did you read the comment? +} diff --git a/iso/default.nix b/iso/default.nix new file mode 100644 index 0000000..2f87220 --- /dev/null +++ b/iso/default.nix @@ -0,0 +1,86 @@ +{ + system ? "x86_64-linux", +}: +(import { + inherit system; + modules = [ + + ./configuration.nix + ({ config, pkgs, lib, ... }: { + systemd.services.install = { + description = "Bootstrap a NixOS installation"; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" "polkit.service" ]; + path = [ "/run/current-system/sw/" ]; + script = with pkgs; '' + echo 'journalctl -fb -n100 -uinstall' >>~nixos/.bash_history + + set -eux + + wait-for() { + for _ in seq 10; do + if $@; then + break + fi + sleep 1 + done + } + + dev=/dev/sda + [ -b /dev/nvme0n1 ] && dev=/dev/nvme0n1 + [ -b /dev/vda ] && dev=/dev/vda + + ${utillinux}/bin/sfdisk --wipe=always $dev <<-END + label: gpt + + name=BOOT, size=512MiB, type=C12A7328-F81F-11D2-BA4B-00A0C93EC93B + name=NIXOS + END + + sync + wait-for [ -b /dev/disk/by-partlabel/BOOT ] + + mkfs.fat -F 32 -n boot /dev/disk/by-partlabel/BOOT + wait-for mkfs.fat -F 32 -n boot /dev/disk/by-partlabel/BOOT + + wait-for [ -b /dev/disk/by-partlabel/NIXOS ] + ${cryptsetup}/bin/cryptsetup luksFormat --type=luks2 --label=root /dev/disk/by-partlabel/NIXOS /dev/zero --keyfile-size=1 + ${cryptsetup}/bin/cryptsetup luksOpen /dev/disk/by-partlabel/NIXOS root --key-file=/dev/zero --keyfile-size=1 + mkfs.ext4 -L nixos /dev/mapper/root + + sync + mount /dev/mapper/root /mnt + + mkdir /mnt/boot + wait-for mount /dev/disk/by-label/boot /mnt/boot + + install -D ${./configuration.nix} /mnt/etc/nixos/configuration.nix + install -D ${./hardware-configuration.nix} /mnt/etc/nixos/hardware-configuration.nix + + sed -i -E 's/(\w*)#installer-only /\1/' /mnt/etc/nixos/* + + ${config.system.build.nixos-install}/bin/nixos-install \ + --system ${(import { + inherit system; + modules = [ + ./configuration.nix + ./hardware-configuration.nix + ]; + }).config.system.build.toplevel} \ + --no-root-passwd \ + --cores 0 + + echo 'Shutting off in 1min' + ${systemd}/bin/shutdown +1 + ''; + environment = config.nix.envVars // { + inherit (config.environment.sessionVariables) NIX_PATH; + HOME = "/root"; + }; + serviceConfig = { + Type = "oneshot"; + }; + }; + }) + ]; +}).config.system.build.isoImage diff --git a/iso/hardware-configuration.nix b/iso/hardware-configuration.nix new file mode 100644 index 0000000..ee3a8b6 --- /dev/null +++ b/iso/hardware-configuration.nix @@ -0,0 +1,26 @@ +{ config, pkgs, ... }: { + boot.loader.systemd-boot.enable = true; + + fileSystems."/boot" = { + device = "/dev/disk/by-label/boot"; + fsType = "vfat"; + }; + + boot.initrd.luks.devices.root = { + device = "/dev/disk/by-label/root"; + + # WARNING: Leaks some metadata, see cryptsetup man page for --allow-discards. + allowDiscards = true; + + # Set your own key with: + # cryptsetup luksChangeKey /dev/disk/by-label/root --key-file=/dev/zero --keyfile-size=1 + # You can then delete the rest of this block. + keyFile = "/dev/zero"; + keyFileSize = 1; + }; + + fileSystems."/" = { + device = "/dev/mapper/root"; + fsType = "ext4"; + }; +}