From 57f3bbe7945551741b7cea57f4ca50b2b7b842ba Mon Sep 17 00:00:00 2001 From: Jack O'Sullivan Date: Mon, 6 Jun 2022 14:11:52 +0100 Subject: [PATCH] nixos/pdns: Move file records into module --- nixos/boxes/colony/vms/estuary/dns.nix | 116 ++----------------------- nixos/modules/pdns.nix | 110 ++++++++++++++++++++++- 2 files changed, 113 insertions(+), 113 deletions(-) diff --git a/nixos/boxes/colony/vms/estuary/dns.nix b/nixos/boxes/colony/vms/estuary/dns.nix index af98562..fe42d81 100644 --- a/nixos/boxes/colony/vms/estuary/dns.nix +++ b/nixos/boxes/colony/vms/estuary/dns.nix @@ -2,7 +2,7 @@ let inherit (builtins) attrNames stringLength genList filter; inherit (lib) - concatStrings concatStringsSep concatMapStringsSep mapAttrsToList filterAttrs genAttrs optional optionalString; + concatStrings concatStringsSep concatMapStringsSep mapAttrsToList filterAttrs genAttrs optionalString; ptrDots = 2; reverseZone = "100.10.in-addr.arpa"; @@ -10,102 +10,9 @@ let reverseZone6 = "b.b.b.0.d.4.0.0.c.7.9.e.0.a.2.ip6.arpa"; authZones = attrNames config.my.pdns.auth.bind.zones; - - pdns-file-record = pkgs.writeShellApplication { - name = "pdns-file-record"; - runtimeInputs = with pkgs; [ gnused moreutils pdns ]; - text = '' - die() { - echo "$@" >&2 - exit 1 - } - usage() { - die "usage: $0 [content]" - } - - add() { - if [ $# -ne 2 ]; then - usage - fi - - echo "$2" >> "$dir"/"$1"txt - } - del() { - if [ $# -lt 1 ]; then - usage - fi - - file="$dir"/"$1"txt - if [ $# -eq 1 ]; then - rm "$file" - else - sed -i "/^""$2""$/!{q1}; /^""$2""$/d" "$file" - exit $? - fi - } - - dir=/run/pdns/file-records - mkdir -p "$dir" - - if [ $# -lt 2 ]; then - usage - fi - zone="$1" - shift - cmd="$1" - shift - - case "$cmd" in - add) - add "$@";; - del) - del "$@";; - *) - usage;; - esac - - # TODO: This feels pretty hacky? - zDat=/var/lib/pdns/bind-zones/"$zone".dat - # shellcheck disable=SC1090 - source "$zDat" - ((serial++)) - # Use sponge instead of `sed -i` because that actually uses a temporary file and clobbers ownership... - sed "s/^serial=.*$/serial=$serial/g" "$zDat" | sponge "$zDat" - sed "s/@@SERIAL@@/$serial/g" < /etc/pdns/bind-zones/"$zone".zone > /run/pdns/bind-zones/"$zone".zone - pdns_control bind-reload-now "$zone" - ''; - }; in { config = { - users = { - users = { - "pdns-file-records" = - let - script = pkgs.writeShellScript "pdns-file-records-ssh.sh" '' - read -r -a args <<< "$SSH_ORIGINAL_COMMAND" - exec ${pdns-file-record}/bin/pdns-file-record "''${args[@]}" - ''; - in - { - group = "pdns"; - isSystemUser = true; - shell = pkgs.bashInteractive; - openssh.authorizedKeys.keys = [ - ''command="${script}" ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBSvcgbEesOgvKJLt3FLXPaLOcCIuOUYtZXXtEv6k4Yd'' - ]; - }; - }; - }; - - systemd = { - services = { - pdns.preStart = '' - install -d -m 775 /run/pdns/file-records - ''; - }; - }; - services.pdns-recursor = { enable = true; dns = { @@ -131,7 +38,6 @@ in # For rec_control environment.systemPackages = with pkgs; [ pdns-recursor - pdns-file-record ]; my.pdns.auth = { @@ -150,6 +56,10 @@ in #log-dns-details = true; }; + bind = { + file-records.sshKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBSvcgbEesOgvKJLt3FLXPaLOcCIuOUYtZXXtEv6k4Yd"; + }; + bind.zones = let genRecords = f: @@ -203,20 +113,6 @@ in wildcardPtr6' = n: root: ''*.${wildcardPtr6Zeroes n}${root} ${wildcardPtr6Def}''; wildcardPtr6 = n: root: concatStringsSep "\n" (genList (i: wildcardPtr6' i root) (n - 1)); wildcardPtr6Z = wildcardPtr6 ptrDots6; - - fileRecScript = pkgs.writeText "file-record.lua" '' - local path = "/run/pdns/file-records/" .. string.lower(qname:toStringNoDot()) .. ".txt" - if not os.execute("test -e " .. path) then - return {} - end - - local values = {} - for line in io.lines(path) do - table.insert(values, line) - end - return values - ''; - fileRecVal = ''"dofile('${fileRecScript}')"''; in { "${config.networking.domain}" = { @@ -239,7 +135,7 @@ in http IN AAAA ${allAssignments.middleman.internal.ipv6.address} $TTL 3 - _acme-challenge IN LUA TXT ${fileRecVal} + _acme-challenge IN LUA TXT @@FILE@@ $TTL 60 ${intRecords} diff --git a/nixos/modules/pdns.nix b/nixos/modules/pdns.nix index ac477d7..cddab75 100644 --- a/nixos/modules/pdns.nix +++ b/nixos/modules/pdns.nix @@ -78,6 +78,85 @@ let } ''; + pdns-file-record = pkgs.writeShellApplication { + name = "pdns-file-record"; + runtimeInputs = with pkgs; [ gnused moreutils pdns ]; + text = '' + die() { + echo "$@" >&2 + exit 1 + } + usage() { + die "usage: $0 [content]" + } + + add() { + if [ $# -ne 2 ]; then + usage + fi + + echo "$2" >> "$dir"/"$1"txt + } + del() { + if [ $# -lt 1 ]; then + usage + fi + + file="$dir"/"$1"txt + if [ $# -eq 1 ]; then + rm "$file" + else + sed -i "/^""$2""$/!{q1}; /^""$2""$/d" "$file" + exit $? + fi + } + + dir=/run/pdns/file-records + mkdir -p "$dir" + + if [ $# -lt 2 ]; then + usage + fi + zone="$1" + shift + cmd="$1" + shift + + case "$cmd" in + add) + add "$@";; + del) + del "$@";; + *) + usage;; + esac + + # TODO: This feels pretty hacky? + zDat=/var/lib/pdns/bind-zones/"$zone".dat + # shellcheck disable=SC1090 + source "$zDat" + ((serial++)) + + # Use sponge instead of `sed -i` because that actually uses a temporary file and clobbers ownership... + sed "s/^serial=.*$/serial=$serial/g" "$zDat" | sponge "$zDat" + sed "s/@@SERIAL@@/$serial/g" < /etc/pdns/bind-zones/"$zone".zone > /run/pdns/bind-zones/"$zone".zone + pdns_control bind-reload-now "$zone" + ''; + }; + + fileRecScript = pkgs.writeText "file-record.lua" '' + local path = "/run/pdns/file-records/" .. string.lower(qname:toStringNoDot()) .. ".txt" + if not os.execute("test -e " .. path) then + return {} + end + + local values = {} + for line in io.lines(path) do + table.insert(values, line) + end + return values + ''; + cfg = config.my.pdns; namedConf = pkgs.writeText "pdns-named.conf" '' @@ -99,10 +178,13 @@ let def ptr(m): ip = ipaddress.ip_address(m.group(1)) return '.'.join(ip.reverse_pointer.split('.')[:int(m.group(2))]) - ex = re.compile(r'@@PTR:(.+):(\d+)@@') + ex_ptr = re.compile(r'@@PTR:(.+):(\d+)@@') + + fr = '"dofile(\'${fileRecScript}\')"' + ex_fr = re.compile(r'@@FILE@@') for line in sys.stdin: - print(ex.sub(ptr, line), end=''') + print(ex_fr.sub(fr, ex_ptr.sub(ptr, line)), end=''') ''; } '' ${pkgs.python310}/bin/python "$scriptPath" < "${s}" > "$out" @@ -111,6 +193,8 @@ let name = "${n}.zone"; path = if o.template then templateZone n o.path else o.path; }) cfg.auth.bind.zones); + + enableFileRecSSH = cfg.auth.bind.file-records.sshKey != null; in { options.my.pdns = with lib.types; { @@ -123,6 +207,9 @@ in also-notify = bindAlsoNotify; }; zones = mkOpt' (attrsOf (submodule bindZoneOpts)) { } "BIND-style zones definitions."; + file-records = { + sshKey = mkOpt' (nullOr str) null "SSH public key for file record update user."; + }; }; }; }; @@ -139,10 +226,27 @@ in }; }; + users.users."pdns-file-records" = + let + script = pkgs.writeShellScript "pdns-file-records-ssh.sh" '' + read -r -a args <<< "$SSH_ORIGINAL_COMMAND" + exec ${pdns-file-record}/bin/pdns-file-record "''${args[@]}" + ''; + in + (mkIf enableFileRecSSH { + group = "pdns"; + isSystemUser = true; + shell = pkgs.bashInteractive; + openssh.authorizedKeys.keys = [ + ''command="${script}" ${cfg.auth.bind.file-records.sshKey}'' + ]; + }); + environment = { # For pdns_control etc systemPackages = with pkgs; [ pdns + pdns-file-record ]; etc."pdns/bind-zones".source = "${zones}/*"; @@ -152,7 +256,7 @@ in preStart = '' source ${loadZonesCommon} - mkdir /run/pdns/bind-zones + mkdir /run/pdns/{bind-zones,file-records} mkdir -p /var/lib/pdns/bind-zones loadZones start '';