diff options
| author | Alejandro Soto <alejandro@34project.org> | 2025-08-24 18:55:06 -0600 |
|---|---|---|
| committer | Alejandro Soto <alejandro@34project.org> | 2025-08-24 18:55:06 -0600 |
| commit | d7ac88762db111a7962c4e14b5f4e37ab85ccac7 (patch) | |
| tree | 0c2c8c4383bef74215e3b7c48a2f6b0117f084bc /sys/ns/rr.nix | |
| parent | 504589d1035f27b766bd33040b415b2725ece4ca (diff) | |
tree-wide: reformat using alejandra after enabling trivionomicon
Diffstat (limited to 'sys/ns/rr.nix')
| -rw-r--r-- | sys/ns/rr.nix | 764 |
1 files changed, 392 insertions, 372 deletions
diff --git a/sys/ns/rr.nix b/sys/ns/rr.nix index e4fbe12..8b4d119 100644 --- a/sys/ns/rr.nix +++ b/sys/ns/rr.nix @@ -1,4 +1,10 @@ -{ config, lib, options, pkgs, ... }: +{ + config, + lib, + options, + pkgs, + ... +}: with lib; let inherit (config.local) nets; @@ -11,15 +17,13 @@ with lib; let domainRefType = lib.types.strMatching "@|${segmentRegex}\\.?"; domainNameType = lib.types.strMatching "${segmentRegex}\\."; - zoneHashCheck = name: zone: - let - zoneHash = algorithm: "${algorithm}-${builtins.hashString algorithm cfg.nullSerialZones.${name}.content}"; - expected = zoneHash "sha256"; - in - { - inherit expected zone; - needsUpdate = zone.soa.serial == null || zone.nullSerialHash != expected; - }; + zoneHashCheck = name: zone: let + zoneHash = algorithm: "${algorithm}-${builtins.hashString algorithm cfg.nullSerialZones.${name}.content}"; + expected = zoneHash "sha256"; + in { + inherit expected zone; + needsUpdate = zone.soa.serial == null || zone.nullSerialHash != expected; + }; rrTypes = [ "A" @@ -32,8 +36,7 @@ with lib; let "SRV" "TXT" ]; -in -{ +in { options.local.ns = { nullSerialZones = mkOption { type = options.local.ns.zones.type; @@ -41,54 +44,62 @@ in }; ptr = mkOption { - default = { }; + default = {}; - type = with lib.types; attrsOf (submodule { - options = { - v4 = { - zone = mkOption { - type = nullOr str; - default = null; - }; + type = with lib.types; + attrsOf (submodule { + options = { + v4 = { + zone = mkOption { + type = nullOr str; + default = null; + }; - targets = mkOption { - type = attrsOf str; - default = { }; + targets = mkOption { + type = attrsOf str; + default = {}; + }; }; - }; - v6 = { - zone = mkOption { - type = nullOr str; - default = null; - }; + v6 = { + zone = mkOption { + type = nullOr str; + default = null; + }; - targets = mkOption { - type = attrsOf str; - default = { }; + targets = mkOption { + type = attrsOf str; + default = {}; + }; }; }; - }; - }); + }); }; zones = mkOption { - default = { }; - - type = with lib.types; attrsOf (submodule ({ config, name, ... }: - let - nameOption = args@{ defaultZone ? "${name}.", permitRelative ? true, ... }: - mkOption (removeAttrs args [ "defaultZone" "permitRelative" ] // { + default = {}; + + type = with lib.types; attrsOf (submodule ({ + config, + name, + ... + }: let + nameOption = args @ { + defaultZone ? "${name}.", + permitRelative ? true, + ... + }: + mkOption (removeAttrs args ["defaultZone" "permitRelative"] + // { type = domainRefType; - apply = value: - let - zone = - throwIfNot - (hasSuffix "." defaultZone) - "zone expression '${defaultZone}' must be absolute, not relative" - defaultZone; - in + apply = value: let + zone = + throwIfNot + (hasSuffix "." defaultZone) + "zone expression '${defaultZone}' must be absolute, not relative" + defaultZone; + in if value == "@" then zone else if hasSuffix "." value @@ -98,401 +109,410 @@ in else throw "zone expression '${value}' in zone '${zone}' must be absolute, not relative"; }); - rrType = options: mkOption { - default = [ ]; + rrType = options: + mkOption { + default = []; type = listOf (submodule { - options = options // { - name = nameOption { }; - - ttl = mkOption { - type = int; - default = config.defaultTTL; + options = + options + // { + name = nameOption {}; + + ttl = mkOption { + type = int; + default = config.defaultTTL; + }; }; - }; }); }; - rrConfig = { rrs, type, format, applyName ? (rr: rr.name) }: (map - (rr: { - inherit type; - inherit (rr) ttl; + rrConfig = { + rrs, + type, + format, + applyName ? (rr: rr.name), + }: (map + (rr: { + inherit type; + inherit (rr) ttl; + + data = format rr; + name = applyName rr; + }) + rrs); + in { + options = { + local = mkOption { + type = unspecified; + default = globalConfig.local; + readOnly = true; + }; - data = format rr; - name = applyName rr; - }) - rrs); - in - { - options = { - local = mkOption { - type = unspecified; - default = globalConfig.local; - readOnly = true; - }; + defaultTTL = mkOption { + type = int; + default = 3600; + }; - defaultTTL = mkOption { - type = int; - default = 3600; + ptrName = mkOption { + type = nullOr str; + default = null; + }; + + defaultPtr = { + v4 = mkOption { + type = nullOr str; + default = null; }; - ptrName = mkOption { + v6 = mkOption { type = nullOr str; default = null; }; + }; - defaultPtr = { - v4 = mkOption { - type = nullOr str; - default = null; - }; + nsdConfig = mkOption { + type = attrsOf unspecified; + default = {}; + }; - v6 = mkOption { - type = nullOr str; - default = null; - }; + content = mkOption { + type = lines; + readOnly = true; + }; + + nullSerialHash = mkOption { + type = nullOr str; + default = null; + }; + + rr = mkOption { + default = []; + type = + listOf + (submodule { + options = { + name = nameOption {}; + + ttl = mkOption { + type = int; + }; + + class = mkOption { + type = enum ["IN"]; + default = "IN"; + }; + + type = mkOption { + type = enum rrTypes; + }; + + data = mkOption { + type = listOf (either int str); + default = []; + }; + }; + }); + }; + + soa = { + authorityZone = nameOption { + default = "@"; + permitRelative = false; + }; + + ttl = mkOption { + type = int; + default = config.defaultTTL; }; - nsdConfig = mkOption { - type = attrsOf unspecified; - default = { }; + primary = nameOption { + default = "ns1"; + defaultZone = config.soa.authorityZone; }; - content = mkOption { - type = lines; - readOnly = true; + hostmaster = mkOption { + type = emailType; + default = "hostmaster"; + + apply = address: let + split = splitString "@" address; + + user = head split; + domain = + if length split == 2 + then head (tail split) + else removeSuffix "." config.soa.authorityZone; + in + if hasSuffix "." address + then address + else "${replaceStrings ["."] ["\\."] user}.${domain}."; }; - nullSerialHash = mkOption { - type = nullOr str; + serial = mkOption { + type = nullOr int; default = null; }; - rr = mkOption { - default = [ ]; - type = listOf - (submodule { - options = { - name = nameOption { }; - - ttl = mkOption { - type = int; - }; - - class = mkOption { - type = enum [ "IN" ]; - default = "IN"; - }; - - type = mkOption { - type = enum rrTypes; - }; - - data = mkOption { - type = listOf (either int str); - default = [ ]; - }; - }; - }); + refresh = mkOption { + type = int; + default = 3 * 3600; }; - soa = { - authorityZone = nameOption { default = "@"; permitRelative = false; }; - - ttl = mkOption { - type = int; - default = config.defaultTTL; - }; + retry = mkOption { + type = int; + default = 3600; + }; - primary = nameOption { - default = "ns1"; - defaultZone = config.soa.authorityZone; - }; + expire = mkOption { + type = int; + default = 7 * 24 * 3600; + }; - hostmaster = mkOption { - type = emailType; - default = "hostmaster"; + negativeTTL = mkOption { + type = int; + default = 3600; + }; + }; - apply = address: - let - split = splitString "@" address; + a = rrType { + ipv4 = mkOption { + type = str; + }; - user = head split; - domain = if length split == 2 then head (tail split) else removeSuffix "." config.soa.authorityZone; - in - if hasSuffix "." address - then address - else "${replaceStrings [ "." ] [ "\\." ] user}.${domain}."; - }; + ptr = mkOption { + type = nullOr str; + default = config.defaultPtr.v4; + }; + }; - serial = mkOption { - type = nullOr int; - default = null; - }; + aaaa = rrType { + ipv6 = mkOption { + type = str; + }; - refresh = mkOption { - type = int; - default = 3 * 3600; - }; + ptr = mkOption { + type = nullOr str; + default = config.defaultPtr.v6; + }; + }; - retry = mkOption { - type = int; - default = 3600; - }; + cname = rrType { + target = nameOption {}; + }; - expire = mkOption { - type = int; - default = 7 * 24 * 3600; - }; + mx = rrType { + host = nameOption {}; - negativeTTL = mkOption { - type = int; - default = 3600; - }; + priority = mkOption { + type = int; }; + }; - a = rrType { - ipv4 = mkOption { - type = str; - }; + ns = rrType { + host = nameOption {}; + }; - ptr = mkOption { - type = nullOr str; - default = config.defaultPtr.v4; - }; - }; + ptr = rrType { + target = nameOption {}; + }; - aaaa = rrType { - ipv6 = mkOption { - type = str; - }; + srv = rrType { + host = nameOption {}; - ptr = mkOption { - type = nullOr str; - default = config.defaultPtr.v6; - }; + port = mkOption { + type = port; }; - cname = rrType { - target = nameOption { }; + priority = mkOption { + type = int; }; - mx = rrType { - host = nameOption { }; + proto = mkOption { + type = enum ["tcp" "udp"]; + }; - priority = mkOption { - type = int; - }; + service = mkOption { + type = str; }; - ns = rrType { - host = nameOption { }; + weight = mkOption { + type = int; }; + }; - ptr = rrType { - target = nameOption { }; + txt = rrType { + text = mkOption { + type = strMatching "[^\"\n\\]*\n?"; + apply = removeSuffix "\n"; }; + }; + }; - srv = rrType { - host = nameOption { }; + config = { + nsdConfig.data = config.content; + + content = let + rrLine = rr: concatMapStringsSep " " toString ([rr.name rr.ttl rr.class rr.type] ++ rr.data); + in + '' + $ORIGIN ${name}. + $TTL ${toString config.defaultTTL} + '' + + concatLines (map rrLine config.rr); + + rr = mkMerge [ + (mkOrder 0 (singleton { + inherit (config.soa) ttl; + + name = "${name}."; + type = "SOA"; + + data = with config.soa; [ + primary + hostmaster + (throwIf (serial == null) "No serial defined for zone ${name}" serial) + refresh + retry + expire + negativeTTL + ]; + })) + + (mkOrder 1 (rrConfig { + rrs = config.ns; + type = "NS"; + format = rr: [rr.host]; + })) + + (rrConfig { + rrs = config.a; + type = "A"; + format = rr: [rr.ipv4]; + }) - port = mkOption { - type = port; - }; + (rrConfig { + rrs = config.aaaa; + type = "AAAA"; + format = rr: [rr.ipv6]; + }) - priority = mkOption { - type = int; - }; + (rrConfig { + rrs = config.cname; + type = "CNAME"; + format = rr: [rr.target]; + }) - proto = mkOption { - type = enum [ "tcp" "udp" ]; - }; + (rrConfig { + rrs = config.mx; + type = "MX"; + format = rr: [rr.priority rr.host]; + }) - service = mkOption { - type = str; - }; + (rrConfig { + rrs = config.ptr; + type = "PTR"; + format = rr: [rr.target]; + }) - weight = mkOption { - type = int; - }; - }; + (rrConfig { + rrs = config.srv; + type = "SRV"; - txt = rrType { - text = mkOption { - type = strMatching "[^\"\n\\]*\n?"; - apply = removeSuffix "\n"; - }; - }; - }; + format = rr: [rr.priority rr.weight rr.port rr.host]; + applyName = rr: "_${rr.service}._${rr.proto}.${rr.name}"; + }) - config = { - nsdConfig.data = config.content; + (rrConfig { + rrs = config.txt; + type = "TXT"; - content = - let - rrLine = rr: concatMapStringsSep " " toString ([ rr.name rr.ttl rr.class rr.type ] ++ rr.data); + format = rr: let + # nsd-zonecheck: text string is longer than 255 characters, try splitting it into multiple parts + txtFragments = text: let + max = 255; + length = stringLength text; + in + singleton (substring 0 max text) ++ optionals (length > max) (txtFragments (substring max length text)); in - '' - $ORIGIN ${name}. - $TTL ${toString config.defaultTTL} - '' + concatLines (map rrLine config.rr); - - rr = mkMerge [ - (mkOrder 0 (singleton { - inherit (config.soa) ttl; - - name = "${name}."; - type = "SOA"; - - data = with config.soa; [ - primary - hostmaster - (throwIf (serial == null) "No serial defined for zone ${name}" serial) - refresh - retry - expire - negativeTTL - ]; - })) - - (mkOrder 1 (rrConfig { - rrs = config.ns; - type = "NS"; - format = rr: [ rr.host ]; - })) - - (rrConfig { - rrs = config.a; - type = "A"; - format = rr: [ rr.ipv4 ]; - }) - - (rrConfig { - rrs = config.aaaa; - type = "AAAA"; - format = rr: [ rr.ipv6 ]; - }) - - (rrConfig { - rrs = config.cname; - type = "CNAME"; - format = rr: [ rr.target ]; - }) - - (rrConfig { - rrs = config.mx; - type = "MX"; - format = rr: [ rr.priority rr.host ]; - }) - - (rrConfig { - rrs = config.ptr; - type = "PTR"; - format = rr: [ rr.target ]; - }) - - (rrConfig { - rrs = config.srv; - type = "SRV"; - - format = rr: [ rr.priority rr.weight rr.port rr.host ]; - applyName = rr: "_${rr.service}._${rr.proto}.${rr.name}"; - }) - - (rrConfig { - rrs = config.txt; - type = "TXT"; - - format = rr: - let - # nsd-zonecheck: text string is longer than 255 characters, try splitting it into multiple parts - txtFragments = text: - let - max = 255; - length = stringLength text; - in - singleton (substring 0 max text) ++ optionals (length > max) (txtFragments (substring max length text)); - in - map (fragment: "\"${fragment}\"") (txtFragments rr.text); - }) - ]; - }; - })); + map (fragment: "\"${fragment}\"") (txtFragments rr.text); + }) + ]; + }; + })); }; }; config = { - assertions = [ - ( - let - badZones = attrNames (filterAttrs (name: zone: (zoneHashCheck name zone).needsUpdate) cfg.zones); - in - { - assertion = badZones == [ ]; - message = "Update serials for these zones (null-serial hash mismatch): ${concatStringsSep ", " badZones}"; - } - ) - ] ++ flatten (mapAttrsToList - (name: ptr: [ - { - assertion = ptr.v4.targets != { } -> ptr.v4.zone != null; - message = "undefined v4 PTR net '${name}': ${concatStringsSep ", " (attrValues ptr.v4.targets)}"; - } - { - assertion = ptr.v6.targets != { } -> ptr.v6.zone != null; - message = "undefined v6 PTR net '${name}': ${concatStringsSep ", " (attrValues ptr.v6.targets)}"; - } - ]) - cfg.ptr); - - lib.local.zoneSerialUpdates = - let - ptrChecks = filterAttrs (_: check: check.zone.ptrName != null) allZoneChecks; - zoneChecks = filterAttrs (_: check: check.zone.ptrName == null) allZoneChecks; - allZoneChecks = filterAttrs (_: check: check.needsUpdate) (mapAttrs zoneHashCheck cfg.zones); - - updateInfo = name: check: { - inherit name; - inherit (check) expected; - inherit (check.zone.soa) serial; - }; - in - { - ptr = mapAttrs (_: check: updateInfo check.zone.ptrName check) ptrChecks; - zones = mapAttrs updateInfo zoneChecks; + assertions = + [ + ( + let + badZones = attrNames (filterAttrs (name: zone: (zoneHashCheck name zone).needsUpdate) cfg.zones); + in { + assertion = badZones == []; + message = "Update serials for these zones (null-serial hash mismatch): ${concatStringsSep ", " badZones}"; + } + ) + ] + ++ flatten (mapAttrsToList + (name: ptr: [ + { + assertion = ptr.v4.targets != {} -> ptr.v4.zone != null; + message = "undefined v4 PTR net '${name}': ${concatStringsSep ", " (attrValues ptr.v4.targets)}"; + } + { + assertion = ptr.v6.targets != {} -> ptr.v6.zone != null; + message = "undefined v6 PTR net '${name}': ${concatStringsSep ", " (attrValues ptr.v6.targets)}"; + } + ]) + cfg.ptr); + + lib.local.zoneSerialUpdates = let + ptrChecks = filterAttrs (_: check: check.zone.ptrName != null) allZoneChecks; + zoneChecks = filterAttrs (_: check: check.zone.ptrName == null) allZoneChecks; + allZoneChecks = filterAttrs (_: check: check.needsUpdate) (mapAttrs zoneHashCheck cfg.zones); + + updateInfo = name: check: { + inherit name; + inherit (check) expected; + inherit (check.zone.soa) serial; }; + in { + ptr = mapAttrs (_: check: updateInfo check.zone.ptrName check) ptrChecks; + zones = mapAttrs updateInfo zoneChecks; + }; local.ns = { - nullSerialZones = - let - defaultAttrs = [ "defaultTTL" "defaultPtr" "ptrName" ]; - filteredAttrs = defaultAttrs ++ map toLower rrTypes; - in + nullSerialZones = let + defaultAttrs = ["defaultTTL" "defaultPtr" "ptrName"]; + filteredAttrs = defaultAttrs ++ map toLower rrTypes; + in mapAttrs - (_: zone: mkMerge [ + (_: zone: + mkMerge [ (filterAttrs (name: _: elem name filteredAttrs) zone) - { soa.serial = mkOverride 0 0; } + {soa.serial = mkOverride 0 0;} ]) - cfg.zones; - - ptr = - let - zonePtrs = zone: - let - v4Ptrs = map - (a: { - ${a.ptr}.v4.targets.${nets.${a.ptr}.v4.ptrRecordName a.ipv4 32} = a.name; - }) - (filter (a: a.ptr != null) zone.a); - - v6Ptrs = map - (aaaa: { - ${aaaa.ptr}.v6.targets.${nets.${aaaa.ptr}.v6.ptrRecordName aaaa.ipv6 128} = aaaa.name; - }) - (filter (aaaa: aaaa.ptr != null) zone.aaaa); - in - v4Ptrs ++ v6Ptrs; + cfg.zones; + + ptr = let + zonePtrs = zone: let + v4Ptrs = + map + (a: { + ${a.ptr}.v4.targets.${nets.${a.ptr}.v4.ptrRecordName a.ipv4 32} = a.name; + }) + (filter (a: a.ptr != null) zone.a); + v6Ptrs = + map + (aaaa: { + ${aaaa.ptr}.v6.targets.${nets.${aaaa.ptr}.v6.ptrRecordName aaaa.ipv6 128} = aaaa.name; + }) + (filter (aaaa: aaaa.ptr != null) zone.aaaa); in + v4Ptrs ++ v6Ptrs; + in mkMerge (flatten (mapAttrsToList (_: zonePtrs) cfg.zones)); }; }; |
