summary refs log tree commit diff
path: root/nixos/modules/services/networking/pdns-recursor.nix
diff options
context:
space:
mode:
Diffstat (limited to 'nixos/modules/services/networking/pdns-recursor.nix')
-rw-r--r--nixos/modules/services/networking/pdns-recursor.nix168
1 files changed, 168 insertions, 0 deletions
diff --git a/nixos/modules/services/networking/pdns-recursor.nix b/nixos/modules/services/networking/pdns-recursor.nix
new file mode 100644
index 000000000000..26be72d2a61e
--- /dev/null
+++ b/nixos/modules/services/networking/pdns-recursor.nix
@@ -0,0 +1,168 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  dataDir  = "/var/lib/pdns-recursor";
+  username = "pdns-recursor";
+
+  cfg   = config.services.pdns-recursor;
+  zones = mapAttrsToList (zone: uri: "${zone}.=${uri}") cfg.forwardZones;
+
+  configFile = pkgs.writeText "recursor.conf" ''
+    local-address=${cfg.dns.address}
+    local-port=${toString cfg.dns.port}
+    allow-from=${concatStringsSep "," cfg.dns.allowFrom}
+
+    webserver-address=${cfg.api.address}
+    webserver-port=${toString cfg.api.port}
+    webserver-allow-from=${concatStringsSep "," cfg.api.allowFrom}
+
+    forward-zones=${concatStringsSep "," zones}
+    export-etc-hosts=${if cfg.exportHosts then "yes" else "no"}
+    dnssec=${cfg.dnssecValidation}
+    serve-rfc1918=${if cfg.serveRFC1918 then "yes" else "no"}
+
+    ${cfg.extraConfig}
+  '';
+
+in {
+  options.services.pdns-recursor = {
+    enable = mkEnableOption "PowerDNS Recursor, a recursive DNS server";
+
+    dns.address = mkOption {
+      type = types.str;
+      default = "0.0.0.0";
+      description = ''
+        IP address Recursor DNS server will bind to.
+      '';
+    };
+
+    dns.port = mkOption {
+      type = types.int;
+      default = 53;
+      description = ''
+        Port number Recursor DNS server will bind to.
+      '';
+    };
+
+    dns.allowFrom = mkOption {
+      type = types.listOf types.str;
+      default = [ "10.0.0.0/8" "172.16.0.0/12" "192.168.0.0/16" ];
+      example = [ "0.0.0.0/0" ];
+      description = ''
+        IP address ranges of clients allowed to make DNS queries.
+      '';
+    };
+
+    api.address = mkOption {
+      type = types.str;
+      default = "0.0.0.0";
+      description = ''
+        IP address Recursor REST API server will bind to.
+      '';
+    };
+
+    api.port = mkOption {
+      type = types.int;
+      default = 8082;
+      description = ''
+        Port number Recursor REST API server will bind to.
+      '';
+    };
+
+    api.allowFrom = mkOption {
+      type = types.listOf types.str;
+      default = [ "0.0.0.0/0" ];
+      description = ''
+        IP address ranges of clients allowed to make API requests.
+      '';
+    };
+
+    exportHosts = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+       Whether to export names and IP addresses defined in /etc/hosts.
+      '';
+    };
+
+    forwardZones = mkOption {
+      type = types.attrs;
+      example = { eth = "127.0.0.1:5353"; };
+      default = {};
+      description = ''
+        DNS zones to be forwarded to other servers.
+      '';
+    };
+
+    dnssecValidation = mkOption {
+      type = types.enum ["off" "process-no-validate" "process" "log-fail" "validate"];
+      default = "validate";
+      description = ''
+        Controls the level of DNSSEC processing done by the PowerDNS Recursor.
+        See https://doc.powerdns.com/md/recursor/dnssec/ for a detailed explanation.
+      '';
+    };
+
+    serveRFC1918 = mkOption {
+      type = types.bool;
+      default = true;
+      description = ''
+        Whether to directly resolve the RFC1918 reverse-mapping domains:
+        <literal>10.in-addr.arpa</literal>,
+        <literal>168.192.in-addr.arpa</literal>,
+        <literal>16-31.172.in-addr.arpa</literal>
+        This saves load on the AS112 servers.
+      '';
+    };
+
+    extraConfig = mkOption {
+      type = types.lines;
+      default = "";
+      description = ''
+        Extra options to be appended to the configuration file.
+      '';
+    };
+  };
+
+  config = mkIf cfg.enable {
+
+    users.extraUsers."${username}" = {
+      home = dataDir;
+      createHome = true;
+      uid = config.ids.uids.pdns-recursor;
+      description = "PowerDNS Recursor daemon user";
+    };
+
+    systemd.services.pdns-recursor = {
+      unitConfig.Documentation = "man:pdns_recursor(1) man:rec_control(1)";
+      description = "PowerDNS recursive server";
+      wantedBy = [ "multi-user.target" ];
+      after    = [ "network.target" ];
+
+      serviceConfig = {
+        User = username;
+        Restart    ="on-failure";
+        RestartSec = "5";
+        PrivateTmp = true;
+        PrivateDevices = true;
+        AmbientCapabilities = "cap_net_bind_service";
+        ExecStart = ''${pkgs.pdns-recursor}/bin/pdns_recursor \
+          --config-dir=${dataDir} \
+          --socket-dir=${dataDir} \
+          --disable-syslog
+        '';
+      };
+
+      preStart = ''
+        # Link configuration file into recursor home directory
+        configPath=${dataDir}/recursor.conf
+        if [ "$(realpath $configPath)" != "${configFile}" ]; then
+          rm -f $configPath
+          ln -s ${configFile} $configPath
+        fi
+      '';
+    };
+  };
+}