.dotfiles/modules/nixos/server/wireguard.nix
2025-12-05 02:25:45 +01:00

176 lines
6.8 KiB
Nix

{ self, lib, pkgs, config, confLib, nodes, globals, ... }:
let
wgInterface = "wg0";
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";
inherit (config.swarselsystems.server.wireguard) peers isClient isServer serverName serverNetConfigPrefix ifName;
in
{
options = {
swarselmodules.server.${serviceName} = lib.mkEnableOption "enable ${serviceName} settings";
swarselsystems.server.wireguard = {
isServer = lib.mkEnableOption "set this as a wireguard server";
isClient = lib.mkEnableOption "set this as a wireguard client";
serverName = lib.mkOption {
type = lib.types.str;
default = "";
};
serverNetConfigPrefix = lib.mkOption {
type = lib.types.str;
default = "${if nodes.${serverName}.config.swarselsystems.isCloud then nodes.${serverName}.config.node.name else "home"}";
readOnly = true;
};
ifName = lib.mkOption {
type = lib.types.str;
default = wgInterface;
};
peers = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ ];
description = "Wireguard peer config names";
};
};
};
config = lib.mkIf config.swarselmodules.server.${serviceName} {
environment.systemPackages = with pkgs; [
wireguard-tools
];
sops = {
secrets = {
wireguard-private-key = { inherit sopsFile; owner = serviceUser; group = serviceGroup; mode = "0600"; };
# create this secret only if this is a simple client with only one peer (the server)
"wireguard-${serverName}-${config.node.name}-presharedKey" = lib.mkIf (isClient && peers == [ ]) { sopsFile = wgSopsFile; owner = serviceUser; group = serviceGroup; mode = "0600"; };
}
# create these secrets only if this host has multiple peers
// lib.optionalAttrs (peers != [ ]) (builtins.listToAttrs (map
(clientName: {
name = "wireguard-${config.node.name}-${clientName}-presharedKey";
value = { sopsFile = wgSopsFile; owner = serviceUser; group = serviceGroup; mode = "0600"; };
})
peers));
};
networking = {
firewall.checkReversePath = lib.mkIf isClient "loose";
firewall.allowedUDPPorts = [ servicePort ];
# nat = lib.mkIf (config.swarselsystems.isCloud && isServer) {
# enable = true;
# enableIPv6 = true;
# externalInterface = "enp0s6";
# internalInterfaces = [ ifName ];
# };
# interfaces.${ifName}.mtu = 1280; # the default (1420) is not enough!
};
systemd.network = {
enable = true;
networks."50-${ifName}" = {
matchConfig.Name = ifName;
linkConfig = {
MTUBytes = 1408; # TODO: figure out where we lose those 12 bits (8 from pppoe maybe + ???)
};
# networkConfig = lib.mkIf (config.swarselsystems.isCloud && isServer) {
# IPv4Forwarding = true;
# IPv6Forwarding = true;
# };
address =
if isServer then [
globals.networks."${config.swarselsystems.server.netConfigPrefix}-wg".hosts.${config.node.name}.cidrv4
globals.networks."${config.swarselsystems.server.netConfigPrefix}-wg".hosts.${config.node.name}.cidrv6
] else [
globals.networks."${serverNetConfigPrefix}-wg".hosts.${config.node.name}.cidrv4
globals.networks."${serverNetConfigPrefix}-wg".hosts.${config.node.name}.cidrv6
];
};
netdevs."50-${ifName}" = {
netdevConfig = {
Kind = "wireguard";
Name = ifName;
};
wireguardConfig = {
ListenPort = lib.mkIf isServer servicePort;
# ensure file is readable by `systemd-network` user
PrivateKeyFile = config.sops.secrets.wireguard-private-key.path;
# To automatically create routes for everything in AllowedIPs,
# add RouteTable=main
RouteTable = lib.mkIf isClient "main";
# FirewallMark marks all packets send and received by wg0
# with the number 42, which can be used to define policy rules on these packets.
# FirewallMark = 42;
};
wireguardPeers = lib.optionals isClient [
{
PublicKey = builtins.readFile "${self}/secrets/public/wg/${serverName}.pub";
PresharedKeyFile = config.sops.secrets."wireguard-${serverName}-${config.node.name}-presharedKey".path;
Endpoint = "server.${serverName}.${globals.domains.main}:${toString servicePort}";
# Access to the whole network is routed through our entry node.
# PersistentKeepalive = 25;
AllowedIPs =
let
wgNetwork = globals.networks."${serverNetConfigPrefix}-wg";
in
(lib.optional (wgNetwork.cidrv4 != null) wgNetwork.cidrv4)
++ (lib.optional (wgNetwork.cidrv6 != null) wgNetwork.cidrv6);
}
] ++ lib.optionals isServer (map
(clientName: {
PublicKey = builtins.readFile "${self}/secrets/public/wg/${clientName}.pub";
PresharedKeyFile = config.sops.secrets."wireguard-${config.node.name}-${clientName}-presharedKey".path;
# PersistentKeepalive = 25;
AllowedIPs =
let
clientInWgNetwork = globals.networks."${config.swarselsystems.server.netConfigPrefix}-wg".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));
})
peers);
};
};
# networking = {
# wireguard = {
# enable = true;
# interfaces = {
# wg1 = {
# privateKeyFile = config.sops.secrets.wireguard-private-key.path;
# ips = [ "192.168.178.201/24" ];
# peers = [
# {
# publicKey = "PmeFInoEJcKx+7Kva4dNnjOEnJ8lbudSf1cbdo/tzgw=";
# presharedKeyFile = config.sops.secrets.wireguard-home-preshared-key.path;
# name = "moonside";
# persistentKeepalive = 25;
# # endpoint = "${config.repo.secrets.common.ipv4}:51820";
# endpoint = "${config.repo.secrets.common.wireguardEndpoint}";
# # allowedIPs = [
# # "192.168.3.0/24"
# # "192.168.1.0/24"
# # ];
# allowedIPs = [
# "192.168.178.0/24"
# ];
# }
# ];
# };
# };
# };
# };
};
}