diff options
Diffstat (limited to '')
| -rw-r--r-- | sys/ns/mx.nix | 79 | ||||
| -rw-r--r-- | sys/ns/ns.nix | 237 | ||||
| -rw-r--r-- | sys/ns/nsd.nix | 23 | ||||
| -rw-r--r-- | sys/ns/ptr/default.nix | 6 | ||||
| -rw-r--r-- | sys/ns/ptr/gate-public-v4/default.nix | 6 | ||||
| -rw-r--r-- | sys/ns/ptr/gate-public-v4/serial.nix | 1 | ||||
| -rw-r--r-- | sys/ns/ptr/gate-public-v6/default.nix | 6 | ||||
| -rw-r--r-- | sys/ns/ptr/gate-public-v6/serial.nix | 1 | ||||
| -rw-r--r-- | sys/ns/ptr/static-prefix-v6/default.nix | 6 | ||||
| -rw-r--r-- | sys/ns/ptr/static-prefix-v6/serial.nix | 1 | ||||
| -rw-r--r-- | sys/ns/rr.nix | 764 | ||||
| -rw-r--r-- | sys/nspawn/dmz.nix | 129 |
12 files changed, 662 insertions, 597 deletions
diff --git a/sys/ns/mx.nix b/sys/ns/mx.nix index 40a5574..892b684 100644 --- a/sys/ns/mx.nix +++ b/sys/ns/mx.nix @@ -1,33 +1,60 @@ -{ config, lib, ... }: +{ + config, + lib, + ... +}: with lib; let inherit (config.local) domains; -in -{ +in { options.local.ns.zones = mkOption { - type = with lib.types; attrsOf (submodule ({ config, name, ... }: { - options.localMX = { - enable = mkEnableOption "local MX settings"; - }; + type = with lib.types; + attrsOf (submodule ({ + config, + name, + ... + }: { + options.localMX = { + enable = mkEnableOption "local MX settings"; + }; - config = mkIf config.localMX.enable { - mx = [ - { name = "@"; priority = 10; host = "${domains.smtp.gated}."; } - { name = "@"; priority = 20; host = "${domains.smtp-backup.main}."; } - # Many thanks to junkemailfilter.com for all their years of service. RIP. - #{ name = "@"; priority = 30; host = "mxbackup1.junkemailfilter.com."; } - #{ name = "@"; priority = 40; host = "mxbackup2.junkemailfilter.com."; } - ]; + config = mkIf config.localMX.enable { + mx = [ + { + name = "@"; + priority = 10; + host = "${domains.smtp.gated}."; + } + { + name = "@"; + priority = 20; + host = "${domains.smtp-backup.main}."; + } + # Many thanks to junkemailfilter.com for all their years of service. RIP. + #{ name = "@"; priority = 30; host = "mxbackup1.junkemailfilter.com."; } + #{ name = "@"; priority = 40; host = "mxbackup2.junkemailfilter.com."; } + ]; - txt = [ - { name = "@"; text = "v=spf1 mx a -all"; } - { name = "_dmarc"; text = "v=DMARC1;p=reject;sp=reject;adkim=r;aspf=r;fo=1;rf=afrf;rua=mailto:postmaster@${name}"; } - { name = "_adsp._domainkey"; text = "dkim=all"; } - ] ++ map - (selector: { - name = "${toString selector}._domainkey"; - text = readFile (./dkim + "/${toString selector}.txt"); - }) [ 202001 202102 202402 202408 ]; - }; - })); + txt = + [ + { + name = "@"; + text = "v=spf1 mx a -all"; + } + { + name = "_dmarc"; + text = "v=DMARC1;p=reject;sp=reject;adkim=r;aspf=r;fo=1;rf=afrf;rua=mailto:postmaster@${name}"; + } + { + name = "_adsp._domainkey"; + text = "dkim=all"; + } + ] + ++ map + (selector: { + name = "${toString selector}._domainkey"; + text = readFile (./dkim + "/${toString selector}.txt"); + }) [202001 202102 202402 202408]; + }; + })); }; } diff --git a/sys/ns/ns.nix b/sys/ns/ns.nix index 1e74502..e5b30e8 100644 --- a/sys/ns/ns.nix +++ b/sys/ns/ns.nix @@ -1,130 +1,153 @@ -{ config, lib, ... }: +{ + config, + lib, + ... +}: with lib; let inherit (config.networking) domain; inherit (config.local.nets) gate-public; inherit (config.local.ns.server) tsigName; ptrNets = config.local.ns.ptr; -in -{ +in { options.local.ns.zones = mkOption { - type = with lib.types; attrsOf + type = with lib.types; + attrsOf (submodule - ({ config, name, ... }: - let - inherit (config.soa) primary; - - cfg = config.localNS; - ptrDomain = cfg.ptrNet.v4 != null || cfg.ptrNet.v6 != null; - in - { - options.localNS = { - enable = mkEnableOption "local NS settings"; - - acme = mkOption { - default = { }; - type = attrsOf str; - }; + ({ + config, + name, + ... + }: let + inherit (config.soa) primary; + + cfg = config.localNS; + ptrDomain = cfg.ptrNet.v4 != null || cfg.ptrNet.v6 != null; + in { + options.localNS = { + enable = mkEnableOption "local NS settings"; + + acme = mkOption { + default = {}; + type = attrsOf str; + }; - ptrNet = { - v4 = mkOption { - type = nullOr str; - default = null; - }; + ptrNet = { + v4 = mkOption { + type = nullOr str; + default = null; + }; - v6 = mkOption { - type = nullOr str; - default = null; - }; + v6 = mkOption { + type = nullOr str; + default = null; }; }; - - config = mkIf cfg.enable - { - ptrName = - let - name = - if cfg.ptrNet.v6 != null - then "${cfg.ptrNet.v6}-v6" - else "${cfg.ptrNet.v4}-v4"; - in - mkIf ptrDomain name; - - # https://docs.gandi.net/en/domain_names/advanced_users/secondary_nameserver.html - nsdConfig = - let - providerSecondary = [ - "37.205.15.45 ${tsigName}" # ns3.vpsfree.cz - "37.205.11.85 ${tsigName}" # ns4.vpsfree.cz - "2a03:3b40:fe:2be::1 ${tsigName}" # ns3.vpsfree.cz - "2a03:3b40:101:4::1 ${tsigName}" # ns4.vpsfree.cz - ]; - in - { - notify = providerSecondary; - provideXFR = providerSecondary; - }; - - ns = [ - { name = "@"; host = primary; } - { name = "@"; host = "ns3.vpsfree.cz."; } - { name = "@"; host = "ns4.vpsfree.cz."; } + }; + + config = + mkIf cfg.enable + { + ptrName = let + name = + if cfg.ptrNet.v6 != null + then "${cfg.ptrNet.v6}-v6" + else "${cfg.ptrNet.v4}-v4"; + in + mkIf ptrDomain name; + + # https://docs.gandi.net/en/domain_names/advanced_users/secondary_nameserver.html + nsdConfig = let + providerSecondary = [ + "37.205.15.45 ${tsigName}" # ns3.vpsfree.cz + "37.205.11.85 ${tsigName}" # ns4.vpsfree.cz + "2a03:3b40:fe:2be::1 ${tsigName}" # ns3.vpsfree.cz + "2a03:3b40:101:4::1 ${tsigName}" # ns4.vpsfree.cz ]; + in { + notify = providerSecondary; + provideXFR = providerSecondary; + }; - a = optional (!ptrDomain) - { name = primary; ipv4 = gate-public.hosts.gate.v4.address; ptr = null; }; - - aaaa = optional (!ptrDomain) - { name = primary; ipv6 = gate-public.hosts.gate.v6.address; ptr = null; }; + ns = [ + { + name = "@"; + host = primary; + } + { + name = "@"; + host = "ns3.vpsfree.cz."; + } + { + name = "@"; + host = "ns4.vpsfree.cz."; + } + ]; + + a = + optional (!ptrDomain) + { + name = primary; + ipv4 = gate-public.hosts.gate.v4.address; + ptr = null; + }; - ptr = - let - ptrsToRecords = mapAttrsToList (suffix: target: { - name = suffix; - inherit target; - }); + aaaa = + optional (!ptrDomain) + { + name = primary; + ipv6 = gate-public.hosts.gate.v6.address; + ptr = null; + }; - v4Net = cfg.ptrNet.v4; - v6Net = cfg.ptrNet.v6; + ptr = let + ptrsToRecords = mapAttrsToList (suffix: target: { + name = suffix; + inherit target; + }); - v4Records = optionals (v4Net != null) (ptrsToRecords ptrNets.${v4Net}.v4.targets); - v6Records = optionals (v6Net != null) (ptrsToRecords ptrNets.${v6Net}.v6.targets); - in - v4Records ++ v6Records; + v4Net = cfg.ptrNet.v4; + v6Net = cfg.ptrNet.v6; - soa = mkIf ptrDomain { - authorityZone = mkDefault "${domain}."; - }; + v4Records = optionals (v4Net != null) (ptrsToRecords ptrNets.${v4Net}.v4.targets); + v6Records = optionals (v6Net != null) (ptrsToRecords ptrNets.${v6Net}.v6.targets); + in + v4Records ++ v6Records; - cname = mapAttrsToList - (name: id: { - name = "_acme-challenge" + optionalString (name != "@") ".${name}"; - target = "${id}.acme-challenge.${domain}."; - }) - cfg.acme; + soa = mkIf ptrDomain { + authorityZone = mkDefault "${domain}."; }; - })); - }; - config = - { - assertions = mapAttrsToList - (name: zone: { - assertion = zone.localNS.ptrNet.v4 != null -> zone.localNS.ptrNet.v6 == null; - message = "zone '${name}' defined as both a v4 and v6 PTR zone"; - }) - config.local.ns.zones; - - local.ns.ptr = - let - zonePtrNets = name: zone: - optionalAttrs (zone.localNS.ptrNet.v4 != null) - { - ${zone.localNS.ptrNet.v4}.v4.zone = name; - } // optionalAttrs (zone.localNS.ptrNet.v6 != null) { - ${zone.localNS.ptrNet.v6}.v6.zone = name; + cname = + mapAttrsToList + (name: id: { + name = "_acme-challenge" + optionalString (name != "@") ".${name}"; + target = "${id}.acme-challenge.${domain}."; + }) + cfg.acme; }; - in - mkMerge (flatten (mapAttrsToList zonePtrNets (filterAttrs (_: zone: zone.localNS.enable) config.local.ns.zones))); - }; + })); + }; + + config = { + assertions = + mapAttrsToList + (name: zone: { + assertion = zone.localNS.ptrNet.v4 != null -> zone.localNS.ptrNet.v6 == null; + message = "zone '${name}' defined as both a v4 and v6 PTR zone"; + }) + config.local.ns.zones; + + local.ns.ptr = let + zonePtrNets = name: zone: + optionalAttrs (zone.localNS.ptrNet.v4 != null) + { + ${zone.localNS.ptrNet.v4}.v4.zone = name; + } + // optionalAttrs (zone.localNS.ptrNet.v6 != null) { + ${zone.localNS.ptrNet.v6}.v6.zone = name; + }; + in + mkMerge (flatten (mapAttrsToList zonePtrNets (filterAttrs (_: zone: zone.localNS.enable) config.local.ns.zones))); + }; } diff --git a/sys/ns/nsd.nix b/sys/ns/nsd.nix index 1dfa16b..d49e464 100644 --- a/sys/ns/nsd.nix +++ b/sys/ns/nsd.nix @@ -1,12 +1,15 @@ -{ config, lib, ... }: +{ + config, + lib, + ... +}: with lib; let inherit (config.networking) domain; cfg = config.local.ns.server; acmeChallengeDomain = "acme-challenge.${domain}"; -in -{ +in { options. local. ns. server = { enable = mkEnableOption "nsd authoritative server"; @@ -34,14 +37,12 @@ in } ]; - networking.firewall = - let - inherit (config.services.nsd) port; - in - { - allowedTCPPorts = [ port ]; - allowedUDPPorts = [ port ]; - }; + networking.firewall = let + inherit (config.services.nsd) port; + in { + allowedTCPPorts = [port]; + allowedUDPPorts = [port]; + }; services = { acme-dns = { diff --git a/sys/ns/ptr/default.nix b/sys/ns/ptr/default.nix index d583dd7..b4fba7e 100644 --- a/sys/ns/ptr/default.nix +++ b/sys/ns/ptr/default.nix @@ -1,8 +1,6 @@ -{ config, ... }: -let +{config, ...}: let inherit (config.local) nets; -in -{ +in { config.local.ns.zones = { ${nets.gate-public.v4.ptrDomain} = import ./gate-public-v4; ${nets.gate-public.v6.ptrDomain} = import ./gate-public-v6; diff --git a/sys/ns/ptr/gate-public-v4/default.nix b/sys/ns/ptr/gate-public-v4/default.nix index a2595d9..44c7f2e 100644 --- a/sys/ns/ptr/gate-public-v4/default.nix +++ b/sys/ns/ptr/gate-public-v4/default.nix @@ -1,8 +1,6 @@ -{ config, ... }: -let +{config, ...}: let inherit (config.local) nets; -in -{ +in { imports = [ ./serial.nix ]; diff --git a/sys/ns/ptr/gate-public-v4/serial.nix b/sys/ns/ptr/gate-public-v4/serial.nix index c3a41e9..008e5d8 100644 --- a/sys/ns/ptr/gate-public-v4/serial.nix +++ b/sys/ns/ptr/gate-public-v4/serial.nix @@ -4,4 +4,3 @@ nullSerialHash = "sha256-afaedee02017aabd45b944a657ce91515866982c7cb900927edcee6d2b39c731"; }; } - diff --git a/sys/ns/ptr/gate-public-v6/default.nix b/sys/ns/ptr/gate-public-v6/default.nix index 15a4095..674421f 100644 --- a/sys/ns/ptr/gate-public-v6/default.nix +++ b/sys/ns/ptr/gate-public-v6/default.nix @@ -1,8 +1,6 @@ -{ config, ... }: -let +{config, ...}: let inherit (config.local) nets; -in -{ +in { imports = [ ./serial.nix ]; diff --git a/sys/ns/ptr/gate-public-v6/serial.nix b/sys/ns/ptr/gate-public-v6/serial.nix index 2f1b4a9..126a17e 100644 --- a/sys/ns/ptr/gate-public-v6/serial.nix +++ b/sys/ns/ptr/gate-public-v6/serial.nix @@ -4,4 +4,3 @@ nullSerialHash = "sha256-9a8ac8849ea6c8993e44feefe439b96c643e2ccf3a03d0d700558e9a188f57d7"; }; } - diff --git a/sys/ns/ptr/static-prefix-v6/default.nix b/sys/ns/ptr/static-prefix-v6/default.nix index f02222c..7688b97 100644 --- a/sys/ns/ptr/static-prefix-v6/default.nix +++ b/sys/ns/ptr/static-prefix-v6/default.nix @@ -1,8 +1,6 @@ -{ config, ... }: -let +{config, ...}: let inherit (config.local) nets; -in -{ +in { imports = [ ./serial.nix ]; diff --git a/sys/ns/ptr/static-prefix-v6/serial.nix b/sys/ns/ptr/static-prefix-v6/serial.nix index 454b3dd..a7c214a 100644 --- a/sys/ns/ptr/static-prefix-v6/serial.nix +++ b/sys/ns/ptr/static-prefix-v6/serial.nix @@ -4,4 +4,3 @@ nullSerialHash = "sha256-a5ce7781b014aa816998410db440dd40278d8b566d1de76e06776a83c9839b35"; }; } - 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)); }; }; diff --git a/sys/nspawn/dmz.nix b/sys/nspawn/dmz.nix index fb3acea..805ca72 100644 --- a/sys/nspawn/dmz.nix +++ b/sys/nspawn/dmz.nix @@ -1,4 +1,10 @@ -{ lib, config, flakes, pkgs, ... }: +{ + lib, + config, + flakes, + pkgs, + ... +}: with lib; let cfg = config.local.nspawn.dmz; inherit (config.local) mailHost; @@ -7,8 +13,7 @@ with lib; let hassPort = config.services.home-assistant.config.http.server_port; hassEnable = config.local.home-assistant.enable; -in -{ +in { options.local.nspawn.dmz = { enable = mkEnableOption "DMZ services in a container"; @@ -60,71 +65,70 @@ in nspawn.dmz = { hostAddr6 = dmzNet.hosts.gateway.v6.address; - system = - let - containerModule = { ... }: { - #TODO: urgente: bloquear puertos de dovecot a non-postfix con iptables - config = { - local = { - preset.dmz = { - enable = true; - container = true; - }; + system = let + containerModule = {...}: { + #TODO: urgente: bloquear puertos de dovecot a non-postfix con iptables + config = { + local = { + preset.dmz = { + enable = true; + container = true; + }; - mta = { - mdaAddr = "[${mailHost.mdaListen}]"; - inherit (mailHost) saslPort lmtpPort; - }; + mta = { + mdaAddr = "[${mailHost.mdaListen}]"; + inherit (mailHost) saslPort lmtpPort; + }; - web.sites = { - home = { - enable = hassEnable; - proxyUrl = "http://[${cfg.hostAddr6}]:${toString hassPort}"; - }; + web.sites = { + home = { + enable = hassEnable; + proxyUrl = "http://[${cfg.hostAddr6}]:${toString hassPort}"; }; }; + }; - nixpkgs = { - pkgs = mkDefault pkgs; - localSystem = mkDefault pkgs.stdenv.hostPlatform; - }; + nixpkgs = { + pkgs = mkDefault pkgs; + localSystem = mkDefault pkgs.stdenv.hostPlatform; + }; - services.nginx.virtualHosts = { - "${config.local.domains.imap.main}".locations."^~ /.well-known/acme-challenge/" = { - root = "/var/lib/acme/acme-challenge"; + services.nginx.virtualHosts = { + "${config.local.domains.imap.main}".locations."^~ /.well-known/acme-challenge/" = { + root = "/var/lib/acme/acme-challenge"; - extraConfig = '' - auth_basic off; - auth_request off; - ''; - }; + extraConfig = '' + auth_basic off; + auth_request off; + ''; }; + }; - systemd.network.networks."40-host0" = { - name = "host0"; + systemd.network.networks."40-host0" = { + name = "host0"; - networkConfig = { - DNS = [ cfg.dns64 ]; + networkConfig = { + DNS = [cfg.dns64]; - DHCP = "no"; - IPv6AcceptRA = "yes"; - LinkLocalAddressing = "ipv6"; - }; + DHCP = "no"; + IPv6AcceptRA = "yes"; + LinkLocalAddressing = "ipv6"; + }; - ipv6AcceptRAConfig = { - Token = [ - "static:::${dmzNet.hosts.dmz.v6.suffix}" - "eui64" - "static:::${dmzNet.hosts.mta.v6.suffix}" - "static:::${dmzNet.hosts.web.v6.suffix}" - ]; + ipv6AcceptRAConfig = { + Token = [ + "static:::${dmzNet.hosts.dmz.v6.suffix}" + "eui64" + "static:::${dmzNet.hosts.mta.v6.suffix}" + "static:::${dmzNet.hosts.web.v6.suffix}" + ]; - UseDNS = false; - }; + UseDNS = false; }; }; }; - in + }; + in # Tomado de la definición de pkgs.nixos junto con definición de nixpkgs.{pkgs,localSystem} arriba import "${flakes.nixpkgs}/nixos/lib/eval-config.nix" { modules = [ @@ -133,15 +137,15 @@ in ]; system = null; - specialArgs = { inherit flakes; }; + specialArgs = {inherit flakes;}; }; }; }; services = { home-assistant.config.http = mkIf hassEnable { - server_host = [ cfg.hostAddr6 ]; - trusted_proxies = [ dmzNet.hosts.web.v6.address ]; + server_host = [cfg.hostAddr6]; + trusted_proxies = [dmzNet.hosts.web.v6.address]; use_x_forwarded_for = true; }; }; @@ -199,26 +203,27 @@ in }; services = { - dovecot2.after = [ "systemd-nspawn@dmz.service" ]; + dovecot2.after = ["systemd-nspawn@dmz.service"]; "systemd-nspawn@dmz" = { overrideStrategy = "asDropin"; - after = [ "network-online.target" ]; - wants = [ "network-online.target" ]; - wantedBy = [ "machines.target" ]; + after = ["network-online.target"]; + wants = ["network-online.target"]; + wantedBy = ["machines.target"]; }; }; }; networking.firewall = { - allowedTCPPorts = [ 25 80 443 ]; + allowedTCPPorts = [25 80 443]; interfaces.ve-dmz = { - allowedTCPPorts = [ mailHost.saslPort mailHost.lmtpPort ] + allowedTCPPorts = + [mailHost.saslPort mailHost.lmtpPort] ++ optional hassEnable hassPort; - allowedUDPPorts = [ 67 ]; # DHCP + allowedUDPPorts = [67]; # DHCP }; }; }; |
