{ pkgs, lib, config, ... }: with lib; let cfg = config.fudo.vpn; generate-pubkey-pkg = name: privkey: pkgs.runCommand "wireguard-${name}-pubkey" { WIREGUARD_PRIVATE_KEY = privkey; } '' mkdir $out PUBKEY=$(echo $WIREGUARD_PRIVATE_KEY | ${pkgs.wireguard-tools}/bin/wg pubkey) echo $PUBKEY > $out/pubkey.key ''; generate-client-config = privkey-file: server-pubkey: network: server-ip: listen-port: dns-servers: '' [Interface] Address = ${ip.networkMinIp network} PrivateKey = ${fileContents privkey-file} ListenPort = ${toString listen-port} DNS = ${concatStringsSep ", " dns-servers} [Peer] PublicKey = ${server-pubkey} Endpoint = ${server-ip}:${toString listen-port} AllowedIps = 0.0.0.0/0, ::/0 PersistentKeepalive = 25 ''; generate-peer-entry = peer-name: peer-privkey-path: peer-allowed-ips: let peer-pkg = generate-pubkey-pkg "client-${peer-name}" (fileContents peer-privkey-path); pubkey-path = "${peer-pkg}/pubkey.key"; in { publicKey = fileContents pubkey-path; allowedIPs = peer-allowed-ips; }; in { options.fudo.vpn = with types; { enable = mkEnableOption "Enable Fudo VPN"; network = mkOption { type = str; description = "Network range to assign this interface."; default = "10.100.0.0/16"; }; private-key-file = mkOption { type = str; description = "Path to the secret key (generated with wg [genkey/pubkey])."; example = "/path/to/secret.key"; }; listen-port = mkOption { type = port; description = "Port on which to listen for incoming connections."; default = 51820; }; dns-servers = mkOption { type = listOf str; description = "A list of dns servers to pass to clients."; default = ["1.1.1.1" "8.8.8.8"]; }; server-ip = mkOption { type = str; description = "IP of this WireGuard server."; }; peers = mkOption { type = attrsOf str; description = "A map of peers to shared private keys."; default = {}; example = { peer0 = "/path/to/priv.key"; }; }; }; config = mkIf cfg.enable { environment.etc = let peer-data = imap1 (i: peer:{ name = peer.name; privkey-path = peer.privkey-path; network-range = let base = ip.intToIpv4 ((ip.ipv4ToInt (ip.getNetworkBase cfg.network)) + (i * 256)); in "${base}/24"; }) (mapAttrsToList (name: privkey-path: { name = name; privkey-path = privkey-path; }) cfg.peers); server-pubkey-pkg = generate-pubkey-pkg "server-pubkey" (fileContents cfg.private-key-file); server-pubkey = fileContents "${server-pubkey-pkg}/pubkey.key"; in listToAttrs (map (peer: nameValuePair "wireguard/clients/${peer.name}.conf" { mode = "0400"; user = "root"; group = "root"; text = generate-client-config peer.privkey-path server-pubkey peer.network-range cfg.server-ip cfg.listen-port cfg.dns-servers; }) peer-data); networking.wireguard = { enable = true; interfaces.wgtun0 = { generatePrivateKeyFile = false; ips = [ cfg.network ]; listenPort = cfg.listen-port; peers = mapAttrsToList (name: private-key: generate-peer-entry name private-key ["0.0.0.0/0" "::/0"]) cfg.peers; privateKeyFile = cfg.private-key-file; }; }; }; }