From 23351e725d31dfab8fab8634da1ef04d5c296bbd Mon Sep 17 00:00:00 2001 From: niten Date: Sun, 21 Nov 2021 14:20:06 -0800 Subject: [PATCH] Initial checkin --- flake.nix | 7 +++++ lib/dns.nix | 70 +++++++++++++++++++++++++++++++++++++++++ lib/filesystem.nix | 32 +++++++++++++++++++ lib/ip.nix | 78 ++++++++++++++++++++++++++++++++++++++++++++++ lib/lisp.nix | 8 +++++ lib/network.nix | 53 +++++++++++++++++++++++++++++++ lib/passwd.nix | 54 ++++++++++++++++++++++++++++++++ overlay.nix | 14 +++++++++ 8 files changed, 316 insertions(+) create mode 100644 flake.nix create mode 100644 lib/dns.nix create mode 100644 lib/filesystem.nix create mode 100644 lib/ip.nix create mode 100644 lib/lisp.nix create mode 100644 lib/network.nix create mode 100644 lib/passwd.nix create mode 100644 overlay.nix diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..7b70842 --- /dev/null +++ b/flake.nix @@ -0,0 +1,7 @@ +{ + description = "Fudo Nix Helper Functions"; + + outputs = { self, nixpkgs, ... }: { + overlay = import ./overlay.nix; + }; +} diff --git a/lib/dns.nix b/lib/dns.nix new file mode 100644 index 0000000..c0979f4 --- /dev/null +++ b/lib/dns.nix @@ -0,0 +1,70 @@ +{ pkgs, ... }: + +with pkgs.lib; +let + join-lines = concatStringsSep "\n"; + + dump = obj: builtins.trace obj obj; + + makeSrvRecords = protocol: service: records: let + service-blah = (dump service); + record-blah = (dump records); + in + join-lines (map (record: + "_${service}._${protocol} IN SRV ${toString record.priority} ${ + toString record.weight + } ${toString record.port} ${record.host}.") records); + + makeSrvProtocolRecords = protocol: services: + join-lines (mapAttrsToList (makeSrvRecords protocol) services); + + srvRecordOpts = with types; { + options = { + weight = mkOption { + type = int; + description = "Weight relative to other records."; + default = 1; + }; + + priority = mkOption { + type = int; + description = "Priority to give this record."; + default = 0; + }; + + port = mkOption { + type = port; + description = "Port to use when connecting."; + }; + + host = mkOption { + type = str; + description = "Host to contact for this service."; + example = "my-host.my-domain.com."; + }; + }; + }; + + srvRecordPair = domain: protocol: service: record: { + "_${service}._${protocol}.${domain}" = + "${toString record.priority} ${toString record.weight} ${ + toString record.port + } ${record.host}."; + }; + +in rec { + + srvRecords = with types; attrsOf (attrsOf (listOf (submodule srvRecordOpts))); + + srvRecordsToBindZone = srvRecords: + join-lines (mapAttrsToList makeSrvProtocolRecords srvRecords); + + concatMapAttrs = f: attrs: + concatMap (x: x) (mapAttrsToList (key: val: f key val) attrs); + + srvRecordsToPairs = domain: srvRecords: + listToAttrs (concatMapAttrs (protocol: services: + concatMapAttrs + (service: records: map (srvRecordPair domain protocol service) records) services) + srvRecords); +} diff --git a/lib/filesystem.nix b/lib/filesystem.nix new file mode 100644 index 0000000..b330564 --- /dev/null +++ b/lib/filesystem.nix @@ -0,0 +1,32 @@ +{ pkgs, ... }: + +with pkgs.lib; +let + head-or-null = lst: if (lst == []) then null else head lst; + is-regular-file = filename: type: type == "regular" || type == "link"; + regular-files = path: filterAttrs is-regular-file (builtins.readDir path); + matches-ext = ext: filename: type: (builtins.match ".+[.]${ext}$" filename) != null; + is-nix-file = matches-ext "nix"; + strip-ext = ext: filename: head-or-null (builtins.match "(.+)[.]${ext}$" filename); + get-ext = filename: head-or-null (builtins.match "^.+[.](.+)$" filename); + hostname-from-file = filename: strip-ext "nix"; + nix-files = path: + attrNames + (filterAttrs is-nix-file + (filterAttrs is-regular-file + (builtins.readDir path))); + + basename-to-file-map = path: let + files = nix-files path; + in listToAttrs + (map (file: + nameValuePair (strip-ext "nix" file) + (path + "/${file}")) + files); + + import-by-basename = path: + mapAttrs (attr: attr-file: import attr-file) + (basename-to-file path); +in { + inherit basename-to-file-map import-by-basename; +} diff --git a/lib/ip.nix b/lib/ip.nix new file mode 100644 index 0000000..33d477a --- /dev/null +++ b/lib/ip.nix @@ -0,0 +1,78 @@ +{ pkgs, ... }: + +with pkgs.lib; +let + pow = x: e: if (e == 0) then 1 else x * (pow x (e - 1)); + + generateNBits = n: + let + helper = n: c: + if (c == n) then pow 2 c else (pow 2 c) + (helper n (c + 1)); + in if (n <= 0) then + throw "Can't generate 0 or fewer bits" + else + helper (n - 1) 0; + + rightPadBits = int: bits: bitOr int (generateNBits bits); + + reverseIpv4 = ip: concatStringsSep "." (reverseList (splitString "." ip)); + + intToBinaryList = int: + let + helper = int: cur: + let curExp = pow 2 cur; + in if (curExp > int) then + [ ] + else + [ (if ((bitAnd curExp int) > 0) then 1 else 0) ] + ++ (helper int (cur + 1)); + in reverseList (helper int 0); + + leftShift = int: n: int * (pow 2 n); + + rightShift = int: n: int / (pow 2 n); + +in rec { + + ipv4ToInt = ip: + let els = map toInt (reverseList (splitString "." ip)); + in foldr (a: b: a + b) 0 (imap0 (i: el: (leftShift el (i * 8))) els); + + intToIpv4 = int: + concatStringsSep "." + (map (i: toString (bitAnd (rightShift int (i * 8)) 255)) [ 3 2 1 0 ]); + + maskFromV32Network = network: + let + fullMask = ipv4ToInt "255.255.255.255"; + insignificantBits = 32 - (getNetworkMask network); + in intToIpv4 + (leftShift (rightShift fullMask insignificantBits) insignificantBits); + + networkMinIp = network: intToIpv4 (1 + (ipv4ToInt (getNetworkBase network))); + + networkMaxIp = network: + intToIpv4 (rightPadBits (ipv4ToInt (getNetworkBase network)) + (32 - (getNetworkMask network))); + + # To avoid broadcast IP... + networkMaxButOneIp = network: + intToIpv4 ((rightPadBits (ipv4ToInt (getNetworkBase network)) + (32 - (getNetworkMask network))) - 1); + + ipv4OnNetwork = ip: network: + let + ip-int = ipv4ToInt ip; + net-min = networkMinIp network; + net-max = networkMaxIp network; + in (ip-int >= networkMinIp) && (ip-int <= networkMaxIp); + + getNetworkMask = network: toInt (elemAt (splitString "/" network) 1); + + getNetworkBase = network: + let + ip = elemAt (splitString "/" network) 0; + insignificantBits = 32 - (getNetworkMask network); + in intToIpv4 + (leftShift (rightShift (ipv4ToInt ip) insignificantBits) insignificantBits); +} diff --git a/lib/lisp.nix b/lib/lisp.nix new file mode 100644 index 0000000..1eea537 --- /dev/null +++ b/lib/lisp.nix @@ -0,0 +1,8 @@ +{ pkgs, ... }: + +with pkgs.lib; +rec { + gather-dependencies = pkg: unique (pkg.propagatedBuildInputs ++ (concatMap gather-dependencies pkg.propagatedBuildInputs)); + + lisp-source-registry = pkg: concatStringsSep ":" (map (p: "${p}//") (gather-dependencies pkg)); +} diff --git a/lib/network.nix b/lib/network.nix new file mode 100644 index 0000000..944b722 --- /dev/null +++ b/lib/network.nix @@ -0,0 +1,53 @@ +{ pkgs, ... }: + +with pkgs.lib; +let + generate-mac-address = hostname: interface: pkgs.stdenv.mkDerivation { + name = "mk-mac-${hostname}-${interface}"; + phases = [ "installPhase" ]; + installPhase = '' + echo ${hostname}-${interface} | sha1sum | sed 's/^\(..\)\(..\)\(..\)\(..\)\(..\).*$/02:\1:\2:\3:\4:\5/' > $out + ''; + }; + + # dropUntil = pred: lst: let + # drop-until-helper = pred: lst: + # if (length lst) == 0 then [] else + # if (pred (head lst)) then lst else (drop-until-helper pred (tail lst)); + # in drop-until-helper pred lst; + + # dropWhile = pred: dropUntil (el: !(pred el)); + + # is-whitespace = str: (builtins.match "^[[:space:]]*$" str) != null; + + # stripWhitespace = str: let + # lines = builtins.split "\n" str; + # lines-front-stripped = dropWhile is-whitespace lines; + # lines-rear-stripped = lib.reverseList + # (dropWhile is-whitespace + # (lib.reverseList lines-front-stripped)); + # in concatStringsSep "\n" lines-rear-stripped; + + host-ipv4 = config: hostname: let + domain = config.fudo.hosts.${hostname}.domain; + host-network = config.fudo.networks.${domain}; + in host-network.hosts.${hostname}.ipv4-address; + + host-ipv6 = config: hostname: let + domain = config.fudo.hosts.${hostname}.domain; + host-network = config.fudo.networks.${domain}; + in host-network.hosts.${hostname}.ipv6-address; + + host-ips = config: hostname: let + ipv4 = host-ipv4 config hostname; + ipv6 = host-ipv6 config hostname; + not-null = o: o != null; + in filter not-null [ ipv4 ipv6 ]; + +in { + inherit host-ipv4 host-ipv6 host-ips; + + generate-mac-address = hostname: interface: let + pkg = generate-mac-address hostname interface; + in removeSuffix "\n" (builtins.readFile "${pkg}"); +} diff --git a/lib/passwd.nix b/lib/passwd.nix new file mode 100644 index 0000000..97c2088 --- /dev/null +++ b/lib/passwd.nix @@ -0,0 +1,54 @@ +{ pkgs, ... }: + +with pkgs.lib; +let + hash-ldap-passwd-pkg = name: passwd-file: pkgs.stdenv.mkDerivation { + name = "${name}-ldap-passwd"; + + phases = [ "installPhase" ]; + + buildInputs = with pkgs; [ openldap ]; + + installPhase = '' + slappasswd -T ${passwd-file} > $out + ''; + }; + + hash-ldap-passwd = name: passwd-file: + builtins.readFile "${hash-ldap-passwd-pkg name passwd-file}"; + + generate-random-passwd = name: length: pkgs.stdenv.mkDerivation { + name = "${name}-random-passwd"; + + phases = [ "installPhase" ]; + + buildInputs = with pkgs; [ pwgen ]; + + installPhase = '' + pwgen --secure --num-passwords=1 ${toString length} > $out + ''; + }; + + generate-stablerandom-passwd = name: { seed, length ? 20, ... }: + pkgs.stdenv.mkDerivation { + name = "${name}-stablerandom-passwd"; + + phases = [ "installPhase" ]; + + buildInputs = with pkgs; [ pwgen ]; + + installPhase = '' + echo "${name}-${seed}" > seedfile + pwgen --secure --num-passwords=1 -H seedfile ${toString length} > $out + ''; + }; + +in { + hash-ldap-passwd = hash-ldap-passwd; + + random-passwd-file = name: length: + builtins.toPath "${generate-random-passwd name length}"; + + stablerandom-passwd-file = name: seed: + builtins.toPath "${generate-stablerandom-passwd name { seed = seed; }}"; +} diff --git a/overlay.nix b/overlay.nix new file mode 100644 index 0000000..86b7c29 --- /dev/null +++ b/overlay.nix @@ -0,0 +1,14 @@ +(final: prev: with builtins; { + lib = let + pkgs = prev; + lib = prev.lib; + in + lib // { + ip = import ./lib/ip.nix { inherit pkgs; }; + dns = import ./lib/dns.nix { inherit pkgs; }; + passwd = import ./lib/passwd.nix { inherit pkgs; }; + lisp = import ./lib/lisp.nix { inherit pkgs; }; + network = import ./lib/network.nix { inherit pkgs; }; + fs = import ./lib/filesystem.nix { inherit pkgs; }; + }; +})