From 6cffccd75c900f2ad39b2652fb23f54a51bfc6a3 Mon Sep 17 00:00:00 2001 From: oddlama Date: Wed, 12 Apr 2023 01:07:55 +0200 Subject: [PATCH] feat: add wireguard key and psk generator app --- hosts/ward/net.nix | 4 +- modules/wireguard.nix | 10 ++-- nix/apps/generate-wireguard-keys.nix | 75 +++++++++++++++++++++++++++- nix/apps/show-wireguard-qr.nix | 2 +- 4 files changed, 82 insertions(+), 9 deletions(-) diff --git a/hosts/ward/net.nix b/hosts/ward/net.nix index 8978b57..f07f6e4 100644 --- a/hosts/ward/net.nix +++ b/hosts/ward/net.nix @@ -28,7 +28,9 @@ listenPort = 51822; openFirewall = true; externalPeers = { - test = ["10.0.0.91/32"]; + test1 = ["10.0.0.91/32"]; + test2 = ["10.0.0.92/32"]; + test3 = ["10.0.0.93/32"]; }; }; } diff --git a/modules/wireguard.nix b/modules/wireguard.nix index 2277a18..c423ed0 100644 --- a/modules/wireguard.nix +++ b/modules/wireguard.nix @@ -49,18 +49,18 @@ }; configForNetwork = wgName: wg: let - peerPublicKey = peerName: builtins.readFile (../secrets/wireguard + "/${wgName}/${peerName}.pub"); - peerPrivateKeyFile = peerName: ../secrets/wireguard + "/${wgName}/${peerName}.priv.age"; - peerPrivateKeySecret = peerName: "wireguard-${wgName}-${peerName}.priv"; + peerPublicKey = peerName: builtins.readFile (../secrets/wireguard + "/${wgName}/keys/${peerName}.pub"); + peerPrivateKeyFile = peerName: ../secrets/wireguard + "/${wgName}/keys/${peerName}.age"; + peerPrivateKeySecret = peerName: "wireguard-${wgName}-priv-${peerName}"; peerPresharedKeyFile = peerA: peerB: let inherit (sortedPeers peerA peerB) peer1 peer2; in - ../secrets/wireguard + "/${wgName}/${peer1}-${peer2}.psk.age"; + ../secrets/wireguard + "/${wgName}/psks/${peer1}-${peer2}.age"; peerPresharedKeySecret = peerA: peerB: let inherit (sortedPeers peerA peerB) peer1 peer2; - in "wireguard-${wgName}-${peer1}-${peer2}.psk"; + in "wireguard-${wgName}-psks-${peer1}-${peer2}"; # All peers that are other nodes nodesWithThisNetwork = filter (n: builtins.hasAttr wgName nodes.${n}.config.extra.wireguard.networks) (attrNames nodes); diff --git a/nix/apps/generate-wireguard-keys.nix b/nix/apps/generate-wireguard-keys.nix index 2b4573e..8342cc1 100644 --- a/nix/apps/generate-wireguard-keys.nix +++ b/nix/apps/generate-wireguard-keys.nix @@ -3,9 +3,80 @@ pkgs, ... }: let - inherit (pkgs.lib) escapeShellArg; + inherit + (pkgs.lib) + attrNames + concatMap + concatMapStrings + concatStringsSep + escapeShellArg + filter + substring + unique + ; + + isAbsolutePath = x: substring 0 1 x == "/"; + masterIdentityArgs = concatMapStrings (x: ''-i ${escapeShellArg x} '') self.secrets.masterIdentities; + extraEncryptionPubkeys = + concatMapStrings ( + x: + if isAbsolutePath x + then ''-R ${escapeShellArg x} '' + else ''-r ${escapeShellArg x} '' + ) + self.secrets.extraEncryptionPubkeys; + + sortedPeers = peerA: peerB: + if peerA < peerB + then { + peer1 = peerA; + peer2 = peerB; + } + else { + peer1 = peerB; + peer2 = peerA; + }; + + nodeNames = attrNames self.nodes; + nodesWithNet = wgName: filter (n: builtins.hasAttr wgName self.nodes.${n}.config.extra.wireguard.networks) nodeNames; + wireguardNetworks = unique (concatMap (n: attrNames self.nodes.${n}.config.extra.wireguard.networks) nodeNames); + externalPeersForNet = wgName: concatMap (n: attrNames self.nodes.${n}.config.extra.wireguard.networks.${wgName}.externalPeers) (nodesWithNet wgName); + + externalPeers = wgName: concatMap (n: attrNames self.nodes.${n}.config.extra.wireguard.networks.${wgName}.externalPeers) (nodesWithNet wgName); + peers = wgName: nodesWithNet wgName ++ externalPeers wgName; + + peerKeyBasename = wgName: peerName: "./secrets/wireguard/${wgName}/keys/${peerName}"; + generatePeerKeys = wgName: peerName: let + keyBasename = peerKeyBasename wgName peerName; + privkeyFile = escapeShellArg "${keyBasename}.age"; + pubkeyFile = escapeShellArg "${keyBasename}.pub"; + in '' + if [[ ! -e ${privkeyFile} ]] || [[ ! -e ${pubkeyFile} ]]; then + mkdir -p $(dirname ${privkeyFile}) + echo "Generating "${escapeShellArg keyBasename}".{age,pub}" + privkey=$(${pkgs.wireguard-tools}/bin/wg genkey) + echo "$privkey" | ${pkgs.wireguard-tools}/bin/wg pubkey > ${pubkeyFile} + ${pkgs.rage}/bin/rage -e ${masterIdentityArgs} ${extraEncryptionPubkeys} <<< "$privkey" > ${pubkeyFile} \ + || { echo "error: Failed to encrypt wireguard private key for peer ${peerName} on network ${wgName}!" >&2; exit 1; } + fi + ''; + + generatePeerPsks = wgName: nodePeerName: + concatStringsSep "\n" (map (peerName: let + inherit (sortedPeers nodePeerName peerName) peer1 peer2; + pskFile = "./secrets/wireguard/${wgName}/psks/${peer1}-${peer2}.age"; + in '' + if [[ ! -e ${pskFile} ]]; then + mkdir -p $(dirname ${pskFile}) + echo "Generating "${pskFile}"" + psk=$(${pkgs.wireguard-tools}/bin/wg genpsk) + ${pkgs.rage}/bin/rage -e ${masterIdentityArgs} ${extraEncryptionPubkeys} <<< "$psk" > ${pskFile} \ + || { echo "error: Failed to encrypt wireguard psk for peers ${peer1} and ${peer2} on network ${wgName}!" >&2; exit 1; } + fi + '') (filter (x: x != nodePeerName) (peers wgName))); in pkgs.writeShellScript "generate-wireguard-keys" '' set -euo pipefail - echo TODO + ${concatStringsSep "\n" (concatMap (wgName: map (generatePeerKeys wgName) (peers wgName)) wireguardNetworks)} + ${concatStringsSep "\n" (concatMap (wgName: map (generatePeerPsks wgName) (nodesWithNet wgName)) wireguardNetworks)} '' diff --git a/nix/apps/show-wireguard-qr.nix b/nix/apps/show-wireguard-qr.nix index 8db21df..756bbb9 100644 --- a/nix/apps/show-wireguard-qr.nix +++ b/nix/apps/show-wireguard-qr.nix @@ -22,5 +22,5 @@ in # TODO generate "classic" config and run qrencode pkgs.writeShellScript "show-wireguard-qr" '' set -euo pipefail - echo ${concatStringsSep " " (map (x: "${x.net}.${x.peer}") externalPeers)} | fzf + echo ${escapeShellArg (concatStringsSep "\n" (map (x: "${x.net}.${x.peer}") externalPeers))} | fzf ''