diff options
| author | Alejandro Soto <alejandro@34project.org> | 2024-08-04 16:36:17 -0600 |
|---|---|---|
| committer | Alejandro Soto <alejandro@34project.org> | 2024-08-04 16:57:19 -0600 |
| commit | c918238932f0d776666e552b8ccf353375703249 (patch) | |
| tree | 5c5f2bc90d9fa3a5ad19989df823b413363ca285 | |
| parent | c1fed32b662c4697fa1e1e9ce85a42d88d4e3db5 (diff) | |
sys/ns: initial commit
| -rw-r--r-- | sys/default.nix | 1 | ||||
| -rw-r--r-- | sys/ns/default.nix | 7 | ||||
| -rw-r--r-- | sys/ns/nsd.nix | 37 | ||||
| -rw-r--r-- | sys/ns/rr.nix | 257 | ||||
| -rw-r--r-- | sys/ns/zones/README.md | 1 |
5 files changed, 303 insertions, 0 deletions
diff --git a/sys/default.nix b/sys/default.nix index 59a8743..e3d1b11 100644 --- a/sys/default.nix +++ b/sys/default.nix @@ -19,6 +19,7 @@ with lib; { ./mail ./mta ./net + ./ns ./nspawn ./preset ./seat diff --git a/sys/ns/default.nix b/sys/ns/default.nix new file mode 100644 index 0000000..8c02d1d --- /dev/null +++ b/sys/ns/default.nix @@ -0,0 +1,7 @@ +{ + imports = [ + ./nsd.nix + ./rr.nix + ./zones + ]; +} diff --git a/sys/ns/nsd.nix b/sys/ns/nsd.nix new file mode 100644 index 0000000..46ec1e6 --- /dev/null +++ b/sys/ns/nsd.nix @@ -0,0 +1,37 @@ +{ config, lib, ... }: +with lib; let + cfg = config.local.ns.server; +in +{ + options.local.ns.server = { + enable = mkEnableOption "nsd authoritative server"; + }; + + config = mkIf cfg.enable { + networking.firewall = + let + inherit (config.services.nsd) port; + in + { + allowedTCPPorts = [ port ]; + allowedUDPPorts = [ port ]; + }; + + services.nsd = { + enable = true; + + ipFreebind = true; + + bind8Stats = true; + statistics = 3600; + + tcpCount = 128; + tcpTimeout = 30; + tcpQueryCount = 128; + + zones = mapAttrs + (_: zone: { data = zone.content; }) + config.local.ns.zones; + }; + }; +} diff --git a/sys/ns/rr.nix b/sys/ns/rr.nix new file mode 100644 index 0000000..f1c6aca --- /dev/null +++ b/sys/ns/rr.nix @@ -0,0 +1,257 @@ +{ lib, pkgs, ... }: +with lib; let + segmentRegex = "[a-z0-9_-]+(\\.[a-z0-9_-]+)*"; + + emailType = lib.types.strMatching "[a-z0-9._-]+(@${segmentRegex})?"; + domainRefType = lib.types.strMatching "@|${segmentRegex}\\.?"; + domainNameType = lib.types.strMatching "${segmentRegex}\\."; +in +{ + options.local.ns.zones = mkOption { + default = { }; + + type = with lib.types; attrsOf (submodule ({ config, name, ... }: + let + nameOption = mkOption { + type = domainRefType; + + apply = value: + if value == "@" + then "${name}." + else if ! hasSuffix "." value + then "${value}.${name}." + else value; + }; + + rrType = options: mkOption { + default = [ ]; + type = listOf (submodule { + options = options // { + name = nameOption; + + ttl = mkOption { + type = int; + default = config.defaultTTL; + }; + }; + }); + }; + + rrConfig = { rrs, type, format }: (map + (rr: { + inherit type; + inherit (rr) name ttl; + + data = format rr; + }) + rrs); + in + { + options = { + defaultTTL = mkOption { + type = int; + default = 3600; + }; + + content = mkOption { + type = lines; + readOnly = true; + }; + + rr = mkOption { + default = [ ]; + type = listOf + (submodule { + options = { + name = nameOption; + + ttl = mkOption { + type = int; + }; + + class = mkOption { + type = enum [ "IN" ]; + default = "IN"; + }; + + type = mkOption { + type = enum [ + "A" + "AAAA" + "CNAME" + "MX" + "NS" + "SOA" + "SRV" + "TXT" + ]; + }; + + data = mkOption { + type = listOf (either int str); + default = [ ]; + }; + }; + }); + }; + + soa = { + ttl = mkOption { + type = int; + default = config.defaultTTL; + }; + + primary = nameOption; + + hostmaster = mkOption { + type = emailType; + apply = address: + let + split = splitString "@" address; + + user = head split; + domain = if length split == 2 then head (tail split) else name; + in + "${replaceStrings [ "." ] [ "\\." ] user}.${domain}."; + }; + + serial = mkOption { + type = int; + }; + + refresh = mkOption { + type = int; + default = 3 * 3600; + }; + + retry = mkOption { + type = int; + default = 3600; + }; + + expire = mkOption { + type = int; + default = 7 * 24 * 3600; + }; + + negativeTTL = mkOption { + type = int; + default = 3600; + }; + }; + + a = rrType { + ipv4 = mkOption { + type = str; + }; + }; + + aaaa = rrType { + ipv6 = mkOption { + type = str; + }; + }; + + cname = rrType { + target = nameOption; + }; + + mx = rrType { + host = nameOption; + + priority = mkOption { + type = int; + }; + }; + + ns = rrType { + host = nameOption; + }; + + txt = rrType { + text = mkOption { + type = strMatching "[^\"\n\\]*\n?"; + apply = removeSuffix "\n"; + }; + }; + }; + + config = { + 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 + 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.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); + }) + ]; + }; + })); + }; +} diff --git a/sys/ns/zones/README.md b/sys/ns/zones/README.md new file mode 100644 index 0000000..37073ba --- /dev/null +++ b/sys/ns/zones/README.md @@ -0,0 +1 @@ +# This directory has been lustrated. |
