nixpkgs/nixos/modules/services/networking/bind.nix
Matt Christ c355b2729c nixos/bind: configurable "forward" setting
Sometimes it is preferable to configure forwarding only for bind
instead of relying on direct lookups.

This patch makes it possible to configure the forward setting to
either "first" (the default) or "only".
2022-01-01 08:33:51 -06:00

275 lines
7.5 KiB
Nix

{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.services.bind;
bindPkg = config.services.bind.package;
bindUser = "named";
bindZoneCoerce = list: builtins.listToAttrs (lib.forEach list (zone: { name = zone.name; value = zone; }));
bindZoneOptions = { name, config, ... }: {
options = {
name = mkOption {
type = types.str;
default = name;
description = "Name of the zone.";
};
master = mkOption {
description = "Master=false means slave server";
type = types.bool;
};
file = mkOption {
type = types.either types.str types.path;
description = "Zone file resource records contain columns of data, separated by whitespace, that define the record.";
};
masters = mkOption {
type = types.listOf types.str;
description = "List of servers for inclusion in stub and secondary zones.";
};
slaves = mkOption {
type = types.listOf types.str;
description = "Addresses who may request zone transfers.";
default = [ ];
};
extraConfig = mkOption {
type = types.str;
description = "Extra zone config to be appended at the end of the zone section.";
default = "";
};
};
};
confFile = pkgs.writeText "named.conf"
''
include "/etc/bind/rndc.key";
controls {
inet 127.0.0.1 allow {localhost;} keys {"rndc-key";};
};
acl cachenetworks { ${concatMapStrings (entry: " ${entry}; ") cfg.cacheNetworks} };
acl badnetworks { ${concatMapStrings (entry: " ${entry}; ") cfg.blockedNetworks} };
options {
listen-on { ${concatMapStrings (entry: " ${entry}; ") cfg.listenOn} };
listen-on-v6 { ${concatMapStrings (entry: " ${entry}; ") cfg.listenOnIpv6} };
allow-query { cachenetworks; };
blackhole { badnetworks; };
forward ${cfg.forward};
forwarders { ${concatMapStrings (entry: " ${entry}; ") cfg.forwarders} };
directory "${cfg.directory}";
pid-file "/run/named/named.pid";
${cfg.extraOptions}
};
${cfg.extraConfig}
${ concatMapStrings
({ name, file, master ? true, slaves ? [], masters ? [], extraConfig ? "" }:
''
zone "${name}" {
type ${if master then "master" else "slave"};
file "${file}";
${ if master then
''
allow-transfer {
${concatMapStrings (ip: "${ip};\n") slaves}
};
''
else
''
masters {
${concatMapStrings (ip: "${ip};\n") masters}
};
''
}
allow-query { any; };
${extraConfig}
};
'')
(attrValues cfg.zones) }
'';
in
{
###### interface
options = {
services.bind = {
enable = mkEnableOption "BIND domain name server";
package = mkOption {
type = types.package;
default = pkgs.bind;
defaultText = literalExpression "pkgs.bind";
description = "The BIND package to use.";
};
cacheNetworks = mkOption {
default = [ "127.0.0.0/24" ];
type = types.listOf types.str;
description = "
What networks are allowed to use us as a resolver. Note
that this is for recursive queries -- all networks are
allowed to query zones configured with the `zones` option.
It is recommended that you limit cacheNetworks to avoid your
server being used for DNS amplification attacks.
";
};
blockedNetworks = mkOption {
default = [ ];
type = types.listOf types.str;
description = "
What networks are just blocked.
";
};
ipv4Only = mkOption {
default = false;
type = types.bool;
description = "
Only use ipv4, even if the host supports ipv6.
";
};
forwarders = mkOption {
default = config.networking.nameservers;
defaultText = literalExpression "config.networking.nameservers";
type = types.listOf types.str;
description = "
List of servers we should forward requests to.
";
};
forward = mkOption {
default = "first";
type = types.enum ["first" "only"];
description = "
Whether to forward 'first' (try forwarding but lookup directly if forwarding fails) or 'only'.
";
};
listenOn = mkOption {
default = [ "any" ];
type = types.listOf types.str;
description = "
Interfaces to listen on.
";
};
listenOnIpv6 = mkOption {
default = [ "any" ];
type = types.listOf types.str;
description = "
Ipv6 interfaces to listen on.
";
};
directory = mkOption {
type = types.str;
default = "/run/named";
description = "Working directory of BIND.";
};
zones = mkOption {
default = [ ];
type = with types; coercedTo (listOf attrs) bindZoneCoerce (attrsOf (types.submodule bindZoneOptions));
description = "
List of zones we claim authority over.
";
example = {
"example.com" = {
master = false;
file = "/var/dns/example.com";
masters = [ "192.168.0.1" ];
slaves = [ ];
extraConfig = "";
};
};
};
extraConfig = mkOption {
type = types.lines;
default = "";
description = "
Extra lines to be added verbatim to the generated named configuration file.
";
};
extraOptions = mkOption {
type = types.lines;
default = "";
description = ''
Extra lines to be added verbatim to the options section of the
generated named configuration file.
'';
};
configFile = mkOption {
type = types.path;
default = confFile;
defaultText = literalExpression "confFile";
description = "
Overridable config file to use for named. By default, that
generated by nixos.
";
};
};
};
###### implementation
config = mkIf cfg.enable {
networking.resolvconf.useLocalResolver = mkDefault true;
users.users.${bindUser} =
{
group = bindUser;
description = "BIND daemon user";
isSystemUser = true;
};
users.groups.${bindUser} = {};
systemd.services.bind = {
description = "BIND Domain Name Server";
after = [ "network.target" ];
wantedBy = [ "multi-user.target" ];
preStart = ''
mkdir -m 0755 -p /etc/bind
if ! [ -f "/etc/bind/rndc.key" ]; then
${bindPkg.out}/sbin/rndc-confgen -c /etc/bind/rndc.key -u ${bindUser} -a -A hmac-sha256 2>/dev/null
fi
${pkgs.coreutils}/bin/mkdir -p /run/named
chown ${bindUser} /run/named
${pkgs.coreutils}/bin/mkdir -p ${cfg.directory}
chown ${bindUser} ${cfg.directory}
'';
serviceConfig = {
ExecStart = "${bindPkg.out}/sbin/named -u ${bindUser} ${optionalString cfg.ipv4Only "-4"} -c ${cfg.configFile} -f";
ExecReload = "${bindPkg.out}/sbin/rndc -k '/etc/bind/rndc.key' reload";
ExecStop = "${bindPkg.out}/sbin/rndc -k '/etc/bind/rndc.key' stop";
};
unitConfig.Documentation = "man:named(8)";
};
};
}