From 84d8c448761156f6c91586f97236597cadf6ff24 Mon Sep 17 00:00:00 2001 From: Dominik Polakovics Date: Thu, 2 Apr 2026 15:19:44 +0200 Subject: [PATCH] feat: nb only serve ddev.site locally which is currently running --- hosts/nb/modules/networking.nix | 76 +++++++++++++++++++++++++++++++-- 1 file changed, 73 insertions(+), 3 deletions(-) diff --git a/hosts/nb/modules/networking.nix b/hosts/nb/modules/networking.nix index 0bf1995..3182ca3 100644 --- a/hosts/nb/modules/networking.nix +++ b/hosts/nb/modules/networking.nix @@ -1,5 +1,27 @@ { config, lib, pkgs, ... }: +let + ddev-dns-update = pkgs.writeShellScriptBin "ddev-dns-update" '' + set -e + hosts_file="/var/lib/dnsmasq/ddev-hosts" + new_hosts=$(${pkgs.coreutils}/bin/mktemp) + trap '${pkgs.coreutils}/bin/rm -f "$new_hosts"' EXIT + # Get running DDEV project names + running=$(${pkgs.ddev}/bin/ddev list --json-output 2>/dev/null \ + | ${pkgs.jq}/bin/jq -r '.raw[]? | select(.status == "running") | .name // empty' 2>/dev/null) || true + + # Build hosts entries + for name in $running; do + echo "127.0.0.1 $name.ddev.site" >> "$new_hosts" + done + + # Only reload dnsmasq if content changed + if ! ${pkgs.diffutils}/bin/cmp -s "$new_hosts" "$hosts_file"; then + ${pkgs.coreutils}/bin/cp "$new_hosts" "$hosts_file" + /run/wrappers/bin/sudo /run/current-system/systemd/bin/systemctl reload dnsmasq.service 2>/dev/null || true + fi + ''; +in { # Enable systemd-resolved with split DNS for ddev.site services.resolved = { @@ -14,18 +36,66 @@ # Integrate NetworkManager with systemd-resolved networking.networkmanager.dns = "systemd-resolved"; - # Local dnsmasq for .ddev.site resolution only (port 5353) + # Local dnsmasq for .ddev.site resolution (port 5353) + # Dynamic hosts file resolves running DDEV projects to 127.0.0.1; + # unmatched .ddev.site queries forward to VPN DNS (returns dev server IP) services.dnsmasq = { enable = true; + resolveLocalQueries = false; settings = { port = 5353; listen-address = "127.0.0.1"; bind-interfaces = true; - no-resolv = true; - address = "/.ddev.site/127.0.0.1"; + server = [ "/ddev.site/10.42.97.1" ]; + addn-hosts = "/var/lib/dnsmasq/ddev-hosts"; }; }; + # Ensure hosts file exists before dnsmasq starts (owned by dominik so the + # user-level service can write it) + systemd.tmpfiles.rules = [ + "d /var/lib/dnsmasq 0755 root root -" + "f /var/lib/dnsmasq/ddev-hosts 0644 dominik root -" + ]; + + # Poll running DDEV projects and update dnsmasq hosts + # Runs as dominik because ddev needs user-level Docker access + systemd.services.ddev-dns-update = { + description = "Update dnsmasq hosts for running DDEV projects"; + after = [ "dnsmasq.service" "docker.service" ]; + serviceConfig = { + Type = "oneshot"; + User = "dominik"; + ExecStart = "${ddev-dns-update}/bin/ddev-dns-update"; + }; + }; + + systemd.timers.ddev-dns-update = { + wantedBy = [ "timers.target" ]; + timerConfig = { + OnBootSec = "30s"; + OnUnitActiveSec = "10s"; + }; + }; + + environment.systemPackages = [ ddev-dns-update ]; + + security.sudo.extraRules = [ + { + users = [ "dominik" ]; + commands = [ + { + command = "${ddev-dns-update}/bin/ddev-dns-update"; + options = [ "NOPASSWD" ]; + } + { + command = "/run/current-system/systemd/bin/systemctl reload dnsmasq.service"; + options = [ "NOPASSWD" ]; + } + ]; + } + ]; + # WireGuard VPN configuration networking.wireguard.interfaces = { wg0 = {