feat(utsuho): dnsmasq

This commit is contained in:
arcnmx 2024-03-25 15:18:55 -07:00
parent 0fbd142a16
commit 6ad465e779
17 changed files with 337 additions and 43 deletions

View file

@ -139,10 +139,10 @@ in {
firewall = {
interfaces.local = {
nftables.conditions = [
"ip saddr { ${concatStringsSep ", " cfg.cidrForNetwork.local.v4} }"
"ip saddr { ${concatStringsSep ", " (cfg.cidrForNetwork.local.v4 ++ cfg.cidrForNetwork.int.v4)} }"
(
mkIf networking.enableIPv6
"ip6 saddr { $localrange6, ${concatStringsSep ", " cfg.cidrForNetwork.local.v6} }"
"ip6 saddr { $localrange6, ${concatStringsSep ", " (cfg.cidrForNetwork.local.v6 ++ cfg.cidrForNetwork.int.v6)} }"
)
];
};

View file

@ -0,0 +1,70 @@
{config, lib, ...}: let
inherit (lib.options) mkOption mkEnableOption;
inherit (lib.modules) mkIf mkDefault mkOptionDefault;
inherit (lib.lists) filter optional;
inherit (lib.strings) hasInfix concatStrings;
inherit (config.services) resolved;
enabledNameservers = filter (ns: ns.enable) (config.networking.nameservers');
nameserverModule = {config, ...}: let
dnsPort = 53;
mkResolvedValue = { address, port, interface ? null, host ? null }: let
isIpv6 = hasInfix ":" address;
isPlain = port == dnsPort && interface == null && host == null;
addr = if isIpv6 && !isPlain then "[${address}]" else address;
in concatStrings (
[ addr ]
++ optional (port != dnsPort) ":${toString port}"
++ optional (interface != null) "%${interface}"
++ optional (host != null) "#${host}"
);
in {
options = with lib.types; {
enable = mkEnableOption "nameserver" // {
default = true;
};
address = mkOption {
type = str;
};
port = mkOption {
type = port;
default = dnsPort;
};
interface = mkOption {
type = nullOr str;
default = null;
};
host = mkOption {
type = nullOr str;
default = null;
};
resolvedValue = mkOption {
type = str;
readOnly = true;
};
value = mkOption {
type = str;
internal = true;
};
};
config = {
resolvedValue = mkOptionDefault (mkResolvedValue {
inherit (config) address port interface host;
});
value = mkOptionDefault (mkResolvedValue {
inherit (config) address port;
});
};
};
in {
options.networking = with lib.types; {
nameservers' = mkOption {
type = listOf (submodule nameserverModule);
default = { };
};
};
config = {
networking.nameservers = mkIf (config.networking.nameservers' != [ ]) (
map (ns: if resolved.enable then ns.resolvedValue else ns.value) enabledNameservers
);
};
}

View file

@ -10,16 +10,26 @@
inherit (inputs.self.lib) systems;
inherit (inputs.self.lib.lib) domain;
inherit (lib.options) mkOption mkEnableOption;
inherit (lib.modules) mkIf mkDefault mkOptionDefault;
inherit (lib.modules) mkIf mkMerge mkDefault mkOptionDefault;
inherit (lib.strings) removeSuffix;
cfg = config.access;
systemConfig = config;
systemAccess = access;
hasInt = config.proxmox.enabled && config.proxmox.network.internal.interface != null;
hasLocal = config.proxmox.enabled && config.proxmox.network.local.interface != null;
hasTail = cfg.tailscale.enable;
nixosModule = {
config,
system,
access,
...
}: let
cfg = config.networking.access;
addressForAttr = if config.networking.enableIPv6 then "address6ForNetwork" else "address4ForNetwork";
has'Int = system.proxmox.enabled && system.proxmox.network.internal.interface != null;
has'Local = system.proxmox.enabled && system.proxmox.network.local.interface != null;
has'Tail' = config.services.tailscale.enable;
has'Tail = lib.warnIf (hasTail != has'Tail') "tailscale set incorrectly in system.access for ${config.networking.hostName}" has'Tail';
in {
options.networking.access = with lib.types; {
global.enable =
@ -34,8 +44,22 @@
};
config = {
networking.access = {
moduleArgAttrs = {
inherit (systemAccess) hostnameForNetwork;
moduleArgAttrs = let
mkGetAddressFor = addressForAttr: hostName: network: let
forSystem = access.systemFor hostName;
err = throw "no lan interface found between ${config.networking.hostName} and ${hostName}";
in {
lan =
if has'Int then forSystem.access.${addressForAttr}.int or forSystem.access.${addressForAttr}.local or err
else if hasLocal then forSystem.access.${addressForAttr}.local or err
else err;
${if has'Local then "local" else null} = forSystem.access.${addressForAttr}.local or err;
${if has'Int then "int" else null} = forSystem.access.${addressForAttr}.int or err;
# TODO: tail
}.${network} or err;
in {
inherit (systemAccess) hostnameForNetwork address4ForNetwork address6ForNetwork;
addressForNetwork = systemAccess.${addressForAttr};
systemFor = hostName:
if hostName == config.networking.hostName
then systemConfig
@ -52,6 +76,21 @@
if hostName == config.networking.hostName
then config
else systemAccess.nixosForOrNull hostName;
getAddressFor = mkGetAddressFor addressForAttr;
getAddress4For = mkGetAddressFor "address4ForNetwork";
getAddress6For = mkGetAddressFor "address6ForNetwork";
getHostnameFor = hostName: network: let
forSystem = access.systemFor hostName;
err = throw "no ${network} interface found between ${config.networking.hostName} and ${hostName}";
in {
lan =
if hasInt then forSystem.access.hostnameForNetwork.int or forSystem.access.hostnameForNetwork.local or err
else if hasLocal then forSystem.access.hostnameForNetwork.local or err
else err;
${if has'Local then "local" else null} = forSystem.access.hostnameForNetwork.local or err;
${if has'Int then "int" else null} = forSystem.access.hostnameForNetwork.int or err;
${if has'Tail then "tail" else null} = forSystem.access.hostnameForNetwork.tail or err;
}.${network} or err;
};
};
networking.tempAddresses = mkIf cfg.global.enable (
@ -77,22 +116,71 @@ in {
type = attrsOf str;
default = {};
};
address4ForNetwork = mkOption {
type = attrsOf str;
default = {};
};
address6ForNetwork = mkOption {
type = attrsOf str;
default = {};
};
};
config = {
modules = [
nixosModule
];
access = {
hostnameForNetwork = {
local = mkOptionDefault "${cfg.hostName}.local.${cfg.domain}";
tail = mkIf cfg.tailscale.enable (mkOptionDefault "${cfg.hostName}.tail.${cfg.domain}");
global = mkIf cfg.global.enable (mkOptionDefault "${cfg.hostName}.${cfg.domain}");
access = let
local'interface = config.proxmox.network.local.interface;
int'interface = config.proxmox.network.internal.interface;
hasInt4 = hasInt && int'interface.address4 != null;
hasInt6 = hasInt && int'interface.address6 != null;
hasLocal4 = hasLocal && local'interface.local.address4 or null != null;
hasLocal6 = hasLocal && local'interface.local.address6 or null != null;
in {
hostnameForNetwork = let
int = "${cfg.hostName}.int.${cfg.domain}";
local = "${cfg.hostName}.local.${cfg.domain}";
tail = "${cfg.hostName}.tail.${cfg.domain}";
global = "${cfg.hostName}.${cfg.domain}";
in {
lan = mkMerge [
(mkIf hasInt (mkDefault int))
(mkOptionDefault local)
];
int = mkIf hasInt (mkOptionDefault int);
local = mkOptionDefault local;
tail = mkIf hasTail (mkOptionDefault tail);
global = mkIf cfg.global.enable (mkOptionDefault global);
};
address4ForNetwork = let
int = removeSuffix "/24" int'interface.address4;
local = removeSuffix "/24" local'interface.local.address4;
in {
lan = mkMerge [
(mkIf hasInt4 (mkDefault int))
(mkIf hasLocal4 (mkOptionDefault local))
];
int = mkIf hasInt4 (mkOptionDefault int);
local = mkIf hasLocal4 (mkOptionDefault local);
# TODO: tail
};
address6ForNetwork = let
int = removeSuffix "/64" int'interface.address6;
local = removeSuffix "/64" local'interface.local.address6;
in {
lan = mkMerge [
(mkIf hasInt6 (mkDefault int))
(mkIf hasLocal6 (mkOptionDefault local))
];
int = mkIf hasInt6 (mkOptionDefault int);
local = mkIf hasLocal6 (mkOptionDefault local);
# TODO: tail
};
};
_module.args.access = {
inherit (cfg) hostnameForNetwork;
inherit (cfg) hostnameForNetwork address4ForNetwork address6ForNetwork;
systemFor = hostName: systems.${hostName}.config;
systemForOrNull = hostName: systems.${hostName}.config or null;
nixosFor = hostName: nixosConfigurations.${hostName}.config or (access.systemFor hostName).built.config;

View file

@ -3,7 +3,7 @@
inherit (lib.options) mkOption mkEnableOption;
inherit (lib.modules) mkIf mkMerge mkOptionDefault mkOverride;
inherit (lib.attrsets) attrValues;
inherit (lib.lists) elem findSingle;
inherit (lib.lists) elem findSingle findFirst;
inherit (lib.strings) hasPrefix removePrefix replaceStrings;
inherit (lib.trivial) mapNullable;
mkAlmostOptionDefault = mkOverride 1250;
@ -52,7 +52,7 @@
};
mdns = {
enable = mkEnableOption "mDNS" // {
default = system.proxmox.node.name == "reisen" && config.id == "net0";
default = config.local.enable && config.id == "net0";
};
};
slaac = {
@ -63,6 +63,18 @@
internal = {
enable = mkEnableOption "internal network interface";
};
local = {
enable = mkOption {
type = bool;
default = system.proxmox.node.name == "reisen" && config.id == "net0" && config.bridge == "vmbr0";
};
address4 = mkOption {
type = nullOr str;
};
address6 = mkOption {
type = nullOr str;
};
};
networkd = {
enable = mkEnableOption "systemd.network" // {
default = true;
@ -73,7 +85,17 @@
};
};
config = let
hasAddr4 = ! elem config.address4 [ null "dhcp" ];
hasAddr6 = ! elem config.address6 [ null "dhcp" "auto" ];
conf = {
local = mkIf config.local.enable {
address4 = mkOptionDefault (if hasAddr4 then config.address4 else null);
address6 = mkOptionDefault (
if config.address6 == "auto" && config.slaac.postfix != null then "fd0a::${config.slaac.postfix}"
else if hasAddr6 then config.address6
else null
);
};
name = mkMerge [
(mkIf (hasPrefix "net" config.id && system.proxmox.container.enable) (mkOptionDefault ("eth" + removePrefix "net" config.id)))
# VMs have names like `ens18` for net0...
@ -146,10 +168,16 @@ in {
type = nullOr unspecified;
};
};
local = {
interface = mkOption {
type = nullOr unspecified;
};
};
};
config.proxmox.network = {
internal = {
interface = mkOptionDefault (findSingle (interface: interface.internal.enable) null (throw "expected only one internal network interface") (attrValues cfg.interfaces));
};
local.interface = mkOptionDefault (findFirst (interface: interface.local.enable) null (attrValues cfg.interfaces));
};
}