forked from mirrors_public/oddlama_nix-config
feat(wireguard): generate psks only if needed; add most of the qr code generator
This commit is contained in:
parent
925d3856e0
commit
d5f2880457
13 changed files with 225 additions and 108 deletions
|
@ -52,6 +52,8 @@
|
||||||
...
|
...
|
||||||
} @ inputs:
|
} @ inputs:
|
||||||
{
|
{
|
||||||
|
extraLib = import ./nix/lib.nix inputs;
|
||||||
|
|
||||||
# The identities that are used to rekey agenix secrets and to
|
# The identities that are used to rekey agenix secrets and to
|
||||||
# decrypt all repository-wide secrets.
|
# decrypt all repository-wide secrets.
|
||||||
secrets = {
|
secrets = {
|
||||||
|
|
|
@ -21,5 +21,8 @@
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
extra.wireguard.vms.address = ["10.0.0.10/32"];
|
extra.wireguard.vms = {
|
||||||
|
via = "ward";
|
||||||
|
addresses = ["10.0.0.10/32"];
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,12 +27,12 @@
|
||||||
enable = true;
|
enable = true;
|
||||||
port = 51822;
|
port = 51822;
|
||||||
openFirewall = true;
|
openFirewall = true;
|
||||||
|
externalPeers = {
|
||||||
|
test1 = ["10.0.0.91/32"];
|
||||||
|
test2 = ["10.0.0.92/32"];
|
||||||
|
test3 = ["10.0.0.93/32"];
|
||||||
|
};
|
||||||
};
|
};
|
||||||
address = ["10.0.0.1/24"];
|
addresses = ["10.0.0.1/24"];
|
||||||
externalPeers = {
|
|
||||||
test1 = ["10.0.0.91/32"];
|
|
||||||
test2 = ["10.0.0.92/32"];
|
|
||||||
test3 = ["10.0.0.93/32"];
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,10 +23,10 @@
|
||||||
enable = true;
|
enable = true;
|
||||||
port = 51822;
|
port = 51822;
|
||||||
openFirewall = true;
|
openFirewall = true;
|
||||||
|
externalPeers = {
|
||||||
|
zack1 = ["10.0.0.90/32"];
|
||||||
|
};
|
||||||
};
|
};
|
||||||
address = ["10.0.0.2/24"];
|
addresses = ["10.0.0.2/24"];
|
||||||
externalPeers = {
|
|
||||||
zack1 = ["10.0.0.90/32"];
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,9 +11,11 @@
|
||||||
(lib)
|
(lib)
|
||||||
any
|
any
|
||||||
attrNames
|
attrNames
|
||||||
|
attrValues
|
||||||
concatMap
|
concatMap
|
||||||
concatMapStrings
|
concatMapStrings
|
||||||
concatStringsSep
|
concatStringsSep
|
||||||
|
filter
|
||||||
filterAttrs
|
filterAttrs
|
||||||
head
|
head
|
||||||
mapAttrsToList
|
mapAttrsToList
|
||||||
|
@ -23,6 +25,7 @@
|
||||||
mkOption
|
mkOption
|
||||||
mkEnableOption
|
mkEnableOption
|
||||||
optionalAttrs
|
optionalAttrs
|
||||||
|
optionals
|
||||||
splitString
|
splitString
|
||||||
types
|
types
|
||||||
;
|
;
|
||||||
|
@ -35,10 +38,12 @@
|
||||||
|
|
||||||
cfg = config.extra.wireguard;
|
cfg = config.extra.wireguard;
|
||||||
|
|
||||||
configForNetwork = wgName: wg: let
|
configForNetwork = wgName: wgCfg: let
|
||||||
inherit
|
inherit
|
||||||
(extraLib.wireguard wgName nodes)
|
(extraLib.wireguard wgName)
|
||||||
allPeers
|
associatedServerNodes
|
||||||
|
associatedClientNodes
|
||||||
|
externalPeerName
|
||||||
peerPresharedKeyPath
|
peerPresharedKeyPath
|
||||||
peerPresharedKeySecret
|
peerPresharedKeySecret
|
||||||
peerPrivateKeyPath
|
peerPrivateKeyPath
|
||||||
|
@ -46,17 +51,32 @@
|
||||||
peerPublicKeyPath
|
peerPublicKeyPath
|
||||||
;
|
;
|
||||||
|
|
||||||
otherPeers = filterAttrs (n: _: n != nodeName) allPeers;
|
filterSelf = filter (x: x != nodeName);
|
||||||
|
wgCfgOf = node: nodes.${node}.config.extra.wireguard.${wgName};
|
||||||
|
|
||||||
|
ourClientNodes =
|
||||||
|
optionals wgCfg.server.enable
|
||||||
|
(filter (n: (wgCfgOf n).via == nodeName) associatedClientNodes);
|
||||||
|
|
||||||
|
# The list of peers that we have to know the psk to.
|
||||||
|
neededPeers =
|
||||||
|
if wgCfg.server.enable
|
||||||
|
then
|
||||||
|
filterSelf associatedServerNodes
|
||||||
|
++ map externalPeerName (attrNames wgCfg.server.externalPeers)
|
||||||
|
++ ourClientNodes
|
||||||
|
else [wgCfg.via];
|
||||||
in {
|
in {
|
||||||
secrets =
|
secrets =
|
||||||
concatAttrs (map (other: {
|
concatAttrs (map (other: {
|
||||||
${peerPresharedKeySecret nodeName other}.file = peerPresharedKeyPath nodeName other;
|
${peerPresharedKeySecret nodeName other}.file = peerPresharedKeyPath nodeName other;
|
||||||
}) (attrNames otherPeers))
|
})
|
||||||
|
neededPeers)
|
||||||
// {
|
// {
|
||||||
${peerPrivateKeySecret nodeName}.file = peerPrivateKeyPath nodeName;
|
${peerPrivateKeySecret nodeName}.file = peerPrivateKeyPath nodeName;
|
||||||
};
|
};
|
||||||
|
|
||||||
netdevs."${wg.priority}-${wgName}" = {
|
netdevs."${wgCfg.priority}-${wgName}" = {
|
||||||
netdevConfig = {
|
netdevConfig = {
|
||||||
Kind = "wireguard";
|
Kind = "wireguard";
|
||||||
Name = "${wgName}";
|
Name = "${wgName}";
|
||||||
|
@ -66,27 +86,64 @@
|
||||||
{
|
{
|
||||||
PrivateKeyFile = config.rekey.secrets.${peerPrivateKeySecret nodeName}.path;
|
PrivateKeyFile = config.rekey.secrets.${peerPrivateKeySecret nodeName}.path;
|
||||||
}
|
}
|
||||||
// optionalAttrs wg.server.enable {
|
// optionalAttrs wgCfg.server.enable {
|
||||||
ListenPort = wg.server.port;
|
ListenPort = wgCfg.server.port;
|
||||||
};
|
};
|
||||||
wireguardPeers =
|
wireguardPeers =
|
||||||
mapAttrsToList (peerName: peerAllowedIPs: {
|
if wgCfg.server.enable
|
||||||
wireguardPeerConfig =
|
then
|
||||||
{
|
# Always include all other server nodes.
|
||||||
|
map (serverNode: {
|
||||||
|
wireguardPeerConfig = {
|
||||||
|
PublicKey = builtins.readFile (peerPublicKeyPath serverNode);
|
||||||
|
PresharedKeyFile = config.rekey.secrets.${peerPresharedKeySecret nodeName serverNode}.path;
|
||||||
|
# The allowed ips of a server node are it's own addreses,
|
||||||
|
# plus each external peer's addresses,
|
||||||
|
# plus each client's addresses that is connected via this node.
|
||||||
|
AllowedIPs =
|
||||||
|
(wgCfgOf serverNode).addresses
|
||||||
|
++ attrValues (wgCfgOf serverNode).server.externalPeers
|
||||||
|
++ map (n: (wgCfgOf n).addresses) ourClientNodes;
|
||||||
|
};
|
||||||
|
}) (filterSelf associatedServerNodes)
|
||||||
|
# All our external peers
|
||||||
|
++ mapAttrsToList (extPeer: allowedIPs: let
|
||||||
|
peerName = externalPeerName extPeer;
|
||||||
|
in {
|
||||||
|
wireguardPeerConfig = {
|
||||||
PublicKey = builtins.readFile (peerPublicKeyPath peerName);
|
PublicKey = builtins.readFile (peerPublicKeyPath peerName);
|
||||||
PresharedKeyFile = config.rekey.secrets.${peerPresharedKeySecret nodeName peerName}.path;
|
PresharedKeyFile = config.rekey.secrets.${peerPresharedKeySecret nodeName peerName}.path;
|
||||||
AllowedIPs = peerAllowedIPs;
|
AllowedIPs = allowedIPs;
|
||||||
}
|
|
||||||
// optionalAttrs wg.server.enable {
|
|
||||||
PersistentKeepalive = 25;
|
PersistentKeepalive = 25;
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
otherPeers;
|
wgCfg.server.externalPeers
|
||||||
|
# All client nodes that have their via set to us.
|
||||||
|
++ mapAttrsToList (clientNode: {
|
||||||
|
wireguardPeerConfig = {
|
||||||
|
PublicKey = builtins.readFile (peerPublicKeyPath clientNode);
|
||||||
|
PresharedKeyFile = config.rekey.secrets.${peerPresharedKeySecret nodeName clientNode}.path;
|
||||||
|
AllowedIPs = (wgCfgOf clientNode).addresses;
|
||||||
|
PersistentKeepalive = 25;
|
||||||
|
};
|
||||||
|
})
|
||||||
|
ourClientNodes
|
||||||
|
else
|
||||||
|
# We are a client node, so only include our via server.
|
||||||
|
[
|
||||||
|
{
|
||||||
|
wireguardPeerConfig = {
|
||||||
|
PublicKey = builtins.readFile (peerPublicKeyPath wgCfg.via);
|
||||||
|
PresharedKeyFile = config.rekey.secrets.${peerPresharedKeySecret nodeName wgCfg.via}.path;
|
||||||
|
AllowedIPs = (wgCfgOf wgCfg.via).addresses;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
networks."${wg.priority}-${wgName}" = {
|
networks."${wgCfg.priority}-${wgName}" = {
|
||||||
matchConfig.Name = wgName;
|
matchConfig.Name = wgName;
|
||||||
networkConfig.Address = wg.address;
|
networkConfig.Address = wgCfg.addresses;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
in {
|
in {
|
||||||
|
@ -109,6 +166,20 @@ in {
|
||||||
type = types.bool;
|
type = types.bool;
|
||||||
description = mdDoc "Whether to open the firewall for the specified `listenPort`, if {option}`listen` is `true`.";
|
description = mdDoc "Whether to open the firewall for the specified `listenPort`, if {option}`listen` is `true`.";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
externalPeers = mkOption {
|
||||||
|
type = types.attrsOf (types.listOf types.str);
|
||||||
|
default = {};
|
||||||
|
example = {my-android-phone = ["10.0.0.97/32"];};
|
||||||
|
description = mdDoc ''
|
||||||
|
Allows defining an extra set of peers that should be added to this wireguard network,
|
||||||
|
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.
|
||||||
|
'';
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
priority = mkOption {
|
priority = mkOption {
|
||||||
|
@ -117,27 +188,22 @@ in {
|
||||||
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.";
|
||||||
};
|
};
|
||||||
|
|
||||||
address = mkOption {
|
via = mkOption {
|
||||||
|
default = null;
|
||||||
|
type = types.uniq (types.nullOr types.str);
|
||||||
|
description = mdDoc ''
|
||||||
|
The server node via which to connect to the network.
|
||||||
|
This must defined if and only if this node is not a server.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
addresses = mkOption {
|
||||||
type = types.listOf types.str;
|
type = types.listOf types.str;
|
||||||
description = mdDoc ''
|
description = mdDoc ''
|
||||||
The addresses to configure for this interface. Will automatically be added
|
The addresses to configure for this interface. Will automatically be added
|
||||||
as this peer's allowed addresses to all other peers.
|
as this peer's allowed addresses to all other peers.
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
externalPeers = mkOption {
|
|
||||||
type = types.attrsOf (types.listOf types.str);
|
|
||||||
default = {};
|
|
||||||
example = {my-android-phone = ["10.0.0.97/32"];};
|
|
||||||
description = mdDoc ''
|
|
||||||
Allows defining an extra set of peers that should be added to this wireguard network,
|
|
||||||
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.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -148,12 +214,14 @@ in {
|
||||||
in {
|
in {
|
||||||
assertions = concatMap (wgName: let
|
assertions = concatMap (wgName: let
|
||||||
inherit
|
inherit
|
||||||
(extraLib.wireguard wgName nodes)
|
(extraLib.wireguard wgName)
|
||||||
externalPeerNamesRaw
|
externalPeerNamesRaw
|
||||||
usedAddresses
|
usedAddresses
|
||||||
associatedNodes
|
associatedNodes
|
||||||
;
|
;
|
||||||
|
|
||||||
|
wgCfg = cfg.${wgName};
|
||||||
|
wgCfgOf = node: nodes.${node}.config.extra.wireguard.${wgName};
|
||||||
duplicatePeers = duplicates externalPeerNamesRaw;
|
duplicatePeers = duplicates externalPeerNamesRaw;
|
||||||
duplicateAddrs = duplicates (map (x: head (splitString "/" x)) usedAddresses);
|
duplicateAddrs = duplicates (map (x: head (splitString "/" x)) usedAddresses);
|
||||||
in [
|
in [
|
||||||
|
@ -169,9 +237,19 @@ 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
|
assertion = wgCfg.server.externalPeers != {} -> wgCfg.server.enable;
|
||||||
# TODO psks only between all nodes and each node-externalpeer pair
|
message = "Wireguard network '${wgName}': Defining external peers requires server.enable = true.";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
assertion = wgCfg.server.enable == (wgCfg.via == null);
|
||||||
|
message = "Wireguard network '${wgName}': A via server must be defined exactly iff this isn't a server node.";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
assertion = wgCfg.via != null -> (wgCfgOf wgCfg.via).server.enable;
|
||||||
|
message = "Wireguard network '${wgName}': The specified via node '${wgCfg.via}' must be a wireguard server.";
|
||||||
|
}
|
||||||
|
# TODO externalPeers != {} -> ip forwarding
|
||||||
# TODO no overlapping allowed ip range? 0.0.0.0 would be ok to overlap though
|
# TODO no overlapping allowed ip range? 0.0.0.0 would be ok to overlap though
|
||||||
]) (attrNames cfg);
|
]) (attrNames cfg);
|
||||||
|
|
||||||
|
|
|
@ -1,36 +1,22 @@
|
||||||
{
|
{
|
||||||
self,
|
self,
|
||||||
pkgs,
|
pkgs,
|
||||||
|
nixpkgs,
|
||||||
...
|
...
|
||||||
}: let
|
}: let
|
||||||
inherit
|
inherit (nixpkgs.lib) concatStringsSep;
|
||||||
(pkgs.lib)
|
inherit (extraLib) rageEncryptArgs;
|
||||||
concatMapStrings
|
|
||||||
concatStringsSep
|
|
||||||
escapeShellArg
|
|
||||||
substring
|
|
||||||
;
|
|
||||||
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;
|
|
||||||
in
|
in
|
||||||
pkgs.writeShellScript "format-secrets" ''
|
pkgs.writeShellScript "format-secrets" ''
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
[[ -d .git ]] && [[ -f flake.nix ]] || { echo "[1;31merror:[m Please execute this from the project's root folder (the folder with flake.nix)" >&2; exit 1; }
|
[[ -d .git ]] && [[ -f flake.nix ]] || { echo "[1;31merror:[m Please execute this from the project's root folder (the folder with flake.nix)" >&2; exit 1; }
|
||||||
for f in $(find . -type f -name '*.nix.age'); do
|
for f in $(find . -type f -name '*.nix.age'); do
|
||||||
echo "Formatting $f ..."
|
echo "Formatting $f ..."
|
||||||
decrypted=$(${../rage-decrypt.sh} --print-out-path "$f" ${concatStringsSep " " self.secrets.masterIdentities}) \
|
decrypted=$(${../rage-decrypt-and-cache.sh} --print-out-path "$f" ${concatStringsSep " " self.secrets.masterIdentities}) \
|
||||||
|| { echo "[1;31merror:[m Failed to decrypt!" >&2; exit 1; }
|
|| { echo "[1;31merror:[m Failed to decrypt!" >&2; exit 1; }
|
||||||
formatted=$(${pkgs.alejandra}/bin/alejandra --quiet < "$decrypted") \
|
formatted=$(${pkgs.alejandra}/bin/alejandra --quiet < "$decrypted") \
|
||||||
|| { echo "[1;31merror:[m Failed to format $decrypted!" >&2; exit 1; }
|
|| { echo "[1;31merror:[m Failed to format $decrypted!" >&2; exit 1; }
|
||||||
${pkgs.rage}/bin/rage -e ${masterIdentityArgs} ${extraEncryptionPubkeys} <<< "$formatted" > "$f" \
|
${pkgs.rage}/bin/rage -e ${rageEncryptArgs} <<< "$formatted" > "$f" \
|
||||||
|| { echo "[1;31merror:[m Failed to re-encrypt!" >&2; exit 1; }
|
|| { echo "[1;31merror:[m Failed to re-encrypt!" >&2; exit 1; }
|
||||||
done
|
done
|
||||||
''
|
''
|
||||||
|
|
|
@ -17,24 +17,14 @@
|
||||||
unique
|
unique
|
||||||
;
|
;
|
||||||
|
|
||||||
extraLib = import ../lib.nix inputs;
|
inherit (self.extraLib) rageEncryptArgs;
|
||||||
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;
|
|
||||||
|
|
||||||
nodeNames = attrNames self.nodes;
|
nodeNames = attrNames self.nodes;
|
||||||
wireguardNetworks = unique (concatMap (n: attrNames self.nodes.${n}.config.extra.wireguard) nodeNames);
|
wireguardNetworks = unique (concatMap (n: attrNames self.nodes.${n}.config.extra.wireguard) nodeNames);
|
||||||
|
|
||||||
generateNetworkKeys = wgName: let
|
generateNetworkKeys = wgName: let
|
||||||
inherit
|
inherit
|
||||||
(extraLib.wireguard wgName self.nodes)
|
(self.extraLib.wireguard wgName)
|
||||||
allPeers
|
allPeers
|
||||||
associatedNodes
|
associatedNodes
|
||||||
associatedServerNodes
|
associatedServerNodes
|
||||||
|
@ -57,7 +47,7 @@
|
||||||
echo "Generating [34m"${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" > ${privkeyFile} \
|
${pkgs.rage}/bin/rage -e ${rageEncryptArgs} <<< "$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
|
||||||
'';
|
'';
|
||||||
|
@ -73,7 +63,7 @@
|
||||||
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 ${rageEncryptArgs} <<< "$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 ${peer1} and ${peer2} on network ${wgName}!" >&2; exit 1; }
|
||||||
fi
|
fi
|
||||||
'';
|
'';
|
||||||
|
|
|
@ -9,22 +9,57 @@
|
||||||
concatMap
|
concatMap
|
||||||
concatStringsSep
|
concatStringsSep
|
||||||
escapeShellArg
|
escapeShellArg
|
||||||
filter
|
|
||||||
unique
|
unique
|
||||||
;
|
;
|
||||||
|
|
||||||
extraLib = import ../lib.nix inputs;
|
inherit (self.extraLib) rageDecryptArgs;
|
||||||
|
|
||||||
nodeNames = attrNames self.nodes;
|
nodeNames = attrNames self.nodes;
|
||||||
wireguardNetworks = unique (concatMap (n: attrNames self.nodes.${n}.config.extra.wireguard) nodeNames);
|
wireguardNetworks = unique (concatMap (n: attrNames self.nodes.${n}.config.extra.wireguard) nodeNames);
|
||||||
|
|
||||||
externalPeersForNet = wgName:
|
externalPeersForNet = wgName:
|
||||||
map (peer: {inherit wgName peer;})
|
concatMap (serverNode:
|
||||||
(attrNames (extraLib.wireguard wgName self.nodes).allExternalPeers);
|
map
|
||||||
|
(peer: {inherit wgName serverNode peer;})
|
||||||
|
(attrNames self.nodes.${serverNode}.config.extra.wireguard.${wgName}.server.externalPeers))
|
||||||
|
(self.extraLib.wireguard wgName).associatedServerNodes;
|
||||||
allExternalPeers = concatMap externalPeersForNet wireguardNetworks;
|
allExternalPeers = concatMap externalPeersForNet wireguardNetworks;
|
||||||
in
|
in
|
||||||
# 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.wgName}.${x.peer}") allExternalPeers))} | ${pkgs.fzf}/bin/fzf
|
json_sel=$(echo ${escapeShellArg (concatStringsSep "\n" (map (x: "${builtins.toJSON x}\t[33m${x.wgName}[m.[34m${x.serverNode}[m.[32m${x.peer}[m") allExternalPeers))} \
|
||||||
|
| ${pkgs.fzf}/bin/fzf --delimiter='\t' --ansi --multi --query="''${1-}" --tiebreak=end --bind=tab:down,btab:up,change:top,ctrl-space:toggle --with-nth=2.. --height='~50%' --tac \
|
||||||
|
| ${pkgs.coreutils}/bin/cut -d$'\t' -f1)
|
||||||
|
[[ -n "$json_sel" ]] || exit 1
|
||||||
|
|
||||||
|
# TODO for each output line
|
||||||
|
# TODO maybe just call a json -> make script that gives wireguard config to make this easier
|
||||||
|
|
||||||
|
wgName=$(${pkgs.jq}/bin/jq -r .wgName <<< "$json_sel")
|
||||||
|
serverNode=$(${pkgs.jq}/bin/jq -r .serverNode <<< "$json_sel")
|
||||||
|
peer=$(${pkgs.jq}/bin/jq -r .peer <<< "$json_sel")
|
||||||
|
|
||||||
|
serverPubkey=$(nix eval --raw ".#extraLib" \
|
||||||
|
--apply 'extraLib: builtins.readFile ((extraLib.wireguard "'"$wgName"'").peerPublicKeyPath "'"$serverNode"'")')
|
||||||
|
privKeyPath=$(nix eval --raw ".#extraLib" \
|
||||||
|
--apply 'extraLib: (extraLib.wireguard "'"$wgName"'").peerPrivateKeyPath "'"$peer"'"')
|
||||||
|
serverPskPath=$(nix eval --raw ".#extraLib" \
|
||||||
|
--apply 'extraLib: (extraLib.wireguard "'"$wgName"'").peerPresharedKeyPath "'"$serverNode"'" "'"$peer"'"')
|
||||||
|
|
||||||
|
privKey=$(${pkgs.rage}/bin/rage -d ${rageDecryptArgs} "$privKeyPath") \
|
||||||
|
|| { echo "[1;31merror:[m Failed to decrypt!" >&2; exit 1; }
|
||||||
|
serverPsk=$(${pkgs.rage}/bin/rage -d ${rageDecryptArgs} "$serverPskPath") \
|
||||||
|
|| { echo "[1;31merror:[m Failed to decrypt!" >&2; exit 1; }
|
||||||
|
|
||||||
|
cat <<EOF | tee /dev/tty | ${pkgs.qrencode}/bin/qrencode -t ansiutf8
|
||||||
|
[Interface]
|
||||||
|
Address =
|
||||||
|
PrivateKey = $privKey
|
||||||
|
|
||||||
|
[Peer]
|
||||||
|
PublicKey = $serverPubkey
|
||||||
|
PresharedKey = $serverPsk
|
||||||
|
AllowedIPs =
|
||||||
|
Endpoint =
|
||||||
|
EOF
|
||||||
''
|
''
|
||||||
|
|
|
@ -28,5 +28,5 @@ in {
|
||||||
rageImportEncrypted = identities: nixFile:
|
rageImportEncrypted = identities: nixFile:
|
||||||
assert assertMsg (builtins.isPath nixFile) "The file to decrypt must be given as a path to prevent impurity.";
|
assert assertMsg (builtins.isPath nixFile) "The file to decrypt must be given as a path to prevent impurity.";
|
||||||
assert assertMsg (hasSuffix ".nix.age" nixFile) "The content of the decrypted file must be a nix expression and should therefore end in .nix.age";
|
assert assertMsg (hasSuffix ".nix.age" nixFile) "The content of the decrypted file must be a nix expression and should therefore end in .nix.age";
|
||||||
exec ([./rage-decrypt.sh nixFile] ++ identities);
|
exec ([./rage-decrypt-and-cache.sh nixFile] ++ identities);
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,19 +10,14 @@
|
||||||
agenix-rekey,
|
agenix-rekey,
|
||||||
...
|
...
|
||||||
} @ inputs: let
|
} @ inputs: let
|
||||||
inherit
|
inherit (nixpkgs.lib) optionals;
|
||||||
(nixpkgs.lib)
|
|
||||||
optionals
|
|
||||||
;
|
|
||||||
|
|
||||||
extraLib = import ./lib.nix inputs;
|
|
||||||
in
|
in
|
||||||
nodeName: nodeMeta: {
|
nodeName: nodeMeta: {
|
||||||
inherit (nodeMeta) system;
|
inherit (nodeMeta) system;
|
||||||
pkgs = self.pkgs.${nodeMeta.system};
|
pkgs = self.pkgs.${nodeMeta.system};
|
||||||
specialArgs = {
|
specialArgs = {
|
||||||
inherit (nixpkgs) lib;
|
inherit (nixpkgs) lib;
|
||||||
inherit extraLib;
|
inherit (self) extraLib;
|
||||||
inherit inputs;
|
inherit inputs;
|
||||||
inherit nodeName;
|
inherit nodeName;
|
||||||
inherit nodeMeta;
|
inherit nodeMeta;
|
||||||
|
|
45
nix/lib.nix
45
nix/lib.nix
|
@ -1,9 +1,15 @@
|
||||||
{nixpkgs, ...}: let
|
{
|
||||||
|
self,
|
||||||
|
nixpkgs,
|
||||||
|
...
|
||||||
|
}: let
|
||||||
inherit
|
inherit
|
||||||
(nixpkgs.lib)
|
(nixpkgs.lib)
|
||||||
attrNames
|
attrNames
|
||||||
attrValues
|
attrValues
|
||||||
concatMap
|
concatMap
|
||||||
|
concatMapStrings
|
||||||
|
escapeShellArg
|
||||||
filter
|
filter
|
||||||
flatten
|
flatten
|
||||||
foldl'
|
foldl'
|
||||||
|
@ -12,6 +18,7 @@
|
||||||
mergeAttrs
|
mergeAttrs
|
||||||
nameValuePair
|
nameValuePair
|
||||||
partition
|
partition
|
||||||
|
substring
|
||||||
unique
|
unique
|
||||||
;
|
;
|
||||||
in rec {
|
in rec {
|
||||||
|
@ -33,8 +40,24 @@ in rec {
|
||||||
# Concatenates all given attrsets as if calling a // b in order.
|
# Concatenates all given attrsets as if calling a // b in order.
|
||||||
concatAttrs = foldl' mergeAttrs {};
|
concatAttrs = foldl' mergeAttrs {};
|
||||||
|
|
||||||
|
# True if the path or string starts with /
|
||||||
|
isAbsolutePath = x: substring 0 1 x == "/";
|
||||||
|
|
||||||
|
rageMasterIdentityArgs = concatMapStrings (x: ''-i ${escapeShellArg x} '') self.secrets.masterIdentities;
|
||||||
|
rageExtraEncryptionPubkeys =
|
||||||
|
concatMapStrings (
|
||||||
|
x:
|
||||||
|
if isAbsolutePath x
|
||||||
|
then ''-R ${escapeShellArg x} ''
|
||||||
|
else ''-r ${escapeShellArg x} ''
|
||||||
|
)
|
||||||
|
self.secrets.extraEncryptionPubkeys;
|
||||||
|
# The arguments required to de-/encrypt a secret in this repository
|
||||||
|
rageDecryptArgs = "${rageMasterIdentityArgs}";
|
||||||
|
rageEncryptArgs = "${rageMasterIdentityArgs} ${rageExtraEncryptionPubkeys}";
|
||||||
|
|
||||||
# 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: nodes: rec {
|
wireguard = wgName: rec {
|
||||||
sortedPeers = peerA: peerB:
|
sortedPeers = peerA: peerB:
|
||||||
if peerA < peerB
|
if peerA < peerB
|
||||||
then {
|
then {
|
||||||
|
@ -64,25 +87,27 @@ in rec {
|
||||||
# All nodes that are part of this network
|
# All nodes that are part of this network
|
||||||
associatedNodes =
|
associatedNodes =
|
||||||
filter
|
filter
|
||||||
(n: builtins.hasAttr wgName nodes.${n}.config.extra.wireguard)
|
(n: builtins.hasAttr wgName self.nodes.${n}.config.extra.wireguard)
|
||||||
(attrNames nodes);
|
(attrNames self.nodes);
|
||||||
|
|
||||||
# Partition nodes by whether they are servers
|
# Partition nodes by whether they are servers
|
||||||
_associatedNodes_isServerPartition =
|
_associatedNodes_isServerPartition =
|
||||||
partition
|
partition
|
||||||
(n: nodes.${n}.config.extra.wireguard.${wgName}.server.enable)
|
(n: self.nodes.${n}.config.extra.wireguard.${wgName}.server.enable)
|
||||||
associatedNodes;
|
associatedNodes;
|
||||||
|
|
||||||
associatedServerNodes = _associatedNodes_isServerPartition.right;
|
associatedServerNodes = _associatedNodes_isServerPartition.right;
|
||||||
associatedClientNodes = _associatedNodes_isServerPartition.wrong;
|
associatedClientNodes = _associatedNodes_isServerPartition.wrong;
|
||||||
|
|
||||||
# Maps all nodes that are part of this network to their addresses
|
# Maps all nodes that are part of this network to their addresses
|
||||||
nodePeers = genAttrs associatedNodes (n: nodes.${n}.config.extra.wireguard.${wgName}.address);
|
nodePeers = genAttrs associatedNodes (n: self.nodes.${n}.config.extra.wireguard.${wgName}.addresses);
|
||||||
|
|
||||||
|
externalPeerName = p: "external-${p}";
|
||||||
|
|
||||||
# Only peers that are defined as externalPeers on the given node.
|
# Only peers that are defined as externalPeers on the given node.
|
||||||
# Prepends "external-" to their name.
|
# Prepends "external-" to their name.
|
||||||
externalPeersForNode = node:
|
externalPeersForNode = node:
|
||||||
mapAttrs' (p: nameValuePair "external-${p}") nodes.${node}.config.extra.wireguard.${wgName}.externalPeers;
|
mapAttrs' (p: nameValuePair (externalPeerName p)) self.nodes.${node}.config.extra.wireguard.${wgName}.server.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.
|
||||||
|
@ -92,11 +117,11 @@ in rec {
|
||||||
allPeers = nodePeers // allExternalPeers;
|
allPeers = nodePeers // allExternalPeers;
|
||||||
|
|
||||||
# Concatenation of all external peer names names without any transformations.
|
# Concatenation of all external peer names names without any transformations.
|
||||||
externalPeerNamesRaw = concatMap (n: attrNames nodes.${n}.config.extra.wireguard.${wgName}.externalPeers) associatedNodes;
|
externalPeerNamesRaw = concatMap (n: attrNames self.nodes.${n}.config.extra.wireguard.${wgName}.server.externalPeers) associatedNodes;
|
||||||
|
|
||||||
# A list of all occurring addresses.
|
# A list of all occurring addresses.
|
||||||
usedAddresses =
|
usedAddresses =
|
||||||
concatMap (n: nodes.${n}.config.extra.wireguard.${wgName}.address) associatedNodes
|
concatMap (n: self.nodes.${n}.config.extra.wireguard.${wgName}.addresses) associatedNodes
|
||||||
++ flatten (concatMap (n: attrValues nodes.${n}.config.extra.wireguard.${wgName}.externalPeers) associatedNodes);
|
++ flatten (concatMap (n: attrValues self.nodes.${n}.config.extra.wireguard.${wgName}.server.externalPeers) associatedNodes);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,8 +15,11 @@
|
||||||
self,
|
self,
|
||||||
nixpkgs,
|
nixpkgs,
|
||||||
...
|
...
|
||||||
} @ inputs:
|
} @ inputs: let
|
||||||
with nixpkgs.lib; let
|
inherit
|
||||||
|
(nixpkgs.lib)
|
||||||
|
mapAttrs
|
||||||
|
;
|
||||||
# If the given expression is a bare set, it will be wrapped in a function,
|
# If the given expression is a bare set, it will be wrapped in a function,
|
||||||
# so that the imported file can always be applied to the inputs, similar to
|
# so that the imported file can always be applied to the inputs, similar to
|
||||||
# how modules can be functions or sets.
|
# how modules can be functions or sets.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue