infrastructure/nixos-modules/meta/network.nix

234 lines
7.4 KiB
Nix

{
config,
lib,
pkgs,
options,
...
}:
with lib;
with types;
let
cfg = config.qois.meta.network;
mkStr =
description:
(mkOption {
type = str;
inherit description;
});
mkOptStr =
description:
(mkOption {
type = nullOr str;
default = null;
inherit description;
});
mkNetworkIdOpts =
v:
assert v == 4 || v == 6;
submodule {
options = {
id = mkOption {
type = types.str;
description = ''
IPv${toString v} ID
'';
};
prefixLength = mkOption {
type = types.addCheck types.int (n: n >= 0 && n <= (if v == 4 then 32 else 128));
description = ''
Subnet mask of the ip, specified as the number of
bits in the prefix (<literal>${if v == 4 then "24" else "64"}</literal>).
'';
};
gateway = mkOption {
default = null;
type = nullOr str;
description = ''
Upstream Gateway IP
'';
};
nameservers = mkOption {
default = null;
type = nullOr (listOf str);
description = "Nameserver IP";
};
};
};
mkFqdn =
host: domain:
mkOption {
type = str;
default = "${config.qois.meta.hosts.${host}.hostName}.${domain}";
description = ''
The fully qualified domain name (FYDN) of this host inside of this specific
network. Defaults to the host attribute key and net domain.
'';
};
in
{
options.qois.meta.network.physical = mkOption {
description = "Physical network configuration";
type = attrsOf (
submodule (
{ name, ... }:
let
networkName = name;
in
{
options = {
v4 = mkOption { type = (mkNetworkIdOpts 4); };
v6 = mkOption { type = nullOr (mkNetworkIdOpts 6); };
domain = mkStr "Network DNS Domain suffix";
hosts = mkOption {
type = attrsOf (
submodule (
{ name, ... }:
let
host = name;
in
{
options = {
v4 = mkOption { type = submodule { options.ip = mkStr "The V4 host IP address"; }; };
v6 = mkOption {
default = null;
type = nullOr (submodule {
options.ip = mkStr "The V6 host IP address";
});
};
fqdn = mkFqdn host cfg.physical.${networkName}.domain;
};
}
)
);
};
};
}
)
);
default = { };
};
options.qois.meta.network.virtual = mkOption {
description = "Virtual network configuration";
type = types.attrsOf (
types.submodule (
{ name, ... }:
let
networkName = name;
in
{
options = {
v4 = mkOption { type = (mkNetworkIdOpts 4); };
v6 = mkOption {
default = null;
type = nullOr (mkNetworkIdOpts 6);
};
domain = mkStr "Network DNS Domain suffix";
hosts = mkOption {
type = attrsOf (
submodule (
{ name, ... }:
let
host = name;
in
{
options = {
v4 = mkOption { type = submodule { options.ip = mkStr "The V4 host IP address"; }; };
v6 = mkOption {
default = null;
type = nullOr (submodule {
options.ip = mkStr "The V6 host IP address";
});
};
# Taken from https://github.com/NixOS/nixpkgs/blob/nixos-21.11/nixos/modules/services/networking/wireguard.nix:
publicKey = mkOption {
example = "xTIBA5rboUvnH4htodjb6e697QjLERt1NAB4mZqp8Dg=";
type = str;
description = "The base64 public key of the peer.";
};
persistentKeepalive = mkOption {
default = null;
type = nullOr int;
example = 25;
description = ''
This is optional and is by default off, because most
users will not need it. It represents, in seconds, between 1 and 65535
inclusive, how often to send an authenticated empty packet to the peer,
for the purpose of keeping a stateful firewall or NAT mapping valid
persistently. For example, if the interface very rarely sends traffic,
but it might at anytime receive traffic from a peer, and it is behind
NAT, the interface might benefit from having a persistent keepalive
interval of 25 seconds; however, most users will not need this.'';
};
# Endpoint Configuration:
endpoint = mkOption {
description = ''
FQDN and port of this vpn-endpoint. This option indicates this host is a VPN
server.
'';
default = null;
type = nullOr (submodule {
options = {
fqdn = mkFqdn host cfg.virtual.${networkName}.domain;
port = mkOption {
type = types.addCheck types.int (n: n > 0 && n < 65536);
description = ''
The port on which the wireguard endpoint receives packages.
'';
};
};
});
};
};
}
)
);
};
};
}
)
);
default = { };
};
config = {
programs.ssh.knownHosts =
let
# hostname -> single network cfg attr -> ["known host's names"]
getHostNamesFromNetwork =
hostname: network:
if network.hosts ? ${hostname} && network.hosts.${hostname} != null then
let
hostCfg = network.hosts.${hostname};
in
[
"${hostname}.${network.domain}"
hostCfg.v4.ip
]
++ (if hostCfg.v6 != null then [ hostCfg.v6.ip ] else [ ])
else
[ ];
# hostname -> attr of network defs -> ["known host's names"]
getHostNamesForNetworks =
hostname: networks: lib.flatten (map (getHostNamesFromNetwork hostname) (lib.attrValues networks));
# hostname -> ["known host's names"]
getHostNames =
hostname:
(getHostNamesForNetworks hostname cfg.virtual) ++ (getHostNamesForNetworks hostname cfg.physical);
hostsWithPublicKey = lib.filterAttrs (
hostName: hostConfig: hostConfig.sshKey != null
) config.qois.meta.hosts;
in
mapAttrs (name: hostCfg: { extraHostNames = getHostNames name; }) hostsWithPublicKey;
};
}