summaryrefslogtreecommitdiff
path: root/sys/ns
diff options
context:
space:
mode:
authorAlejandro Soto <alejandro@34project.org>2025-08-24 18:55:06 -0600
committerAlejandro Soto <alejandro@34project.org>2025-08-24 18:55:06 -0600
commitd7ac88762db111a7962c4e14b5f4e37ab85ccac7 (patch)
tree0c2c8c4383bef74215e3b7c48a2f6b0117f084bc /sys/ns
parent504589d1035f27b766bd33040b415b2725ece4ca (diff)
tree-wide: reformat using alejandra after enabling trivionomicon
Diffstat (limited to '')
-rw-r--r--sys/ns/mx.nix79
-rw-r--r--sys/ns/ns.nix237
-rw-r--r--sys/ns/nsd.nix23
-rw-r--r--sys/ns/ptr/default.nix6
-rw-r--r--sys/ns/ptr/gate-public-v4/default.nix6
-rw-r--r--sys/ns/ptr/gate-public-v4/serial.nix1
-rw-r--r--sys/ns/ptr/gate-public-v6/default.nix6
-rw-r--r--sys/ns/ptr/gate-public-v6/serial.nix1
-rw-r--r--sys/ns/ptr/static-prefix-v6/default.nix6
-rw-r--r--sys/ns/ptr/static-prefix-v6/serial.nix1
-rw-r--r--sys/ns/rr.nix764
-rw-r--r--sys/nspawn/dmz.nix129
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
};
};
};