{ vars, ... }: { # Run dnsmasq on the LAN interface as a forwarding-only resolver for # *.ts.net (Tailscale MagicDNS names). FreeIPA's bind-dyndb-ldap # cannot reach vars.tailscaleResolverIp directly because the DC is not a # Tailscale node. This host IS a Tailscale node and can reach it via # tailscale0, so it acts as an intermediary: FreeIPA has a conditional # forward zone for ts.net pointing here (vars.tailscaleRouterIp), and this # dnsmasq instance forwards those queries onward to Tailscale's resolver. # # Configure FreeIPA once after deploying this host: # kinit admin # ipa dnsforwardzone-add ${vars.tailnetDomain} \ # --forwarder=${vars.tailscaleRouterIp} \ # --forward-policy=only # Note: IPA refuses to shadow ts.net (a real public TLD); use the # tailnet-specific subdomain (vars.tailnetDomain) instead. services.dnsmasq = { enable = true; # NixOS's dnsmasq module defaults resolveLocalQueries to true, which adds # 127.0.0.1 to networking.nameservers and makes dnsmasq bind to # listen-address=127.0.0.1. This instance is not the host's local # resolver — it only serves IPA's conditional forwarder for tailnet names. # The host uses domainControllerIp directly (networking.nameservers in # host.nix). Without this, all host DNS goes through dnsmasq, which has # no upstream for general queries (no-resolv=true), breaking resolution. resolveLocalQueries = false; settings = { # Listen only on the LAN interface — not tailscale0 or loopback. # bind-interfaces prevents dnsmasq from binding to 0.0.0.0 and then # filtering by interface later; combined with `interface` this ensures # it genuinely listens only on eth0. bind-interfaces = true; interface = [ vars.lxcLanInterface ]; # Forward-only: no local /etc/hosts or /etc/resolv.conf reading, no # negative caching of NXDOMAIN for names this instance doesn't serve. # All ts.net queries come from FreeIPA's conditional forwarder and must # be answered by Tailscale's resolver. no-hosts = true; no-resolv = true; # Forward *.tailnetDomain to Tailscale's internal resolver, scoped to # the tailnet-specific subdomain rather than all of ts.net (FreeIPA # refuses to shadow ts.net, a real public TLD). server = [ "/${vars.tailnetDomain}/${vars.tailscaleResolverIp}" ]; }; }; networking.firewall.allowedUDPPorts = [ vars.ports.dns ]; networking.firewall.allowedTCPPorts = [ vars.ports.dns ]; }