mirror of
https://github.com/Swarsel/.dotfiles.git
synced 2026-04-14 21:29:12 +02:00
296 lines
10 KiB
Nix
296 lines
10 KiB
Nix
{ self, lib, pkgs, config, confLib, nodes, globals, ... }:
|
|
let
|
|
inherit (confLib.gen {
|
|
name = "wireguard";
|
|
port = 52829;
|
|
user = "systemd-network";
|
|
group = "systemd-network";
|
|
}) servicePort serviceName serviceUser serviceGroup;
|
|
|
|
inherit (config.swarselsystems) sopsFile;
|
|
wgSopsFile = self + "/secrets/repo/wg.yaml";
|
|
|
|
cfg = config.swarselsystems.server.wireguard;
|
|
inherit (cfg) interfaces;
|
|
ifaceList = builtins.attrValues interfaces;
|
|
in
|
|
{
|
|
options = {
|
|
swarselmodules.server.${serviceName} =
|
|
lib.mkEnableOption "enable ${serviceName} settings";
|
|
|
|
swarselsystems.server.wireguard = {
|
|
interfaces =
|
|
let
|
|
topConfig = config;
|
|
in
|
|
lib.mkOption {
|
|
type = lib.types.attrsOf (lib.types.submodule ({ config, name, ... }: {
|
|
options = {
|
|
isServer = lib.mkEnableOption "set this interface as a wireguard server";
|
|
isClient = lib.mkEnableOption "set this interface as a wireguard client";
|
|
|
|
serverName = lib.mkOption {
|
|
type = lib.types.str;
|
|
default = if config.isServer then topConfig.node.name else "";
|
|
description = "Hostname of the WireGuard server this interface connects to (when isClient = true).";
|
|
};
|
|
|
|
serverNetConfigPrefix = lib.mkOption {
|
|
type = lib.types.str;
|
|
default =
|
|
let
|
|
serverCfg = nodes.${config.serverName}.config;
|
|
in
|
|
if serverCfg.swarselsystems.isCloud
|
|
then serverCfg.node.name
|
|
else "home";
|
|
readOnly = true;
|
|
description = "Prefix used to look up the server network in globals.networks.\"<prefix>-wg\".";
|
|
};
|
|
|
|
ifName = lib.mkOption {
|
|
type = lib.types.str;
|
|
default = name;
|
|
description = "Name of the WireGuard interface.";
|
|
};
|
|
|
|
port = lib.mkOption {
|
|
type = lib.types.int;
|
|
default = servicePort;
|
|
description = "Port of the WireGuard interface.";
|
|
};
|
|
|
|
peers = lib.mkOption {
|
|
type = lib.types.listOf lib.types.str;
|
|
default = lib.attrNames (lib.filterAttrs (name: _: name != topConfig.node.name) globals.networks."${config.serverNetConfigPrefix}-${config.ifName}".hosts);
|
|
description = "WireGuard peer config names of this wireguardinterface.";
|
|
};
|
|
};
|
|
}));
|
|
default = { };
|
|
description = "WireGuard interfaces defined on this host.";
|
|
};
|
|
};
|
|
};
|
|
|
|
config = lib.mkIf config.swarselmodules.server.${serviceName} {
|
|
|
|
assertions = lib.concatLists (
|
|
lib.flip lib.mapAttrsToList interfaces (
|
|
ifName: ifCfg:
|
|
let
|
|
assertionPrefix = "While evaluating the wireguard network ${ifName}:";
|
|
in
|
|
[
|
|
{
|
|
assertion = ifCfg.isServer || (ifCfg.isClient && ifCfg.serverName != "");
|
|
message = "${assertionPrefix}: This node must either be a server for the wireguard network or a client with serverName set.";
|
|
}
|
|
{
|
|
assertion = lib.stringLength ifName < 16;
|
|
message = "${assertionPrefix}: The specified linkName '${ifName}' is too long (must be max 15 characters).";
|
|
}
|
|
]
|
|
)
|
|
);
|
|
|
|
environment.systemPackages = with pkgs; [
|
|
wireguard-tools
|
|
];
|
|
|
|
sops.secrets =
|
|
lib.mkMerge (
|
|
[
|
|
{
|
|
wireguard-private-key = {
|
|
inherit sopsFile;
|
|
owner = serviceUser;
|
|
group = serviceGroup;
|
|
mode = "0600";
|
|
};
|
|
}
|
|
] ++ (map
|
|
(i:
|
|
let
|
|
clientSecrets =
|
|
lib.optionalAttrs i.isClient {
|
|
"wireguard-${i.serverName}-${config.node.name}-${i.ifName}-presharedKey" = {
|
|
sopsFile = wgSopsFile;
|
|
owner = serviceUser;
|
|
group = serviceGroup;
|
|
mode = "0600";
|
|
};
|
|
};
|
|
|
|
serverSecrets =
|
|
lib.optionalAttrs i.isServer (builtins.listToAttrs (map
|
|
(clientName: {
|
|
name = "wireguard-${config.node.name}-${clientName}-${i.ifName}-presharedKey";
|
|
value = {
|
|
sopsFile = wgSopsFile;
|
|
owner = serviceUser;
|
|
group = serviceGroup;
|
|
mode = "0600";
|
|
};
|
|
})
|
|
i.peers));
|
|
in
|
|
clientSecrets // serverSecrets
|
|
)
|
|
ifaceList)
|
|
);
|
|
|
|
networking.firewall = {
|
|
checkReversePath = lib.mkIf (lib.any (i: i.isClient) ifaceList) "loose";
|
|
allowedUDPPorts = lib.mkMerge (
|
|
lib.flip lib.mapAttrsToList interfaces (
|
|
_: ifCfg:
|
|
lib.optional ifCfg.isServer ifCfg.port
|
|
)
|
|
);
|
|
};
|
|
|
|
networking.nftables.firewall = {
|
|
zones = lib.mkMerge
|
|
(
|
|
lib.flip lib.mapAttrsToList interfaces (
|
|
ifName: ifCfg:
|
|
{
|
|
${ifName}.interfaces = [ ifName ];
|
|
}
|
|
// lib.listToAttrs (map
|
|
(peer:
|
|
let
|
|
peerNet = globals.networks."${ifCfg.serverNetConfigPrefix}-${ifName}".hosts.${peer};
|
|
in
|
|
lib.nameValuePair "${ifName}-node-${peer}" {
|
|
parent = ifName;
|
|
ipv4Addresses = lib.optional (peerNet.ipv4 != null) peerNet.ipv4;
|
|
ipv6Addresses = lib.optional (peerNet.ipv6 != null) peerNet.ipv6;
|
|
}
|
|
)
|
|
ifCfg.peers)
|
|
)
|
|
);
|
|
rules = lib.mkMerge (
|
|
lib.flip lib.mapAttrsToList interfaces (
|
|
ifName: ifCfg:
|
|
let
|
|
inherit (config.networking.nftables.firewall) localZoneName;
|
|
netCfg = globals.networks."${ifCfg.serverNetConfigPrefix}-${ifName}";
|
|
in
|
|
{
|
|
"${ifName}-to-${localZoneName}" = {
|
|
inherit (netCfg.firewallRuleForAll) allowedTCPPorts allowedUDPPorts allowedTCPPortRanges allowedUDPPortRanges;
|
|
from = [ ifName ];
|
|
to = [ localZoneName ];
|
|
ignoreEmptyRule = true;
|
|
};
|
|
}
|
|
// lib.listToAttrs (map
|
|
(peer:
|
|
lib.nameValuePair "${ifName}-node-${peer}-to-${localZoneName}" (
|
|
lib.mkIf (netCfg.hosts.${config.node.name}.firewallRuleForNode ? ${peer}) {
|
|
inherit (netCfg.hosts.${config.node.name}.firewallRuleForNode.${peer}) allowedTCPPorts allowedTCPPortRanges allowedUDPPorts allowedUDPPortRanges;
|
|
from = [ "${ifName}-node-${peer}" ];
|
|
to = [ localZoneName ];
|
|
ignoreEmptyRule = true;
|
|
}
|
|
)
|
|
)
|
|
ifCfg.peers)
|
|
)
|
|
);
|
|
};
|
|
|
|
systemd.network = {
|
|
enable = true;
|
|
|
|
networks = lib.mkMerge (map
|
|
(i:
|
|
let
|
|
inherit (i) ifName;
|
|
in
|
|
{
|
|
"50-${ifName}" = {
|
|
matchConfig.Name = ifName;
|
|
linkConfig = {
|
|
MTUBytes = 1408; # TODO: figure out where we lose those 12 bits (8 from pppoe maybe + ???)
|
|
};
|
|
|
|
address = [
|
|
globals.networks."${i.serverNetConfigPrefix}-${ifName}".hosts.${config.node.name}.cidrv4
|
|
globals.networks."${i.serverNetConfigPrefix}-${ifName}".hosts.${config.node.name}.cidrv6
|
|
];
|
|
};
|
|
})
|
|
ifaceList);
|
|
|
|
netdevs = lib.mkMerge (map
|
|
(i:
|
|
let
|
|
inherit (i) ifName;
|
|
in
|
|
{
|
|
"50-${ifName}" = {
|
|
netdevConfig = {
|
|
Kind = "wireguard";
|
|
Name = ifName;
|
|
};
|
|
|
|
wireguardConfig = {
|
|
ListenPort = lib.mkIf i.isServer servicePort;
|
|
|
|
PrivateKeyFile = config.sops.secrets.wireguard-private-key.path;
|
|
|
|
RouteTable = lib.mkIf i.isClient "main";
|
|
};
|
|
|
|
wireguardPeers =
|
|
lib.optionals i.isClient [
|
|
{
|
|
PublicKey =
|
|
builtins.readFile "${self}/secrets/public/wg/${i.serverName}.pub";
|
|
|
|
PresharedKeyFile =
|
|
config.sops.secrets."wireguard-${i.serverName}-${config.node.name}-${i.ifName}-presharedKey".path;
|
|
|
|
Endpoint =
|
|
"server.${i.serverName}.${globals.domains.main}:${toString servicePort}";
|
|
|
|
PersistentKeepalive = 25;
|
|
|
|
AllowedIPs =
|
|
let
|
|
wgNetwork = globals.networks."${i.serverNetConfigPrefix}-${i.ifName}";
|
|
in
|
|
(lib.optional (wgNetwork.cidrv4 != null) wgNetwork.cidrv4)
|
|
++ (lib.optional (wgNetwork.cidrv6 != null) wgNetwork.cidrv6);
|
|
}
|
|
]
|
|
++ lib.optionals i.isServer (map
|
|
(clientName: {
|
|
PublicKey =
|
|
builtins.readFile "${self}/secrets/public/wg/${clientName}.pub";
|
|
|
|
PresharedKeyFile =
|
|
config.sops.secrets."wireguard-${i.serverName}-${clientName}-${i.ifName}-presharedKey".path;
|
|
|
|
AllowedIPs =
|
|
let
|
|
clientInWgNetwork =
|
|
globals.networks."${i.serverNetConfigPrefix}-${i.ifName}".hosts.${clientName};
|
|
in
|
|
(lib.optional (clientInWgNetwork.ipv4 != null)
|
|
(lib.net.cidr.make 32 clientInWgNetwork.ipv4))
|
|
++ (lib.optional (clientInWgNetwork.ipv6 != null)
|
|
(lib.net.cidr.make 128 clientInWgNetwork.ipv6));
|
|
})
|
|
i.peers);
|
|
};
|
|
})
|
|
ifaceList);
|
|
};
|
|
};
|
|
}
|