mirror of
https://github.com/oddlama/nix-config.git
synced 2025-10-11 07:10:39 +02:00
refactor(wireguard): extract cross-host aggregation functions into extraLib
This commit is contained in:
parent
6cffccd75c
commit
d522a46f1d
6 changed files with 239 additions and 195 deletions
|
@ -37,6 +37,7 @@ This is my personal nix config.
|
||||||
- `dev-shell.nix` Environment setup for `nix develop` for using this flake
|
- `dev-shell.nix` Environment setup for `nix develop` for using this flake
|
||||||
- `extra-builtins.nix` Extra builtins via nix-plugins to support transparent repository-wide secrets
|
- `extra-builtins.nix` Extra builtins via nix-plugins to support transparent repository-wide secrets
|
||||||
- `hosts.nix` Wrapper that extracts all defined hosts from `hosts/`
|
- `hosts.nix` Wrapper that extracts all defined hosts from `hosts/`
|
||||||
|
- `lib.nix` Commonly used functionality or helpers that weren't available in the standard library
|
||||||
- `rage-decrypt.sh` Auxiliary script for repository-wide secrets
|
- `rage-decrypt.sh` Auxiliary script for repository-wide secrets
|
||||||
- `secrets.nix` Helper to access repository-wide secrets, used by colmena.nix
|
- `secrets.nix` Helper to access repository-wide secrets, used by colmena.nix
|
||||||
- `secrets/` Global secrets and age identities
|
- `secrets/` Global secrets and age identities
|
||||||
|
@ -57,6 +58,12 @@ This is my personal nix config.
|
||||||
- fill net.nix
|
- fill net.nix
|
||||||
- todo: hostid (move to nodeSecrets)
|
- todo: hostid (move to nodeSecrets)
|
||||||
- generate-initrd-keys
|
- generate-initrd-keys
|
||||||
|
- generate-wireguard-keys
|
||||||
|
|
||||||
|
#### Show QR for external wireguard client
|
||||||
|
|
||||||
|
nix run show-wireguard-qr
|
||||||
|
then select the host in the fzf menu
|
||||||
|
|
||||||
#### New secret
|
#### New secret
|
||||||
|
|
||||||
|
|
|
@ -22,11 +22,13 @@
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
extra.wireguard.networks.vms = {
|
extra.wireguard.vms = {
|
||||||
address = ["10.0.0.1/24"];
|
server = {
|
||||||
listen = true;
|
enable = true;
|
||||||
listenPort = 51822;
|
port = 51822;
|
||||||
openFirewall = true;
|
openFirewall = true;
|
||||||
|
};
|
||||||
|
address = ["10.0.0.1/24"];
|
||||||
externalPeers = {
|
externalPeers = {
|
||||||
test1 = ["10.0.0.91/32"];
|
test1 = ["10.0.0.91/32"];
|
||||||
test2 = ["10.0.0.92/32"];
|
test2 = ["10.0.0.92/32"];
|
||||||
|
|
|
@ -11,88 +11,50 @@
|
||||||
(lib)
|
(lib)
|
||||||
any
|
any
|
||||||
attrNames
|
attrNames
|
||||||
attrValues
|
|
||||||
concatMap
|
concatMap
|
||||||
concatMapStrings
|
concatMapStrings
|
||||||
concatStringsSep
|
concatStringsSep
|
||||||
filter
|
filterAttrs
|
||||||
flatten
|
|
||||||
foldl'
|
|
||||||
genAttrs
|
|
||||||
head
|
head
|
||||||
mapAttrs'
|
|
||||||
mapAttrsToList
|
mapAttrsToList
|
||||||
mdDoc
|
mdDoc
|
||||||
mergeAttrs
|
mergeAttrs
|
||||||
mkIf
|
mkIf
|
||||||
mkOption
|
mkOption
|
||||||
nameValuePair
|
mkEnableOption
|
||||||
optional
|
optionalAttrs
|
||||||
recursiveUpdate
|
|
||||||
splitString
|
splitString
|
||||||
types
|
types
|
||||||
;
|
;
|
||||||
|
|
||||||
inherit (extraLib) duplicates;
|
inherit
|
||||||
|
(extraLib)
|
||||||
|
concatAttrs
|
||||||
|
duplicates
|
||||||
|
;
|
||||||
|
|
||||||
cfg = config.extra.wireguard;
|
cfg = config.extra.wireguard;
|
||||||
|
|
||||||
sortedPeers = peerA: peerB:
|
|
||||||
if peerA < peerB
|
|
||||||
then {
|
|
||||||
peer1 = peerA;
|
|
||||||
peer2 = peerB;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
peer1 = peerB;
|
|
||||||
peer2 = peerA;
|
|
||||||
};
|
|
||||||
|
|
||||||
configForNetwork = wgName: wg: let
|
configForNetwork = wgName: wg: let
|
||||||
peerPublicKey = peerName: builtins.readFile (../secrets/wireguard + "/${wgName}/keys/${peerName}.pub");
|
inherit
|
||||||
peerPrivateKeyFile = peerName: ../secrets/wireguard + "/${wgName}/keys/${peerName}.age";
|
(extraLib.wireguard wgName)
|
||||||
peerPrivateKeySecret = peerName: "wireguard-${wgName}-priv-${peerName}";
|
allPeers
|
||||||
|
peerPresharedKeyPath
|
||||||
|
peerPresharedKeySecret
|
||||||
|
peerPrivateKeyPath
|
||||||
|
peerPrivateKeySecret
|
||||||
|
peerPublicKeyPath
|
||||||
|
;
|
||||||
|
|
||||||
peerPresharedKeyFile = peerA: peerB: let
|
otherPeers = filterAttrs (n: _: n != nodeName) (allPeers nodes);
|
||||||
inherit (sortedPeers peerA peerB) peer1 peer2;
|
|
||||||
in
|
|
||||||
../secrets/wireguard + "/${wgName}/psks/${peer1}-${peer2}.age";
|
|
||||||
|
|
||||||
peerPresharedKeySecret = peerA: peerB: let
|
|
||||||
inherit (sortedPeers peerA peerB) peer1 peer2;
|
|
||||||
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);
|
|
||||||
nodePeers = genAttrs (filter (n: n != nodeName) nodesWithThisNetwork) (n: nodes.${n}.config.extra.wireguard.networks.${wgName}.address);
|
|
||||||
# All peers that are defined as externalPeers on any node. Also prepends "external-" to their name.
|
|
||||||
externalPeers = foldl' recursiveUpdate {} (
|
|
||||||
map (n: mapAttrs' (extPeerName: nameValuePair "external-${extPeerName}") nodes.${n}.config.extra.wireguard.networks.${wgName}.externalPeers)
|
|
||||||
nodesWithThisNetwork
|
|
||||||
);
|
|
||||||
|
|
||||||
peers = nodePeers // externalPeers;
|
|
||||||
|
|
||||||
peerDefinition = peerName: peerAllowedIPs: {
|
|
||||||
wireguardPeerConfig =
|
|
||||||
{
|
|
||||||
PublicKey = peerPublicKey wgName peerName;
|
|
||||||
PresharedKeyFile = config.rekey.secrets.${peerPresharedKeySecret wgName nodeName peerName}.path;
|
|
||||||
AllowedIPs = peerAllowedIPs;
|
|
||||||
}
|
|
||||||
// optional wg.listen {
|
|
||||||
PersistentKeepalive = 25;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
in {
|
in {
|
||||||
inherit nodesWithThisNetwork wgName;
|
|
||||||
|
|
||||||
secrets =
|
secrets =
|
||||||
foldl' mergeAttrs {
|
concatAttrs (map (other: {
|
||||||
${peerPrivateKeySecret nodeName}.file = peerPrivateKeyFile nodeName;
|
${peerPresharedKeySecret nodeName other}.file = peerPresharedKeyPath nodeName other;
|
||||||
} (map (peerName: {
|
}) (attrNames otherPeers))
|
||||||
${peerPresharedKeySecret nodeName peerName}.file = peerPresharedKeyFile nodeName peerName;
|
// {
|
||||||
}) (attrNames peers));
|
${peerPrivateKeySecret nodeName}.file = peerPrivateKeyPath nodeName;
|
||||||
|
};
|
||||||
|
|
||||||
netdevs."${wg.priority}-${wgName}" = {
|
netdevs."${wg.priority}-${wgName}" = {
|
||||||
netdevConfig = {
|
netdevConfig = {
|
||||||
|
@ -100,11 +62,26 @@
|
||||||
Name = "${wgName}";
|
Name = "${wgName}";
|
||||||
Description = "Wireguard network ${wgName}";
|
Description = "Wireguard network ${wgName}";
|
||||||
};
|
};
|
||||||
wireguardConfig = {
|
wireguardConfig =
|
||||||
|
{
|
||||||
PrivateKeyFile = config.rekey.secrets.${peerPrivateKeySecret nodeName}.path;
|
PrivateKeyFile = config.rekey.secrets.${peerPrivateKeySecret nodeName}.path;
|
||||||
ListenPort = wg.listenPort;
|
}
|
||||||
|
// optionalAttrs wg.server.enable {
|
||||||
|
ListenPort = wg.server.port;
|
||||||
};
|
};
|
||||||
wireguardPeers = mapAttrsToList peerDefinition peers;
|
wireguardPeers =
|
||||||
|
mapAttrsToList (peerName: peerAllowedIPs: {
|
||||||
|
wireguardPeerConfig =
|
||||||
|
{
|
||||||
|
PublicKey = builtins.readFile (peerPublicKeyPath peerName);
|
||||||
|
PresharedKeyFile = config.rekey.secrets.${peerPresharedKeySecret nodeName peerName}.path;
|
||||||
|
AllowedIPs = peerAllowedIPs;
|
||||||
|
}
|
||||||
|
// optionalAttrs wg.server.enable {
|
||||||
|
PersistentKeepalive = 25;
|
||||||
|
};
|
||||||
|
})
|
||||||
|
otherPeers;
|
||||||
};
|
};
|
||||||
|
|
||||||
networks."${wg.priority}-${wgName}" = {
|
networks."${wg.priority}-${wgName}" = {
|
||||||
|
@ -113,45 +90,39 @@
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
in {
|
in {
|
||||||
options = {
|
options.extra.wireguard = mkOption {
|
||||||
extra.wireguard.networks = mkOption {
|
|
||||||
default = {};
|
default = {};
|
||||||
description = "Configures wireguard networks via systemd-networkd.";
|
description = "Configures wireguard networks via systemd-networkd.";
|
||||||
type = types.attrsOf (types.submodule {
|
type = types.attrsOf (types.submodule {
|
||||||
options = {
|
options = {
|
||||||
address = mkOption {
|
server = {
|
||||||
type = types.listOf types.str;
|
enable = mkEnableOption (mdDoc "wireguard server");
|
||||||
description = mdDoc ''
|
|
||||||
The addresses to configure for this interface. Will automatically be added
|
|
||||||
as this peer's allowed addresses to all other peers.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
listen = mkOption {
|
port = mkOption {
|
||||||
type = types.bool;
|
|
||||||
default = false;
|
|
||||||
description = mdDoc ''
|
|
||||||
Enables listening for incoming wireguard connections.
|
|
||||||
This also causes all other peers to include this as an endpoint in their configuration.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
listenPort = mkOption {
|
|
||||||
default = 51820;
|
default = 51820;
|
||||||
type = types.int;
|
type = types.port;
|
||||||
description = mdDoc "The port to listen on, if {option}`listen` is `true`.";
|
description = mdDoc "The port to listen on, if {option}`listen` is `true`.";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
openFirewall = mkOption {
|
||||||
|
default = false;
|
||||||
|
type = types.bool;
|
||||||
|
description = mdDoc "Whether to open the firewall for the specified `listenPort`, if {option}`listen` is `true`.";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
priority = mkOption {
|
priority = mkOption {
|
||||||
default = "20";
|
default = "20";
|
||||||
type = types.str;
|
type = types.str;
|
||||||
description = mdDoc "The order priority used when creating systemd netdev and network files.";
|
description = mdDoc "The order priority used when creating systemd netdev and network files.";
|
||||||
};
|
};
|
||||||
|
|
||||||
openFirewall = mkOption {
|
address = mkOption {
|
||||||
default = false;
|
type = types.listOf types.str;
|
||||||
type = types.bool;
|
description = mdDoc ''
|
||||||
description = mdDoc "Whether to open the firewall for the specified `listenPort`, if {option}`listen` is `true`.";
|
The addresses to configure for this interface. Will automatically be added
|
||||||
|
as this peer's allowed addresses to all other peers.
|
||||||
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
externalPeers = mkOption {
|
externalPeers = mkOption {
|
||||||
|
@ -166,25 +137,25 @@ in {
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
};
|
|
||||||
|
|
||||||
config = mkIf (cfg.networks != {}) (let
|
config = mkIf (cfg != {}) (let
|
||||||
networkCfgs = mapAttrsToList configForNetwork cfg.networks;
|
networkCfgs = mapAttrsToList configForNetwork cfg;
|
||||||
collectAttrs = x: foldl' mergeAttrs {} (map (y: y.${x}) networkCfgs);
|
collectAllNetworkAttrs = x: concatAttrs (map (y: y.${x}) networkCfgs);
|
||||||
in {
|
in {
|
||||||
assertions =
|
assertions = concatMap (wgName: let
|
||||||
concatMap (netCfg: let
|
inherit
|
||||||
inherit (netCfg) wgName;
|
(extraLib.wireguard wgName)
|
||||||
externalPeers = concatMap (n: attrNames nodes.${n}.config.extra.wireguard.networks.${wgName}.externalPeers) netCfg.nodesWithThisNetwork;
|
externalPeerNamesRaw
|
||||||
duplicatePeers = duplicates externalPeers;
|
usedAddresses
|
||||||
usedAddresses =
|
associatedNodes
|
||||||
concatMap (n: nodes.${n}.config.extra.wireguard.networks.${wgName}.address) netCfg.nodesWithThisNetwork
|
;
|
||||||
++ flatten (concatMap (n: attrValues nodes.${n}.config.extra.wireguard.networks.${wgName}.externalPeers) netCfg.nodesWithThisNetwork);
|
|
||||||
duplicateAddrs = duplicates (map (x: head (splitString "/" x)) usedAddresses);
|
duplicatePeers = duplicates (externalPeerNamesRaw nodes);
|
||||||
|
duplicateAddrs = duplicates (map (x: head (splitString "/" x)) (usedAddresses nodes));
|
||||||
in [
|
in [
|
||||||
{
|
{
|
||||||
assertion = any (n: nodes.${n}.config.extra.wireguard.networks.${wgName}.listen) netCfg.nodesWithThisNetwork;
|
assertion = any (n: nodes.${n}.config.extra.wireguard.${wgName}.server.enable) (associatedNodes nodes);
|
||||||
message = "Wireguard network '${wgName}': At least one node must be listening.";
|
message = "Wireguard network '${wgName}': At least one node must be a server.";
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
assertion = duplicatePeers == [];
|
assertion = duplicatePeers == [];
|
||||||
|
@ -194,14 +165,13 @@ 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}";
|
||||||
}
|
}
|
||||||
])
|
]) (attrNames cfg);
|
||||||
networkCfgs;
|
|
||||||
|
|
||||||
networking.firewall.allowedUDPPorts = mkIf (cfg.listen && cfg.openFirewall) [cfg.listenPort];
|
networking.firewall.allowedUDPPorts = mkIf (cfg.server.enable && cfg.server.openFirewall) [cfg.server.port];
|
||||||
rekey.secrets = collectAttrs "secrets";
|
rekey.secrets = collectAllNetworkAttrs "secrets";
|
||||||
systemd.network = {
|
systemd.network = {
|
||||||
netdevs = collectAttrs "netdevs";
|
netdevs = collectAllNetworkAttrs "netdevs";
|
||||||
networks = collectAttrs "networks";
|
networks = collectAllNetworkAttrs "networks";
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
self,
|
self,
|
||||||
pkgs,
|
pkgs,
|
||||||
...
|
...
|
||||||
}: let
|
} @ inputs: let
|
||||||
inherit
|
inherit
|
||||||
(pkgs.lib)
|
(pkgs.lib)
|
||||||
attrNames
|
attrNames
|
||||||
|
@ -11,10 +11,12 @@
|
||||||
concatStringsSep
|
concatStringsSep
|
||||||
escapeShellArg
|
escapeShellArg
|
||||||
filter
|
filter
|
||||||
|
removeSuffix
|
||||||
substring
|
substring
|
||||||
unique
|
unique
|
||||||
;
|
;
|
||||||
|
|
||||||
|
extraLib = import ../lib.nix inputs;
|
||||||
isAbsolutePath = x: substring 0 1 x == "/";
|
isAbsolutePath = x: substring 0 1 x == "/";
|
||||||
masterIdentityArgs = concatMapStrings (x: ''-i ${escapeShellArg x} '') self.secrets.masterIdentities;
|
masterIdentityArgs = concatMapStrings (x: ''-i ${escapeShellArg x} '') self.secrets.masterIdentities;
|
||||||
extraEncryptionPubkeys =
|
extraEncryptionPubkeys =
|
||||||
|
@ -26,57 +28,55 @@
|
||||||
)
|
)
|
||||||
self.secrets.extraEncryptionPubkeys;
|
self.secrets.extraEncryptionPubkeys;
|
||||||
|
|
||||||
sortedPeers = peerA: peerB:
|
|
||||||
if peerA < peerB
|
|
||||||
then {
|
|
||||||
peer1 = peerA;
|
|
||||||
peer2 = peerB;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
peer1 = peerB;
|
|
||||||
peer2 = peerA;
|
|
||||||
};
|
|
||||||
|
|
||||||
nodeNames = attrNames self.nodes;
|
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) 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);
|
generateNetworkKeys = wgName: let
|
||||||
peers = wgName: nodesWithNet wgName ++ externalPeers wgName;
|
inherit
|
||||||
|
(extraLib.wireguard wgName)
|
||||||
|
allPeers
|
||||||
|
associatedNodes
|
||||||
|
peerPresharedKeyFile
|
||||||
|
peerPrivateKeyFile
|
||||||
|
peerPublicKeyFile
|
||||||
|
;
|
||||||
|
|
||||||
peerKeyBasename = wgName: peerName: "./secrets/wireguard/${wgName}/keys/${peerName}";
|
nodesWithNet = associatedNodes self.nodes;
|
||||||
generatePeerKeys = wgName: peerName: let
|
peers = attrNames (allPeers self.nodes);
|
||||||
keyBasename = peerKeyBasename wgName peerName;
|
|
||||||
privkeyFile = escapeShellArg "${keyBasename}.age";
|
generatePeerKeys = peerName: let
|
||||||
pubkeyFile = escapeShellArg "${keyBasename}.pub";
|
keyBasename = escapeShellArg ("./" + removeSuffix ".pub" (peerPublicKeyFile peerName));
|
||||||
|
pubkeyFile = escapeShellArg ("./" + peerPublicKeyFile peerName);
|
||||||
|
privkeyFile = escapeShellArg ("./" + peerPrivateKeyFile peerName);
|
||||||
in ''
|
in ''
|
||||||
if [[ ! -e ${privkeyFile} ]] || [[ ! -e ${pubkeyFile} ]]; then
|
if [[ ! -e ${privkeyFile} ]] || [[ ! -e ${pubkeyFile} ]]; then
|
||||||
mkdir -p $(dirname ${privkeyFile})
|
mkdir -p $(dirname ${privkeyFile})
|
||||||
echo "Generating [34m"${escapeShellArg keyBasename}".{[31mage[34m,[32mpub[34m}[m"
|
echo "Generating [34m"${keyBasename}".{[31mage[34m,[32mpub[34m}[m"
|
||||||
privkey=$(${pkgs.wireguard-tools}/bin/wg genkey)
|
privkey=$(${pkgs.wireguard-tools}/bin/wg genkey)
|
||||||
echo "$privkey" | ${pkgs.wireguard-tools}/bin/wg pubkey > ${pubkeyFile}
|
echo "$privkey" | ${pkgs.wireguard-tools}/bin/wg pubkey > ${pubkeyFile}
|
||||||
${pkgs.rage}/bin/rage -e ${masterIdentityArgs} ${extraEncryptionPubkeys} <<< "$privkey" > ${pubkeyFile} \
|
${pkgs.rage}/bin/rage -e ${masterIdentityArgs} ${extraEncryptionPubkeys} <<< "$privkey" > ${privkeyFile} \
|
||||||
|| { echo "[1;31merror:[m Failed to encrypt wireguard private key for peer ${peerName} on network ${wgName}!" >&2; exit 1; }
|
|| { echo "[1;31merror:[m Failed to encrypt wireguard private key for peer ${peerName} on network ${wgName}!" >&2; exit 1; }
|
||||||
fi
|
fi
|
||||||
'';
|
'';
|
||||||
|
|
||||||
generatePeerPsks = wgName: nodePeerName:
|
generatePeerPsks = nodePeerName:
|
||||||
concatStringsSep "\n" (map (peerName: let
|
map (peerName: let
|
||||||
inherit (sortedPeers nodePeerName peerName) peer1 peer2;
|
pskFile = escapeShellArg ("./" + peerPresharedKeyFile nodePeerName peerName);
|
||||||
pskFile = "./secrets/wireguard/${wgName}/psks/${peer1}-${peer2}.age";
|
|
||||||
in ''
|
in ''
|
||||||
if [[ ! -e ${pskFile} ]]; then
|
if [[ ! -e ${pskFile} ]]; then
|
||||||
mkdir -p $(dirname ${pskFile})
|
mkdir -p $(dirname ${pskFile})
|
||||||
echo "Generating [33m"${pskFile}"[m"
|
echo "Generating [33m"${pskFile}"[m"
|
||||||
psk=$(${pkgs.wireguard-tools}/bin/wg genpsk)
|
psk=$(${pkgs.wireguard-tools}/bin/wg genpsk)
|
||||||
${pkgs.rage}/bin/rage -e ${masterIdentityArgs} ${extraEncryptionPubkeys} <<< "$psk" > ${pskFile} \
|
${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; }
|
|| { echo "[1;31merror:[m Failed to encrypt wireguard psk for peers ${nodePeerName} and ${peerName} on network ${wgName}!" >&2; exit 1; }
|
||||||
fi
|
fi
|
||||||
'') (filter (x: x != nodePeerName) (peers wgName)));
|
'') (filter (x: x != nodePeerName) peers);
|
||||||
|
in
|
||||||
|
["echo ==== ${wgName} ===="]
|
||||||
|
++ map generatePeerKeys peers
|
||||||
|
++ concatMap generatePeerPsks nodesWithNet;
|
||||||
in
|
in
|
||||||
pkgs.writeShellScript "generate-wireguard-keys" ''
|
pkgs.writeShellScript "generate-wireguard-keys" ''
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
${concatStringsSep "\n" (concatMap (wgName: map (generatePeerKeys wgName) (peers wgName)) wireguardNetworks)}
|
${concatStringsSep "\n" (concatMap generateNetworkKeys wireguardNetworks)}
|
||||||
${concatStringsSep "\n" (concatMap (wgName: map (generatePeerPsks wgName) (nodesWithNet wgName)) wireguardNetworks)}
|
|
||||||
''
|
''
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
self,
|
self,
|
||||||
pkgs,
|
pkgs,
|
||||||
...
|
...
|
||||||
}: let
|
} @ inputs: let
|
||||||
inherit
|
inherit
|
||||||
(pkgs.lib)
|
(pkgs.lib)
|
||||||
attrNames
|
attrNames
|
||||||
|
@ -13,14 +13,18 @@
|
||||||
unique
|
unique
|
||||||
;
|
;
|
||||||
|
|
||||||
|
extraLib = import ../lib.nix inputs;
|
||||||
|
|
||||||
nodeNames = attrNames self.nodes;
|
nodeNames = attrNames self.nodes;
|
||||||
nodesWithNet = net: filter (n: builtins.hasAttr net self.nodes.${n}.config.extra.wireguard.networks) nodeNames;
|
wireguardNetworks = unique (concatMap (n: attrNames self.nodes.${n}.config.extra.wireguard) nodeNames);
|
||||||
wireguardNetworks = unique (concatMap (n: attrNames self.nodes.${n}.config.extra.wireguard.networks) nodeNames);
|
|
||||||
externalPeersForNet = net: concatMap (n: attrNames self.nodes.${n}.config.extra.wireguard.networks.${net}.externalPeers) (nodesWithNet net);
|
externalPeersForNet = wgName:
|
||||||
externalPeers = concatMap (net: map (peer: {inherit net peer;}) (externalPeersForNet net)) wireguardNetworks;
|
map (peer: {inherit wgName peer;})
|
||||||
|
(attrNames ((extraLib.wireguard wgName).externalPeers self.nodes));
|
||||||
|
allExternalPeers = concatMap externalPeersForNet wireguardNetworks;
|
||||||
in
|
in
|
||||||
# TODO generate "classic" config and run qrencode
|
# TODO generate "classic" config and run qrencode
|
||||||
pkgs.writeShellScript "show-wireguard-qr" ''
|
pkgs.writeShellScript "show-wireguard-qr" ''
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
echo ${escapeShellArg (concatStringsSep "\n" (map (x: "${x.net}.${x.peer}") externalPeers))} | fzf
|
echo ${escapeShellArg (concatStringsSep "\n" (map (x: "${x.wgName}.${x.peer}") allExternalPeers))} | ${pkgs.fzf}/bin/fzf
|
||||||
''
|
''
|
||||||
|
|
61
nix/lib.nix
61
nix/lib.nix
|
@ -1,8 +1,16 @@
|
||||||
{nixpkgs, ...}: let
|
{nixpkgs, ...}: let
|
||||||
inherit
|
inherit
|
||||||
(nixpkgs.lib)
|
(nixpkgs.lib)
|
||||||
|
attrNames
|
||||||
|
attrValues
|
||||||
|
concatMap
|
||||||
filter
|
filter
|
||||||
|
flatten
|
||||||
foldl'
|
foldl'
|
||||||
|
genAttrs
|
||||||
|
mapAttrs'
|
||||||
|
mergeAttrs
|
||||||
|
nameValuePair
|
||||||
unique
|
unique
|
||||||
;
|
;
|
||||||
in rec {
|
in rec {
|
||||||
|
@ -20,4 +28,57 @@ in rec {
|
||||||
occurrences = countOccurrences xs;
|
occurrences = countOccurrences xs;
|
||||||
in
|
in
|
||||||
unique (filter (x: occurrences.${x} > 1) xs);
|
unique (filter (x: occurrences.${x} > 1) xs);
|
||||||
|
|
||||||
|
# Concatenates all given attrsets as if calling a // b in order.
|
||||||
|
concatAttrs = foldl' mergeAttrs {};
|
||||||
|
|
||||||
|
# Wireguard related functions that are reused in several files of this flake
|
||||||
|
wireguard = wgName: rec {
|
||||||
|
_sortedPeers = peerA: peerB:
|
||||||
|
if peerA < peerB
|
||||||
|
then {
|
||||||
|
peer1 = peerA;
|
||||||
|
peer2 = peerB;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
peer1 = peerB;
|
||||||
|
peer2 = peerA;
|
||||||
|
};
|
||||||
|
|
||||||
|
peerPublicKeyFile = peerName: "secrets/wireguard/${wgName}/keys/${peerName}.pub";
|
||||||
|
peerPublicKeyPath = peerName: "${../.}/" + peerPublicKeyFile peerName;
|
||||||
|
|
||||||
|
peerPrivateKeyFile = peerName: "secrets/wireguard/${wgName}/keys/${peerName}.age";
|
||||||
|
peerPrivateKeyPath = peerName: "${../.}/" + peerPrivateKeyFile peerName;
|
||||||
|
peerPrivateKeySecret = peerName: "wireguard-${wgName}-priv-${peerName}";
|
||||||
|
|
||||||
|
peerPresharedKeyFile = peerA: peerB: let
|
||||||
|
inherit (_sortedPeers peerA peerB) peer1 peer2;
|
||||||
|
in "secrets/wireguard/${wgName}/psks/${peer1}+${peer2}.age";
|
||||||
|
peerPresharedKeyPath = peerA: peerB: "${../.}/" + peerPresharedKeyFile peerA peerB;
|
||||||
|
peerPresharedKeySecret = peerA: peerB: let
|
||||||
|
inherit (_sortedPeers peerA peerB) peer1 peer2;
|
||||||
|
in "wireguard-${wgName}-psks-${peer1}+${peer2}";
|
||||||
|
|
||||||
|
# All nodes that are part of this network
|
||||||
|
associatedNodes = nodes: filter (n: builtins.hasAttr wgName nodes.${n}.config.extra.wireguard) (attrNames nodes);
|
||||||
|
nodePeers = nodes: genAttrs (associatedNodes nodes) (n: nodes.${n}.config.extra.wireguard.${wgName}.address);
|
||||||
|
# All peers that are defined as externalPeers on any node.
|
||||||
|
# Prepends "external-" to their name.
|
||||||
|
externalPeers = nodes:
|
||||||
|
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;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue