diff --git a/nixos/modules/services/networking/nsd.nix b/nixos/modules/services/networking/nsd.nix index c8b8ed547eb..636af4bbc31 100644 --- a/nixos/modules/services/networking/nsd.nix +++ b/nixos/modules/services/networking/nsd.nix @@ -248,6 +248,46 @@ let Use imports or pkgs.lib.readFile if you don't want this data in your config file. ''; }; + + dnssec = mkEnableOption "DNSSEC"; + + dnssecPolicy = { + algorithm = mkOption { + type = types.str; + default = "RSASHA256"; + description = "Which algorithm to use for DNSSEC"; + }; + keyttl = mkOption { + type = types.str; + default = "1h"; + description = "TTL for dnssec records"; + }; + coverage = mkOption { + type = types.str; + default = "1y"; + description = '' + The length of time to ensure that keys will be correct; no action will be taken to create new keys to be activated after this time. + ''; + }; + zsk = mkOption { + type = keyPolicy; + default = { keySize = 2048; + prePublish = "1w"; + postPublish = "1w"; + rollPeriod = "1mo"; + }; + description = "Key policy for zone signing keys"; + }; + ksk = mkOption { + type = keyPolicy; + default = { keySize = 4096; + prePublish = "1mo"; + postPublish = "1mo"; + rollPeriod = "0"; + }; + description = "Key policy for key signing keys"; + }; + }; maxRefreshSecs = mkOption { type = types.nullOr types.int; @@ -365,10 +405,61 @@ let and stats_noreset. ''; }; - }; }; + keyPolicy = types.submodule { + options = { + keySize = mkOption { + type = types.int; + description = "Key size in bits"; + }; + prePublish = mkOption { + type = types.str; + description = "How long in advance to publish new keys"; + }; + postPublish = mkOption { + type = types.str; + description = "How long after deactivation to keep a key in the zone"; + }; + rollPeriod = mkOption { + type = types.str; + description = "How frequently to change keys"; + }; + }; + }; + + dnssecZones = (filterAttrs (n: v: if v ? dnssec then v.dnssec else false) zoneConfigs); + + dnssec = length (attrNames dnssecZones) != 0; + + signZones = optionalString dnssec '' + mkdir -p ${stateDir}/dnssec + chown ${username}:${username} ${stateDir}/dnssec + chmod 0600 ${stateDir}/dnssec + + ${concatStrings (mapAttrsToList signZone dnssecZones)} + ''; + signZone = name: zone: '' + ${pkgs.bind}/bin/dnssec-keymgr -g ${pkgs.bind}/bin/dnssec-keygen -s ${pkgs.bind}/bin/dnssec-settime -K ${stateDir}/dnssec -c ${policyFile name zone.dnssecPolicy} ${name} + ${pkgs.bind}/bin/dnssec-signzone -S -K ${stateDir}/dnssec -o ${name} -O full -N date ${stateDir}/zones/${name} + ${nsdPkg}/sbin/nsd-checkzone ${name} ${stateDir}/zones/${name}.signed && mv -v ${stateDir}/zones/${name}.signed ${stateDir}/zones/${name} + ''; + policyFile = name: policy: pkgs.writeText "${name}.policy" '' + zone ${name} { + algorithm ${policy.algorithm}; + key-size zsk ${toString policy.zsk.keySize}; + key-size ksk ${toString policy.ksk.keySize}; + keyttl ${policy.keyttl}; + pre-publish zsk ${policy.zsk.prePublish}; + pre-publish ksk ${policy.ksk.prePublish}; + post-publish zsk ${policy.zsk.postPublish}; + post-publish ksk ${policy.ksk.postPublish}; + roll-period zsk ${policy.zsk.rollPeriod}; + roll-period ksk ${policy.ksk.rollPeriod}; + coverage ${policy.coverage}; + }; + ''; in { # options are ordered alphanumerically @@ -378,6 +469,14 @@ in bind8Stats = mkEnableOption "BIND8 like statistics"; + dnssecInterval = mkOption { + type = types.str; + default = "1h"; + description = '' + How often to check whether dnssec key rollover is required + ''; + }; + extraConfig = mkOption { type = types.str; default = ""; @@ -739,7 +838,6 @@ in }; - zones = mkOption { type = types.attrsOf zoneOptions; default = {}; @@ -783,7 +881,6 @@ in serverGroup1. ''; }; - }; config = mkIf cfg.enable { @@ -828,9 +925,9 @@ in mkdir -m 0700 -p "${stateDir}/var" cat > "${stateDir}/don't touch anything in here" << EOF - Everything in this directory except NSD's state in var is - automatically generated and will be purged and redeployed - by the nsd.service pre-start script. + Everything in this directory except NSD's state in var and dnssec + is automatically generated and will be purged and redeployed by + the nsd.service pre-start script. EOF chown ${username}:${username} -R "${stateDir}/private" @@ -844,5 +941,33 @@ in ''; }; + nixpkgs.config = mkIf dnssec { + bind.enablePython = true; + }; + + systemd.timers."nsd-dnssec" = mkIf dnssec { + description = "Automatic DNSSEC key rollover"; + + wantedBy = [ "nsd.service" ]; + + timerConfig = { + OnActiveSec = cfg.dnssecInterval; + OnUnitActiveSec = cfg.dnssecInterval; + }; + }; + + systemd.services."nsd-dnssec" = mkIf dnssec { + description = "DNSSEC key rollover"; + + wantedBy = [ "nsd.service" ]; + before = [ "nsd.service" ]; + + script = signZones; + + postStop = '' + ${pkgs.systemd}/bin/systemctl kill -s SIGHUP nsd.service + ''; + }; + }; } diff --git a/pkgs/servers/dns/bind/default.nix b/pkgs/servers/dns/bind/default.nix index bf7a7266022..c52761e66b6 100644 --- a/pkgs/servers/dns/bind/default.nix +++ b/pkgs/servers/dns/bind/default.nix @@ -1,7 +1,9 @@ { stdenv, lib, fetchurl, openssl, libtool, perl, libxml2 -, enableSeccomp ? false, libseccomp ? null }: +, enableSeccomp ? false, libseccomp ? null +, enablePython ? false, python ? null }: assert enableSeccomp -> libseccomp != null; +assert enablePython -> python != null; let version = "9.11.2"; in @@ -18,8 +20,9 @@ stdenv.mkDerivation rec { patches = [ ./dont-keep-configure-flags.patch ./remove-mkdir-var.patch ] ++ stdenv.lib.optional stdenv.isDarwin ./darwin-openssl-linking-fix.patch; - buildInputs = [ openssl libtool perl libxml2 ] ++ - stdenv.lib.optional enableSeccomp libseccomp; + buildInputs = [ openssl libtool perl libxml2 ] + ++ lib.optional enableSeccomp libseccomp + ++ lib.optional enablePython python; STD_CDEFINES = [ "-DDIG_SIGCHASE=1" ]; # support +sigchase @@ -28,6 +31,7 @@ stdenv.mkDerivation rec { "--with-libtool" "--with-libxml2=${libxml2.dev}" "--with-openssl=${openssl.dev}" + (if enablePython then "--with-python" else "--without-python") "--without-atf" "--without-dlopen" "--without-docbook-xsl" @@ -36,7 +40,6 @@ stdenv.mkDerivation rec { "--without-idnlib" "--without-pkcs11" "--without-purify" - "--without-python" ] ++ lib.optional enableSeccomp "--enable-seccomp"; postInstall = '' diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix index 46a1976adc1..35bf5e73d1c 100644 --- a/pkgs/top-level/all-packages.nix +++ b/pkgs/top-level/all-packages.nix @@ -11462,7 +11462,10 @@ with pkgs; bftpd = callPackage ../servers/ftp/bftpd {}; - bind = callPackage ../servers/dns/bind { }; + bind = callPackage ../servers/dns/bind { + enablePython = config.bind.enablePython or false; + python = python.withPackages (ps: with ps; [ ply ]); + }; dnsutils = bind.dnsutils; bird = callPackage ../servers/bird { };