mirror of
https://github.com/oddlama/nix-config.git
synced 2025-10-10 23:00:39 +02:00
refactor: add lib extensions to nixpkgs.lib as overlays
This commit is contained in:
parent
385d8178a2
commit
e1e7516e1a
19 changed files with 743 additions and 813 deletions
|
@ -2,12 +2,12 @@
|
||||||
|
|
||||||
This is my personal nix config. It's still in the making, but this is what I got so far:
|
This is my personal nix config. It's still in the making, but this is what I got so far:
|
||||||
|
|
||||||
- Full disk encryption using [disko](https://github.com/nix-community/disko), remotely unlockable via ssh
|
|
||||||
- Zoned nftables firewall
|
|
||||||
- Service isolation using [microvms](https://github.com/astro/microvm.nix) instead of containers
|
|
||||||
- Log and system monitoring via loki, telegraf, influxdb, promtail and grafana
|
- Log and system monitoring via loki, telegraf, influxdb, promtail and grafana
|
||||||
- Single-Sign-On for all services using oauth2 via kanidm
|
- Single-Sign-On for all services using oauth2 via kanidm
|
||||||
- Automatic wireguard mesh generation
|
- Automatic wireguard mesh generation
|
||||||
|
- Full disk encryption using [disko](https://github.com/nix-community/disko), remotely unlockable via ssh
|
||||||
|
- Zoned nftables firewall via [nixos-nftables-firewall](https://github.com/thelegy/nixos-nftables-firewall)
|
||||||
|
- Service isolation using [microvms](https://github.com/astro/microvm.nix) instead of containers
|
||||||
- Secret rekeying, generation and bootstrapping using [agenix-rekey](https://github.com/oddlama/agenix-rekey)
|
- Secret rekeying, generation and bootstrapping using [agenix-rekey](https://github.com/oddlama/agenix-rekey)
|
||||||
- Support for repository-wide secrets at evaluation time (hides PII like MACs)
|
- Support for repository-wide secrets at evaluation time (hides PII like MACs)
|
||||||
|
|
||||||
|
|
|
@ -131,7 +131,10 @@
|
||||||
pkgs = import nixpkgs {
|
pkgs = import nixpkgs {
|
||||||
localSystem = system;
|
localSystem = system;
|
||||||
config.allowUnfree = true;
|
config.allowUnfree = true;
|
||||||
overlays = [microvm.overlay] ++ import ./pkgs/default.nix;
|
overlays =
|
||||||
|
import ./lib inputs
|
||||||
|
++ import ./pkgs/default.nix
|
||||||
|
++ [microvm.overlay];
|
||||||
};
|
};
|
||||||
|
|
||||||
apps =
|
apps =
|
||||||
|
|
|
@ -1,16 +1,14 @@
|
||||||
{
|
{
|
||||||
inputs,
|
|
||||||
config,
|
config,
|
||||||
|
lib,
|
||||||
...
|
...
|
||||||
}: let
|
}: {
|
||||||
disko = import ../../lib/disko.nix inputs;
|
|
||||||
in {
|
|
||||||
disko.devices = {
|
disko.devices = {
|
||||||
disk = {
|
disk = {
|
||||||
m2-ssd = {
|
m2-ssd = {
|
||||||
type = "disk";
|
type = "disk";
|
||||||
device = "/dev/disk/by-id/${config.repo.secrets.local.disk.m2-ssd}";
|
device = "/dev/disk/by-id/${config.repo.secrets.local.disk.m2-ssd}";
|
||||||
content = with disko.gpt; {
|
content = with lib.disko.gpt; {
|
||||||
type = "table";
|
type = "table";
|
||||||
format = "gpt";
|
format = "gpt";
|
||||||
partitions = [
|
partitions = [
|
||||||
|
@ -21,7 +19,7 @@ in {
|
||||||
boot-ssd = {
|
boot-ssd = {
|
||||||
type = "disk";
|
type = "disk";
|
||||||
device = "/dev/disk/by-id/${config.repo.secrets.local.disk.boot-ssd}";
|
device = "/dev/disk/by-id/${config.repo.secrets.local.disk.boot-ssd}";
|
||||||
content = with disko.gpt; {
|
content = with lib.disko.gpt; {
|
||||||
type = "table";
|
type = "table";
|
||||||
format = "gpt";
|
format = "gpt";
|
||||||
partitions = [
|
partitions = [
|
||||||
|
@ -31,7 +29,7 @@ in {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
zpool = with disko.zfs; {
|
zpool = with lib.disko.zfs; {
|
||||||
rpool = defaultZpoolOptions // {datasets = defaultZfsDatasets;};
|
rpool = defaultZpoolOptions // {datasets = defaultZfsDatasets;};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,16 +1,14 @@
|
||||||
{
|
{
|
||||||
config,
|
config,
|
||||||
inputs,
|
lib,
|
||||||
...
|
...
|
||||||
}: let
|
}: {
|
||||||
disko = import ../../lib/disko.nix inputs;
|
|
||||||
in {
|
|
||||||
disko.devices = {
|
disko.devices = {
|
||||||
disk = {
|
disk = {
|
||||||
main = {
|
main = {
|
||||||
type = "disk";
|
type = "disk";
|
||||||
device = "/dev/disk/by-id/${config.repo.secrets.local.disk.main}";
|
device = "/dev/disk/by-id/${config.repo.secrets.local.disk.main}";
|
||||||
content = with disko.gpt; {
|
content = with lib.disko.gpt; {
|
||||||
type = "table";
|
type = "table";
|
||||||
format = "gpt";
|
format = "gpt";
|
||||||
partitions = [
|
partitions = [
|
||||||
|
@ -21,7 +19,7 @@ in {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
zpool = with disko.zfs; {
|
zpool = with lib.disko.zfs; {
|
||||||
rpool = defaultZpoolOptions // {datasets = defaultZfsDatasets;};
|
rpool = defaultZpoolOptions // {datasets = defaultZfsDatasets;};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,18 +1,15 @@
|
||||||
{
|
{
|
||||||
config,
|
config,
|
||||||
inputs,
|
|
||||||
lib,
|
lib,
|
||||||
pkgs,
|
pkgs,
|
||||||
...
|
...
|
||||||
}: let
|
}: {
|
||||||
disko = import ../../lib/disko.nix inputs;
|
|
||||||
in {
|
|
||||||
disko.devices = {
|
disko.devices = {
|
||||||
disk = {
|
disk = {
|
||||||
m2-ssd = {
|
m2-ssd = {
|
||||||
type = "disk";
|
type = "disk";
|
||||||
device = "/dev/disk/by-id/${config.repo.secrets.local.disk.m2-ssd}";
|
device = "/dev/disk/by-id/${config.repo.secrets.local.disk.m2-ssd}";
|
||||||
content = with disko.gpt; {
|
content = with lib.disko.gpt; {
|
||||||
type = "table";
|
type = "table";
|
||||||
format = "gpt";
|
format = "gpt";
|
||||||
partitions = [
|
partitions = [
|
||||||
|
@ -23,7 +20,7 @@ in {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
zpool = with disko.zfs; {
|
zpool = with lib.disko.zfs; {
|
||||||
rpool =
|
rpool =
|
||||||
defaultZpoolOptions
|
defaultZpoolOptions
|
||||||
// {
|
// {
|
||||||
|
|
|
@ -1,15 +1,9 @@
|
||||||
{
|
{
|
||||||
config,
|
config,
|
||||||
inputs,
|
|
||||||
lib,
|
lib,
|
||||||
utils,
|
utils,
|
||||||
...
|
...
|
||||||
}: let
|
}: let
|
||||||
inherit
|
|
||||||
(import ../../lib/net.nix inputs)
|
|
||||||
cidr
|
|
||||||
;
|
|
||||||
|
|
||||||
lanCidrv4 = "192.168.100.0/24";
|
lanCidrv4 = "192.168.100.0/24";
|
||||||
lanCidrv6 = "fd10::/64";
|
lanCidrv6 = "fd10::/64";
|
||||||
in {
|
in {
|
||||||
|
@ -60,8 +54,8 @@ in {
|
||||||
};
|
};
|
||||||
"20-lan-self" = {
|
"20-lan-self" = {
|
||||||
address = [
|
address = [
|
||||||
(cidr.hostCidr 1 lanCidrv4)
|
(lib.net.cidr.hostCidr 1 lanCidrv4)
|
||||||
(cidr.hostCidr 1 lanCidrv6)
|
(lib.net.cidr.hostCidr 1 lanCidrv6)
|
||||||
];
|
];
|
||||||
matchConfig.Name = "lan-self";
|
matchConfig.Name = "lan-self";
|
||||||
networkConfig = {
|
networkConfig = {
|
||||||
|
@ -84,7 +78,7 @@ in {
|
||||||
ipv6SendRAConfig = {
|
ipv6SendRAConfig = {
|
||||||
EmitDNS = true;
|
EmitDNS = true;
|
||||||
# TODO change to self later
|
# TODO change to self later
|
||||||
#DNS = cidr.host 1 net.lan.ipv6cidr;
|
#DNS = lib.net.cidr.host 1 net.lan.ipv6cidr;
|
||||||
DNS = ["2606:4700:4700::1111" "2001:4860:4860::8888"];
|
DNS = ["2606:4700:4700::1111" "2001:4860:4860::8888"];
|
||||||
};
|
};
|
||||||
linkConfig.RequiredForOnline = "routable";
|
linkConfig.RequiredForOnline = "routable";
|
||||||
|
@ -160,12 +154,12 @@ in {
|
||||||
interface = "lan-self";
|
interface = "lan-self";
|
||||||
subnet = lanCidrv4;
|
subnet = lanCidrv4;
|
||||||
pools = [
|
pools = [
|
||||||
{pool = "${cidr.host 20 lanCidrv4} - ${cidr.host (-6) lanCidrv4}";}
|
{pool = "${lib.net.cidr.host 20 lanCidrv4} - ${lib.net.cidr.host (-6) lanCidrv4}";}
|
||||||
];
|
];
|
||||||
option-data = [
|
option-data = [
|
||||||
{
|
{
|
||||||
name = "routers";
|
name = "routers";
|
||||||
data = cidr.host 1 lanCidrv4;
|
data = lib.net.cidr.host 1 lanCidrv4;
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,8 @@
|
||||||
{
|
{
|
||||||
config,
|
config,
|
||||||
inputs,
|
|
||||||
lib,
|
lib,
|
||||||
...
|
...
|
||||||
}: let
|
}: let
|
||||||
inherit
|
|
||||||
(import ../../lib/net.nix inputs)
|
|
||||||
cidr
|
|
||||||
;
|
|
||||||
|
|
||||||
iotCidrv4 = "10.90.0.0/24";
|
iotCidrv4 = "10.90.0.0/24";
|
||||||
iotCidrv6 = "fd00:90::/64";
|
iotCidrv6 = "fd00:90::/64";
|
||||||
in {
|
in {
|
||||||
|
@ -31,8 +25,8 @@ in {
|
||||||
};
|
};
|
||||||
"10-wlan1" = {
|
"10-wlan1" = {
|
||||||
address = [
|
address = [
|
||||||
(cidr.hostCidr 1 iotCidrv4)
|
(lib.net.cidr.hostCidr 1 iotCidrv4)
|
||||||
(cidr.hostCidr 1 iotCidrv6)
|
(lib.net.cidr.hostCidr 1 iotCidrv6)
|
||||||
];
|
];
|
||||||
matchConfig.MACAddress = config.repo.secrets.local.networking.interfaces.wlan1.mac;
|
matchConfig.MACAddress = config.repo.secrets.local.networking.interfaces.wlan1.mac;
|
||||||
linkConfig.RequiredForOnline = "no";
|
linkConfig.RequiredForOnline = "no";
|
||||||
|
|
7
lib/default.nix
Normal file
7
lib/default.nix
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
inputs: [
|
||||||
|
(import ./disko.nix inputs)
|
||||||
|
(import ./misc.nix inputs)
|
||||||
|
(import ./net.nix inputs)
|
||||||
|
(import ./types.nix inputs)
|
||||||
|
(import ./wireguard.nix inputs)
|
||||||
|
]
|
160
lib/disko.nix
160
lib/disko.nix
|
@ -1,81 +1,87 @@
|
||||||
inputs: {
|
inputs: self: super: {
|
||||||
gpt = {
|
lib =
|
||||||
partGrub = name: start: end: {
|
super.lib
|
||||||
inherit name start end;
|
// {
|
||||||
part-type = "primary";
|
disko = {
|
||||||
flags = ["bios_grub"];
|
gpt = {
|
||||||
};
|
partGrub = name: start: end: {
|
||||||
partEfi = name: start: end: {
|
inherit name start end;
|
||||||
inherit name start end;
|
part-type = "primary";
|
||||||
fs-type = "fat32";
|
flags = ["bios_grub"];
|
||||||
bootable = true;
|
};
|
||||||
content = {
|
partEfi = name: start: end: {
|
||||||
type = "filesystem";
|
inherit name start end;
|
||||||
format = "vfat";
|
fs-type = "fat32";
|
||||||
mountpoint = "/boot";
|
bootable = true;
|
||||||
};
|
content = {
|
||||||
};
|
type = "filesystem";
|
||||||
partSwap = name: start: end: {
|
format = "vfat";
|
||||||
inherit name start end;
|
mountpoint = "/boot";
|
||||||
fs-type = "linux-swap";
|
};
|
||||||
content = {
|
};
|
||||||
type = "swap";
|
partSwap = name: start: end: {
|
||||||
randomEncryption = true;
|
inherit name start end;
|
||||||
};
|
fs-type = "linux-swap";
|
||||||
};
|
content = {
|
||||||
partLuksZfs = name: start: end: {
|
type = "swap";
|
||||||
inherit start end;
|
randomEncryption = true;
|
||||||
name = "enc-${name}";
|
};
|
||||||
content = {
|
};
|
||||||
type = "luks";
|
partLuksZfs = name: start: end: {
|
||||||
name = "enc-${name}";
|
inherit start end;
|
||||||
extraOpenArgs = ["--allow-discards"];
|
name = "enc-${name}";
|
||||||
content = {
|
content = {
|
||||||
type = "zfs";
|
type = "luks";
|
||||||
pool = name;
|
name = "enc-${name}";
|
||||||
|
extraOpenArgs = ["--allow-discards"];
|
||||||
|
content = {
|
||||||
|
type = "zfs";
|
||||||
|
pool = name;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
zfs = rec {
|
||||||
|
defaultZpoolOptions = {
|
||||||
|
type = "zpool";
|
||||||
|
mountRoot = "/mnt";
|
||||||
|
rootFsOptions = {
|
||||||
|
compression = "zstd";
|
||||||
|
acltype = "posix";
|
||||||
|
atime = "off";
|
||||||
|
xattr = "sa";
|
||||||
|
dnodesize = "auto";
|
||||||
|
mountpoint = "none";
|
||||||
|
canmount = "off";
|
||||||
|
devices = "off";
|
||||||
|
};
|
||||||
|
options.ashift = "12";
|
||||||
|
};
|
||||||
|
|
||||||
|
defaultZfsDatasets = {
|
||||||
|
"local" = unmountable;
|
||||||
|
"local/root" =
|
||||||
|
filesystem "/"
|
||||||
|
// {
|
||||||
|
postCreateHook = "zfs snapshot rpool/local/root@blank";
|
||||||
|
};
|
||||||
|
"local/nix" = filesystem "/nix";
|
||||||
|
"local/state" = filesystem "/state";
|
||||||
|
"safe" = unmountable;
|
||||||
|
"safe/persist" = filesystem "/persist";
|
||||||
|
};
|
||||||
|
|
||||||
|
unmountable = {type = "zfs_fs";};
|
||||||
|
filesystem = mountpoint: {
|
||||||
|
type = "zfs_fs";
|
||||||
|
options = {
|
||||||
|
canmount = "noauto";
|
||||||
|
inherit mountpoint;
|
||||||
|
};
|
||||||
|
# Required to add dependencies for initrd
|
||||||
|
inherit mountpoint;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
|
||||||
zfs = rec {
|
|
||||||
defaultZpoolOptions = {
|
|
||||||
type = "zpool";
|
|
||||||
mountRoot = "/mnt";
|
|
||||||
rootFsOptions = {
|
|
||||||
compression = "zstd";
|
|
||||||
acltype = "posix";
|
|
||||||
atime = "off";
|
|
||||||
xattr = "sa";
|
|
||||||
dnodesize = "auto";
|
|
||||||
mountpoint = "none";
|
|
||||||
canmount = "off";
|
|
||||||
devices = "off";
|
|
||||||
};
|
|
||||||
options.ashift = "12";
|
|
||||||
};
|
|
||||||
|
|
||||||
defaultZfsDatasets = {
|
|
||||||
"local" = unmountable;
|
|
||||||
"local/root" =
|
|
||||||
filesystem "/"
|
|
||||||
// {
|
|
||||||
postCreateHook = "zfs snapshot rpool/local/root@blank";
|
|
||||||
};
|
|
||||||
"local/nix" = filesystem "/nix";
|
|
||||||
"local/state" = filesystem "/state";
|
|
||||||
"safe" = unmountable;
|
|
||||||
"safe/persist" = filesystem "/persist";
|
|
||||||
};
|
|
||||||
|
|
||||||
unmountable = {type = "zfs_fs";};
|
|
||||||
filesystem = mountpoint: {
|
|
||||||
type = "zfs_fs";
|
|
||||||
options = {
|
|
||||||
canmount = "noauto";
|
|
||||||
inherit mountpoint;
|
|
||||||
};
|
|
||||||
# Required to add dependencies for initrd
|
|
||||||
inherit mountpoint;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
150
lib/misc.nix
150
lib/misc.nix
|
@ -1,54 +1,25 @@
|
||||||
inputs: let
|
inputs: self: super: let
|
||||||
inherit
|
inherit
|
||||||
(inputs.nixpkgs.lib)
|
(super.lib)
|
||||||
all
|
|
||||||
any
|
|
||||||
assertMsg
|
|
||||||
attrNames
|
|
||||||
attrValues
|
|
||||||
concatLists
|
|
||||||
concatMap
|
|
||||||
concatMapStrings
|
concatMapStrings
|
||||||
concatStringsSep
|
|
||||||
elem
|
|
||||||
escapeShellArg
|
escapeShellArg
|
||||||
filter
|
filter
|
||||||
flatten
|
|
||||||
flip
|
|
||||||
foldAttrs
|
|
||||||
foldl'
|
foldl'
|
||||||
genAttrs
|
genAttrs
|
||||||
genList
|
genList
|
||||||
hasInfix
|
|
||||||
head
|
|
||||||
isAttrs
|
|
||||||
mapAttrs'
|
|
||||||
mergeAttrs
|
mergeAttrs
|
||||||
min
|
|
||||||
mkMerge
|
mkMerge
|
||||||
mkOptionType
|
|
||||||
nameValuePair
|
|
||||||
optionalAttrs
|
|
||||||
partition
|
|
||||||
range
|
|
||||||
recursiveUpdate
|
|
||||||
removeSuffix
|
|
||||||
reverseList
|
|
||||||
showOption
|
|
||||||
splitString
|
|
||||||
stringToCharacters
|
stringToCharacters
|
||||||
substring
|
substring
|
||||||
types
|
|
||||||
unique
|
unique
|
||||||
warnIf
|
|
||||||
;
|
;
|
||||||
in rec {
|
|
||||||
# Counts how often each element occurrs in xs
|
# Counts how often each element occurrs in xs.
|
||||||
countOccurrences = let
|
# Elements must be strings.
|
||||||
addOrUpdate = acc: x:
|
countOccurrences =
|
||||||
acc // {${x} = (acc.${x} or 0) + 1;};
|
foldl'
|
||||||
in
|
(acc: x: acc // {${x} = (acc.${x} or 0) + 1;})
|
||||||
foldl' addOrUpdate {};
|
{};
|
||||||
|
|
||||||
# Returns all elements in xs that occur at least twice
|
# Returns all elements in xs that occur at least twice
|
||||||
duplicates = xs: let
|
duplicates = xs: let
|
||||||
|
@ -70,50 +41,67 @@ in rec {
|
||||||
# Calculates base^exp, but careful, this overflows for results > 2^62
|
# Calculates base^exp, but careful, this overflows for results > 2^62
|
||||||
pow = base: exp: foldl' (a: x: x * a) 1 (genList (_: base) exp);
|
pow = base: exp: foldl' (a: x: x * a) 1 (genList (_: base) exp);
|
||||||
|
|
||||||
|
hexLiteralValues = {
|
||||||
|
"0" = 0;
|
||||||
|
"1" = 1;
|
||||||
|
"2" = 2;
|
||||||
|
"3" = 3;
|
||||||
|
"4" = 4;
|
||||||
|
"5" = 5;
|
||||||
|
"6" = 6;
|
||||||
|
"7" = 7;
|
||||||
|
"8" = 8;
|
||||||
|
"9" = 9;
|
||||||
|
"a" = 10;
|
||||||
|
"b" = 11;
|
||||||
|
"c" = 12;
|
||||||
|
"d" = 13;
|
||||||
|
"e" = 14;
|
||||||
|
"f" = 15;
|
||||||
|
"A" = 10;
|
||||||
|
"B" = 11;
|
||||||
|
"C" = 12;
|
||||||
|
"D" = 13;
|
||||||
|
"E" = 14;
|
||||||
|
"F" = 15;
|
||||||
|
};
|
||||||
|
|
||||||
# Converts the given hex string to an integer. Only reliable for inputs in [0, 2^63),
|
# Converts the given hex string to an integer. Only reliable for inputs in [0, 2^63),
|
||||||
# after that the sign bit will overflow.
|
# after that the sign bit will overflow.
|
||||||
hexToDec = v: let
|
hexToDec = v: foldl' (acc: x: acc * 16 + hexLiteralValues.${x}) 0 (stringToCharacters v);
|
||||||
literalValues = {
|
in {
|
||||||
"0" = 0;
|
lib =
|
||||||
"1" = 1;
|
super.lib
|
||||||
"2" = 2;
|
// {
|
||||||
"3" = 3;
|
inherit
|
||||||
"4" = 4;
|
concatAttrs
|
||||||
"5" = 5;
|
countOccurrences
|
||||||
"6" = 6;
|
duplicates
|
||||||
"7" = 7;
|
hexToDec
|
||||||
"8" = 8;
|
isAbsolutePath
|
||||||
"9" = 9;
|
mergeToplevelConfigs
|
||||||
"a" = 10;
|
pow
|
||||||
"b" = 11;
|
;
|
||||||
"c" = 12;
|
|
||||||
"d" = 13;
|
|
||||||
"e" = 14;
|
|
||||||
"f" = 15;
|
|
||||||
"A" = 10;
|
|
||||||
"B" = 11;
|
|
||||||
"C" = 12;
|
|
||||||
"D" = 13;
|
|
||||||
"E" = 14;
|
|
||||||
"F" = 15;
|
|
||||||
};
|
|
||||||
in
|
|
||||||
foldl' (acc: x: acc * 16 + literalValues.${x}) 0 (stringToCharacters v);
|
|
||||||
|
|
||||||
secrets = let
|
# TODO separate this or get rid of it
|
||||||
rageMasterIdentityArgs = concatMapStrings (x: "-i ${escapeShellArg x} ") inputs.self.secretsConfig.masterIdentities;
|
# TODO separate this or get rid of it
|
||||||
rageExtraEncryptionPubkeys =
|
# TODO separate this or get rid of it
|
||||||
concatMapStrings (
|
# TODO separate this or get rid of it
|
||||||
x:
|
secrets = let
|
||||||
if isAbsolutePath x
|
rageMasterIdentityArgs = concatMapStrings (x: "-i ${escapeShellArg x} ") inputs.self.secretsConfig.masterIdentities;
|
||||||
then "-R ${escapeShellArg x} "
|
rageExtraEncryptionPubkeys =
|
||||||
else "-r ${escapeShellArg x} "
|
concatMapStrings (
|
||||||
)
|
x:
|
||||||
inputs.self.secretsConfig.extraEncryptionPubkeys;
|
if isAbsolutePath x
|
||||||
in {
|
then "-R ${escapeShellArg x} "
|
||||||
# TODO replace these by lib.agenix-rekey
|
else "-r ${escapeShellArg x} "
|
||||||
# The arguments required to de-/encrypt a secret in this repository
|
)
|
||||||
rageDecryptArgs = "${rageMasterIdentityArgs}";
|
inputs.self.secretsConfig.extraEncryptionPubkeys;
|
||||||
rageEncryptArgs = "${rageMasterIdentityArgs} ${rageExtraEncryptionPubkeys}";
|
in {
|
||||||
};
|
# TODO replace these by lib.agenix-rekey
|
||||||
|
# The arguments required to de-/encrypt a secret in this repository
|
||||||
|
rageDecryptArgs = "${rageMasterIdentityArgs}";
|
||||||
|
rageEncryptArgs = "${rageMasterIdentityArgs} ${rageExtraEncryptionPubkeys}";
|
||||||
|
};
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
662
lib/net.nix
662
lib/net.nix
|
@ -1,50 +1,28 @@
|
||||||
inputs: let
|
inputs: self: super: let
|
||||||
inherit
|
inherit
|
||||||
(inputs.nixpkgs.lib)
|
(inputs.nixpkgs.lib)
|
||||||
all
|
all
|
||||||
any
|
any
|
||||||
assertMsg
|
assertMsg
|
||||||
attrNames
|
|
||||||
attrValues
|
|
||||||
concatLists
|
|
||||||
concatMap
|
|
||||||
concatMapStrings
|
|
||||||
concatStringsSep
|
|
||||||
elem
|
elem
|
||||||
escapeShellArg
|
|
||||||
filter
|
filter
|
||||||
flatten
|
|
||||||
flip
|
flip
|
||||||
foldAttrs
|
|
||||||
foldl'
|
foldl'
|
||||||
genAttrs
|
|
||||||
genList
|
|
||||||
hasInfix
|
hasInfix
|
||||||
head
|
head
|
||||||
isAttrs
|
|
||||||
mapAttrs'
|
|
||||||
mergeAttrs
|
|
||||||
min
|
min
|
||||||
mkMerge
|
|
||||||
mkOptionType
|
|
||||||
nameValuePair
|
|
||||||
optionalAttrs
|
|
||||||
partition
|
partition
|
||||||
range
|
range
|
||||||
recursiveUpdate
|
recursiveUpdate
|
||||||
removeSuffix
|
|
||||||
reverseList
|
reverseList
|
||||||
showOption
|
|
||||||
splitString
|
splitString
|
||||||
stringToCharacters
|
|
||||||
substring
|
substring
|
||||||
types
|
|
||||||
unique
|
unique
|
||||||
warnIf
|
warnIf
|
||||||
;
|
;
|
||||||
|
|
||||||
inherit
|
inherit
|
||||||
(import ./misc.nix inputs)
|
(self.lib)
|
||||||
hexToDec
|
hexToDec
|
||||||
pow
|
pow
|
||||||
;
|
;
|
||||||
|
@ -58,321 +36,325 @@ inputs: let
|
||||||
})
|
})
|
||||||
.lib
|
.lib
|
||||||
.net;
|
.net;
|
||||||
in
|
in {
|
||||||
recursiveUpdate libNet {
|
lib = recursiveUpdate super.lib {
|
||||||
cidr = rec {
|
net = recursiveUpdate (removeAttrs libNet ["types"]) {
|
||||||
# host :: (ip | mac | integer) -> cidr -> ip
|
cidr = rec {
|
||||||
#
|
# host :: (ip | mac | integer) -> cidr -> ip
|
||||||
# Wrapper that extends the original host function to
|
#
|
||||||
# check whether the argument `n` is in-range for the given cidr.
|
# Wrapper that extends the original host function to
|
||||||
#
|
# check whether the argument `n` is in-range for the given cidr.
|
||||||
# Examples:
|
#
|
||||||
#
|
# Examples:
|
||||||
# > net.cidr.host 255 "192.168.1.0/24"
|
#
|
||||||
# "192.168.1.255"
|
# > net.cidr.host 255 "192.168.1.0/24"
|
||||||
# > net.cidr.host (256) "192.168.1.0/24"
|
# "192.168.1.255"
|
||||||
# <fails with an error message>
|
# > net.cidr.host (256) "192.168.1.0/24"
|
||||||
# > net.cidr.host (-1) "192.168.1.0/24"
|
# <fails with an error message>
|
||||||
# "192.168.1.255"
|
# > net.cidr.host (-1) "192.168.1.0/24"
|
||||||
# > net.cidr.host (-256) "192.168.1.0/24"
|
# "192.168.1.255"
|
||||||
# "192.168.1.0"
|
# > net.cidr.host (-256) "192.168.1.0/24"
|
||||||
# > net.cidr.host (-257) "192.168.1.0/24"
|
# "192.168.1.0"
|
||||||
# <fails with an error message>
|
# > net.cidr.host (-257) "192.168.1.0/24"
|
||||||
host = i: n: let
|
# <fails with an error message>
|
||||||
cap = libNet.cidr.capacity n;
|
host = i: n: let
|
||||||
in
|
cap = libNet.cidr.capacity n;
|
||||||
assert assertMsg (i >= (-cap) && i < cap) "The host ${toString i} lies outside of ${n}";
|
in
|
||||||
libNet.cidr.host i n;
|
assert assertMsg (i >= (-cap) && i < cap) "The host ${toString i} lies outside of ${n}";
|
||||||
# hostCidr :: (ip | mac | integer) -> cidr -> cidr
|
libNet.cidr.host i n;
|
||||||
#
|
# hostCidr :: (ip | mac | integer) -> cidr -> cidr
|
||||||
# Returns the nth host in the given cidr range (like cidr.host)
|
#
|
||||||
# but as a cidr that retains the original prefix length.
|
# Returns the nth host in the given cidr range (like cidr.host)
|
||||||
#
|
# but as a cidr that retains the original prefix length.
|
||||||
# Examples:
|
#
|
||||||
#
|
# Examples:
|
||||||
# > net.cidr.hostCidr 2 "192.168.1.0/24"
|
#
|
||||||
# "192.168.1.2/24"
|
# > net.cidr.hostCidr 2 "192.168.1.0/24"
|
||||||
hostCidr = n: x: "${libNet.cidr.host n x}/${toString (libNet.cidr.length x)}";
|
# "192.168.1.2/24"
|
||||||
# ip :: (cidr | ip) -> ip
|
hostCidr = n: x: "${libNet.cidr.host n x}/${toString (libNet.cidr.length x)}";
|
||||||
#
|
# ip :: (cidr | ip) -> ip
|
||||||
# Returns just the ip part of the cidr.
|
#
|
||||||
#
|
# Returns just the ip part of the cidr.
|
||||||
# Examples:
|
#
|
||||||
#
|
# Examples:
|
||||||
# > net.cidr.ip "192.168.1.100/24"
|
#
|
||||||
# "192.168.1.100"
|
# > net.cidr.ip "192.168.1.100/24"
|
||||||
# > net.cidr.ip "192.168.1.100"
|
# "192.168.1.100"
|
||||||
# "192.168.1.100"
|
# > net.cidr.ip "192.168.1.100"
|
||||||
ip = x: head (splitString "/" x);
|
# "192.168.1.100"
|
||||||
# canonicalize :: cidr -> cidr
|
ip = x: head (splitString "/" x);
|
||||||
#
|
# canonicalize :: cidr -> cidr
|
||||||
# Replaces the ip of the cidr with the canonical network address
|
#
|
||||||
# (first contained address in range)
|
# Replaces the ip of the cidr with the canonical network address
|
||||||
#
|
# (first contained address in range)
|
||||||
# Examples:
|
#
|
||||||
#
|
# Examples:
|
||||||
# > net.cidr.canonicalize "192.168.1.100/24"
|
#
|
||||||
# "192.168.1.0/24"
|
# > net.cidr.canonicalize "192.168.1.100/24"
|
||||||
canonicalize = x: libNet.cidr.make (libNet.cidr.length x) (ip x);
|
# "192.168.1.0/24"
|
||||||
# mergev4 :: [cidrv4 | ipv4] -> (cidrv4 | null)
|
canonicalize = x: libNet.cidr.make (libNet.cidr.length x) (ip x);
|
||||||
#
|
# mergev4 :: [cidrv4 | ipv4] -> (cidrv4 | null)
|
||||||
# Returns the smallest cidr network that includes all given networks.
|
#
|
||||||
# If no cidr mask is given, /32 is assumed.
|
# Returns the smallest cidr network that includes all given networks.
|
||||||
#
|
# If no cidr mask is given, /32 is assumed.
|
||||||
# Examples:
|
#
|
||||||
#
|
# Examples:
|
||||||
# > net.cidr.mergev4 ["192.168.1.1/24" "192.168.6.1/32"]
|
#
|
||||||
# "192.168.0.0/21"
|
# > net.cidr.mergev4 ["192.168.1.1/24" "192.168.6.1/32"]
|
||||||
mergev4 = addrs_: let
|
# "192.168.0.0/21"
|
||||||
# Append /32 if necessary
|
mergev4 = addrs_: let
|
||||||
addrs = map (x:
|
# Append /32 if necessary
|
||||||
if hasInfix "/" x
|
addrs = map (x:
|
||||||
then x
|
if hasInfix "/" x
|
||||||
else "${x}/32")
|
then x
|
||||||
addrs_;
|
else "${x}/32")
|
||||||
# The smallest occurring length is the first we need to start checking, since
|
addrs_;
|
||||||
# any greater cidr length represents a smaller address range which
|
# The smallest occurring length is the first we need to start checking, since
|
||||||
# wouldn't contain all of the original addresses.
|
# any greater cidr length represents a smaller address range which
|
||||||
startLength = foldl' min 32 (map libNet.cidr.length addrs);
|
# wouldn't contain all of the original addresses.
|
||||||
possibleLengths = reverseList (range 0 startLength);
|
startLength = foldl' min 32 (map libNet.cidr.length addrs);
|
||||||
# The first ip address will be "expanded" in cidr length until it covers all other
|
possibleLengths = reverseList (range 0 startLength);
|
||||||
# used addresses.
|
# The first ip address will be "expanded" in cidr length until it covers all other
|
||||||
firstIp = ip (head addrs);
|
# used addresses.
|
||||||
# Return the first (i.e. greatest length -> smallest prefix) cidr length
|
firstIp = ip (head addrs);
|
||||||
# in the list that covers all used addresses
|
# Return the first (i.e. greatest length -> smallest prefix) cidr length
|
||||||
bestLength = head (filter
|
# in the list that covers all used addresses
|
||||||
# All given addresses must be contained by the generated address.
|
bestLength = head (filter
|
||||||
(len:
|
# All given addresses must be contained by the generated address.
|
||||||
all (x:
|
(len:
|
||||||
libNet.cidr.contains
|
all (x:
|
||||||
(ip x)
|
libNet.cidr.contains
|
||||||
(libNet.cidr.make len firstIp))
|
(ip x)
|
||||||
addrs)
|
(libNet.cidr.make len firstIp))
|
||||||
possibleLengths);
|
addrs)
|
||||||
in
|
possibleLengths);
|
||||||
assert assertMsg (!any (hasInfix ":") addrs) "mergev4 cannot operate on ipv6 addresses";
|
in
|
||||||
if addrs == []
|
assert assertMsg (!any (hasInfix ":") addrs) "mergev4 cannot operate on ipv6 addresses";
|
||||||
then null
|
if addrs == []
|
||||||
else libNet.cidr.make bestLength firstIp;
|
then null
|
||||||
# mergev6 :: [cidrv6 | ipv6] -> (cidrv6 | null)
|
else libNet.cidr.make bestLength firstIp;
|
||||||
#
|
# mergev6 :: [cidrv6 | ipv6] -> (cidrv6 | null)
|
||||||
# Returns the smallest cidr network that includes all given networks.
|
#
|
||||||
# If no cidr mask is given, /128 is assumed.
|
# Returns the smallest cidr network that includes all given networks.
|
||||||
#
|
# If no cidr mask is given, /128 is assumed.
|
||||||
# Examples:
|
#
|
||||||
#
|
# Examples:
|
||||||
# > net.cidr.mergev6 ["fd00:dead:cafe::/64" "fd00:fd12:3456:7890::/56"]
|
#
|
||||||
# "fd00:c000::/18"
|
# > net.cidr.mergev6 ["fd00:dead:cafe::/64" "fd00:fd12:3456:7890::/56"]
|
||||||
mergev6 = addrs_: let
|
# "fd00:c000::/18"
|
||||||
# Append /128 if necessary
|
mergev6 = addrs_: let
|
||||||
addrs = map (x:
|
# Append /128 if necessary
|
||||||
if hasInfix "/" x
|
addrs = map (x:
|
||||||
then x
|
if hasInfix "/" x
|
||||||
else "${x}/128")
|
then x
|
||||||
addrs_;
|
else "${x}/128")
|
||||||
# The smallest occurring length is the first we need to start checking, since
|
addrs_;
|
||||||
# any greater cidr length represents a smaller address range which
|
# The smallest occurring length is the first we need to start checking, since
|
||||||
# wouldn't contain all of the original addresses.
|
# any greater cidr length represents a smaller address range which
|
||||||
startLength = foldl' min 128 (map libNet.cidr.length addrs);
|
# wouldn't contain all of the original addresses.
|
||||||
possibleLengths = reverseList (range 0 startLength);
|
startLength = foldl' min 128 (map libNet.cidr.length addrs);
|
||||||
# The first ip address will be "expanded" in cidr length until it covers all other
|
possibleLengths = reverseList (range 0 startLength);
|
||||||
# used addresses.
|
# The first ip address will be "expanded" in cidr length until it covers all other
|
||||||
firstIp = ip (head addrs);
|
# used addresses.
|
||||||
# Return the first (i.e. greatest length -> smallest prefix) cidr length
|
firstIp = ip (head addrs);
|
||||||
# in the list that covers all used addresses
|
# Return the first (i.e. greatest length -> smallest prefix) cidr length
|
||||||
bestLength = head (filter
|
# in the list that covers all used addresses
|
||||||
# All given addresses must be contained by the generated address.
|
bestLength = head (filter
|
||||||
(len:
|
# All given addresses must be contained by the generated address.
|
||||||
all (x:
|
(len:
|
||||||
libNet.cidr.contains
|
all (x:
|
||||||
(ip x)
|
libNet.cidr.contains
|
||||||
(libNet.cidr.make len firstIp))
|
(ip x)
|
||||||
addrs)
|
(libNet.cidr.make len firstIp))
|
||||||
possibleLengths);
|
addrs)
|
||||||
in
|
possibleLengths);
|
||||||
assert assertMsg (all (hasInfix ":") addrs) "mergev6 cannot operate on ipv4 addresses";
|
in
|
||||||
if addrs == []
|
assert assertMsg (all (hasInfix ":") addrs) "mergev6 cannot operate on ipv4 addresses";
|
||||||
then null
|
if addrs == []
|
||||||
else libNet.cidr.make bestLength firstIp;
|
then null
|
||||||
# merge :: [cidr] -> { cidrv4 = (cidrv4 | null); cidrv6 = (cidrv4 | null); }
|
else libNet.cidr.make bestLength firstIp;
|
||||||
#
|
# merge :: [cidr] -> { cidrv4 = (cidrv4 | null); cidrv6 = (cidrv4 | null); }
|
||||||
# Returns the smallest cidr network that includes all given networks,
|
#
|
||||||
# but yields two separate result for all given ipv4 and ipv6 addresses.
|
# Returns the smallest cidr network that includes all given networks,
|
||||||
# Equivalent to calling mergev4 and mergev6 on a partition individually.
|
# but yields two separate result for all given ipv4 and ipv6 addresses.
|
||||||
merge = addrs: let
|
# Equivalent to calling mergev4 and mergev6 on a partition individually.
|
||||||
v4_and_v6 = partition (hasInfix ":") addrs;
|
merge = addrs: let
|
||||||
in {
|
v4_and_v6 = partition (hasInfix ":") addrs;
|
||||||
cidrv4 = mergev4 v4_and_v6.wrong;
|
in {
|
||||||
cidrv6 = mergev6 v4_and_v6.right;
|
cidrv4 = mergev4 v4_and_v6.wrong;
|
||||||
|
cidrv6 = mergev6 v4_and_v6.right;
|
||||||
|
};
|
||||||
|
# assignIps :: cidr -> [int | ip] -> [string] -> [ip]
|
||||||
|
#
|
||||||
|
# Assigns a semi-stable ip address from the given cidr network to each hostname.
|
||||||
|
# The algorithm is based on hashing (abusing sha256) with linear probing.
|
||||||
|
# The order of hosts doesn't matter. No ip (or offset) from the reserved list
|
||||||
|
# will be assigned. The network address and broadcast address will always be reserved
|
||||||
|
# automatically.
|
||||||
|
#
|
||||||
|
# Examples:
|
||||||
|
#
|
||||||
|
# > net.cidr.assignIps "192.168.100.1/24" [] ["a" "b" "c"]
|
||||||
|
# { a = "192.168.100.202"; b = "192.168.100.74"; c = "192.168.100.226"; }
|
||||||
|
#
|
||||||
|
# > net.cidr.assignIps "192.168.100.1/24" [] ["a" "b" "c" "a-new-elem"]
|
||||||
|
# { a = "192.168.100.202"; a-new-elem = "192.168.100.88"; b = "192.168.100.74"; c = "192.168.100.226"; }
|
||||||
|
#
|
||||||
|
# > net.cidr.assignIps "192.168.100.1/24" [202 "192.168.100.74"] ["a" "b" "c"]
|
||||||
|
# { a = "192.168.100.203"; b = "192.168.100.75"; c = "192.168.100.226"; }
|
||||||
|
assignIps = net: reserved: hosts: let
|
||||||
|
cidrSize = libNet.cidr.size net;
|
||||||
|
capacity = libNet.cidr.capacity net;
|
||||||
|
# The base address of the network. Used to convert ip-based reservations to offsets
|
||||||
|
baseAddr = host 0 net;
|
||||||
|
# Reserve some values for the network, host and broadcast address.
|
||||||
|
# The network and broadcast address should never be used, and we
|
||||||
|
# want to reserve the host address for the host. We also convert
|
||||||
|
# any ips to offsets here.
|
||||||
|
init = unique (
|
||||||
|
[0 (capacity - 1)]
|
||||||
|
++ flip map reserved (x:
|
||||||
|
if builtins.typeOf x == "int"
|
||||||
|
then x
|
||||||
|
else -(libNet.ip.diff baseAddr x))
|
||||||
|
);
|
||||||
|
nHosts = builtins.length hosts;
|
||||||
|
nInit = builtins.length init;
|
||||||
|
# Pre-sort all hosts, to ensure ordering invariance
|
||||||
|
sortedHosts =
|
||||||
|
warnIf
|
||||||
|
((nInit + nHosts) > 0.3 * capacity)
|
||||||
|
"assignIps: hash stability may be degraded since utilization is >30%"
|
||||||
|
(builtins.sort builtins.lessThan hosts);
|
||||||
|
# Generates a hash (i.e. offset value) for a given hostname
|
||||||
|
hashElem = x:
|
||||||
|
builtins.bitAnd (capacity - 1)
|
||||||
|
(hexToDec (builtins.substring 0 16 (builtins.hashString "sha256" x)));
|
||||||
|
# Do linear probing. Returns the first unused value at or after the given value.
|
||||||
|
probe = avoid: value:
|
||||||
|
if elem value avoid
|
||||||
|
# TODO lib.mod
|
||||||
|
# Poor man's modulo, because nix has no modulo. Luckily we operate on a residue
|
||||||
|
# class of x modulo 2^n, so we can use bitAnd instead.
|
||||||
|
then probe avoid (builtins.bitAnd (capacity - 1) (value + 1))
|
||||||
|
else value;
|
||||||
|
# Hash a new element and avoid assigning any existing values.
|
||||||
|
assignOne = {
|
||||||
|
assigned,
|
||||||
|
used,
|
||||||
|
}: x: let
|
||||||
|
value = probe used (hashElem x);
|
||||||
|
in {
|
||||||
|
assigned =
|
||||||
|
assigned
|
||||||
|
// {
|
||||||
|
${x} = host value net;
|
||||||
|
};
|
||||||
|
used = [value] ++ used;
|
||||||
|
};
|
||||||
|
in
|
||||||
|
assert assertMsg (cidrSize >= 2 && cidrSize <= 62)
|
||||||
|
"assignIps: cidrSize=${toString cidrSize} is not in [2, 62].";
|
||||||
|
assert assertMsg (nHosts <= capacity - nInit)
|
||||||
|
"assignIps: number of hosts (${toString nHosts}) must be <= capacity (${toString capacity}) - reserved (${toString nInit})";
|
||||||
|
# Assign an ip in the subnet to each element, in order
|
||||||
|
(foldl' assignOne {
|
||||||
|
assigned = {};
|
||||||
|
used = init;
|
||||||
|
}
|
||||||
|
sortedHosts)
|
||||||
|
.assigned;
|
||||||
|
};
|
||||||
|
ip = rec {
|
||||||
|
# Checks whether the given address (with or without cidr notation) is an ipv4 address.
|
||||||
|
isv4 = x: !isv6 x;
|
||||||
|
# Checks whether the given address (with or without cidr notation) is an ipv6 address.
|
||||||
|
isv6 = hasInfix ":";
|
||||||
|
};
|
||||||
|
mac = {
|
||||||
|
# Adds offset to the given base address and ensures the result is in
|
||||||
|
# a locally administered range by replacing the second nibble with a 2.
|
||||||
|
addPrivate = base: offset: let
|
||||||
|
added = libNet.mac.add base offset;
|
||||||
|
pre = substring 0 1 added;
|
||||||
|
suf = substring 2 (-1) added;
|
||||||
|
in "${pre}2${suf}";
|
||||||
|
# assignMacs :: mac (base) -> int (size) -> [int | mac] (reserved) -> [string] (hosts) -> [mac]
|
||||||
|
#
|
||||||
|
# Assigns a semi-stable MAC address starting in [base, base + 2^size) to each hostname.
|
||||||
|
# The algorithm is based on hashing (abusing sha256) with linear probing.
|
||||||
|
# The order of hosts doesn't matter. No mac (or offset) from the reserved list
|
||||||
|
# will be assigned.
|
||||||
|
#
|
||||||
|
# Examples:
|
||||||
|
#
|
||||||
|
# > net.mac.assignMacs "11:22:33:00:00:00" 24 [] ["a" "b" "c"]
|
||||||
|
# { a = "11:22:33:1b:bd:ca"; b = "11:22:33:39:59:4a"; c = "11:22:33:50:7a:e2"; }
|
||||||
|
#
|
||||||
|
# > net.mac.assignMacs "11:22:33:00:00:00" 24 [] ["a" "b" "c" "a-new-elem"]
|
||||||
|
# { a = "11:22:33:1b:bd:ca"; a-new-elem = "11:22:33:d6:5d:58"; b = "11:22:33:39:59:4a"; c = "11:22:33:50:7a:e2"; }
|
||||||
|
#
|
||||||
|
# > net.mac.assignMacs "11:22:33:00:00:00" 24 ["11:22:33:1b:bd:ca"] ["a" "b" "c"]
|
||||||
|
# { a = "11:22:33:1b:bd:cb"; b = "11:22:33:39:59:4a"; c = "11:22:33:50:7a:e2"; }
|
||||||
|
assignMacs = base: size: reserved: hosts: let
|
||||||
|
capacity = pow 2 size;
|
||||||
|
baseAsInt = libNet.mac.diff base "00:00:00:00:00:00";
|
||||||
|
init = unique (
|
||||||
|
flip map reserved (x:
|
||||||
|
if builtins.typeOf x == "int"
|
||||||
|
then x
|
||||||
|
else libNet.mac.diff x base)
|
||||||
|
);
|
||||||
|
nHosts = builtins.length hosts;
|
||||||
|
nInit = builtins.length init;
|
||||||
|
# Pre-sort all hosts, to ensure ordering invariance
|
||||||
|
sortedHosts =
|
||||||
|
warnIf
|
||||||
|
((nInit + nHosts) > 0.3 * capacity)
|
||||||
|
"assignMacs: hash stability may be degraded since utilization is >30%"
|
||||||
|
(builtins.sort builtins.lessThan hosts);
|
||||||
|
# Generates a hash (i.e. offset value) for a given hostname
|
||||||
|
hashElem = x:
|
||||||
|
builtins.bitAnd (capacity - 1)
|
||||||
|
(hexToDec (substring 0 16 (builtins.hashString "sha256" x)));
|
||||||
|
# Do linear probing. Returns the first unused value at or after the given value.
|
||||||
|
probe = avoid: value:
|
||||||
|
if elem value avoid
|
||||||
|
# TODO lib.mod
|
||||||
|
# Poor man's modulo, because nix has no modulo. Luckily we operate on a residue
|
||||||
|
# class of x modulo 2^n, so we can use bitAnd instead.
|
||||||
|
then probe avoid (builtins.bitAnd (capacity - 1) (value + 1))
|
||||||
|
else value;
|
||||||
|
# Hash a new element and avoid assigning any existing values.
|
||||||
|
assignOne = {
|
||||||
|
assigned,
|
||||||
|
used,
|
||||||
|
}: x: let
|
||||||
|
value = probe used (hashElem x);
|
||||||
|
in {
|
||||||
|
assigned =
|
||||||
|
assigned
|
||||||
|
// {
|
||||||
|
${x} = libNet.mac.add value base;
|
||||||
|
};
|
||||||
|
used = [value] ++ used;
|
||||||
|
};
|
||||||
|
in
|
||||||
|
assert assertMsg (size >= 2 && size <= 62)
|
||||||
|
"assignMacs: size=${toString size} is not in [2, 62].";
|
||||||
|
assert assertMsg (builtins.bitAnd (capacity - 1) baseAsInt == 0)
|
||||||
|
"assignMacs: the size=${toString size} least significant bits of the base mac address must be 0.";
|
||||||
|
assert assertMsg (nHosts <= capacity - nInit)
|
||||||
|
"assignMacs: number of hosts (${toString nHosts}) must be <= capacity (${toString capacity}) - reserved (${toString nInit})";
|
||||||
|
# Assign an ip in the subnet to each element, in order
|
||||||
|
(foldl' assignOne {
|
||||||
|
assigned = {};
|
||||||
|
used = init;
|
||||||
|
}
|
||||||
|
sortedHosts)
|
||||||
|
.assigned;
|
||||||
};
|
};
|
||||||
# assignIps :: cidr -> [int | ip] -> [string] -> [ip]
|
|
||||||
#
|
|
||||||
# Assigns a semi-stable ip address from the given cidr network to each hostname.
|
|
||||||
# The algorithm is based on hashing (abusing sha256) with linear probing.
|
|
||||||
# The order of hosts doesn't matter. No ip (or offset) from the reserved list
|
|
||||||
# will be assigned. The network address and broadcast address will always be reserved
|
|
||||||
# automatically.
|
|
||||||
#
|
|
||||||
# Examples:
|
|
||||||
#
|
|
||||||
# > net.cidr.assignIps "192.168.100.1/24" [] ["a" "b" "c"]
|
|
||||||
# { a = "192.168.100.202"; b = "192.168.100.74"; c = "192.168.100.226"; }
|
|
||||||
#
|
|
||||||
# > net.cidr.assignIps "192.168.100.1/24" [] ["a" "b" "c" "a-new-elem"]
|
|
||||||
# { a = "192.168.100.202"; a-new-elem = "192.168.100.88"; b = "192.168.100.74"; c = "192.168.100.226"; }
|
|
||||||
#
|
|
||||||
# > net.cidr.assignIps "192.168.100.1/24" [202 "192.168.100.74"] ["a" "b" "c"]
|
|
||||||
# { a = "192.168.100.203"; b = "192.168.100.75"; c = "192.168.100.226"; }
|
|
||||||
assignIps = net: reserved: hosts: let
|
|
||||||
cidrSize = libNet.cidr.size net;
|
|
||||||
capacity = libNet.cidr.capacity net;
|
|
||||||
# The base address of the network. Used to convert ip-based reservations to offsets
|
|
||||||
baseAddr = host 0 net;
|
|
||||||
# Reserve some values for the network, host and broadcast address.
|
|
||||||
# The network and broadcast address should never be used, and we
|
|
||||||
# want to reserve the host address for the host. We also convert
|
|
||||||
# any ips to offsets here.
|
|
||||||
init = unique (
|
|
||||||
[0 (capacity - 1)]
|
|
||||||
++ flip map reserved (x:
|
|
||||||
if builtins.typeOf x == "int"
|
|
||||||
then x
|
|
||||||
else -(libNet.ip.diff baseAddr x))
|
|
||||||
);
|
|
||||||
nHosts = builtins.length hosts;
|
|
||||||
nInit = builtins.length init;
|
|
||||||
# Pre-sort all hosts, to ensure ordering invariance
|
|
||||||
sortedHosts =
|
|
||||||
warnIf
|
|
||||||
((nInit + nHosts) > 0.3 * capacity)
|
|
||||||
"assignIps: hash stability may be degraded since utilization is >30%"
|
|
||||||
(builtins.sort builtins.lessThan hosts);
|
|
||||||
# Generates a hash (i.e. offset value) for a given hostname
|
|
||||||
hashElem = x:
|
|
||||||
builtins.bitAnd (capacity - 1)
|
|
||||||
(hexToDec (builtins.substring 0 16 (builtins.hashString "sha256" x)));
|
|
||||||
# Do linear probing. Returns the first unused value at or after the given value.
|
|
||||||
probe = avoid: value:
|
|
||||||
if elem value avoid
|
|
||||||
# TODO lib.mod
|
|
||||||
# Poor man's modulo, because nix has no modulo. Luckily we operate on a residue
|
|
||||||
# class of x modulo 2^n, so we can use bitAnd instead.
|
|
||||||
then probe avoid (builtins.bitAnd (capacity - 1) (value + 1))
|
|
||||||
else value;
|
|
||||||
# Hash a new element and avoid assigning any existing values.
|
|
||||||
assignOne = {
|
|
||||||
assigned,
|
|
||||||
used,
|
|
||||||
}: x: let
|
|
||||||
value = probe used (hashElem x);
|
|
||||||
in {
|
|
||||||
assigned =
|
|
||||||
assigned
|
|
||||||
// {
|
|
||||||
${x} = host value net;
|
|
||||||
};
|
|
||||||
used = [value] ++ used;
|
|
||||||
};
|
|
||||||
in
|
|
||||||
assert assertMsg (cidrSize >= 2 && cidrSize <= 62)
|
|
||||||
"assignIps: cidrSize=${toString cidrSize} is not in [2, 62].";
|
|
||||||
assert assertMsg (nHosts <= capacity - nInit)
|
|
||||||
"assignIps: number of hosts (${toString nHosts}) must be <= capacity (${toString capacity}) - reserved (${toString nInit})";
|
|
||||||
# Assign an ip in the subnet to each element, in order
|
|
||||||
(foldl' assignOne {
|
|
||||||
assigned = {};
|
|
||||||
used = init;
|
|
||||||
}
|
|
||||||
sortedHosts)
|
|
||||||
.assigned;
|
|
||||||
};
|
};
|
||||||
ip = rec {
|
types.net = libNet.types;
|
||||||
# Checks whether the given address (with or without cidr notation) is an ipv4 address.
|
};
|
||||||
isv4 = x: !isv6 x;
|
}
|
||||||
# Checks whether the given address (with or without cidr notation) is an ipv6 address.
|
|
||||||
isv6 = hasInfix ":";
|
|
||||||
};
|
|
||||||
mac = {
|
|
||||||
# Adds offset to the given base address and ensures the result is in
|
|
||||||
# a locally administered range by replacing the second nibble with a 2.
|
|
||||||
addPrivate = base: offset: let
|
|
||||||
added = libNet.mac.add base offset;
|
|
||||||
pre = substring 0 1 added;
|
|
||||||
suf = substring 2 (-1) added;
|
|
||||||
in "${pre}2${suf}";
|
|
||||||
# assignMacs :: mac (base) -> int (size) -> [int | mac] (reserved) -> [string] (hosts) -> [mac]
|
|
||||||
#
|
|
||||||
# Assigns a semi-stable MAC address starting in [base, base + 2^size) to each hostname.
|
|
||||||
# The algorithm is based on hashing (abusing sha256) with linear probing.
|
|
||||||
# The order of hosts doesn't matter. No mac (or offset) from the reserved list
|
|
||||||
# will be assigned.
|
|
||||||
#
|
|
||||||
# Examples:
|
|
||||||
#
|
|
||||||
# > net.mac.assignMacs "11:22:33:00:00:00" 24 [] ["a" "b" "c"]
|
|
||||||
# { a = "11:22:33:1b:bd:ca"; b = "11:22:33:39:59:4a"; c = "11:22:33:50:7a:e2"; }
|
|
||||||
#
|
|
||||||
# > net.mac.assignMacs "11:22:33:00:00:00" 24 [] ["a" "b" "c" "a-new-elem"]
|
|
||||||
# { a = "11:22:33:1b:bd:ca"; a-new-elem = "11:22:33:d6:5d:58"; b = "11:22:33:39:59:4a"; c = "11:22:33:50:7a:e2"; }
|
|
||||||
#
|
|
||||||
# > net.mac.assignMacs "11:22:33:00:00:00" 24 ["11:22:33:1b:bd:ca"] ["a" "b" "c"]
|
|
||||||
# { a = "11:22:33:1b:bd:cb"; b = "11:22:33:39:59:4a"; c = "11:22:33:50:7a:e2"; }
|
|
||||||
assignMacs = base: size: reserved: hosts: let
|
|
||||||
capacity = pow 2 size;
|
|
||||||
baseAsInt = libNet.mac.diff base "00:00:00:00:00:00";
|
|
||||||
init = unique (
|
|
||||||
flip map reserved (x:
|
|
||||||
if builtins.typeOf x == "int"
|
|
||||||
then x
|
|
||||||
else libNet.mac.diff x base)
|
|
||||||
);
|
|
||||||
nHosts = builtins.length hosts;
|
|
||||||
nInit = builtins.length init;
|
|
||||||
# Pre-sort all hosts, to ensure ordering invariance
|
|
||||||
sortedHosts =
|
|
||||||
warnIf
|
|
||||||
((nInit + nHosts) > 0.3 * capacity)
|
|
||||||
"assignMacs: hash stability may be degraded since utilization is >30%"
|
|
||||||
(builtins.sort builtins.lessThan hosts);
|
|
||||||
# Generates a hash (i.e. offset value) for a given hostname
|
|
||||||
hashElem = x:
|
|
||||||
builtins.bitAnd (capacity - 1)
|
|
||||||
(hexToDec (builtins.substring 0 16 (builtins.hashString "sha256" x)));
|
|
||||||
# Do linear probing. Returns the first unused value at or after the given value.
|
|
||||||
probe = avoid: value:
|
|
||||||
if elem value avoid
|
|
||||||
# TODO lib.mod
|
|
||||||
# Poor man's modulo, because nix has no modulo. Luckily we operate on a residue
|
|
||||||
# class of x modulo 2^n, so we can use bitAnd instead.
|
|
||||||
then probe avoid (builtins.bitAnd (capacity - 1) (value + 1))
|
|
||||||
else value;
|
|
||||||
# Hash a new element and avoid assigning any existing values.
|
|
||||||
assignOne = {
|
|
||||||
assigned,
|
|
||||||
used,
|
|
||||||
}: x: let
|
|
||||||
value = probe used (hashElem x);
|
|
||||||
in {
|
|
||||||
assigned =
|
|
||||||
assigned
|
|
||||||
// {
|
|
||||||
${x} = libNet.mac.add value base;
|
|
||||||
};
|
|
||||||
used = [value] ++ used;
|
|
||||||
};
|
|
||||||
in
|
|
||||||
assert assertMsg (size >= 2 && size <= 62)
|
|
||||||
"assignMacs: size=${toString size} is not in [2, 62].";
|
|
||||||
assert assertMsg (builtins.bitAnd (capacity - 1) baseAsInt == 0)
|
|
||||||
"assignMacs: the size=${toString size} least significant bits of the base mac address must be 0.";
|
|
||||||
assert assertMsg (nHosts <= capacity - nInit)
|
|
||||||
"assignMacs: number of hosts (${toString nHosts}) must be <= capacity (${toString capacity}) - reserved (${toString nInit})";
|
|
||||||
# Assign an ip in the subnet to each element, in order
|
|
||||||
(foldl' assignOne {
|
|
||||||
assigned = {};
|
|
||||||
used = init;
|
|
||||||
}
|
|
||||||
sortedHosts)
|
|
||||||
.assigned;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,48 +1,15 @@
|
||||||
inputs: let
|
inputs: self: super: let
|
||||||
inherit
|
inherit
|
||||||
(inputs.nixpkgs.lib)
|
(inputs.nixpkgs.lib)
|
||||||
all
|
all
|
||||||
any
|
|
||||||
assertMsg
|
assertMsg
|
||||||
attrNames
|
|
||||||
attrValues
|
|
||||||
concatLists
|
|
||||||
concatMap
|
|
||||||
concatMapStrings
|
|
||||||
concatStringsSep
|
|
||||||
elem
|
|
||||||
escapeShellArg
|
|
||||||
filter
|
|
||||||
flatten
|
|
||||||
flip
|
|
||||||
foldAttrs
|
|
||||||
foldl'
|
|
||||||
genAttrs
|
|
||||||
genList
|
|
||||||
hasInfix
|
|
||||||
head
|
|
||||||
isAttrs
|
isAttrs
|
||||||
mapAttrs'
|
|
||||||
mergeAttrs
|
|
||||||
min
|
|
||||||
mkMerge
|
|
||||||
mkOptionType
|
mkOptionType
|
||||||
nameValuePair
|
|
||||||
optionalAttrs
|
|
||||||
partition
|
|
||||||
range
|
|
||||||
recursiveUpdate
|
recursiveUpdate
|
||||||
removeSuffix
|
|
||||||
reverseList
|
|
||||||
showOption
|
showOption
|
||||||
splitString
|
|
||||||
stringToCharacters
|
|
||||||
substring
|
|
||||||
types
|
types
|
||||||
unique
|
|
||||||
warnIf
|
|
||||||
;
|
;
|
||||||
in rec {
|
|
||||||
# Checks whether the value is a lazy value without causing
|
# Checks whether the value is a lazy value without causing
|
||||||
# it's value to be evaluated
|
# it's value to be evaluated
|
||||||
isLazyValue = x: isAttrs x && x ? _lazyValue;
|
isLazyValue = x: isAttrs x && x ? _lazyValue;
|
||||||
|
@ -71,4 +38,15 @@ in rec {
|
||||||
# Represents a value or lazy value of the given type that will
|
# Represents a value or lazy value of the given type that will
|
||||||
# automatically be coerced to the given type when merged.
|
# automatically be coerced to the given type when merged.
|
||||||
lazyOf = type: types.coercedTo (lazyValueOf type) (x: x._lazyValue) type;
|
lazyOf = type: types.coercedTo (lazyValueOf type) (x: x._lazyValue) type;
|
||||||
|
in {
|
||||||
|
lib = recursiveUpdate super.lib {
|
||||||
|
types = {
|
||||||
|
inherit
|
||||||
|
isLazyValue
|
||||||
|
lazyValue
|
||||||
|
lazyValueOf
|
||||||
|
lazyOf
|
||||||
|
;
|
||||||
|
};
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,218 +1,225 @@
|
||||||
inputs: wgName: let
|
inputs: self: super: let
|
||||||
inherit
|
inherit
|
||||||
(inputs.nixpkgs.lib)
|
(inputs.nixpkgs.lib)
|
||||||
all
|
|
||||||
any
|
|
||||||
assertMsg
|
assertMsg
|
||||||
attrNames
|
attrNames
|
||||||
attrValues
|
attrValues
|
||||||
concatLists
|
concatLists
|
||||||
concatMap
|
concatMap
|
||||||
concatMapStrings
|
|
||||||
concatStringsSep
|
concatStringsSep
|
||||||
elem
|
|
||||||
escapeShellArg
|
escapeShellArg
|
||||||
filter
|
filter
|
||||||
flatten
|
flatten
|
||||||
flip
|
flip
|
||||||
foldAttrs
|
|
||||||
foldl'
|
|
||||||
genAttrs
|
genAttrs
|
||||||
genList
|
|
||||||
hasInfix
|
|
||||||
head
|
|
||||||
isAttrs
|
|
||||||
mapAttrs'
|
mapAttrs'
|
||||||
mergeAttrs
|
|
||||||
min
|
|
||||||
mkMerge
|
|
||||||
mkOptionType
|
|
||||||
nameValuePair
|
nameValuePair
|
||||||
optionalAttrs
|
|
||||||
partition
|
partition
|
||||||
range
|
|
||||||
recursiveUpdate
|
|
||||||
removeSuffix
|
removeSuffix
|
||||||
reverseList
|
|
||||||
showOption
|
|
||||||
splitString
|
|
||||||
stringToCharacters
|
|
||||||
substring
|
|
||||||
types
|
|
||||||
unique
|
|
||||||
warnIf
|
|
||||||
;
|
|
||||||
|
|
||||||
net = import ./net.nix inputs;
|
|
||||||
misc = import ./misc.nix inputs;
|
|
||||||
inherit
|
|
||||||
(import ./types.nix inputs)
|
|
||||||
isLazyValue
|
|
||||||
;
|
;
|
||||||
|
|
||||||
inherit
|
inherit
|
||||||
(misc)
|
(self.lib)
|
||||||
|
net
|
||||||
concatAttrs
|
concatAttrs
|
||||||
|
types
|
||||||
;
|
;
|
||||||
|
|
||||||
inherit
|
inherit
|
||||||
(misc.secrets)
|
(self.lib.secrets)
|
||||||
rageDecryptArgs
|
rageDecryptArgs
|
||||||
;
|
;
|
||||||
|
|
||||||
inherit (inputs.self) nodes;
|
inherit (inputs.self) nodes;
|
||||||
in rec {
|
in {
|
||||||
# Returns the given node's wireguard configuration of this network
|
lib =
|
||||||
wgCfgOf = node: nodes.${node}.config.meta.wireguard.${wgName};
|
super.lib
|
||||||
|
// {
|
||||||
|
wireguard = wgName: let
|
||||||
|
# Returns the given node's wireguard configuration of this network
|
||||||
|
wgCfgOf = node: nodes.${node}.config.meta.wireguard.${wgName};
|
||||||
|
|
||||||
sortedPeers = peerA: peerB:
|
sortedPeers = peerA: peerB:
|
||||||
if peerA < peerB
|
if peerA < peerB
|
||||||
then {
|
then {
|
||||||
peer1 = peerA;
|
peer1 = peerA;
|
||||||
peer2 = peerB;
|
peer2 = peerB;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
peer1 = peerB;
|
peer1 = peerB;
|
||||||
peer2 = peerA;
|
peer2 = peerA;
|
||||||
|
};
|
||||||
|
|
||||||
|
peerPublicKeyFile = peerName: "/secrets/wireguard/${wgName}/keys/${peerName}.pub";
|
||||||
|
peerPublicKeyPath = peerName: inputs.self.outPath + peerPublicKeyFile peerName;
|
||||||
|
|
||||||
|
peerPrivateKeyFile = peerName: "/secrets/wireguard/${wgName}/keys/${peerName}.age";
|
||||||
|
peerPrivateKeyPath = peerName: inputs.self.outPath + 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: inputs.self.outPath + 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
|
||||||
|
participatingNodes =
|
||||||
|
filter
|
||||||
|
(n: builtins.hasAttr wgName nodes.${n}.config.meta.wireguard)
|
||||||
|
(attrNames nodes);
|
||||||
|
|
||||||
|
# Partition nodes by whether they are servers
|
||||||
|
_participatingNodes_isServerPartition =
|
||||||
|
partition
|
||||||
|
(n: (wgCfgOf n).server.host != null)
|
||||||
|
participatingNodes;
|
||||||
|
|
||||||
|
participatingServerNodes = _participatingNodes_isServerPartition.right;
|
||||||
|
participatingClientNodes = _participatingNodes_isServerPartition.wrong;
|
||||||
|
|
||||||
|
# Maps all nodes that are part of this network to their addresses
|
||||||
|
nodePeers = genAttrs participatingNodes (n: (wgCfgOf n).addresses);
|
||||||
|
|
||||||
|
externalPeerName = p: "external-${p}";
|
||||||
|
|
||||||
|
# Only peers that are defined as externalPeers on the given node.
|
||||||
|
# Prepends "external-" to their name.
|
||||||
|
externalPeersForNode = node:
|
||||||
|
mapAttrs' (p: nameValuePair (externalPeerName p)) (wgCfgOf node).server.externalPeers;
|
||||||
|
|
||||||
|
# All peers that are defined as externalPeers on any node.
|
||||||
|
# Prepends "external-" to their name.
|
||||||
|
allExternalPeers = concatAttrs (map externalPeersForNode participatingNodes);
|
||||||
|
|
||||||
|
# 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 (wgCfgOf n).server.externalPeers) participatingNodes;
|
||||||
|
|
||||||
|
# A list of all occurring addresses.
|
||||||
|
usedAddresses =
|
||||||
|
concatMap (n: (wgCfgOf n).addresses) participatingNodes
|
||||||
|
++ flatten (concatMap (n: attrValues (wgCfgOf n).server.externalPeers) participatingNodes);
|
||||||
|
|
||||||
|
# A list of all occurring addresses, but only includes addresses that
|
||||||
|
# are not assigned automatically.
|
||||||
|
explicitlyUsedAddresses =
|
||||||
|
flip concatMap participatingNodes
|
||||||
|
(n:
|
||||||
|
filter (x: !types.isLazyValue x)
|
||||||
|
(concatLists
|
||||||
|
(nodes.${n}.options.meta.wireguard.type.functor.wrapped.getSubOptions (wgCfgOf n)).addresses.definitions))
|
||||||
|
++ flatten (concatMap (n: attrValues (wgCfgOf n).server.externalPeers) participatingNodes);
|
||||||
|
|
||||||
|
# The cidrv4 and cidrv6 of the network spanned by all participating peer addresses.
|
||||||
|
# This also takes into account any reserved address ranges that should be part of the network.
|
||||||
|
networkAddresses =
|
||||||
|
net.cidr.merge (usedAddresses
|
||||||
|
++ concatMap (n: (wgCfgOf n).server.reservedAddresses) participatingServerNodes);
|
||||||
|
|
||||||
|
# The network spanning cidr addresses. The respective cidrv4 and cirdv6 are only
|
||||||
|
# included if they exist.
|
||||||
|
networkCidrs = filter (x: x != null) (attrValues networkAddresses);
|
||||||
|
|
||||||
|
# The cidrv4 and cidrv6 of the network spanned by all reserved addresses only.
|
||||||
|
# Used to determine automatically assigned addresses first.
|
||||||
|
spannedReservedNetwork =
|
||||||
|
net.cidr.merge (concatMap (n: (wgCfgOf n).server.reservedAddresses) participatingServerNodes);
|
||||||
|
|
||||||
|
# Assigns an ipv4 address from spannedReservedNetwork.cidrv4
|
||||||
|
# to each participant that has not explicitly specified an ipv4 address.
|
||||||
|
assignedIpv4Addresses = assert assertMsg
|
||||||
|
(spannedReservedNetwork.cidrv4 != null)
|
||||||
|
"Wireguard network '${wgName}': At least one participating node must reserve a cidrv4 address via `reservedAddresses` so that ipv4 addresses can be assigned automatically from that network.";
|
||||||
|
net.cidr.assignIps
|
||||||
|
spannedReservedNetwork.cidrv4
|
||||||
|
# Don't assign any addresses that are explicitly configured on other hosts
|
||||||
|
(filter (x: net.cidr.contains x spannedReservedNetwork.cidrv4) (filter net.ip.isv4 explicitlyUsedAddresses))
|
||||||
|
participatingNodes;
|
||||||
|
|
||||||
|
# Assigns an ipv4 address from spannedReservedNetwork.cidrv4
|
||||||
|
# to each participant that has not explicitly specified an ipv4 address.
|
||||||
|
assignedIpv6Addresses = assert assertMsg
|
||||||
|
(spannedReservedNetwork.cidrv6 != null)
|
||||||
|
"Wireguard network '${wgName}': At least one participating node must reserve a cidrv6 address via `reservedAddresses` so that ipv4 addresses can be assigned automatically from that network.";
|
||||||
|
net.cidr.assignIps
|
||||||
|
spannedReservedNetwork.cidrv6
|
||||||
|
# Don't assign any addresses that are explicitly configured on other hosts
|
||||||
|
(filter (x: net.cidr.contains x spannedReservedNetwork.cidrv6) (filter net.ip.isv6 explicitlyUsedAddresses))
|
||||||
|
participatingNodes;
|
||||||
|
|
||||||
|
# Appends / replaces the correct cidr length to the argument,
|
||||||
|
# so that the resulting address is in the cidr.
|
||||||
|
toNetworkAddr = addr: let
|
||||||
|
relevantNetworkAddr =
|
||||||
|
if net.ip.isv6 addr
|
||||||
|
then networkAddresses.cidrv6
|
||||||
|
else networkAddresses.cidrv4;
|
||||||
|
in "${net.cidr.ip addr}/${toString (net.cidr.length relevantNetworkAddr)}";
|
||||||
|
|
||||||
|
# Creates a script that when executed outputs a wg-quick compatible configuration
|
||||||
|
# file for use with external peers. This is a script so we can access secrets without
|
||||||
|
# storing them in the nix-store.
|
||||||
|
wgQuickConfigScript = system: serverNode: extPeer: let
|
||||||
|
pkgs = inputs.self.pkgs.${system};
|
||||||
|
snCfg = wgCfgOf serverNode;
|
||||||
|
peerName = externalPeerName extPeer;
|
||||||
|
addresses = map toNetworkAddr snCfg.server.externalPeers.${extPeer};
|
||||||
|
in
|
||||||
|
pkgs.writeShellScript "create-wg-conf-${wgName}-${serverNode}-${extPeer}" ''
|
||||||
|
privKey=$(${pkgs.rage}/bin/rage -d ${rageDecryptArgs} ${escapeShellArg (peerPrivateKeyPath peerName)}) \
|
||||||
|
|| { echo "[1;31merror:[m Failed to decrypt!" >&2; exit 1; }
|
||||||
|
serverPsk=$(${pkgs.rage}/bin/rage -d ${rageDecryptArgs} ${escapeShellArg (peerPresharedKeyPath serverNode peerName)}) \
|
||||||
|
|| { echo "[1;31merror:[m Failed to decrypt!" >&2; exit 1; }
|
||||||
|
|
||||||
|
cat <<EOF
|
||||||
|
[Interface]
|
||||||
|
Address = ${concatStringsSep ", " addresses}
|
||||||
|
PrivateKey = $privKey
|
||||||
|
|
||||||
|
[Peer]
|
||||||
|
PublicKey = ${removeSuffix "\n" (builtins.readFile (peerPublicKeyPath serverNode))}
|
||||||
|
PresharedKey = $serverPsk
|
||||||
|
AllowedIPs = ${concatStringsSep ", " networkCidrs}
|
||||||
|
Endpoint = ${snCfg.server.host}:${toString snCfg.server.port}
|
||||||
|
PersistentKeepalive = 25
|
||||||
|
EOF
|
||||||
|
'';
|
||||||
|
in {
|
||||||
|
inherit
|
||||||
|
allExternalPeers
|
||||||
|
allPeers
|
||||||
|
assignedIpv4Addresses
|
||||||
|
assignedIpv6Addresses
|
||||||
|
explicitlyUsedAddresses
|
||||||
|
externalPeerName
|
||||||
|
externalPeerNamesRaw
|
||||||
|
externalPeersForNode
|
||||||
|
networkAddresses
|
||||||
|
networkCidrs
|
||||||
|
nodePeers
|
||||||
|
participatingClientNodes
|
||||||
|
participatingNodes
|
||||||
|
participatingServerNodes
|
||||||
|
peerPresharedKeyFile
|
||||||
|
peerPresharedKeyPath
|
||||||
|
peerPresharedKeySecret
|
||||||
|
peerPrivateKeyFile
|
||||||
|
peerPrivateKeyPath
|
||||||
|
peerPrivateKeySecret
|
||||||
|
peerPublicKeyFile
|
||||||
|
peerPublicKeyPath
|
||||||
|
sortedPeers
|
||||||
|
spannedReservedNetwork
|
||||||
|
toNetworkAddr
|
||||||
|
usedAddresses
|
||||||
|
wgCfgOf
|
||||||
|
wgQuickConfigScript
|
||||||
|
;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
peerPublicKeyFile = peerName: "/secrets/wireguard/${wgName}/keys/${peerName}.pub";
|
|
||||||
peerPublicKeyPath = peerName: inputs.self.outPath + peerPublicKeyFile peerName;
|
|
||||||
|
|
||||||
peerPrivateKeyFile = peerName: "/secrets/wireguard/${wgName}/keys/${peerName}.age";
|
|
||||||
peerPrivateKeyPath = peerName: inputs.self.outPath + 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: inputs.self.outPath + 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
|
|
||||||
participatingNodes =
|
|
||||||
filter
|
|
||||||
(n: builtins.hasAttr wgName nodes.${n}.config.meta.wireguard)
|
|
||||||
(attrNames nodes);
|
|
||||||
|
|
||||||
# Partition nodes by whether they are servers
|
|
||||||
_participatingNodes_isServerPartition =
|
|
||||||
partition
|
|
||||||
(n: (wgCfgOf n).server.host != null)
|
|
||||||
participatingNodes;
|
|
||||||
|
|
||||||
participatingServerNodes = _participatingNodes_isServerPartition.right;
|
|
||||||
participatingClientNodes = _participatingNodes_isServerPartition.wrong;
|
|
||||||
|
|
||||||
# Maps all nodes that are part of this network to their addresses
|
|
||||||
nodePeers = genAttrs participatingNodes (n: (wgCfgOf n).addresses);
|
|
||||||
|
|
||||||
externalPeerName = p: "external-${p}";
|
|
||||||
|
|
||||||
# Only peers that are defined as externalPeers on the given node.
|
|
||||||
# Prepends "external-" to their name.
|
|
||||||
externalPeersForNode = node:
|
|
||||||
mapAttrs' (p: nameValuePair (externalPeerName p)) (wgCfgOf node).server.externalPeers;
|
|
||||||
|
|
||||||
# All peers that are defined as externalPeers on any node.
|
|
||||||
# Prepends "external-" to their name.
|
|
||||||
allExternalPeers = concatAttrs (map externalPeersForNode participatingNodes);
|
|
||||||
|
|
||||||
# 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 (wgCfgOf n).server.externalPeers) participatingNodes;
|
|
||||||
|
|
||||||
# A list of all occurring addresses.
|
|
||||||
usedAddresses =
|
|
||||||
concatMap (n: (wgCfgOf n).addresses) participatingNodes
|
|
||||||
++ flatten (concatMap (n: attrValues (wgCfgOf n).server.externalPeers) participatingNodes);
|
|
||||||
|
|
||||||
# A list of all occurring addresses, but only includes addresses that
|
|
||||||
# are not assigned automatically.
|
|
||||||
explicitlyUsedAddresses =
|
|
||||||
flip concatMap participatingNodes
|
|
||||||
(n:
|
|
||||||
filter (x: !isLazyValue x)
|
|
||||||
(concatLists
|
|
||||||
(nodes.${n}.options.meta.wireguard.type.functor.wrapped.getSubOptions (wgCfgOf n)).addresses.definitions))
|
|
||||||
++ flatten (concatMap (n: attrValues (wgCfgOf n).server.externalPeers) participatingNodes);
|
|
||||||
|
|
||||||
# The cidrv4 and cidrv6 of the network spanned by all participating peer addresses.
|
|
||||||
# This also takes into account any reserved address ranges that should be part of the network.
|
|
||||||
networkAddresses =
|
|
||||||
net.cidr.merge (usedAddresses
|
|
||||||
++ concatMap (n: (wgCfgOf n).server.reservedAddresses) participatingServerNodes);
|
|
||||||
|
|
||||||
# The network spanning cidr addresses. The respective cidrv4 and cirdv6 are only
|
|
||||||
# included if they exist.
|
|
||||||
networkCidrs = filter (x: x != null) (attrValues networkAddresses);
|
|
||||||
|
|
||||||
# The cidrv4 and cidrv6 of the network spanned by all reserved addresses only.
|
|
||||||
# Used to determine automatically assigned addresses first.
|
|
||||||
spannedReservedNetwork =
|
|
||||||
net.cidr.merge (concatMap (n: (wgCfgOf n).server.reservedAddresses) participatingServerNodes);
|
|
||||||
|
|
||||||
# Assigns an ipv4 address from spannedReservedNetwork.cidrv4
|
|
||||||
# to each participant that has not explicitly specified an ipv4 address.
|
|
||||||
assignedIpv4Addresses = assert assertMsg
|
|
||||||
(spannedReservedNetwork.cidrv4 != null)
|
|
||||||
"Wireguard network '${wgName}': At least one participating node must reserve a cidrv4 address via `reservedAddresses` so that ipv4 addresses can be assigned automatically from that network.";
|
|
||||||
net.cidr.assignIps
|
|
||||||
spannedReservedNetwork.cidrv4
|
|
||||||
# Don't assign any addresses that are explicitly configured on other hosts
|
|
||||||
(filter (x: net.cidr.contains x spannedReservedNetwork.cidrv4) (filter net.ip.isv4 explicitlyUsedAddresses))
|
|
||||||
participatingNodes;
|
|
||||||
|
|
||||||
# Assigns an ipv4 address from spannedReservedNetwork.cidrv4
|
|
||||||
# to each participant that has not explicitly specified an ipv4 address.
|
|
||||||
assignedIpv6Addresses = assert assertMsg
|
|
||||||
(spannedReservedNetwork.cidrv6 != null)
|
|
||||||
"Wireguard network '${wgName}': At least one participating node must reserve a cidrv6 address via `reservedAddresses` so that ipv4 addresses can be assigned automatically from that network.";
|
|
||||||
net.cidr.assignIps
|
|
||||||
spannedReservedNetwork.cidrv6
|
|
||||||
# Don't assign any addresses that are explicitly configured on other hosts
|
|
||||||
(filter (x: net.cidr.contains x spannedReservedNetwork.cidrv6) (filter net.ip.isv6 explicitlyUsedAddresses))
|
|
||||||
participatingNodes;
|
|
||||||
|
|
||||||
# Appends / replaces the correct cidr length to the argument,
|
|
||||||
# so that the resulting address is in the cidr.
|
|
||||||
toNetworkAddr = addr: let
|
|
||||||
relevantNetworkAddr =
|
|
||||||
if net.ip.isv6 addr
|
|
||||||
then networkAddresses.cidrv6
|
|
||||||
else networkAddresses.cidrv4;
|
|
||||||
in "${net.cidr.ip addr}/${toString (net.cidr.length relevantNetworkAddr)}";
|
|
||||||
|
|
||||||
# Creates a script that when executed outputs a wg-quick compatible configuration
|
|
||||||
# file for use with external peers. This is a script so we can access secrets without
|
|
||||||
# storing them in the nix-store.
|
|
||||||
wgQuickConfigScript = system: serverNode: extPeer: let
|
|
||||||
pkgs = inputs.self.pkgs.${system};
|
|
||||||
snCfg = wgCfgOf serverNode;
|
|
||||||
peerName = externalPeerName extPeer;
|
|
||||||
addresses = map toNetworkAddr snCfg.server.externalPeers.${extPeer};
|
|
||||||
in
|
|
||||||
pkgs.writeShellScript "create-wg-conf-${wgName}-${serverNode}-${extPeer}" ''
|
|
||||||
privKey=$(${pkgs.rage}/bin/rage -d ${rageDecryptArgs} ${escapeShellArg (peerPrivateKeyPath peerName)}) \
|
|
||||||
|| { echo "[1;31merror:[m Failed to decrypt!" >&2; exit 1; }
|
|
||||||
serverPsk=$(${pkgs.rage}/bin/rage -d ${rageDecryptArgs} ${escapeShellArg (peerPresharedKeyPath serverNode peerName)}) \
|
|
||||||
|| { echo "[1;31merror:[m Failed to decrypt!" >&2; exit 1; }
|
|
||||||
|
|
||||||
cat <<EOF
|
|
||||||
[Interface]
|
|
||||||
Address = ${concatStringsSep ", " addresses}
|
|
||||||
PrivateKey = $privKey
|
|
||||||
|
|
||||||
[Peer]
|
|
||||||
PublicKey = ${removeSuffix "\n" (builtins.readFile (peerPublicKeyPath serverNode))}
|
|
||||||
PresharedKey = $serverPsk
|
|
||||||
AllowedIPs = ${concatStringsSep ", " networkCidrs}
|
|
||||||
Endpoint = ${snCfg.server.host}:${toString snCfg.server.port}
|
|
||||||
PersistentKeepalive = 25
|
|
||||||
EOF
|
|
||||||
'';
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,32 +12,27 @@
|
||||||
attrNames
|
attrNames
|
||||||
attrValues
|
attrValues
|
||||||
concatStringsSep
|
concatStringsSep
|
||||||
|
disko
|
||||||
escapeShellArg
|
escapeShellArg
|
||||||
filterAttrs
|
filterAttrs
|
||||||
foldl'
|
foldl'
|
||||||
makeBinPath
|
makeBinPath
|
||||||
mapAttrsToList
|
mapAttrsToList
|
||||||
mdDoc
|
mdDoc
|
||||||
|
mergeToplevelConfigs
|
||||||
mkDefault
|
mkDefault
|
||||||
mkEnableOption
|
mkEnableOption
|
||||||
mkForce
|
mkForce
|
||||||
mkIf
|
mkIf
|
||||||
mkMerge
|
mkMerge
|
||||||
mkOption
|
mkOption
|
||||||
|
net
|
||||||
optional
|
optional
|
||||||
optionalAttrs
|
optionalAttrs
|
||||||
recursiveUpdate
|
recursiveUpdate
|
||||||
types
|
types
|
||||||
;
|
;
|
||||||
|
|
||||||
inherit
|
|
||||||
(import ../../lib/misc.nix inputs)
|
|
||||||
mergeToplevelConfigs
|
|
||||||
;
|
|
||||||
|
|
||||||
net = import ../../lib/net.nix inputs;
|
|
||||||
disko = import ../../lib/disko.nix inputs;
|
|
||||||
|
|
||||||
parentConfig = config;
|
parentConfig = config;
|
||||||
cfg = config.meta.microvms;
|
cfg = config.meta.microvms;
|
||||||
nodeName = config.node.name;
|
nodeName = config.node.name;
|
||||||
|
@ -236,7 +231,7 @@ in {
|
||||||
|
|
||||||
networking = {
|
networking = {
|
||||||
baseMac = mkOption {
|
baseMac = mkOption {
|
||||||
type = net.types.mac;
|
type = types.net.mac;
|
||||||
description = mdDoc ''
|
description = mdDoc ''
|
||||||
This MAC address will be used as a base address to derive all MicroVM MAC addresses from.
|
This MAC address will be used as a base address to derive all MicroVM MAC addresses from.
|
||||||
A good practise is to use the physical address of the macvtap interface.
|
A good practise is to use the physical address of the macvtap interface.
|
||||||
|
@ -250,13 +245,13 @@ in {
|
||||||
|
|
||||||
wireguard = {
|
wireguard = {
|
||||||
cidrv4 = mkOption {
|
cidrv4 = mkOption {
|
||||||
type = net.types.cidrv4;
|
type = types.net.cidrv4;
|
||||||
description = mdDoc "The ipv4 network address range to use for internal vm traffic.";
|
description = mdDoc "The ipv4 network address range to use for internal vm traffic.";
|
||||||
default = "172.31.0.0/24";
|
default = "172.31.0.0/24";
|
||||||
};
|
};
|
||||||
|
|
||||||
cidrv6 = mkOption {
|
cidrv6 = mkOption {
|
||||||
type = net.types.cidrv6;
|
type = types.net.cidrv6;
|
||||||
description = mdDoc "The ipv6 network address range to use for internal vm traffic.";
|
description = mdDoc "The ipv6 network address range to use for internal vm traffic.";
|
||||||
default = "fd00:172:31::/120";
|
default = "fd00:172:31::/120";
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
{
|
{
|
||||||
config,
|
config,
|
||||||
inputs,
|
|
||||||
lib,
|
lib,
|
||||||
nodes,
|
nodes,
|
||||||
pkgs,
|
pkgs,
|
||||||
|
@ -12,10 +11,12 @@
|
||||||
assertMsg
|
assertMsg
|
||||||
attrNames
|
attrNames
|
||||||
attrValues
|
attrValues
|
||||||
|
concatAttrs
|
||||||
concatLists
|
concatLists
|
||||||
concatMap
|
concatMap
|
||||||
concatMapStrings
|
concatMapStrings
|
||||||
concatStringsSep
|
concatStringsSep
|
||||||
|
duplicates
|
||||||
escapeShellArg
|
escapeShellArg
|
||||||
filter
|
filter
|
||||||
filterAttrs
|
filterAttrs
|
||||||
|
@ -27,41 +28,28 @@
|
||||||
mapAttrsToList
|
mapAttrsToList
|
||||||
mdDoc
|
mdDoc
|
||||||
mergeAttrs
|
mergeAttrs
|
||||||
|
mergeToplevelConfigs
|
||||||
mkForce
|
mkForce
|
||||||
mkIf
|
mkIf
|
||||||
mkMerge
|
mkMerge
|
||||||
mkOption
|
mkOption
|
||||||
nameValuePair
|
nameValuePair
|
||||||
|
net
|
||||||
optionalAttrs
|
optionalAttrs
|
||||||
optionals
|
optionals
|
||||||
partition
|
partition
|
||||||
removeSuffix
|
removeSuffix
|
||||||
stringLength
|
stringLength
|
||||||
types
|
types
|
||||||
|
wireguard
|
||||||
;
|
;
|
||||||
|
|
||||||
inherit
|
|
||||||
(import ../../lib/misc.nix inputs)
|
|
||||||
concatAttrs
|
|
||||||
duplicates
|
|
||||||
mergeToplevelConfigs
|
|
||||||
;
|
|
||||||
|
|
||||||
inherit
|
|
||||||
(import ../../lib/types.nix inputs)
|
|
||||||
lazyOf
|
|
||||||
lazyValue
|
|
||||||
;
|
|
||||||
|
|
||||||
net = import ../../lib/net.nix inputs;
|
|
||||||
wgLibFor = import ../../lib/wireguard.nix inputs;
|
|
||||||
|
|
||||||
cfg = config.meta.wireguard;
|
cfg = config.meta.wireguard;
|
||||||
nodeName = config.node.name;
|
nodeName = config.node.name;
|
||||||
|
|
||||||
configForNetwork = wgName: wgCfg: let
|
configForNetwork = wgName: wgCfg: let
|
||||||
inherit
|
inherit
|
||||||
(wgLibFor wgName)
|
(wireguard wgName)
|
||||||
externalPeerName
|
externalPeerName
|
||||||
externalPeerNamesRaw
|
externalPeerNamesRaw
|
||||||
networkCidrs
|
networkCidrs
|
||||||
|
@ -307,7 +295,7 @@ in {
|
||||||
};
|
};
|
||||||
|
|
||||||
externalPeers = mkOption {
|
externalPeers = mkOption {
|
||||||
type = types.attrsOf (types.listOf (net.types.ip-in config.addresses));
|
type = types.attrsOf (types.listOf (types.net.ip-in config.addresses));
|
||||||
default = {};
|
default = {};
|
||||||
example = {my-android-phone = ["10.0.0.97"];};
|
example = {my-android-phone = ["10.0.0.97"];};
|
||||||
description = mdDoc ''
|
description = mdDoc ''
|
||||||
|
@ -321,7 +309,7 @@ in {
|
||||||
};
|
};
|
||||||
|
|
||||||
reservedAddresses = mkOption {
|
reservedAddresses = mkOption {
|
||||||
type = types.listOf net.types.cidr;
|
type = types.listOf types.net.cidr;
|
||||||
default = [];
|
default = [];
|
||||||
example = ["10.0.0.1/24" "fd00:cafe::/64"];
|
example = ["10.0.0.1/24" "fd00:cafe::/64"];
|
||||||
description = mdDoc ''
|
description = mdDoc ''
|
||||||
|
@ -377,8 +365,8 @@ in {
|
||||||
};
|
};
|
||||||
|
|
||||||
ipv4 = mkOption {
|
ipv4 = mkOption {
|
||||||
type = lazyOf net.types.ipv4;
|
type = types.lazyOf types.net.ipv4;
|
||||||
default = lazyValue (wgLibFor name).assignedIpv4Addresses.${nodeName};
|
default = types.lazyValue (wireguard name).assignedIpv4Addresses.${nodeName};
|
||||||
description = mdDoc ''
|
description = mdDoc ''
|
||||||
The ipv4 address for this machine. If you do not set this explicitly,
|
The ipv4 address for this machine. If you do not set this explicitly,
|
||||||
a semi-stable ipv4 address will be derived automatically based on the
|
a semi-stable ipv4 address will be derived automatically based on the
|
||||||
|
@ -389,8 +377,8 @@ in {
|
||||||
};
|
};
|
||||||
|
|
||||||
ipv6 = mkOption {
|
ipv6 = mkOption {
|
||||||
type = lazyOf net.types.ipv6;
|
type = types.lazyOf types.net.ipv6;
|
||||||
default = lazyValue (wgLibFor name).assignedIpv6Addresses.${nodeName};
|
default = types.lazyValue (wireguard name).assignedIpv6Addresses.${nodeName};
|
||||||
description = mdDoc ''
|
description = mdDoc ''
|
||||||
The ipv6 address for this machine. If you do not set this explicitly,
|
The ipv6 address for this machine. If you do not set this explicitly,
|
||||||
a semi-stable ipv6 address will be derived automatically based on the
|
a semi-stable ipv6 address will be derived automatically based on the
|
||||||
|
@ -401,7 +389,7 @@ in {
|
||||||
};
|
};
|
||||||
|
|
||||||
addresses = mkOption {
|
addresses = mkOption {
|
||||||
type = types.listOf (lazyOf net.types.ip);
|
type = types.listOf (types.lazyOf types.net.ip);
|
||||||
default = [
|
default = [
|
||||||
(head options.ipv4.definitions)
|
(head options.ipv4.definitions)
|
||||||
(head options.ipv6.definitions)
|
(head options.ipv6.definitions)
|
||||||
|
@ -420,7 +408,7 @@ in {
|
||||||
# to use the network without routing additional stuff.
|
# to use the network without routing additional stuff.
|
||||||
# - allow specifying the route metric.
|
# - allow specifying the route metric.
|
||||||
routedAddresses = mkOption {
|
routedAddresses = mkOption {
|
||||||
type = types.listOf net.types.cidr;
|
type = types.listOf types.net.cidr;
|
||||||
default = [];
|
default = [];
|
||||||
example = ["0.0.0.0/0"];
|
example = ["0.0.0.0/0"];
|
||||||
description = mdDoc ''
|
description = mdDoc ''
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
# Provides an option to easily rename interfaces by their mac addresses.
|
# Provides an option to easily rename interfaces by their mac addresses.
|
||||||
{
|
{
|
||||||
config,
|
config,
|
||||||
inputs,
|
|
||||||
lib,
|
lib,
|
||||||
pkgs,
|
pkgs,
|
||||||
...
|
...
|
||||||
|
@ -10,17 +9,13 @@
|
||||||
(lib)
|
(lib)
|
||||||
attrValues
|
attrValues
|
||||||
concatStringsSep
|
concatStringsSep
|
||||||
|
duplicates
|
||||||
mapAttrsToList
|
mapAttrsToList
|
||||||
mkIf
|
mkIf
|
||||||
mkOption
|
mkOption
|
||||||
types
|
types
|
||||||
;
|
;
|
||||||
|
|
||||||
inherit
|
|
||||||
(import ../../lib/misc.nix inputs)
|
|
||||||
duplicates
|
|
||||||
;
|
|
||||||
|
|
||||||
cfg = config.networking.renameInterfacesByMac;
|
cfg = config.networking.renameInterfacesByMac;
|
||||||
|
|
||||||
interfaceNamesUdevRules = pkgs.writeTextFile {
|
interfaceNamesUdevRules = pkgs.writeTextFile {
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
inherit system;
|
inherit system;
|
||||||
pkgs = self.pkgs.${system};
|
pkgs = self.pkgs.${system};
|
||||||
specialArgs = {
|
specialArgs = {
|
||||||
inherit (nixpkgs) lib;
|
inherit (self.pkgs.${system}) lib;
|
||||||
inherit (self) nodes;
|
inherit (self) nodes;
|
||||||
inherit inputs;
|
inherit inputs;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
final: prev: let
|
self: super: let
|
||||||
inherit
|
inherit
|
||||||
(final.lib)
|
(self.lib)
|
||||||
escapeShellArg
|
escapeShellArg
|
||||||
concatMapStrings
|
concatMapStrings
|
||||||
flatten
|
flatten
|
||||||
|
@ -21,9 +21,9 @@ final: prev: let
|
||||||
version,
|
version,
|
||||||
}: "go get ${escapeShellArg name}@${escapeShellArg version}\n");
|
}: "go get ${escapeShellArg name}@${escapeShellArg version}\n");
|
||||||
in
|
in
|
||||||
prev.caddy.override {
|
super.caddy.override {
|
||||||
buildGoModule = args:
|
buildGoModule = args:
|
||||||
prev.buildGoModule (args
|
super.buildGoModule (args
|
||||||
// {
|
// {
|
||||||
inherit vendorHash;
|
inherit vendorHash;
|
||||||
passthru.plugins = plugins;
|
passthru.plugins = plugins;
|
||||||
|
@ -45,5 +45,5 @@ in {
|
||||||
# ];
|
# ];
|
||||||
# vendorHash = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=";
|
# vendorHash = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=";
|
||||||
# }
|
# }
|
||||||
caddy = prev.caddy.overrideAttrs (_: {passthru.withPackages = make-custom-caddy;});
|
caddy = super.caddy.overrideAttrs (_: {passthru.withPackages = make-custom-caddy;});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
final: prev: {
|
self: super: {
|
||||||
oauth2-proxy = prev.oauth2-proxy.overrideAttrs (_: {
|
oauth2-proxy = super.oauth2-proxy.overrideAttrs (_: {
|
||||||
patches = [./0001-scopes-as-groups.patch];
|
patches = [./0001-scopes-as-groups.patch];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue