2020-06-25 20:38:50 -07:00
|
|
|
{ lib, config, pkgs, ... }:
|
|
|
|
|
|
|
|
with lib;
|
|
|
|
|
|
|
|
let
|
|
|
|
cfg = config.fudo.dns;
|
|
|
|
|
|
|
|
join-lines = concatStringsSep "\n";
|
|
|
|
|
|
|
|
hostOpts = { host, ...}: {
|
|
|
|
options = {
|
|
|
|
ip-addresses = mkOption {
|
|
|
|
type = with types; listOf str;
|
|
|
|
description = ''
|
|
|
|
A list of IPv4 addresses assigned to this host.
|
|
|
|
'';
|
|
|
|
default = [];
|
|
|
|
};
|
|
|
|
|
|
|
|
ipv6-addresses = mkOption {
|
|
|
|
type = with types; listOf str;
|
|
|
|
description = ''
|
|
|
|
A list of IPv6 addresses assigned to this host.
|
|
|
|
'';
|
|
|
|
default = [];
|
|
|
|
};
|
|
|
|
|
|
|
|
ssh-fingerprints = mkOption {
|
|
|
|
type = with types; listOf str;
|
|
|
|
description = ''
|
|
|
|
A list of DNS SSHFP records for this host.
|
|
|
|
'';
|
2020-11-23 14:22:28 -08:00
|
|
|
default = [];
|
2020-06-25 20:38:50 -07:00
|
|
|
};
|
2020-11-19 14:21:18 -08:00
|
|
|
|
|
|
|
description = mkOption {
|
|
|
|
type = with types; nullOr str;
|
|
|
|
description = "Description of this host for a TXT record.";
|
|
|
|
default = null;
|
|
|
|
};
|
|
|
|
|
|
|
|
rp = mkOption {
|
|
|
|
type = with types; nullOr str;
|
|
|
|
description = "Responsible person.";
|
|
|
|
default = null;
|
|
|
|
};
|
2020-06-25 20:38:50 -07:00
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
srvRecordOpts = with types; {
|
|
|
|
options = {
|
|
|
|
weight = mkOption {
|
|
|
|
type = int;
|
|
|
|
description = "Weight relative to other records.";
|
|
|
|
default = 1;
|
|
|
|
};
|
|
|
|
|
|
|
|
priority = mkOption {
|
|
|
|
type = int;
|
|
|
|
description = "Priority to give this record.";
|
|
|
|
default = 0;
|
|
|
|
};
|
|
|
|
|
|
|
|
port = mkOption {
|
|
|
|
type = port;
|
|
|
|
description = "Port to use while connecting to this service.";
|
|
|
|
};
|
|
|
|
|
|
|
|
host = mkOption {
|
|
|
|
type = str;
|
|
|
|
description = "Host that provides this service.";
|
|
|
|
example = "my-host.my-domain.com";
|
|
|
|
};
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
domainOpts = { domain, ... }: with types; {
|
|
|
|
options = {
|
|
|
|
hosts = mkOption {
|
|
|
|
type = loaOf (submodule hostOpts);
|
|
|
|
default = {};
|
|
|
|
description = "A map of hostname to { host_attributes }.";
|
|
|
|
};
|
|
|
|
|
2020-11-23 14:22:28 -08:00
|
|
|
dnssec = mkOption {
|
|
|
|
type = bool;
|
|
|
|
description = "Enable DNSSEC security for this zone.";
|
|
|
|
default = true;
|
|
|
|
};
|
2020-06-25 20:38:50 -07:00
|
|
|
|
|
|
|
mx = mkOption {
|
|
|
|
type = listOf str;
|
|
|
|
description = "A list of mail servers serving this domain.";
|
|
|
|
default = [];
|
|
|
|
};
|
|
|
|
|
|
|
|
srv-records = mkOption {
|
|
|
|
type = attrsOf (attrsOf (listOf (submodule srvRecordOpts)));
|
|
|
|
description = "Map of traffic type to srv records.";
|
|
|
|
default = {};
|
|
|
|
example = {
|
|
|
|
tcp = {
|
|
|
|
kerberos = {
|
|
|
|
port = 88;
|
|
|
|
host = "auth-host.my-domain.com";
|
|
|
|
};
|
|
|
|
};
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
aliases = mkOption {
|
|
|
|
type = loaOf str;
|
|
|
|
default = {};
|
|
|
|
description = "A mapping of host-alias => hostnames to add to DNS.";
|
|
|
|
example = {
|
|
|
|
"music" = "host.dom.com.";
|
|
|
|
"mail" = "hostname";
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
extra-dns-records = mkOption {
|
|
|
|
type = listOf str;
|
|
|
|
description = "Records to be inserted verbatim into the DNS zone.";
|
|
|
|
example = ["some-host IN CNAME base-host"];
|
|
|
|
default = [];
|
|
|
|
};
|
|
|
|
|
|
|
|
dmarc-report-address = mkOption {
|
|
|
|
type = nullOr str;
|
|
|
|
description = "The email to use to recieve DMARC reports, if any.";
|
|
|
|
example = "admin-user@domain.com";
|
|
|
|
default = null;
|
|
|
|
};
|
|
|
|
|
|
|
|
default-host = mkOption {
|
|
|
|
type = nullOr str;
|
|
|
|
description = "IP of the host which will act as the default server for this domain, if any.";
|
|
|
|
default = null;
|
|
|
|
};
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
2020-11-19 14:21:18 -08:00
|
|
|
hostRecords = host: data:
|
2020-06-25 20:38:50 -07:00
|
|
|
join-lines ((map (ip: "${host} IN A ${ip}") data.ip-addresses) ++
|
2020-11-19 14:21:18 -08:00
|
|
|
(map (ip: "${host} IN AAAA ${ip}") data.ipv6-addresses) ++
|
|
|
|
(map (sshfp: "${host} IN SSHFP ${sshfp}") data.ssh-fingerprints) ++
|
|
|
|
(optional (data.rp != null) "${host} IN RP ${data.rp}") ++
|
|
|
|
(optional (data.description != null) "${host} IN TXT ${data.description}"));
|
2020-06-25 20:38:50 -07:00
|
|
|
|
|
|
|
makeSrvRecords = protocol: type: records:
|
|
|
|
join-lines (map (record: "_${type}._${protocol} IN SRV ${toString record.priority} ${toString record.weight} ${toString record.port} ${toString record.host}.")
|
|
|
|
records);
|
|
|
|
|
|
|
|
makeSrvProtocolRecords = protocol: types: join-lines (mapAttrsToList (makeSrvRecords protocol) types);
|
|
|
|
|
|
|
|
cnameRecord = alias: host: "${alias} IN CNAME ${host}";
|
|
|
|
|
|
|
|
mxRecords = mxs:
|
|
|
|
concatStringsSep "\n"
|
|
|
|
(map (mx: "@ IN MX 10 ${mx}.") mxs);
|
|
|
|
|
|
|
|
dmarcRecord = dmarc-email:
|
|
|
|
optionalString (dmarc-email != null)
|
|
|
|
''_dmarc IN TXT "v=DMARC1;p=quarantine;sp=quarantine;rua=mailto:${dmarc-email};"'';
|
|
|
|
|
2020-11-23 14:22:28 -08:00
|
|
|
nsRecords = dom: ns-hosts: join-lines (mapAttrsToList (host: _: "@ IN NS ${host}.${dom}.") ns-hosts);
|
2020-06-25 20:38:50 -07:00
|
|
|
|
|
|
|
in {
|
|
|
|
|
|
|
|
options.fudo.dns = with types; {
|
|
|
|
enable = mkEnableOption "Enable master DNS services.";
|
|
|
|
|
|
|
|
# FIXME: This should allow for AAAA addresses too...
|
2020-11-23 14:22:28 -08:00
|
|
|
nameservers = mkOption {
|
|
|
|
type = loaOf (submodule hostOpts);
|
2020-06-25 20:38:50 -07:00
|
|
|
description = "Map of domain nameserver FQDNs to IP.";
|
2020-11-23 14:22:28 -08:00
|
|
|
example = {
|
|
|
|
"ns1.domain.com" = {
|
|
|
|
ip-addresses = [ "1.1.1.1" ];
|
|
|
|
ipv6-addresses = [];
|
|
|
|
description = "my fancy dns server";
|
|
|
|
};
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
identity = mkOption {
|
|
|
|
type = str;
|
|
|
|
description = "The identity (CH TXT ID.SERVER) of this host.";
|
2020-06-25 20:38:50 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
domains = mkOption {
|
|
|
|
type = loaOf (submodule domainOpts);
|
|
|
|
default = {};
|
|
|
|
description = "A map of domain to domain options.";
|
|
|
|
};
|
|
|
|
|
|
|
|
listen-ips = mkOption {
|
|
|
|
type = listOf str;
|
|
|
|
description = "A list of IPs on which to listen for DNS queries.";
|
|
|
|
example = ["1.2.3.4"];
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
config = mkIf cfg.enable {
|
|
|
|
services.nsd = {
|
|
|
|
enable = true;
|
2020-11-23 14:22:28 -08:00
|
|
|
identity = cfg.identity;
|
2020-06-25 20:38:50 -07:00
|
|
|
interfaces = cfg.listen-ips;
|
|
|
|
zones = mapAttrs' (dom: dom-cfg:
|
|
|
|
nameValuePair "${dom}." {
|
|
|
|
dnssec = dom-cfg.dnssec;
|
|
|
|
|
|
|
|
data = ''
|
|
|
|
$ORIGIN ${dom}.
|
|
|
|
$TTL 12h
|
|
|
|
|
|
|
|
@ IN SOA ns1.${dom}. hostmaster.${dom}. (
|
|
|
|
${toString builtins.currentTime}
|
|
|
|
5m
|
|
|
|
2m
|
|
|
|
6w
|
|
|
|
5m)
|
|
|
|
|
|
|
|
${optionalString (dom-cfg.default-host != null) "@ IN A ${dom-cfg.default-host}"}
|
|
|
|
|
|
|
|
${mxRecords dom-cfg.mx}
|
|
|
|
|
|
|
|
$TTL 6h
|
|
|
|
|
2020-11-23 14:22:28 -08:00
|
|
|
${nsRecords dom cfg.nameservers}
|
|
|
|
${join-lines (mapAttrsToList hostRecords cfg.nameservers)}
|
2020-06-25 20:38:50 -07:00
|
|
|
|
|
|
|
${dmarcRecord dom-cfg.dmarc-report-address}
|
|
|
|
|
|
|
|
${join-lines (mapAttrsToList makeSrvProtocolRecords dom-cfg.srv-records)}
|
2020-11-19 14:21:18 -08:00
|
|
|
${join-lines (mapAttrsToList hostRecords dom-cfg.hosts)}
|
2020-06-25 20:38:50 -07:00
|
|
|
${join-lines (mapAttrsToList cnameRecord dom-cfg.aliases)}
|
|
|
|
${join-lines dom-cfg.extra-dns-records}
|
|
|
|
'';
|
|
|
|
}) cfg.domains;
|
|
|
|
};
|
|
|
|
};
|
|
|
|
}
|