# SPDX-FileCopyrightText: 2024 Simon Bruder # # SPDX-License-Identifier: AGPL-3.0-or-later { lib, pkgs, ... }: let guests = { ci-runner = { mac = "42:80:00:00:00:02"; v4 = "10.80.32.2"; v6 = "2a01:4f9:3051:39c6:1::2"; }; hiroshi = { mac = "42:80:00:00:00:03"; v4 = "10.80.32.3"; v6 = "2a01:4f9:3051:39c6:1::3"; }; }; # port forwarding for IPv4 portForwards = { tcp = { }; udp = { }; }; in { sbruder.restic = { enable = true; backups.vm-image = { enable = true; lvm.lvs = [ "hiroshi" ]; }; }; virtualisation.libvirtd = { enable = true; qemu.package = pkgs.qemu_kvm; }; boot.kernel.sysctl = { "net.ipv4.conf.all.forwarding" = true; "net.ipv6.conf.all.forwarding" = true; }; systemd.network = { enable = true; netdevs = { br-virt = { netdevConfig = { Name = "br-virt"; Kind = "bridge"; }; }; }; networks = { br-virt = { name = "br-virt"; address = [ "10.80.32.1/24" "2a01:4f9:3051:39c6:1::1/80" ]; }; }; }; services.resolved.enable = false; services.dnsmasq = { enable = true; settings = { interface = [ "br-virt" ]; bind-interfaces = true; # do not bind to the wildcard interface bogus-priv = true; # do not forward revese lookups of internal addresses dhcp-fqdn = true; # only insert qualified names of DHCP clients into DNS domain-needed = true; # do not forward names without domain no-hosts = true; # do not resolve hosts from /etc/hosts no-resolv = true; # only use explicitly configured resolvers domain = [ "koyomi.sbruder.de" ]; enable-ra = true; # required to tell clients to use DHCPv6 # Force static configuration dhcp-range = [ "10.80.32.0,static,255.255.255.0" "2a01:4f9:3051:39c6:1::,static,80" ]; dhcp-host = lib.flatten (lib.mapAttrsToList (name: { mac, v4, v6 }: [ "${mac},${v4},${name}" "${mac},[${v6}],${name}" ]) guests); # Hetzner recursive name servers # https://docs.hetzner.com/dns-console/dns/general/recursive-name-servers/ server = [ "185.12.64.1" "185.12.64.2" "2a01:4ff:ff00::add:1" "2a01:4ff:ff00::add:2" ]; }; }; networking.firewall = { allowedTCPPorts = map lib.toInt (lib.attrNames portForwards.tcp); allowedUDPPorts = map lib.toInt (lib.attrNames portForwards.udp); interfaces.br-virt = { allowedTCPPorts = [ 53 ]; # EDNS allowedUDPPorts = [ 53 67 547 ]; # DNS / DHCP / DHCPv6 }; }; networking.nftables = { enable = true; ruleset = '' # only IPv4 table ip hypervisor-nat { chain postrouting { type nat hook postrouting priority filter; policy accept oifname eth0 masquerade } chain prerouting { type nat hook prerouting priority dstnat; policy accept ${lib.concatStrings (lib.mapAttrsToList (port: guest: '' iifname eth0 tcp dport ${port} dnat to ${guests.${guest}.v4} '') portForwards.tcp)} ${lib.concatStrings (lib.mapAttrsToList (port: guest: '' iifname eth0 udp dport ${port} dnat to ${guests.${guest}.v4} '') portForwards.udp)} } } table inet hypervisor-filter { chain forward { type filter hook forward priority filter; policy drop iifname br-virt oifname eth0 counter accept iifname eth0 oifname br-virt counter accept } } ''; }; }