trust-dns: rework the module to be more suitable for upstreaming

still need to do hardening and docs
This commit is contained in:
Colin 2023-07-02 08:21:33 +00:00
parent 154711432f
commit 9777e5f83c
3 changed files with 120 additions and 83 deletions

View File

@ -3,13 +3,21 @@
{ {
sane.services.trust-dns.enable = true; sane.services.trust-dns.enable = true;
sane.services.trust-dns.listenAddrsIPv4 = [ sane.services.trust-dns.settings.listen_addrs_ipv4 = [
# specify each address explicitly, instead of using "*". # specify each address explicitly, instead of using "*".
# this ensures responses are sent from the address at which the request was received. # this ensures responses are sent from the address at which the request was received.
config.sane.hosts.by-name."servo".lan-ip config.sane.hosts.by-name."servo".lan-ip
"10.0.1.5" "10.0.1.5"
]; ];
sane.services.trust-dns.quiet = true; sane.services.trust-dns.quiet = true;
# sane.services.trust-dns.debug = true;
sane.ports.ports."53" = {
protocol = [ "udp" "tcp" ];
visibleTo.lan = true;
visibleTo.wan = true;
description = "colin-dns-hosting";
};
sane.dns.zones."uninsane.org".TTL = 900; sane.dns.zones."uninsane.org".TTL = 900;
@ -53,8 +61,11 @@
# we need trust-dns to load our zone by relative path instead of /nix/store path # we need trust-dns to load our zone by relative path instead of /nix/store path
# because we generate it at runtime. # because we generate it at runtime.
sane.services.trust-dns.zones."uninsane.org".file = lib.mkForce "uninsane.org.zone"; sane.services.trust-dns.settings.zones = [
sane.services.trust-dns.zonedir = null; {
zone = "uninsane.org";
}
];
sane.services.trust-dns.package = sane.services.trust-dns.package =
let let
@ -62,7 +73,7 @@
zone-dir = "/var/lib/trust-dns"; zone-dir = "/var/lib/trust-dns";
zone-wan = "${zone-dir}/wan/uninsane.org.zone"; zone-wan = "${zone-dir}/wan/uninsane.org.zone";
zone-lan = "${zone-dir}/lan/uninsane.org.zone"; zone-lan = "${zone-dir}/lan/uninsane.org.zone";
zone-template = pkgs.writeText "uninsane.org.zone.in" config.sane.services.trust-dns.zones."uninsane.org".text; zone-template = pkgs.writeText "uninsane.org.zone.in" config.sane.dns.zones."uninsane.org".rendered;
in pkgs.writeShellScriptBin "named" '' in pkgs.writeShellScriptBin "named" ''
# compute wan/lan values # compute wan/lan values
mkdir -p ${zone-dir}/{ovpn,wan,lan} mkdir -p ${zone-dir}/{ovpn,wan,lan}

View File

@ -1,3 +1,5 @@
# TODO: consider using this library for .zone file generation:
# - <https://github.com/kirelagin/dns.nix>
{ config, lib, pkgs, ... }: { config, lib, pkgs, ... }:
with builtins; with builtins;
@ -63,12 +65,22 @@ in
options = { options = {
sane.dns = with lib; { sane.dns = with lib; {
zones = mkOption { zones = mkOption {
type = types.attrsOf (types.submodule { description = ''
declarative zone config.
this doesn't feed into anything, rather, one should read `config.sane.dns.zones."foo".rendered`
and do something with it.
'';
default = {};
type = types.attrsOf (types.submodule ({ name, config, ... }: {
options = { options = {
name = mkOption { name = mkOption {
type = types.nullOr types.str; type = types.nullOr types.str;
description = "zone name. defaults to the attribute name in zones"; description = "zone name. defaults to the attribute name in zones";
default = null; default = name;
};
rendered = mkOption {
type = types.str;
description = "rendered .zone text for this zone (read-only)";
}; };
TTL = mkOption { TTL = mkOption {
type = types.int; type = types.int;
@ -122,25 +134,12 @@ in
default = {}; default = {};
}; };
}; };
file = mkOption {
type = types.nullOr types.str;
default = null;
description = ''
instead of using the generated zone file, use the specified path (user should populate the file specified here).
'';
};
}; };
}); config = {
default = {}; rendered = genZone config;
description = "Declarative zone config"; };
}));
}; };
}; };
}; };
config = {
sane.services.trust-dns.zones = mapAttrs (_name: zcfg: {
text = genZone zcfg;
}) cfg.zones;
};
} }

View File

