forked from mirrors_public/oddlama_nix-config
feat(wireguard): associate external peers to the specific defining node
This commit is contained in:
parent
d522a46f1d
commit
925d3856e0
6 changed files with 103 additions and 47 deletions
|
@ -20,4 +20,6 @@
|
||||||
dhcpV6Config.RouteMetric = 40;
|
dhcpV6Config.RouteMetric = 40;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
extra.wireguard.vms.address = ["10.0.0.10/32"];
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,4 +17,16 @@
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
extra.wireguard.vms = {
|
||||||
|
server = {
|
||||||
|
enable = true;
|
||||||
|
port = 51822;
|
||||||
|
openFirewall = true;
|
||||||
|
};
|
||||||
|
address = ["10.0.0.2/24"];
|
||||||
|
externalPeers = {
|
||||||
|
zack1 = ["10.0.0.90/32"];
|
||||||
|
};
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,7 +37,7 @@
|
||||||
|
|
||||||
configForNetwork = wgName: wg: let
|
configForNetwork = wgName: wg: let
|
||||||
inherit
|
inherit
|
||||||
(extraLib.wireguard wgName)
|
(extraLib.wireguard wgName nodes)
|
||||||
allPeers
|
allPeers
|
||||||
peerPresharedKeyPath
|
peerPresharedKeyPath
|
||||||
peerPresharedKeySecret
|
peerPresharedKeySecret
|
||||||
|
@ -46,7 +46,7 @@
|
||||||
peerPublicKeyPath
|
peerPublicKeyPath
|
||||||
;
|
;
|
||||||
|
|
||||||
otherPeers = filterAttrs (n: _: n != nodeName) (allPeers nodes);
|
otherPeers = filterAttrs (n: _: n != nodeName) allPeers;
|
||||||
in {
|
in {
|
||||||
secrets =
|
secrets =
|
||||||
concatAttrs (map (other: {
|
concatAttrs (map (other: {
|
||||||
|
@ -130,8 +130,12 @@ in {
|
||||||
default = {};
|
default = {};
|
||||||
example = {my-android-phone = ["10.0.0.97/32"];};
|
example = {my-android-phone = ["10.0.0.97/32"];};
|
||||||
description = mdDoc ''
|
description = mdDoc ''
|
||||||
Allows defining extra set of external peers that should be added to the configuration.
|
Allows defining an extra set of peers that should be added to this wireguard network,
|
||||||
For each external peers you can define one or multiple allowed ips.
|
but will not be managed by this flake. (e.g. phones)
|
||||||
|
|
||||||
|
These external peers will only know this node as a peer, which will forward
|
||||||
|
their traffic to other members of the network if required. This requires
|
||||||
|
this node to act as a server.
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -144,17 +148,17 @@ in {
|
||||||
in {
|
in {
|
||||||
assertions = concatMap (wgName: let
|
assertions = concatMap (wgName: let
|
||||||
inherit
|
inherit
|
||||||
(extraLib.wireguard wgName)
|
(extraLib.wireguard wgName nodes)
|
||||||
externalPeerNamesRaw
|
externalPeerNamesRaw
|
||||||
usedAddresses
|
usedAddresses
|
||||||
associatedNodes
|
associatedNodes
|
||||||
;
|
;
|
||||||
|
|
||||||
duplicatePeers = duplicates (externalPeerNamesRaw nodes);
|
duplicatePeers = duplicates externalPeerNamesRaw;
|
||||||
duplicateAddrs = duplicates (map (x: head (splitString "/" x)) (usedAddresses nodes));
|
duplicateAddrs = duplicates (map (x: head (splitString "/" x)) usedAddresses);
|
||||||
in [
|
in [
|
||||||
{
|
{
|
||||||
assertion = any (n: nodes.${n}.config.extra.wireguard.${wgName}.server.enable) (associatedNodes nodes);
|
assertion = any (n: nodes.${n}.config.extra.wireguard.${wgName}.server.enable) associatedNodes;
|
||||||
message = "Wireguard network '${wgName}': At least one node must be a server.";
|
message = "Wireguard network '${wgName}': At least one node must be a server.";
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
|
@ -165,6 +169,10 @@ in {
|
||||||
assertion = duplicateAddrs == [];
|
assertion = duplicateAddrs == [];
|
||||||
message = "Wireguard network '${wgName}': Addresses used multiple times: ${concatStringsSep ", " duplicateAddrs}";
|
message = "Wireguard network '${wgName}': Addresses used multiple times: ${concatStringsSep ", " duplicateAddrs}";
|
||||||
}
|
}
|
||||||
|
# TODO externalPeers != [] -> server.listen
|
||||||
|
# TODO externalPeers != [] -> ip forwarding
|
||||||
|
# TODO psks only between all nodes and each node-externalpeer pair
|
||||||
|
# TODO no overlapping allowed ip range? 0.0.0.0 would be ok to overlap though
|
||||||
]) (attrNames cfg);
|
]) (attrNames cfg);
|
||||||
|
|
||||||
networking.firewall.allowedUDPPorts = mkIf (cfg.server.enable && cfg.server.openFirewall) [cfg.server.port];
|
networking.firewall.allowedUDPPorts = mkIf (cfg.server.enable && cfg.server.openFirewall) [cfg.server.port];
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
concatStringsSep
|
concatStringsSep
|
||||||
escapeShellArg
|
escapeShellArg
|
||||||
filter
|
filter
|
||||||
|
optionalString
|
||||||
removeSuffix
|
removeSuffix
|
||||||
substring
|
substring
|
||||||
unique
|
unique
|
||||||
|
@ -33,17 +34,19 @@
|
||||||
|
|
||||||
generateNetworkKeys = wgName: let
|
generateNetworkKeys = wgName: let
|
||||||
inherit
|
inherit
|
||||||
(extraLib.wireguard wgName)
|
(extraLib.wireguard wgName self.nodes)
|
||||||
allPeers
|
allPeers
|
||||||
associatedNodes
|
associatedNodes
|
||||||
|
associatedServerNodes
|
||||||
|
associatedClientNodes
|
||||||
|
externalPeersForNode
|
||||||
peerPresharedKeyFile
|
peerPresharedKeyFile
|
||||||
peerPrivateKeyFile
|
peerPrivateKeyFile
|
||||||
peerPublicKeyFile
|
peerPublicKeyFile
|
||||||
|
sortedPeers
|
||||||
;
|
;
|
||||||
|
|
||||||
nodesWithNet = associatedNodes self.nodes;
|
# Every peer needs a private and public key.
|
||||||
peers = attrNames (allPeers self.nodes);
|
|
||||||
|
|
||||||
generatePeerKeys = peerName: let
|
generatePeerKeys = peerName: let
|
||||||
keyBasename = escapeShellArg ("./" + removeSuffix ".pub" (peerPublicKeyFile peerName));
|
keyBasename = escapeShellArg ("./" + removeSuffix ".pub" (peerPublicKeyFile peerName));
|
||||||
pubkeyFile = escapeShellArg ("./" + peerPublicKeyFile peerName);
|
pubkeyFile = escapeShellArg ("./" + peerPublicKeyFile peerName);
|
||||||
|
@ -59,22 +62,35 @@
|
||||||
fi
|
fi
|
||||||
'';
|
'';
|
||||||
|
|
||||||
generatePeerPsks = nodePeerName:
|
# Generates the psk for peer1 and peer2.
|
||||||
map (peerName: let
|
generatePeerPsk = {
|
||||||
pskFile = escapeShellArg ("./" + peerPresharedKeyFile nodePeerName peerName);
|
peer1,
|
||||||
in ''
|
peer2,
|
||||||
if [[ ! -e ${pskFile} ]]; then
|
}: let
|
||||||
mkdir -p $(dirname ${pskFile})
|
pskFile = escapeShellArg ("./" + peerPresharedKeyFile peer1 peer2);
|
||||||
echo "Generating [33m"${pskFile}"[m"
|
in ''
|
||||||
psk=$(${pkgs.wireguard-tools}/bin/wg genpsk)
|
if [[ ! -e ${pskFile} ]]; then
|
||||||
${pkgs.rage}/bin/rage -e ${masterIdentityArgs} ${extraEncryptionPubkeys} <<< "$psk" > ${pskFile} \
|
mkdir -p $(dirname ${pskFile})
|
||||||
|| { echo "[1;31merror:[m Failed to encrypt wireguard psk for peers ${nodePeerName} and ${peerName} on network ${wgName}!" >&2; exit 1; }
|
echo "Generating [33m"${pskFile}"[m"
|
||||||
fi
|
psk=$(${pkgs.wireguard-tools}/bin/wg genpsk)
|
||||||
'') (filter (x: x != nodePeerName) peers);
|
${pkgs.rage}/bin/rage -e ${masterIdentityArgs} ${extraEncryptionPubkeys} <<< "$psk" > ${pskFile} \
|
||||||
|
|| { echo "[1;31merror:[m Failed to encrypt wireguard psk for peers ${peer1} and ${peer2} on network ${wgName}!" >&2; exit 1; }
|
||||||
|
fi
|
||||||
|
'';
|
||||||
|
|
||||||
|
# This generates all psks for each combination of peers given.
|
||||||
|
# xs is a list of peers and fys a function that generates a list of peers
|
||||||
|
# for any given x.
|
||||||
|
psksForPeerCombinations = xs: fys: map generatePeerPsk (unique (concatMap (x: map (sortedPeers x) (fys x)) xs));
|
||||||
in
|
in
|
||||||
["echo ==== ${wgName} ===="]
|
["echo ==== ${wgName} ===="]
|
||||||
++ map generatePeerKeys peers
|
++ map generatePeerKeys (attrNames allPeers)
|
||||||
++ concatMap generatePeerPsks nodesWithNet;
|
# All server-nodes need a psk for each other, but not reflexive.
|
||||||
|
++ psksForPeerCombinations associatedServerNodes (n: filter (x: x != n) associatedServerNodes)
|
||||||
|
# Each server-node need a psk for all client nodes
|
||||||
|
++ psksForPeerCombinations associatedServerNodes (_: associatedClientNodes)
|
||||||
|
# Each server-node need a psk for all their external peers
|
||||||
|
++ psksForPeerCombinations associatedServerNodes (n: attrNames (externalPeersForNode n));
|
||||||
in
|
in
|
||||||
pkgs.writeShellScript "generate-wireguard-keys" ''
|
pkgs.writeShellScript "generate-wireguard-keys" ''
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
|
|
||||||
externalPeersForNet = wgName:
|
externalPeersForNet = wgName:
|
||||||
map (peer: {inherit wgName peer;})
|
map (peer: {inherit wgName peer;})
|
||||||
(attrNames ((extraLib.wireguard wgName).externalPeers self.nodes));
|
(attrNames (extraLib.wireguard wgName self.nodes).allExternalPeers);
|
||||||
allExternalPeers = concatMap externalPeersForNet wireguardNetworks;
|
allExternalPeers = concatMap externalPeersForNet wireguardNetworks;
|
||||||
in
|
in
|
||||||
# TODO generate "classic" config and run qrencode
|
# TODO generate "classic" config and run qrencode
|
||||||
|
|
58
nix/lib.nix
58
nix/lib.nix
|
@ -11,6 +11,7 @@
|
||||||
mapAttrs'
|
mapAttrs'
|
||||||
mergeAttrs
|
mergeAttrs
|
||||||
nameValuePair
|
nameValuePair
|
||||||
|
partition
|
||||||
unique
|
unique
|
||||||
;
|
;
|
||||||
in rec {
|
in rec {
|
||||||
|
@ -33,8 +34,8 @@ in rec {
|
||||||
concatAttrs = foldl' mergeAttrs {};
|
concatAttrs = foldl' mergeAttrs {};
|
||||||
|
|
||||||
# Wireguard related functions that are reused in several files of this flake
|
# Wireguard related functions that are reused in several files of this flake
|
||||||
wireguard = wgName: rec {
|
wireguard = wgName: nodes: rec {
|
||||||
_sortedPeers = peerA: peerB:
|
sortedPeers = peerA: peerB:
|
||||||
if peerA < peerB
|
if peerA < peerB
|
||||||
then {
|
then {
|
||||||
peer1 = peerA;
|
peer1 = peerA;
|
||||||
|
@ -53,32 +54,49 @@ in rec {
|
||||||
peerPrivateKeySecret = peerName: "wireguard-${wgName}-priv-${peerName}";
|
peerPrivateKeySecret = peerName: "wireguard-${wgName}-priv-${peerName}";
|
||||||
|
|
||||||
peerPresharedKeyFile = peerA: peerB: let
|
peerPresharedKeyFile = peerA: peerB: let
|
||||||
inherit (_sortedPeers peerA peerB) peer1 peer2;
|
inherit (sortedPeers peerA peerB) peer1 peer2;
|
||||||
in "secrets/wireguard/${wgName}/psks/${peer1}+${peer2}.age";
|
in "secrets/wireguard/${wgName}/psks/${peer1}+${peer2}.age";
|
||||||
peerPresharedKeyPath = peerA: peerB: "${../.}/" + peerPresharedKeyFile peerA peerB;
|
peerPresharedKeyPath = peerA: peerB: "${../.}/" + peerPresharedKeyFile peerA peerB;
|
||||||
peerPresharedKeySecret = peerA: peerB: let
|
peerPresharedKeySecret = peerA: peerB: let
|
||||||
inherit (_sortedPeers peerA peerB) peer1 peer2;
|
inherit (sortedPeers peerA peerB) peer1 peer2;
|
||||||
in "wireguard-${wgName}-psks-${peer1}+${peer2}";
|
in "wireguard-${wgName}-psks-${peer1}+${peer2}";
|
||||||
|
|
||||||
# All nodes that are part of this network
|
# All nodes that are part of this network
|
||||||
associatedNodes = nodes: filter (n: builtins.hasAttr wgName nodes.${n}.config.extra.wireguard) (attrNames nodes);
|
associatedNodes =
|
||||||
nodePeers = nodes: genAttrs (associatedNodes nodes) (n: nodes.${n}.config.extra.wireguard.${wgName}.address);
|
filter
|
||||||
|
(n: builtins.hasAttr wgName nodes.${n}.config.extra.wireguard)
|
||||||
|
(attrNames nodes);
|
||||||
|
|
||||||
|
# Partition nodes by whether they are servers
|
||||||
|
_associatedNodes_isServerPartition =
|
||||||
|
partition
|
||||||
|
(n: nodes.${n}.config.extra.wireguard.${wgName}.server.enable)
|
||||||
|
associatedNodes;
|
||||||
|
|
||||||
|
associatedServerNodes = _associatedNodes_isServerPartition.right;
|
||||||
|
associatedClientNodes = _associatedNodes_isServerPartition.wrong;
|
||||||
|
|
||||||
|
# Maps all nodes that are part of this network to their addresses
|
||||||
|
nodePeers = genAttrs associatedNodes (n: nodes.${n}.config.extra.wireguard.${wgName}.address);
|
||||||
|
|
||||||
|
# Only peers that are defined as externalPeers on the given node.
|
||||||
|
# Prepends "external-" to their name.
|
||||||
|
externalPeersForNode = node:
|
||||||
|
mapAttrs' (p: nameValuePair "external-${p}") nodes.${node}.config.extra.wireguard.${wgName}.externalPeers;
|
||||||
|
|
||||||
# All peers that are defined as externalPeers on any node.
|
# All peers that are defined as externalPeers on any node.
|
||||||
# Prepends "external-" to their name.
|
# Prepends "external-" to their name.
|
||||||
externalPeers = nodes:
|
allExternalPeers = concatAttrs (map externalPeersForNode associatedNodes);
|
||||||
concatAttrs (
|
|
||||||
map (n: mapAttrs' (extPeerName: nameValuePair "external-${extPeerName}") nodes.${n}.config.extra.wireguard.${wgName}.externalPeers)
|
|
||||||
(associatedNodes nodes)
|
|
||||||
);
|
|
||||||
# Concatenation of all external peer names names without any transformations.
|
|
||||||
externalPeerNamesRaw = nodes: concatMap (n: attrNames nodes.${n}.config.extra.wireguard.${wgName}.externalPeers) (associatedNodes nodes);
|
|
||||||
# A list of all occurring addresses.
|
|
||||||
usedAddresses = nodes: let
|
|
||||||
nodesWithNet = associatedNodes nodes;
|
|
||||||
in
|
|
||||||
concatMap (n: nodes.${n}.config.extra.wireguard.${wgName}.address) nodesWithNet
|
|
||||||
++ flatten (concatMap (n: attrValues nodes.${n}.config.extra.wireguard.${wgName}.externalPeers) nodesWithNet);
|
|
||||||
|
|
||||||
allPeers = nodes: nodePeers nodes // externalPeers nodes;
|
# All peers that are part of this network
|
||||||
|
allPeers = nodePeers // allExternalPeers;
|
||||||
|
|
||||||
|
# Concatenation of all external peer names names without any transformations.
|
||||||
|
externalPeerNamesRaw = concatMap (n: attrNames nodes.${n}.config.extra.wireguard.${wgName}.externalPeers) associatedNodes;
|
||||||
|
|
||||||
|
# A list of all occurring addresses.
|
||||||
|
usedAddresses =
|
||||||
|
concatMap (n: nodes.${n}.config.extra.wireguard.${wgName}.address) associatedNodes
|
||||||
|
++ flatten (concatMap (n: attrValues nodes.${n}.config.extra.wireguard.${wgName}.externalPeers) associatedNodes);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue