network/nix/lib/config/options.nix

318 lines
8.9 KiB
Nix

{ config, options, lib, ... }:
with lib;
let
dhcpOpts = {
start = mkOption {
description = "First IP address in pool";
type = types.str;
};
end = mkOption {
description = "Last IP address in pool";
type = types.str;
};
time = mkOption {
description = "Renew time in seconds";
type = types.int;
};
max-time = mkOption {
description = "Max renew time in seconds";
type = types.int;
};
server = mkOption {
description = "Container that runs the DHCP server";
type = types.str;
};
router = mkOption {
description = "Gateway";
type = types.str;
};
fixed-hosts = mkOption {
type = with types; attrsOf str;
default = {};
};
};
netOpts = { name, ... }: {
options = {
vlan = mkOption {
description = "VLAN tag number";
type = types.int;
};
subnet4 = mkOption {
description = "v.w.x.y/z";
type = with types; nullOr str;
default = null;
};
subnet4Net = mkOption {
type = with types; nullOr types.str;
default =
let
inherit (config.site.net.${name}) subnet4;
s = lib.splitString "/" subnet4;
in
if subnet4 != null && builtins.length s == 2
then builtins.head s
else null;
};
subnet4Len = mkOption {
type = with types; nullOr types.int;
default =
let
inherit (config.site.net.${name}) subnet4;
s = lib.splitString "/" subnet4;
in
if subnet4 != null && builtins.length s == 2
then lib.toInt (elemAt s 1)
else null;
};
subnets6 = mkOption {
description = "IPv6 subnets w/o prefixlen (always 64)";
type = with types; attrsOf str;
default = {};
};
hosts4 = mkOption {
description = "Attribute set of hostnames to IPv4 addresses";
type = with types; attrsOf str;
default = {};
};
hosts6 = mkOption {
description = "Attribute set of contexts to attribute sets of hostnames to IPv4 addresses";
type = with types; attrsOf (attrsOf str);
default = {};
};
ospf = {
secret = mkOption {
type = with types; nullOr str;
default = null;
};
};
dhcp = mkOption {
type = with types; nullOr (submodule { options = dhcpOpts; });
default = null;
};
domainName = mkOption {
description = "Domain name option";
type = types.str;
default = "${name}.zentralwerk.org";
};
};
};
upstreamOpts = {
upBandwidth = mkOption {
type = with types; nullOr int;
};
};
interfaceOpts = { name, ... }: {
options = {
hwaddr = mkOption {
type = with types; nullOr str;
default = null;
description = "Static MAC address";
};
type = mkOption {
type = types.enum [ "phys" "veth" ];
description = ''
veth: Virtual ethernet to be attached to a bridge.
phys: (Physical) interface from a server moved into the
container. Do not use with VLAN interfaces because they
won't be moved back after lxc-stop.
'';
};
gw4 = mkOption {
type = with types; nullOr str;
default = null;
description = "IPv4 gateway";
};
gw6 = mkOption {
type = with types; nullOr str;
default = null;
description = "IPv6 gateway";
};
upstream = mkOption {
type = with types; nullOr (submodule { options = upstreamOpts; });
default = null;
description = "Upstream interface configuration";
};
};
};
hostOpts = { name, ... }: {
options = {
prebuilt = mkOption {
type = types.bool;
default = false;
description = ''
Include the container system in the server's `build-container` script.
'';
};
role = mkOption {
type = types.enum [ "ap" "switch" "server" "container" "client" ];
default = "client";
};
model = mkOption {
type = types.str;
default = {
ap = "unknown";
switch = "unknown";
server = "pc";
container = "lxc";
client = "any";
}."${config.site.hosts.${name}.role}";
};
password = mkOption {
type = with types; nullOr str;
default = null;
};
location = mkOption {
type = with types; nullOr str;
default = null;
};
interfaces = mkOption {
default = {};
type = with types; attrsOf (submodule interfaceOpts);
description = "Network interfaces";
};
isRouter = mkOption {
type = types.bool;
# isRouter = Part of the core network?
default =
config.site.hosts.${name}.interfaces ? core &&
config.site.net.core.hosts4 ? ${name};
description = "Should this host route?";
};
firewall.enable = mkOption {
type = types.bool;
default = false;
description = "Enable firewall to disallow incoming connections from core";
};
forwardPorts = mkOption {
type = with types; listOf (submodule { options = {
proto = mkOption {
type = types.enum [ "tcp" "udp" ];
};
sourcePort = mkOption {
type = types.int;
};
destination = mkOption {
type = types.str;
};
}; });
default = [];
};
ospf.stubNets4 = mkOption {
type = with types; listOf str;
default = [];
description = "Additional IPv4 networks to announce";
};
ospf.stubNets6 = mkOption {
type = with types; listOf str;
default = [];
description = "Additional IPv6 networks to announce";
};
ospf.allowedUpstreams = mkOption {
type = with types; listOf str;
default = [];
description = "Accept default routes from these OSPF routers, in order of preference";
};
wireguard = mkOption {
default = {};
type = with types; attrsOf (submodule (
{ name, ... }: {
options = {
endpoint = mkOption {
type = str;
};
publicKey = mkOption {
type = str;
};
privateKey = mkOption {
type = str;
};
addresses = mkOption {
type = listOf str;
};
upBandwidth = mkOption {
type = with types; nullOr int;
};
};
}
));
};
bgp = mkOption {
default = null;
type = with types; nullOr (submodule {
options = bgpOpts;
});
};
services.dnscache.enable = mkOption {
type = types.bool;
default = false;
};
};
};
bgpOpts = {
asn = mkOption {
type = types.int;
};
peers = mkOption {
type = with types; attrsOf (submodule ({ name, ... }: {
options = {
asn = mkOption {
type = types.int;
};
};
}));
default = {};
};
};
in
{
options.site = {
net = mkOption {
default = {};
type = with types; attrsOf (submodule netOpts);
};
hosts = mkOption {
default = {};
type = with types; attrsOf (submodule hostOpts);
};
};
config.warnings =
let
findCollisions = getter: xs:
(builtins.foldl' ({ known, dup }: k:
let
ks = builtins.toString k;
in
if known ? ${ks}
then { inherit known; dup = dup ++ [ks]; }
else { known = known // { ${ks} = true; }; inherit dup; }
) {
known = {}; dup = [];
} (
concatMap getter (builtins.attrValues xs)
)).dup;
reportCollisions = name: getter: xs:
map (k: "Duplicate ${name}: ${k}") (findCollisions getter xs);
ospfUpstreamXorGw =
builtins.concatMap (hostName:
let
hostConf = config.site.hosts.${hostName};
gwNets = builtins.filter (netName:
hostConf.interfaces.${netName}.gw4 != null
) (builtins.attrNames hostConf.interfaces);
in if gwNets != [] && hostConf.ospf.allowedUpstreams != []
then [ ''
Host ${hostName} has gateway on ${builtins.head gwNets} but accepts default routes from OSPF
'' ]
else []
) (builtins.attrNames config.site.hosts);
in
(reportCollisions "VLAN tag" (x: [x.vlan]) config.site.net) ++
(reportCollisions "IPv4 subnet" (x: if x.subnet4 == null then [] else [x.subnet4]) config.site.net) ++
(reportCollisions "IPv6 subnet" (x: builtins.attrValues x.subnets6) config.site.net) ++
ospfUpstreamXorGw;
}