@ -1,23 +1,16 @@
# WIP: porting to the API described here:
# - <https://github.com/NixOS/nixpkgs/pull/205866>
# - TODO: hardening!
{ config, lib, pkgs, ... }: { config, lib, pkgs, ... }:
# TODO: consider using this library for .zone file generation:
# - <https://github.com/kirelagin/dns.nix>
with lib; with lib;
let let
cfg = config.sane.services.trust-dns; cfg = config.sane.services.trust-dns;
toml = pkgs.formats.toml { }; toml = pkgs.formats.toml { };
configFile = toml.generate "trust-dns.toml" { configFile = toml.generate "trust-dns.toml" (
listen_addrs_ipv4 = cfg.listenAddrsIPv4; lib.filterAttrsRecursive (_: v: v != null) cfg.settings
zones = attrValues ( );
mapAttrs (zname: zcfg: rec {
zone = if zcfg.name == null then zname else zcfg.name;
zone_type = "Primary";
file = zcfg.file;
}) cfg.zones
);
};
in in
{ {
options = { options = {
@ -34,79 +27,113 @@ in
should provide bin/named, which will be invoked with --config x and --zonedir d and maybe -q. should provide bin/named, which will be invoked with --config x and --zonedir d and maybe -q.
''; '';
}; };
listenAddrsIPv4 = mkOption {
type = types.listOf types.str;
default = [];
description = "array of ipv4 addresses on which to listen for DNS queries";
};
quiet = mkOption { quiet = mkOption {
type = types.bool; type = types.bool;
default = false; default = false;
};
zonedir = mkOption {
type = types.nullOr types.str;
default = "/";
description = '' description = ''
where the `file` option in zones.* is relative to. log ERROR level messages only.
if not specified, defaults to INFO level logging.
mutually exclusive with the `debug` option.
''; '';
}; };
# reference <nixpkgs:nixos/modules/services/web-servers/nginx/vhost-options.nix> debug = mkOption {
zones = mkOption { type = types.bool;
type = types.attrsOf (types.submodule ({ config, name, ... }: { default = false;
description = ''
log DEBUG, INFO, WARN and ERROR messages.
if not specified, defaults to INFO level logging.
mutually exclusive with the `quiet` option.
'';
};
settings = mkOption {
type = types.submodule {
freeformType = toml.type;
options = { options = {
name = mkOption { listen_addrs_ipv4 = mkOption {
type = types.nullOr types.str; type = types.listOf types.str;
description = "zone name. defaults to the attribute name in zones"; default = [];
default = name; description = "array of ipv4 addresses on which to listen for DNS queries";
}; };
text = mkOption { listen_addrs_ipv6 = mkOption {
type = types.nullOr types.lines; type = types.listOf types.str;
default = null; default = [];
description = "array of ipv6 addresses on which to listen for DNS queries";
}; };
file = mkOption { listen_port = mkOption {
type = types.nullOr (types.either types.path types.str); type = types.port;
default = 53;
description = '' description = ''
path to a .zone file. port to listen on (applies to all listen addresses).
if omitted, will be generated from the `text` option.
''; '';
}; };
directory = mkOption {
type = types.nullOr types.str;
default = null;
description = ''
directory in which trust-dns will look for .zone files
whenever zones aren't specified by absolute path.
upstream defaults this to "/var/named".
'';
};
zones = mkOption {
description = "Declarative zone config";
default = {};
type = types.listOf (types.submodule ({ config, name, ... }: {
options = {
zone = mkOption {
type = types.str;
description = ''
zone name, like "example.com", "localhost", or "0.0.127.in-addr.arpa".
'';
};
zone_type = mkOption {
type = types.enum [ "Primary" "Secondary" "Hint" "Forward" ];
default = "Primary";
description = ''
one of:
- "Primary" (the master, authority for the zone)
- "Secondary" (the slave, replicated from the primary)
- "Hint" (a cached zone with recursive resolver abilities)
- "Forward" (a cached zone where all requests are forwarded to another resolver)
'';
};
file = mkOption {
type = types.either types.path types.str;
description = ''
path to a .zone file.
if not a fully-qualified path, it will be interpreted relative to the `directory` option.
defaults to the value of `zone` suffixed with ".zone".
'';
};
};
config = {
file = lib.mkDefault "${config.zone}.zone";
};
}));
};
}; };
};
config = {
file = lib.mkIf (config.text != null) (pkgs.writeText "${config.name}.zone" config.text);
};
}));
default = {};
description = "Declarative zone config";
}; };
}; };
}; };
config = mkIf cfg.enable { config = mkIf cfg.enable {
sane.ports.ports."53" = {
protocol = [ "udp" "tcp" ];
visibleTo.lan = true;
visibleTo.wan = true;
description = "colin-dns-hosting";
};
systemd.services.trust-dns = { systemd.services.trust-dns = {
description = "trust-dns DNS server"; description = "trust-dns DNS server";
serviceConfig = { serviceConfig = {
ExecStart = ExecStart =
let let
flags = lib.optional cfg.quiet "-q" ++ flags = lib.optional cfg.debug "--debug"
lib.optionals (cfg.zonedir != null) [ "--zonedir" cfg.zonedir ]; ++ lib.optional cfg.quiet "--quiet";
flagsStr = builtins.concatStringsSep " " flags; flagsStr = builtins.concatStringsSep " " flags;
in '' in ''
${cfg.package}/bin/named \ ${cfg.package}/bin/named --config ${configFile} ${flagsStr}
--config ${configFile} \ '';
${flagsStr}
'';
Type = "simple"; Type = "simple";
Restart = "on-failure"; Restart = "on-failure";
RestartSec = "10s"; RestartSec = "10s";
# TODO: hardening (like, don't run as root!) # TODO: hardening (like, don't run as root!)
# TODO: link to docs
}; };
after = [ "network.target" ]; after = [ "network.target" ];
wantedBy = [ "multi-user.target" ]; wantedBy = [ "multi-user.target" ];