diff --git a/flake-parts/checks.nix b/checks.nix similarity index 100% rename from flake-parts/checks.nix rename to checks.nix diff --git a/flake-parts/deploy.nix b/deploy.nix similarity index 100% rename from flake-parts/deploy.nix rename to deploy.nix diff --git a/flake-parts/applyset-deploy.sh b/flake-parts/applyset-deploy.sh deleted file mode 100755 index d6cf7b9..0000000 --- a/flake-parts/applyset-deploy.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail -IFS=$'\n\t' - -export KUBECTL_APPLYSET=true -vals eval -fail-on-missing-key-in-map <$MANIFEST | kubectl apply -f - --prune --applyset $APPLYSET --namespace $NAMESPACE diff --git a/flake.nix b/flake.nix index 1349edf..a321172 100644 --- a/flake.nix +++ b/flake.nix @@ -42,13 +42,13 @@ outputs = inputs@{ self, nixpkgs, flake-utils, ... }: flake-utils.lib.meld inputs [ - ./flake-parts/scripts - ./flake-parts/checks.nix - ./flake-parts/deploy.nix - ./flake-parts/nixos.nix - ./flake-parts/shell.nix - ./flake-parts/utils - ./flake-parts/machines + ./scripts + ./checks.nix + ./deploy.nix + ./nixos.nix + ./shell.nix + ./utils + ./machines ] // (flake-utils.lib.eachDefaultSystem (system: { formatter = nixpkgs.legacyPackages.${system}.nixfmt; })); diff --git a/flake-parts/machines/atlas.nix b/machines/atlas.nix similarity index 100% rename from flake-parts/machines/atlas.nix rename to machines/atlas.nix diff --git a/flake-parts/machines/default.nix b/machines/default.nix similarity index 100% rename from flake-parts/machines/default.nix rename to machines/default.nix diff --git a/flake-parts/machines/jefke.nix b/machines/jefke.nix similarity index 100% rename from flake-parts/machines/jefke.nix rename to machines/jefke.nix diff --git a/flake-parts/machines/lewis.nix b/machines/lewis.nix similarity index 100% rename from flake-parts/machines/lewis.nix rename to machines/lewis.nix diff --git a/flake-parts/machines/pikvm.nix b/machines/pikvm.nix similarity index 100% rename from flake-parts/machines/pikvm.nix rename to machines/pikvm.nix diff --git a/flake-parts/machines/talos.nix b/machines/talos.nix similarity index 100% rename from flake-parts/machines/talos.nix rename to machines/talos.nix diff --git a/flake-parts/machines/warwick.nix b/machines/warwick.nix similarity index 100% rename from flake-parts/machines/warwick.nix rename to machines/warwick.nix diff --git a/nixos-modules/backups.nix b/modules/backups.nix similarity index 100% rename from nixos-modules/backups.nix rename to modules/backups.nix diff --git a/nixos-modules/data-sharing.nix b/modules/data-sharing.nix similarity index 100% rename from nixos-modules/data-sharing.nix rename to modules/data-sharing.nix diff --git a/configuration.nix b/modules/default.nix similarity index 93% rename from configuration.nix rename to modules/default.nix index 2a5bf0d..a05a57f 100644 --- a/configuration.nix +++ b/modules/default.nix @@ -1,6 +1,12 @@ -{ pkgs, self, config, lib, inputs, machine, ... }: { +{ self, pkgs, config, lib, inputs, machine, ... }: { imports = [ - "${self}/nixos-modules" + ./storage.nix + ./backups.nix + ./networking + ./data-sharing.nix + ./monitoring + ./k3s + ./tailscale.nix machine.nixosModule inputs.disko.nixosModules.disko inputs.sops-nix.nixosModules.sops @@ -140,7 +146,7 @@ sops = { age.keyFile = "/root/.config/sops/age/keys.txt"; - defaultSopsFile = ./secrets/nixos.yaml; + defaultSopsFile = "${self}/secrets/nixos.yaml"; }; }; } diff --git a/nixos-modules/k3s/bootstrap.nix b/modules/k3s/bootstrap.nix similarity index 100% rename from nixos-modules/k3s/bootstrap.nix rename to modules/k3s/bootstrap.nix diff --git a/nixos-modules/k3s/default.nix b/modules/k3s/default.nix similarity index 100% rename from nixos-modules/k3s/default.nix rename to modules/k3s/default.nix diff --git a/nixos-modules/k3s/k3s-ca/client-ca.crt b/modules/k3s/k3s-ca/client-ca.crt similarity index 100% rename from nixos-modules/k3s/k3s-ca/client-ca.crt rename to modules/k3s/k3s-ca/client-ca.crt diff --git a/nixos-modules/k3s/k3s-ca/etcd/peer-ca.crt b/modules/k3s/k3s-ca/etcd/peer-ca.crt similarity index 100% rename from nixos-modules/k3s/k3s-ca/etcd/peer-ca.crt rename to modules/k3s/k3s-ca/etcd/peer-ca.crt diff --git a/nixos-modules/k3s/k3s-ca/etcd/server-ca.crt b/modules/k3s/k3s-ca/etcd/server-ca.crt similarity index 100% rename from nixos-modules/k3s/k3s-ca/etcd/server-ca.crt rename to modules/k3s/k3s-ca/etcd/server-ca.crt diff --git a/nixos-modules/k3s/k3s-ca/request-header-ca.crt b/modules/k3s/k3s-ca/request-header-ca.crt similarity index 100% rename from nixos-modules/k3s/k3s-ca/request-header-ca.crt rename to modules/k3s/k3s-ca/request-header-ca.crt diff --git a/nixos-modules/k3s/k3s-ca/server-ca.crt b/modules/k3s/k3s-ca/server-ca.crt similarity index 100% rename from nixos-modules/k3s/k3s-ca/server-ca.crt rename to modules/k3s/k3s-ca/server-ca.crt diff --git a/nixos-modules/monitoring/default.nix b/modules/monitoring/default.nix similarity index 100% rename from nixos-modules/monitoring/default.nix rename to modules/monitoring/default.nix diff --git a/nixos-modules/networking/default.nix b/modules/networking/default.nix similarity index 100% rename from nixos-modules/networking/default.nix rename to modules/networking/default.nix diff --git a/nixos-modules/storage.nix b/modules/storage.nix similarity index 100% rename from nixos-modules/storage.nix rename to modules/storage.nix diff --git a/nixos-modules/tailscale.nix b/modules/tailscale.nix similarity index 100% rename from nixos-modules/tailscale.nix rename to modules/tailscale.nix diff --git a/my-lib/default.nix b/my-lib/default.nix deleted file mode 100644 index 766186b..0000000 --- a/my-lib/default.nix +++ /dev/null @@ -1,6 +0,0 @@ -lib: rec { - net = import ./net.nix lib; - globals = import ./globals.nix; - - imagePath = name: "nix:0${globals.imageDir}/${name}.tar"; -} diff --git a/my-lib/globals.nix b/my-lib/globals.nix deleted file mode 100644 index 38aadd5..0000000 --- a/my-lib/globals.nix +++ /dev/null @@ -1,65 +0,0 @@ -{ - routerPublicIPv4 = "192.145.57.90"; - routerPublicIPv6 = "2a0d:6e00:1a77::1"; - bind9Ipv6 = "2a0d:6e00:1a77:30::134"; - - # Load balancer IPv4 - traefikIPv4 = "192.168.30.128"; - kmsIPv4 = "192.168.30.129"; - inbucketIPv4 = "192.168.30.130"; - piholeIPv4 = "192.168.30.131"; - gitIPv4 = "192.168.30.132"; - transmissionIPv4 = "192.168.30.133"; - bind9IPv4 = "192.168.30.134"; - dnsmasqIPv4 = "192.168.30.135"; - minecraftIPv4 = "192.168.30.136"; - jellyseerrIPv4 = "192.168.30.137"; - syncthingIPv4 = "192.168.30.138"; - longhornIPv4 = "192.168.30.139"; - radarrIPv4 = "192.168.30.140"; - prowlarrIPv4 = "192.168.30.141"; - sonarrIPv4 = "192.168.30.142"; - bazarrIPv4 = "192.168.30.143"; - paperlessIPv4 = "192.168.30.144"; - radicaleIPv4 = "192.168.30.145"; - freshrssIPv4 = "192.168.30.146"; - immichIPv4 = "192.168.30.147"; - nextcloudIPv4 = "192.168.30.148"; - - imageDir = "/var/docker_images"; - - images = { - jellyfin = "jellyfin/jellyfin:10.9.9"; - deluge = "linuxserver/deluge:2.1.1"; - jellyseerr = "fallenbagel/jellyseerr:1.9.2"; - radarr = "lscr.io/linuxserver/radarr:5.9.1"; - prowlarr = "lscr.io/linuxserver/prowlarr:1.21.2"; - sonarr = "lscr.io/linuxserver/sonarr:4.0.8"; - bazarr = "lscr.io/linuxserver/bazarr:1.4.3"; - atuin = "ghcr.io/atuinsh/atuin:18.3.0"; - postgres14 = "postgres:14"; - kms = "teddysun/kms:latest"; - paperless = "ghcr.io/paperless-ngx/paperless-ngx:2.11.6"; - redis7 = "docker.io/library/redis:7"; - nextcloud = "nextcloud:29.0.5"; - postgres15 = "postgres:15"; - inbucket = "inbucket/inbucket:edge"; - syncthing = "lscr.io/linuxserver/syncthing:1.27.10"; - radicale = "tomsquest/docker-radicale:3.2.3.0"; - ntfy = "binwiederhier/ntfy:v2.11.0"; - forgejo = "codeberg.org/forgejo/forgejo:8.0.1"; - pihole = "pihole/pihole:2024.07.0"; - immich = "ghcr.io/immich-app/immich-server:v1.114.0"; - immich-machine-learning = "ghcr.io/immich-app/immich-machine-learning:v1.114.0"; - immich-redis = "docker.io/redis:6.2-alpine@sha256:e3b17ba9479deec4b7d1eeec1548a253acc5374d68d3b27937fcfe4df8d18c7e"; - immich-postgres = "docker.io/tensorchord/pgvecto-rs:pg14-v0.2.0@sha256:90724186f0a3517cf6914295b5ab410db9ce23190a2d9d0b9dd6463e3fa298f0"; - kitchenowl = "tombursch/kitchenowl:v0.5.2"; - cyberchef = "mpepping/cyberchef:latest"; - freshrss = "freshrss/freshrss:1.24.3"; - bind9 = "ubuntu/bind9:9.18-22.04_beta"; - dnsmasq = "dockurr/dnsmasq:2.90"; - attic = "git.kun.is/home/atticd:fd910d91c2143295e959d2c903e9ea25cf94ba27"; - hedgedoc = "quay.io/hedgedoc/hedgedoc:1.9.9"; - minecraft = "itzg/minecraft-server:latest"; - }; -} diff --git a/my-lib/net.nix b/my-lib/net.nix deleted file mode 100644 index 9f5b0e5..0000000 --- a/my-lib/net.nix +++ /dev/null @@ -1,1323 +0,0 @@ -# IP address arithmetic and validation in Nix by @duairc: -# https://gist.github.com/duairc/5c9bb3c922e5d501a1edb9e7b3b845ba - -lib: - -let - - net = { - ip = { - - # add :: (ip | mac | integer) -> ip -> ip - # - # Examples: - # - # Adding integer to IPv4: - # > net.ip.add 100 "10.0.0.1" - # "10.0.0.101" - # - # Adding IPv4 to IPv4: - # > net.ip.add "127.0.0.1" "10.0.0.1" - # "137.0.0.2" - # - # Adding IPv6 to IPv4: - # > net.ip.add "::cafe:beef" "10.0.0.1" - # "212.254.186.191" - # - # Adding MAC to IPv4 (overflows): - # > net.ip.add "fe:ed:fa:ce:f0:0d" "10.0.0.1" - # "4.206.240.14" - # - # Adding integer to IPv6: - # > net.ip.add 100 "dead:cafe:beef::" - # "dead:cafe:beef::64" - # - # Adding IPv4 to to IPv6: - # > net.ip.add "127.0.0.1" "dead:cafe:beef::" - # "dead:cafe:beef::7f00:1" - # - # Adding MAC to IPv6: - # > net.ip.add "fe:ed:fa:ce:f0:0d" "dead:cafe:beef::" - # "dead:cafe:beef::feed:face:f00d" - add = delta: ip: - let - function = "net.ip.add"; - delta' = typechecks.numeric function "delta" delta; - ip' = typechecks.ip function "ip" ip; - in - builders.ip (implementations.ip.add delta' ip'); - - # diff :: ip -> ip -> (integer | ipv6) - # - # net.ip.diff is the reverse of net.ip.add: - # - # net.ip.diff (net.ip.add a b) a = b - # net.ip.diff (net.ip.add a b) b = a - # - # The difference between net.ip.diff and net.ip.subtract is that - # net.ip.diff will try its best to return an integer (falling back - # to an IPv6 if the result is too big to fit in an integer). This is - # useful if you have two hosts that you know are on the same network - # and you just want to calculate the offset between them — a result - # like "0.0.0.10" is not very useful (which is what you would get - # from net.ip.subtract). - diff = minuend: subtrahend: - let - function = "net.ip.diff"; - minuend' = typechecks.ip function "minuend" minuend; - subtrahend' = typechecks.ip function "subtrahend" subtrahend; - result = implementations.ip.diff minuend' subtrahend'; - in - if result ? ipv6 - then builders.ipv6 result - else result; - - # subtract :: (ip | mac | integer) -> ip -> ip - # - # net.ip.subtract is also the reverse of net.ip.add: - # - # net.ip.subtract a (net.ip.add a b) = b - # net.ip.subtract b (net.ip.add a b) = a - # - # The difference between net.ip.subtract and net.ip.diff is that - # net.ip.subtract will always return the same type as its "ip" - # parameter. Its implementation takes the "delta" parameter, - # coerces it to be the same type as the "ip" paramter, negates it - # (using two's complement), and then adds it to "ip". - subtract = delta: ip: - let - function = "net.ip.subtract"; - delta' = typechecks.numeric function "delta" delta; - ip' = typechecks.ip function "ip" ip; - in - builders.ip (implementations.ip.subtract delta' ip'); - }; - - mac = { - - # add :: (ip | mac | integer) -> mac -> mac - # - # Examples: - # - # Adding integer to MAC: - # > net.mac.add 100 "fe:ed:fa:ce:f0:0d" - # "fe:ed:fa:ce:f0:71" - # - # Adding IPv4 to MAC: - # > net.mac.add "127.0.0.1" "fe:ed:fa:ce:f0:0d" - # "fe:ee:79:ce:f0:0e" - # - # Adding IPv6 to MAC: - # > net.mac.add "::cafe:beef" "fe:ed:fa:ce:f0:0d" - # "fe:ee:c5:cd:aa:cb - # - # Adding MAC to MAC: - # > net.mac.add "fe:ed:fa:00:00:00" "00:00:00:ce:f0:0d" - # "fe:ed:fa:ce:f0:0d" - add = delta: mac: - let - function = "net.mac.add"; - delta' = typechecks.numeric function "delta" delta; - mac' = typechecks.mac function "mac" mac; - in - builders.mac (implementations.mac.add delta' mac'); - - # diff :: mac -> mac -> integer - # - # net.mac.diff is the reverse of net.mac.add: - # - # net.mac.diff (net.mac.add a b) a = b - # net.mac.diff (net.mac.add a b) b = a - # - # The difference between net.mac.diff and net.mac.subtract is that - # net.mac.diff will always return an integer. - diff = minuend: subtrahend: - let - function = "net.mac.diff"; - minuend' = typechecks.mac function "minuend" minuend; - subtrahend' = typechecks.mac function "subtrahend" subtrahend; - in - implementations.mac.diff minuend' subtrahend'; - - # subtract :: (ip | mac | integer) -> mac -> mac - # - # net.mac.subtract is also the reverse of net.ip.add: - # - # net.mac.subtract a (net.mac.add a b) = b - # net.mac.subtract b (net.mac.add a b) = a - # - # The difference between net.mac.subtract and net.mac.diff is that - # net.mac.subtract will always return a MAC address. - subtract = delta: mac: - let - function = "net.mac.subtract"; - delta' = typechecks.numeric function "delta" delta; - mac' = typechecks.mac function "mac" mac; - in - builders.mac (implementations.mac.subtract delta' mac'); - }; - - cidr = { - # add :: (ip | mac | integer) -> cidr -> cidr - # - # > net.cidr.add 2 "127.0.0.0/8" - # "129.0.0.0/8" - # - # > net.cidr.add (-2) "127.0.0.0/8" - # "125.0.0.0/8" - add = delta: cidr: - let - function = "net.cidr.add"; - delta' = typechecks.numeric function "delta" delta; - cidr' = typechecks.cidr function "cidr" cidr; - in - builders.cidr (implementations.cidr.add delta' cidr'); - - # child :: cidr -> cidr -> bool - # - # > net.cidr.child "10.10.10.0/24" "10.0.0.0/8" - # true - # - # > net.cidr.child "127.0.0.0/8" "10.0.0.0/8" - # false - child = subcidr: cidr: - let - function = "net.cidr.child"; - subcidr' = typechecks.cidr function "subcidr" subcidr; - cidr' = typechecks.cidr function "cidr" cidr; - in - implementations.cidr.child subcidr' cidr'; - - # contains :: ip -> cidr -> bool - # - # > net.cidr.contains "127.0.0.1" "127.0.0.0/8" - # true - # - # > net.cidr.contains "127.0.0.1" "192.168.0.0/16" - # false - contains = ip: cidr: - let - function = "net.cidr.contains"; - ip' = typechecks.ip function "ip" ip; - cidr' = typechecks.cidr function "cidr" cidr; - in - implementations.cidr.contains ip' cidr'; - - # capacity :: cidr -> integer - # - # > net.cidr.capacity "172.16.0.0/12" - # 1048576 - # - # > net.cidr.capacity "dead:cafe:beef::/96" - # 4294967296 - # - # > net.cidr.capacity "dead:cafe:beef::/48" (saturates to maxBound) - # 9223372036854775807 - capacity = cidr: - let - function = "net.cidr.capacity"; - cidr' = typechecks.cidr function "cidr" cidr; - in - implementations.cidr.capacity cidr'; - - # host :: (ip | mac | integer) -> cidr -> ip - # - # > net.cidr.host 10000 "10.0.0.0/8" - # 10.0.39.16 - # - # > net.cidr.host 10000 "dead:cafe:beef::/64" - # "dead:cafe:beef::2710" - # - # net.cidr.host "127.0.0.1" "dead:cafe:beef::/48" - # > "dead:cafe:beef::7f00:1" - # - # Inpsired by: - # https://www.terraform.io/docs/configuration/functions/cidrhost.html - host = hostnum: cidr: - let - function = "net.cidr.host"; - hostnum' = typechecks.numeric function "hostnum" hostnum; - cidr' = typechecks.cidr function "cidr" cidr; - in - builders.ip (implementations.cidr.host hostnum' cidr'); - - # length :: cidr -> integer - # - # > net.cidr.prefix "127.0.0.0/8" - # 8 - # - # > net.cidr.prefix "dead:cafe:beef::/48" - # 48 - length = cidr: - let - function = "net.cidr.length"; - cidr' = typechecks.cidr function "cidr" cidr; - in - implementations.cidr.length cidr'; - - # make :: integer -> ip -> cidr - # - # > net.cidr.make 24 "192.168.0.150" - # "192.168.0.0/24" - # - # > net.cidr.make 40 "dead:cafe:beef::feed:face:f00d" - # "dead:cafe:be00::/40" - make = length: base: - let - function = "net.cidr.make"; - length' = typechecks.int function "length" length; - base' = typechecks.ip function "base" base; - in - builders.cidr (implementations.cidr.make length' base'); - - # netmask :: cidr -> ip - # - # > net.cidr.netmask "192.168.0.0/24" - # "255.255.255.0" - # - # > net.cidr.netmask "dead:cafe:beef::/64" - # "ffff:ffff:ffff:ffff::" - netmask = cidr: - let - function = "net.cidr.netmask"; - cidr' = typechecks.cidr function "cidr" cidr; - in - builders.ip (implementations.cidr.netmask cidr'); - - # size :: cidr -> integer - # - # > net.cidr.prefix "127.0.0.0/8" - # 24 - # - # > net.cidr.prefix "dead:cafe:beef::/48" - # 80 - size = cidr: - let - function = "net.cidr.size"; - cidr' = typechecks.cidr function "cidr" cidr; - in - implementations.cidr.size cidr'; - - # subnet :: integer -> (ip | mac | integer) -> cidr -> cidr - # - # > net.cidr.subnet 4 2 "172.16.0.0/12" - # "172.18.0.0/16" - # - # > net.cidr.subnet 4 15 "10.1.2.0/24" - # "10.1.2.240/28" - # - # > net.cidr.subnet 16 162 "fd00:fd12:3456:7890::/56" - # "fd00:fd12:3456:7800:a200::/72" - # - # Inspired by: - # https://www.terraform.io/docs/configuration/functions/cidrsubnet.html - subnet = length: netnum: cidr: - let - function = "net.cidr.subnet"; - length' = typechecks.int function "length" length; - netnum' = typechecks.numeric function "netnum" netnum; - cidr' = typechecks.cidr function "cidr" cidr; - in - builders.cidr (implementations.cidr.subnet length' netnum' cidr'); - - }; - } // ({ - types = - let - - mkParsedOptionType = { name, description, parser, builder }: - let - normalize = def: def // { - value = builder (parser def.value); - }; - in - lib.mkOptionType { - inherit name description; - check = x: builtins.isString x && parser x != null; - merge = loc: defs: lib.mergeEqualOption loc (map normalize defs); - }; - - dependent-ip = type: cidr: - let - cidrs = - if builtins.isList cidr - then cidr - else [ cidr ]; - in - lib.types.addCheck type (i: lib.any (net.cidr.contains i) cidrs) // { - description = type.description + " in ${builtins.concatStringsSep " or " cidrs}"; - }; - - dependent-cidr = type: cidr: - let - cidrs = - if builtins.isList cidr - then cidr - else [ cidr ]; - in - lib.types.addCheck type (i: lib.any (net.cidr.child i) cidrs) // { - description = type.description + " in ${builtins.concatStringsSep " or " cidrs}"; - }; - - in - rec { - - ip = mkParsedOptionType { - name = "ip"; - description = "IPv4 or IPv6 address"; - parser = parsers.ip; - builder = builders.ip; - }; - - ip-in = dependent-ip ip; - - ipv4 = mkParsedOptionType { - name = "ipv4"; - description = "IPv4 address"; - parser = parsers.ipv4; - builder = builders.ipv4; - }; - - ipv4-in = dependent-ip ipv4; - - ipv6 = mkParsedOptionType { - name = "ipv6"; - description = "IPv6 address"; - parser = parsers.ipv6; - builder = builders.ipv6; - }; - - ipv6-in = dependent-ip ipv6; - - cidr = mkParsedOptionType { - name = "cidr"; - description = "IPv4 or IPv6 address range in CIDR notation"; - parser = parsers.cidr; - builder = builders.cidr; - }; - - cidr-in = dependent-cidr cidr; - - cidrv4 = mkParsedOptionType { - name = "cidrv4"; - description = "IPv4 address range in CIDR notation"; - parser = parsers.cidrv4; - builder = builders.cidrv4; - }; - - cidrv4-in = dependent-cidr cidrv4; - - cidrv6 = mkParsedOptionType { - name = "cidrv6"; - description = "IPv6 address range in CIDR notation"; - parser = parsers.cidrv6; - builder = builders.cidrv6; - }; - - cidrv6-in = dependent-cidr cidrv6; - - mac = mkParsedOptionType { - name = "mac"; - description = "MAC address"; - parser = parsers.mac; - builder = builders.mac; - }; - - }; - } - ); - - list = { - cons = a: b: [ a ] ++ b; - }; - - bit = - let - shift = n: x: - if n < 0 - then x * math.pow 2 (-n) - else - let - safeDiv = n: d: if d == 0 then 0 else n / d; - d = math.pow 2 n; - in - if x < 0 - then not (safeDiv (not x) d) - else safeDiv x d; - - left = n: shift (-n); - - right = shift; - - and = builtins.bitAnd; - - or = builtins.bitOr; - - xor = builtins.bitXor; - - not = xor (-1); - - mask = n: and (left n 1 - 1); - in - { - inherit left right and or xor not mask; - }; - - math = rec { - max = a: b: - if a > b - then a - else b; - - min = a: b: - if a < b - then a - else b; - - clamp = a: b: c: max a (min b c); - - pow = x: n: - if n == 0 - then 1 - else if bit.and n 1 != 0 - then x * pow (x * x) ((n - 1) / 2) - else pow (x * x) (n / 2); - }; - - parsers = - let - - # fmap :: (a -> b) -> parser a -> parser b - fmap = f: ma: bind ma (a: pure (f a)); - - # pure :: a -> parser a - pure = a: string: { - leftovers = string; - result = a; - }; - - # liftA2 :: (a -> b -> c) -> parser a -> parser b -> parser c - liftA2 = f: ma: mb: bind ma (a: bind mb (b: pure (f a b))); - liftA3 = f: a: b: ap (liftA2 f a b); - liftA4 = f: a: b: c: ap (liftA3 f a b c); - liftA5 = f: a: b: c: d: ap (liftA4 f a b c d); - liftA6 = f: a: b: c: d: e: ap (liftA5 f a b c d e); - - # ap :: parser (a -> b) -> parser a -> parser b - ap = liftA2 (a: a); - - # then_ :: parser a -> parser b -> parser b - then_ = liftA2 (a: b: b); - - # empty :: parser a - empty = string: null; - - # alt :: parser a -> parser a -> parser a - alt = left: right: string: - let - result = left string; - in - if builtins.isNull result - then right string - else result; - - # guard :: bool -> parser {} - guard = condition: if condition then pure { } else empty; - - # mfilter :: (a -> bool) -> parser a -> parser a - mfilter = f: parser: bind parser (a: then_ (guard (f a)) (pure a)); - - # some :: parser a -> parser [a] - some = v: liftA2 list.cons v (many v); - - # many :: parser a -> parser [a] - many = v: alt (some v) (pure [ ]); - - # bind :: parser a -> (a -> parser b) -> parser b - bind = parser: f: string: - let - a = parser string; - in - if builtins.isNull a - then null - else f a.result a.leftovers; - - # run :: parser a -> string -> maybe a - run = parser: string: - let - result = parser string; - in - if builtins.isNull result || result.leftovers != "" - then null - else result.result; - - next = string: - if string == "" - then null - else { - leftovers = builtins.substring 1 (-1) string; - result = builtins.substring 0 1 string; - }; - - # Count how many characters were consumed by a parser - count = parser: string: - let - result = parser string; - in - if builtins.isNull result - then null - else result // { - result = { - inherit (result) result; - count = with result; - builtins.stringLength string - builtins.stringLength leftovers; - }; - }; - - # Limit the parser to n characters at most - limit = n: parser: - fmap (a: a.result) (mfilter (a: a.count <= n) (count parser)); - - # Ensure the parser consumes exactly n characters - exactly = n: parser: - fmap (a: a.result) (mfilter (a: a.count == n) (count parser)); - - char = c: bind next (c': guard (c == c')); - - string = css: - if css == "" - then pure { } - else - let - c = builtins.substring 0 1 css; - cs = builtins.substring 1 (-1) css; - in - then_ (char c) (string cs); - - digit = set: bind next ( - c: then_ - (guard (builtins.hasAttr c set)) - (pure (builtins.getAttr c set)) - ); - - decimalDigits = { - "0" = 0; - "1" = 1; - "2" = 2; - "3" = 3; - "4" = 4; - "5" = 5; - "6" = 6; - "7" = 7; - "8" = 8; - "9" = 9; - }; - - hexadecimalDigits = decimalDigits // { - "a" = 10; - "b" = 11; - "c" = 12; - "d" = 13; - "e" = 14; - "f" = 15; - "A" = 10; - "B" = 11; - "C" = 12; - "D" = 13; - "E" = 14; - "F" = 15; - }; - - fromDecimalDigits = builtins.foldl' (a: c: a * 10 + c) 0; - fromHexadecimalDigits = builtins.foldl' (a: bit.or (bit.left 4 a)) 0; - - # disallow leading zeros - decimal = bind (digit decimalDigits) ( - n: - if n == 0 - then pure 0 - else - fmap - (ns: fromDecimalDigits (list.cons n ns)) - (many (digit decimalDigits)) - ); - - hexadecimal = fmap fromHexadecimalDigits (some (digit hexadecimalDigits)); - - ipv4 = - let - dot = char "."; - - octet = mfilter (n: n < 256) decimal; - - octet' = then_ dot octet; - - fromOctets = a: b: c: d: { - ipv4 = bit.or (bit.left 8 (bit.or (bit.left 8 (bit.or (bit.left 8 a) b)) c)) d; - }; - in - liftA4 fromOctets octet octet' octet' octet'; - - # This is more or less a literal translation of - # https://hackage.haskell.org/package/ip/docs/src/Net.IPv6.html#parser - ipv6 = - let - colon = char ":"; - - hextet = limit 4 hexadecimal; - - fromHextets = hextets: - if builtins.length hextets != 8 - then empty - else - let - a = builtins.elemAt hextets 0; - b = builtins.elemAt hextets 1; - c = builtins.elemAt hextets 2; - d = builtins.elemAt hextets 3; - e = builtins.elemAt hextets 4; - f = builtins.elemAt hextets 5; - g = builtins.elemAt hextets 6; - h = builtins.elemAt hextets 7; - in - pure { - ipv6 = { - a = bit.or (bit.left 16 a) b; - b = bit.or (bit.left 16 c) d; - c = bit.or (bit.left 16 e) f; - d = bit.or (bit.left 16 g) h; - }; - }; - - ipv4' = fmap - ( - address: - let - upper = bit.right 16 address.ipv4; - lower = bit.mask 16 address.ipv4; - in - [ upper lower ] - ) - ipv4; - - part = n: - let - n' = n + 1; - hex = liftA2 list.cons hextet - ( - then_ colon - ( - alt - (then_ colon (doubleColon n')) - (part n') - ) - ); - in - if n == 7 - then fmap (a: [ a ]) hextet - else - if n == 6 - then alt ipv4' hex - else hex; - - doubleColon = n: - bind (alt afterDoubleColon (pure [ ])) ( - rest: - let - missing = 8 - n - builtins.length rest; - in - if missing < 0 - then empty - else pure (builtins.genList (_: 0) missing ++ rest) - ); - - afterDoubleColon = - alt ipv4' - ( - liftA2 list.cons hextet - ( - alt - (then_ colon afterDoubleColon) - (pure [ ]) - ) - ); - - in - bind - ( - alt - ( - then_ - (string "::") - (doubleColon 0) - ) - (part 0) - ) - fromHextets; - - cidrv4 = - liftA2 - (base: length: implementations.cidr.make length base) - ipv4 - (then_ (char "/") (mfilter (n: n <= 32) decimal)); - - cidrv6 = - liftA2 - (base: length: implementations.cidr.make length base) - ipv6 - (then_ (char "/") (mfilter (n: n <= 128) decimal)); - - mac = - let - colon = char ":"; - - octet = exactly 2 hexadecimal; - - octet' = then_ colon octet; - - fromOctets = a: b: c: d: e: f: { - mac = bit.or (bit.left 8 (bit.or (bit.left 8 (bit.or (bit.left 8 (bit.or (bit.left 8 (bit.or (bit.left 8 a) b)) c)) d)) e)) f; - }; - in - liftA6 fromOctets octet octet' octet' octet' octet' octet'; - - in - { - ipv4 = run ipv4; - ipv6 = run ipv6; - ip = run (alt ipv4 ipv6); - cidrv4 = run cidrv4; - cidrv6 = run cidrv6; - cidr = run (alt cidrv4 cidrv6); - mac = run mac; - numeric = run (alt (alt ipv4 ipv6) mac); - }; - - builders = - let - - ipv4 = address: - let - abcd = address.ipv4; - abc = bit.right 8 abcd; - ab = bit.right 8 abc; - a = bit.right 8 ab; - b = bit.mask 8 ab; - c = bit.mask 8 abc; - d = bit.mask 8 abcd; - in - builtins.concatStringsSep "." (map toString [ a b c d ]); - - # This is more or less a literal translation of - # https://hackage.haskell.org/package/ip/docs/src/Net.IPv6.html#encode - ipv6 = address: - let - - digits = "0123456789abcdef"; - - toHexString = n: - let - rest = bit.right 4 n; - current = bit.mask 4 n; - prefix = - if rest == 0 - then "" - else toHexString rest; - in - "${prefix}${builtins.substring current 1 digits}"; - - in - if (with address.ipv6; a == 0 && b == 0 && c == 0 && d > 65535) - then "::${ipv4 { ipv4 = address.ipv6.d; }}" - else - if (with address.ipv6; a == 0 && b == 0 && c == 65535) - then "::ffff:${ipv4 { ipv4 = address.ipv6.d; }}" - else - let - - a = bit.right 16 address.ipv6.a; - b = bit.mask 16 address.ipv6.a; - c = bit.right 16 address.ipv6.b; - d = bit.mask 16 address.ipv6.b; - e = bit.right 16 address.ipv6.c; - f = bit.mask 16 address.ipv6.c; - g = bit.right 16 address.ipv6.d; - h = bit.mask 16 address.ipv6.d; - - hextets = [ a b c d e f g h ]; - - # calculate the position and size of the longest sequence of - # zeroes within the list of hextets - longest = - let - go = i: current: best: - if i < builtins.length hextets - then - let - n = builtins.elemAt hextets i; - - current' = - if n == 0 - then - if builtins.isNull current - then { - size = 1; - position = i; - } - else current // { - size = current.size + 1; - } - else null; - - best' = - if n == 0 - then - if builtins.isNull best - then current' - else - if current'.size > best.size - then current' - else best - else best; - in - go (i + 1) current' best' - else best; - in - go 0 null null; - - format = hextets: - builtins.concatStringsSep ":" (map toHexString hextets); - in - if builtins.isNull longest - then format hextets - else - let - sublist = i: length: xs: - map - (builtins.elemAt xs) - (builtins.genList (x: x + i) length); - - end = longest.position + longest.size; - - before = sublist 0 longest.position hextets; - - after = sublist end (builtins.length hextets - end) hextets; - in - "${format before}::${format after}"; - - ip = address: - if address ? ipv4 - then ipv4 address - else ipv6 address; - - cidrv4 = cidr: - "${ipv4 cidr.base}/${toString cidr.length}"; - - cidrv6 = cidr: - "${ipv6 cidr.base}/${toString cidr.length}"; - - cidr = cidr: - "${ip cidr.base}/${toString cidr.length}"; - - mac = address: - let - digits = "0123456789abcdef"; - octet = n: - let - upper = bit.right 4 n; - lower = bit.mask 4 n; - in - "${builtins.substring upper 1 digits}${builtins.substring lower 1 digits}"; - in - let - a = bit.mask 8 (bit.right 40 address.mac); - b = bit.mask 8 (bit.right 32 address.mac); - c = bit.mask 8 (bit.right 24 address.mac); - d = bit.mask 8 (bit.right 16 address.mac); - e = bit.mask 8 (bit.right 8 address.mac); - f = bit.mask 8 (bit.right 0 address.mac); - in - "${octet a}:${octet b}:${octet c}:${octet d}:${octet e}:${octet f}"; - - in - { - inherit ipv4 ipv6 ip cidrv4 cidrv6 cidr mac; - }; - - arithmetic = rec { - # or :: (ip | mac | integer) -> (ip | mac | integer) -> (ip | mac | integer) - or = a_: b: - let - a = coerce b a_; - in - if a ? ipv6 - then { - ipv6 = { - a = bit.or a.ipv6.a b.ipv6.a; - b = bit.or a.ipv6.b b.ipv6.b; - c = bit.or a.ipv6.c b.ipv6.c; - d = bit.or a.ipv6.d b.ipv6.d; - }; - } - else if a ? ipv4 - then { - ipv4 = bit.or a.ipv4 b.ipv4; - } - else if a ? mac - then { - mac = bit.or a.mac b.mac; - } - else bit.or a b; - - # and :: (ip | mac | integer) -> (ip | mac | integer) -> (ip | mac | integer) - and = a_: b: - let - a = coerce b a_; - in - if a ? ipv6 - then { - ipv6 = { - a = bit.and a.ipv6.a b.ipv6.a; - b = bit.and a.ipv6.b b.ipv6.b; - c = bit.and a.ipv6.c b.ipv6.c; - d = bit.and a.ipv6.d b.ipv6.d; - }; - } - else if a ? ipv4 - then { - ipv4 = bit.and a.ipv4 b.ipv4; - } - else if a ? mac - then { - mac = bit.and a.mac b.mac; - } - else bit.and a b; - - # not :: (ip | mac | integer) -> (ip | mac | integer) - not = a: - if a ? ipv6 - then { - ipv6 = { - a = bit.mask 32 (bit.not a.ipv6.a); - b = bit.mask 32 (bit.not a.ipv6.b); - c = bit.mask 32 (bit.not a.ipv6.c); - d = bit.mask 32 (bit.not a.ipv6.d); - }; - } - else if a ? ipv4 - then { - ipv4 = bit.mask 32 (bit.not a.ipv4); - } - else if a ? mac - then { - mac = bit.mask 48 (bit.not a.mac); - } - else bit.not a; - - # add :: (ip | mac | integer) -> (ip | mac | integer) -> (ip | mac | integer) - add = - let - split = a: { - fst = bit.mask 32 (bit.right 32 a); - snd = bit.mask 32 a; - }; - in - a_: b: - let - a = coerce b a_; - in - if a ? ipv6 - then - let - a' = split (a.ipv6.a + b.ipv6.a + b'.fst); - b' = split (a.ipv6.b + b.ipv6.b + c'.fst); - c' = split (a.ipv6.c + b.ipv6.c + d'.fst); - d' = split (a.ipv6.d + b.ipv6.d); - in - { - ipv6 = { - a = a'.snd; - b = b'.snd; - c = c'.snd; - d = d'.snd; - }; - } - else if a ? ipv4 - then { - ipv4 = bit.mask 32 (a.ipv4 + b.ipv4); - } - else if a ? mac - then { - mac = bit.mask 48 (a.mac + b.mac); - } - else a + b; - - # subtract :: (ip | mac | integer) -> (ip | mac | integer) -> (ip | mac | integer) - subtract = a: b: add (add 1 (not (coerce b a))) b; - - # diff :: (ip | mac | integer) -> (ip | mac | integer) -> (ipv6 | integer) - diff = a: b: - let - toIPv6 = coerce ({ ipv6.a = 0; }); - result = (subtract b (toIPv6 a)).ipv6; - max32 = bit.left 32 1 - 1; - in - if result.a == 0 && result.b == 0 && bit.right 31 result.c == 0 || result.a == max32 && result.b == max32 && bit.right 31 result.c == 1 - then bit.or (bit.left 32 result.c) result.d - else { - ipv6 = result; - }; - - # left :: integer -> (ip | mac | integer) -> (ip | mac | integer) - left = i: right (-i); - - # right :: integer -> (ip | mac | integer) -> (ip | mac | integer) - right = - let - step = i: x: { - _1 = bit.mask 32 (bit.right (i + 96) x); - _2 = bit.mask 32 (bit.right (i + 64) x); - _3 = bit.mask 32 (bit.right (i + 32) x); - _4 = bit.mask 32 (bit.right i x); - _5 = bit.mask 32 (bit.right (i - 32) x); - _6 = bit.mask 32 (bit.right (i - 64) x); - _7 = bit.mask 32 (bit.right (i - 96) x); - }; - ors = builtins.foldl' bit.or 0; - in - i: x: - if x ? ipv6 - then - let - a' = step i x.ipv6.a; - b' = step i x.ipv6.b; - c' = step i x.ipv6.c; - d' = step i x.ipv6.d; - in - { - ipv6 = { - a = ors [ a'._4 b'._3 c'._2 d'._1 ]; - b = ors [ a'._5 b'._4 c'._3 d'._2 ]; - c = ors [ a'._6 b'._5 c'._4 d'._3 ]; - d = ors [ a'._7 b'._6 c'._5 d'._4 ]; - }; - } - else if x ? ipv4 - then { - ipv4 = bit.mask 32 (bit.right i x.ipv4); - } - else if x ? mac - then { - mac = bit.mask 48 (bit.right i x.mac); - } - else bit.right i x; - - # shadow :: integer -> (ip | mac | integer) -> (ip | mac | integer) - shadow = n: a: and (right n (left n (coerce a (-1)))) a; - - # coshadow :: integer -> (ip | mac | integer) -> (ip | mac | integer) - coshadow = n: a: and (not (right n (left n (coerce a (-1))))) a; - - # coerce :: (ip | mac | integer) -> (ip | mac | integer) -> (ip | mac | integer) - coerce = target: value: - if target ? ipv6 - then - if value ? ipv6 - then value - else if value ? ipv4 - then { - ipv6 = { - a = 0; - b = 0; - c = 0; - d = value.ipv4; - }; - } - else if value ? mac - then { - ipv6 = { - a = 0; - b = 0; - c = bit.right 32 value.mac; - d = bit.mask 32 value.mac; - }; - } - else { - ipv6 = { - a = bit.mask 32 (bit.right 96 value); - b = bit.mask 32 (bit.right 64 value); - c = bit.mask 32 (bit.right 32 value); - d = bit.mask 32 value; - }; - } - else if target ? ipv4 - then - if value ? ipv6 - then { - ipv4 = value.ipv6.d; - } - else if value ? ipv4 - then value - else if value ? mac - then { - ipv4 = bit.mask 32 value.mac; - } - else { - ipv4 = bit.mask 32 value; - } - else if target ? mac - then - if value ? ipv6 - then { - mac = bit.or (bit.left 32 (bit.mask 16 value.ipv6.c)) value.ipv6.d; - } - else if value ? ipv4 - then { - mac = value.ipv4; - } - else if value ? mac - then value - else { - mac = bit.mask 48 value; - } - else - if value ? ipv6 - then - builtins.foldl' bit.or 0 - [ - (bit.left 96 value.ipv6.a) - (bit.left 64 value.ipv6.b) - (bit.left 32 value.ipv6.c) - value.ipv6.d - ] - else if value ? ipv4 - then value.ipv4 - else if value ? mac - then value.mac - else value; - }; - - implementations = { - ip = { - # add :: (ip | mac | integer) -> ip -> ip - add = arithmetic.add; - - # diff :: ip -> ip -> (ipv6 | integer) - diff = arithmetic.diff; - - # subtract :: (ip | mac | integer) -> ip -> ip - subtract = arithmetic.subtract; - }; - - mac = { - # add :: (ip | mac | integer) -> mac -> mac - add = arithmetic.add; - - # diff :: mac -> mac -> (ipv6 | integer) - diff = arithmetic.diff; - - # subtract :: (ip | mac | integer) -> mac -> mac - subtract = arithmetic.subtract; - }; - - cidr = rec { - # add :: (ip | mac | integer) -> cidr -> cidr - add = delta: cidr: - let - size' = size cidr; - in - { - base = arithmetic.left size' (arithmetic.add delta (arithmetic.right size' cidr.base)); - inherit (cidr) length; - }; - - # capacity :: cidr -> integer - capacity = cidr: - let - size' = size cidr; - in - if size' > 62 - then 9223372036854775807 # maxBound to prevent overflow - else bit.left size' 1; - - # child :: cidr -> cidr -> bool - child = subcidr: cidr: - length subcidr > length cidr && contains (host 0 subcidr) cidr; - - # contains :: ip -> cidr -> bool - contains = ip: cidr: host 0 (make cidr.length ip) == host 0 cidr; - - # host :: (ip | mac | integer) -> cidr -> ip - host = index: cidr: - let - index' = arithmetic.coerce cidr.base index; - in - arithmetic.or (arithmetic.shadow cidr.length index') cidr.base; - - # length :: cidr -> integer - length = cidr: cidr.length; - - # netmask :: cidr -> ip - netmask = cidr: arithmetic.coshadow cidr.length (arithmetic.coerce cidr.base (-1)); - - # size :: cidr -> integer - size = cidr: (if cidr.base ? ipv6 then 128 else 32) - cidr.length; - - # subnet :: integer -> (ip | mac | integer) -> cidr -> cidr - subnet = length: index: cidr: - let - length' = cidr.length + length; - index' = arithmetic.coerce cidr.base index; - size = (if cidr.base ? ipv6 then 128 else 32) - length'; - in - make length' (host (arithmetic.left size index') cidr); - - # make :: integer -> ip -> cidr - make = length: base: - let - length' = math.clamp 0 (if base ? ipv6 then 128 else 32) length; - in - { - base = arithmetic.coshadow length' base; - length = length'; - }; - }; - }; - - typechecks = - let - - fail = description: function: argument: - builtins.throw "${function}: ${argument} parameter must be ${description}"; - - meta = parser: description: function: argument: input: - let - error = fail description function argument; - in - if !builtins.isString input - then error - else - let - result = parser input; - in - if builtins.isNull result - then error - else result; - - in - { - int = function: argument: input: - if builtins.isInt input - then input - else fail "an integer" function argument; - ip = meta parsers.ip "an IPv4 or IPv6 address"; - cidr = meta parsers.cidr "an IPv4 or IPv6 address range in CIDR notation"; - mac = meta parsers.mac "a MAC address"; - numeric = function: argument: input: - if builtins.isInt input - then input - else meta parsers.numeric "an integer or IPv4, IPv6 or MAC address" function argument input; - }; - -in -net diff --git a/nixos-modules/default.nix b/nixos-modules/default.nix deleted file mode 100644 index 4ee9a8b..0000000 --- a/nixos-modules/default.nix +++ /dev/null @@ -1,11 +0,0 @@ -{ - imports = [ - ./storage.nix - ./backups.nix - ./networking - ./data-sharing.nix - ./monitoring - ./k3s - ./tailscale.nix - ]; -} diff --git a/flake-parts/nixos.nix b/nixos.nix similarity index 93% rename from flake-parts/nixos.nix rename to nixos.nix index 38afa07..21c0023 100644 --- a/flake-parts/nixos.nix +++ b/nixos.nix @@ -16,7 +16,7 @@ in specialArgs = { inherit self inputs machine machines; }; modules = [ - "${self}/configuration.nix" + "${self}/modules" { networking.hostName = name; } ]; }); diff --git a/flake-parts/scripts/bootstrap.sh b/scripts/bootstrap.sh similarity index 100% rename from flake-parts/scripts/bootstrap.sh rename to scripts/bootstrap.sh diff --git a/flake-parts/scripts/default.nix b/scripts/default.nix similarity index 100% rename from flake-parts/scripts/default.nix rename to scripts/default.nix diff --git a/flake-parts/scripts/gen-k3s-cert.sh b/scripts/gen-k3s-cert.sh similarity index 100% rename from flake-parts/scripts/gen-k3s-cert.sh rename to scripts/gen-k3s-cert.sh diff --git a/flake-parts/shell.nix b/shell.nix similarity index 100% rename from flake-parts/shell.nix rename to shell.nix diff --git a/flake-parts/utils/default.nix b/utils/default.nix similarity index 100% rename from flake-parts/utils/default.nix rename to utils/default.nix diff --git a/flake-parts/utils/globals.nix b/utils/globals.nix similarity index 100% rename from flake-parts/utils/globals.nix rename to utils/globals.nix diff --git a/flake-parts/utils/net.nix b/utils/net.nix similarity index 100% rename from flake-parts/utils/net.nix rename to utils/net.nix