From b46c2a83556ee9385a48ea5aca28f8e02350a1ab Mon Sep 17 00:00:00 2001 From: Asyncnomi Date: Sun, 27 Jul 2025 01:15:27 +0200 Subject: [PATCH] some dns fix + dnsmasq --- mapping.nix | 13 ----- shared/commons.nix | 1 + shared/commons/nftables.nix | 17 ++++-- shared/commons/resolver.nix | 53 ++++++++++++++++++ shared/dns/knot.nix | 106 +++++++++++++++++++++++++++++------- 5 files changed, 154 insertions(+), 36 deletions(-) create mode 100644 shared/commons/resolver.nix diff --git a/mapping.nix b/mapping.nix index 65d0f77..33f02aa 100644 --- a/mapping.nix +++ b/mapping.nix @@ -26,17 +26,4 @@ ./shared/dns.nix ]; }; - - # For instance: - # psql = rec { - # master = "some-node-1"; - # slaves = [ - # "some-node-2" - # "some-node-3" - # ]; - # hosts = [ master ] ++ slaves; - # _inherit = [ - # "./shared/psql.nix" - # ]; - # }; } \ No newline at end of file diff --git a/shared/commons.nix b/shared/commons.nix index 1ebdd98..075ef77 100755 --- a/shared/commons.nix +++ b/shared/commons.nix @@ -9,5 +9,6 @@ ./commons/networking.nix ./commons/mesh.nix ./commons/nftables.nix + ./commons/resolver.nix ]; } \ No newline at end of file diff --git a/shared/commons/nftables.nix b/shared/commons/nftables.nix index 8585f9e..111cfb5 100644 --- a/shared/commons/nftables.nix +++ b/shared/commons/nftables.nix @@ -53,6 +53,18 @@ in udp dport 51920 accept '' else ""} + ${if myName == mapping.dns.master then '' + # DNS Master + iifname mesh tcp dport 53 accept + iifname mesh udp dport 53 accept + '' else ""} + + ${if lib.elem myName mapping.dns.secondary then '' + # DNS Secondary + tcp dport 53 accept + udp dport 53 accept + '' else ""} + # Log anything else ip protocol tcp counter log prefix "tcp.in.dropped: " ip protocol udp counter log prefix "udp.in.dropped: " @@ -65,11 +77,8 @@ in ct state invalid counter drop ${if lib.elem myName mapping.bastion.hosts then '' - iifname mgmt oifname mesh* accept + iifname mgmt oifname mesh accept '' else ""} - - # Allow mesh bounces - iifname mesh* oifname mesh* accept } chain output { type filter hook output priority 0; policy accept; diff --git a/shared/commons/resolver.nix b/shared/commons/resolver.nix new file mode 100644 index 0000000..5cdada1 --- /dev/null +++ b/shared/commons/resolver.nix @@ -0,0 +1,53 @@ +{ ... }: + +let + # Import nodes + nodes = import ./../../nodes.nix; + myName = config.hostName; + myPeer = nodes."${myName}"; + myId = myPeer.id; + myZone = myPeer.zone; + + # Import mapping + mapping = import ./../../mapping.nix; +in +{ + services.resolved.enable = false; + networking.resolvconf.enable = false; + + networking.domain = "lf"; + + environment.etc."resolv.conf".text = '' + # Do not edit, will be overwritten by Nixos + domain ${config.networking.domain} + search ${config.networking.domain} + ${builtins.concatStringsSep "\n" (map (ip: "nameserver ${ip}") config.services.dnsmasq.settings.listen-address)} + options edns0 trust-ad + ''; + + services.dnsmasq = { + enable = true; + settings = { + listen-address = [ + "::1" + "127.0.0.1" + ]; + local = [ + "/${config.networking.domain}/" + ]; + server = [ + "1.1.1.1" + "8.8.8.8" + "9.9.9.9" + ] ++ map (hostName: "/lf/172.19.${nodes.${hostName}.zone}.${nodes.${hostName}.id}") mapping.dns.hosts + ++ map (hostName: "/lf/fc00::${nodes.${hostName}.zone}:${nodes.${hostName}.id}") mapping.dns.hosts; + no-resolv = true; + # Resolvconf can auto-generated /etc/dnsmasq-{conf,resolv}.conf + # By default dnsmasq import them + # We've disable resolvconf, but just to be on the safe side + resolv-file = false; + conf-file = false; + log-queries = false; + }; + }; +} \ No newline at end of file diff --git a/shared/dns/knot.nix b/shared/dns/knot.nix index 9a2d561..faacbbf 100644 --- a/shared/dns/knot.nix +++ b/shared/dns/knot.nix @@ -3,7 +3,8 @@ let # Import nodes nodes = import ./../../nodes.nix; - myNode = nodes."${config.hostName}"; + myName = config.hostName; + myNode = nodes."${myName}"; # And mapping mapping = import ./../../mapping.nix; @@ -24,13 +25,12 @@ let segments = ((lib.stringLength domainkey) / 255); domainkeySplitted = map (x: lib.substring (x*255) 255 domainkey) (lib.range 0 segments); - ##### ## Knot specific ##### # Define remote based on current role - remotes = if myNode == mapping.dns.master then + remotes = if myName == mapping.dns.master then map (hostname: { id = hostname; address = "172.19.${toString nodes.${hostname}.zone}.${toString nodes.${hostname}.id}@53"; @@ -44,23 +44,23 @@ let remotesNames = map (remote: remote.id) remotes; # Remotes ACL - remotesACL = if myNode == mapping.dns.master then + remotesACL = if myName == mapping.dns.master then map (hostname: { id = "acl_${hostname}"; - address = "172.19.${toString nodes.${hostname}.zone}.${toString nodes.${hostname}.id}@53"; + address = "172.19.${toString nodes.${hostname}.zone}.${toString nodes.${hostname}.id}"; action = "transfer"; }) mapping.dns.secondary else [{ id = "acl_${mapping.dns.master}"; - address = "172.19.${toString nodes.${mapping.dns.master}.zone}.${toString nodes.${mapping.dns.master}.id}@53"; + address = "172.19.${toString nodes.${mapping.dns.master}.zone}.${toString nodes.${mapping.dns.master}.id}"; action = "notify"; }]; remotesACLNames = map (remote: remote.id) remotesACL; # Other ACL - letsencryptACL = if myNode == mapping.dns.master then + letsencryptACL = if myName == mapping.dns.master then [{ id = "acl_le_challenge"; address = [ @@ -73,35 +73,64 @@ let }] else []; + acls = remotesACL ++ letsencryptACL; + + # Mod-query ACLs + modQueryACLs = [{ + id = "local"; + address = [ + "172.19.0.0/16" + "fc00::/96" + ]; + }]; + ##### ## Zone specific ##### # host to dn hostToDomain = hostname: builtins.replaceStrings ["-"] ["."] hostname; + hostToLfDomain = hostname: builtins.replaceStrings [".lasuite.federez"] [".lf."] (hostToDomain hostname); + + # Remove cidr notation + rmCidr = ip: builtins.head (builtins.split "/" ip); # Gen NS - toNSRecord = host: "IN NS ${hostToDomain host}.net."; + toNSRecord = host: "\tIN NS ${hostToDomain host}.net."; nsRecords = map toNSRecord mapping.dns.secondary; dnsSecondaryConfigs = lib.filterAttrs (peerName: _peerConfig: lib.elem peerName mapping.dns.secondary) nodes; # Gen A NS nsARecords = lib.flatten (lib.mapAttrsToList (hostname: node: - lib.optional (supportsIPv4 node) "${hostToDomain hostname}.net. IN A ${node.ip4}" + lib.optional (supportsIPv4 node) "${hostToDomain hostname}.net. IN A ${rmCidr node.ip4}" ) dnsSecondaryConfigs); # Gen AAAA NS nsAAAARecords = lib.flatten (lib.mapAttrsToList (hostname: node: - lib.optional (supportsIPv6 node) "${hostToDomain hostname}.net. IN AAAA ${node.ip6}" + lib.optional (supportsIPv6 node) "${hostToDomain hostname}.net. IN AAAA ${rmCidr node.ip6}" ) dnsSecondaryConfigs); + # Gen A records for lf zone + lfARecords = lib.flatten (lib.mapAttrsToList (hostname: node: + lib.optional (supportsIPv4 node) "${hostToLfDomain hostname} IN A 172.19.${toString node.zone}.${toString node.id}" + ) nodes); + + # Gen AAAA records for lf zone + lfAAAARecords = lib.flatten (lib.mapAttrsToList (hostname: node: + lib.optional (supportsIPv6 node) "${hostToLfDomain hostname} IN AAAA fc00::${toString node.zone}:${toString node.id}" + ) nodes); + + # Gen first NS for SOA + firstNS = builtins.head mapping.dns.secondary; + firstNSDn = "${hostToDomain firstNS}.net."; + # Zone conf - zoneFilePath = "/var/lib/knot/zones/zone-lasuite-federez-net"; + zoneLasuiteFederezNetFilePath = "/var/lib/knot/zones/zone-lasuite-federez-net"; zone-lasuite-federez-net = pkgs.writeText "zone-lasuite-federez-net" '' $ORIGIN lasuite.federez.net. $TTL 60 - @ IN SOA ${builtins.head nsRecords}. monitoring.lasuite.federez.net. ( + @ IN SOA ${firstNSDn} monitoring.lasuite.federez.net. ( ${timestamp} ; serial 60 ; refresh 60 ; retry @@ -110,7 +139,7 @@ let IN TXT "v=spf1 a:lasuite.federez.net ~all" - ${builtins.concatStringsSep "\n" nsRecords} + ${builtins.concatStringsSep "\n" nsRecords} ${builtins.concatStringsSep "\n" nsARecords} ${builtins.concatStringsSep "\n" nsAAAARecords} @@ -119,7 +148,21 @@ let _mta-sts IN TXT "v=STSv1; id=1" _smtp._tls IN TXT "v=TLSRPTv1;rua=mailto:postmaster@lasuite.federez.net" default._domainkey IN TXT "${lib.concatStringsSep "\" \"" domainkeySplitted}" - cause-toujours IN TXT "v=spf1 a:lasuite.federez.net ~all" + ''; + + zoneLfFilePath = "/var/lib/knot/zones/zone-lf"; + zone-lf = pkgs.writeText "zone-lf" '' + $ORIGIN lf. + $TTL 60 + @ IN SOA dns.lf. monitoring.lasuite.federez.net. ( + ${timestamp} ; serial + 60 ; refresh + 60 ; retry + 60 ; expire + 60 ) ; minimum TTL + + ${builtins.concatStringsSep "\n" lfARecords} + ${builtins.concatStringsSep "\n" lfAAAARecords} ''; in { @@ -170,23 +213,40 @@ in remote = remotes; - acl = remotesACL ++ otherACL; + acl = acls; + mod-queryacl = modQueryACLs; - zone = if myNode == mapping.dns.master then [ + zone = if myName == mapping.dns.master then [ { domain = "lasuite.federez.net"; - file = zoneFilePath; + file = zoneLasuiteFederezNetFilePath; + dnssec-signing = "on"; + dnssec-policy = "default"; + zonefile-load = "difference"; notify = remotesNames; acl = remotesACLNames ++ [ "acl_le_challenge" ]; } + { + domain = "lf"; + file = zoneLfFilePath; + notify = remotesNames; + acl = remotesACLNames; + module = "mod-queryacl/local"; + } ] else [ { domain = "lasuite.federez.net"; master = builtins.head remotesNames; acl = remotesACLNames; } + { + domain = "lf"; + master = builtins.head remotesNames; + acl = remotesACLNames; + module = "mod-queryacl/local"; + } ]; log = [ @@ -199,11 +259,19 @@ in }; # Write the generated zone file to the writable path - systemd.services.writeZoneFile = { + systemd.services.writeLasuiteFederezNetZoneFile = { before = [ "knot.service" ]; description = "Write initial zone file for lasuite.federez.net"; serviceConfig = { - ExecStart = "${pkgs.coreutils}/bin/cp '${zone-lasuite-federez-net}' ${zoneFilePath}"; + ExecStart = "${pkgs.coreutils}/bin/cp '${zone-lasuite-federez-net}' ${zoneLasuiteFederezNetFilePath}"; + }; + wantedBy = [ "multi-user.target" ]; + }; + systemd.services.writeLfZoneFile = { + before = [ "knot.service" ]; + description = "Write initial zone file for lasuite.federez"; + serviceConfig = { + ExecStart = "${pkgs.coreutils}/bin/cp '${zone-lf}' ${zoneLfFilePath}"; }; wantedBy = [ "multi-user.target" ]; };