--- /dev/null
+{ lib, pkgs, config, dns-nix, ... }:
+{
+ options.myServices.dns = {
+ enable = lib.mkEnableOption "enable DNS resolver";
+ helpers = lib.mkOption {
+ readOnly = true;
+ description = ''
+ Some useful constants or functions for zones definition
+ '';
+ default = rec {
+ servers = config.myEnv.servers;
+ ips = i: { A = i.ip4; AAAA = i.ip6; };
+ letsencrypt = [ { tag = "issue"; value = "letsencrypt.org"; issuerCritical = false; } ];
+ toKV = a: builtins.concatStringsSep ";" (builtins.attrValues (builtins.mapAttrs (n: v: "${n}=${v}") a));
+ mailMX = {
+ hasEmail = true;
+ subdomains = let
+ mxes = lib.filterAttrs (n: v: v ? mx && v.mx.enable) servers;
+ in
+ lib.mapAttrs' (n: v: lib.nameValuePair v.mx.subdomain (ips v.ips.main)) mxes;
+ };
+ zoneHeader = {
+ TTL = 3*60*60;
+ SOA = {
+ # yyyymmdd?? (increment ?? at each change)
+ serial = 2022121902; # Don't change this value, it is replaced automatically!
+ refresh = 10800;
+ retry = 3600;
+ expire = 604800;
+ minimum = 10800; # negative cache ttl
+ adminEmail = "hostmaster@immae.eu"; #email-address s/@/./
+ nameServer = "ns1.immae.eu.";
+ };
+ };
+ mailSend = {
+ # DKIM
+ subdomains._domainkey.subdomains.eldiron.TXT = [
+ (toKV config.myEnv.mail.dkim.eldiron.public)
+ ];
+ # old key, may still be used by verifiers
+ subdomains._domainkey.subdomains.immae_eu.TXT = [
+ (toKV config.myEnv.mail.dkim.immae_eu.public)
+ ];
+ };
+ mailCommon = name: {
+ MX = let
+ mxes = lib.filterAttrs (n: v: v ? mx && v.mx.enable) servers;
+ in
+ lib.mapAttrsToList (n: v: { preference = v.mx.priority; exchange = "${v.mx.subdomain}.${name}."; }) mxes;
+
+ # https://tools.ietf.org/html/rfc6186
+ SRV = [
+ { service = "submission"; proto = "tcp"; priority = 0; weight = 1; port = 587; target = "smtp.immae.eu."; }
+ { service = "submissions"; proto = "tcp"; priority = 0; weight = 1; port = 465; target = "smtp.immae.eu."; }
+
+ { service = "imap"; proto = "tcp"; priority = 0; weight = 1; port = 143; target = "imap.immae.eu."; }
+ { service = "imaps"; proto = "tcp"; priority = 0; weight = 1; port = 993; target = "imap.immae.eu."; }
+ { service = "sieve"; proto = "tcp"; priority = 0; weight = 1; port = 4190; target = "imap.immae.eu."; }
+
+ { service = "pop3"; proto = "tcp"; priority = 10; weight = 1; port = 110; target = "pop3.immae.eu."; }
+ { service = "pop3s"; proto = "tcp"; priority = 10; weight = 1; port = 995; target = "pop3.immae.eu."; }
+ ];
+
+ subdomains = {
+ # MTA-STS
+ # https://blog.delouw.ch/2018/12/16/using-mta-sts-to-enhance-email-transport-security-and-privacy/
+ # https://support.google.com/a/answer/9261504
+ _mta-sts.TXT = [ (toKV { v = "STSv1"; id = "20200109150200Z"; }) ]; # Don't change this value, it is updated automatically!
+ _tls.subdomains._smtp.TXT = [ (toKV { v = "TLSRPTv1"; "rua" = "mailto:postmaster+mta-sts@immae.eu"; }) ];
+ mta-sts = ips servers.eldiron.ips.main;
+
+ # DMARC
+ _dmarc.TXT = [ (toKV { v = "DMARC1"; p = "none"; adkim = "r"; aspf = "r"; fo = "1"; rua = "mailto:postmaster+rua@immae.eu"; ruf = "mailto:postmaster+ruf@immae.eu"; }) ];
+ };
+
+ # SPF
+ TXT = [ (toKV { v = "spf1 mx ~all"; }) ];
+ };
+ };
+ };
+ zones = lib.mkOption {
+ type = lib.types.attrsOf (dns-nix.lib.types.zone.substSubModules (
+ dns-nix.lib.types.zone.getSubModules ++ [
+ ({ name, ... }: {
+ options = {
+ hasEmail = lib.mkEnableOption "This domain has e-mails configuration";
+ emailPolicies = lib.mkOption {
+ default = {};
+ type = lib.types.attrsOf (lib.types.submodule {
+ options = {
+ receive = lib.mkEnableOption "Configure this domain to receive e-mail";
+ };
+ });
+ apply = builtins.mapAttrs (n: v: v // {
+ domain = name;
+ fqdn = if n == "" then name else "${n}.${name}";
+ });
+ };
+ extraConfig = lib.mkOption {
+ type = lib.types.lines;
+ description = "Extra zone configuration for bind";
+ example = ''
+ notify yes;
+ '';
+ default = "";
+ };
+ slaves = lib.mkOption {
+ type = lib.types.listOf lib.types.str;
+ description = "NS slave groups of this zone";
+ default = [];
+ };
+ ns = lib.mkOption {
+ type = lib.types.listOf lib.types.str;
+ default = [];
+ };
+ };
+ })
+ ]));
+ apply = let
+ toNS = n: builtins.map (d: "${d}.") (builtins.concatMap (s: builtins.attrNames config.myEnv.dns.ns."${s}") n);
+ in
+ builtins.mapAttrs (n: v: v // { NS = v.NS or [] ++ toNS (v.ns); });
+ default = {};
+ description = ''
+ attrset of zones to configure
+ '';
+ };
+ };
+ config = let
+ cfg = config.services.bind;
+ in lib.mkIf config.myServices.dns.enable {
+ myServices.chatonsProperties.hostings.dns-secondaire = {
+ file.datetime = "2022-08-22T02:00:00";
+ hosting = {
+ name = "DNS secondaire";
+ description = "DNS secondaire";
+ website = "ns1.immae.eu";
+ status.level = "OK";
+ status.description = "OK";
+ registration.load = "OPEN";
+ install.type = "PACKAGE";
+ };
+ software = {
+ name = "bind9";
+ website = pkgs.bind.meta.homepage;
+ license.url = pkgs.bind.meta.license.url;
+ license.name = pkgs.bind.meta.license.fullName;
+ version = pkgs.bind.version;
+ source.url = "https://www.isc.org/download/";
+ };
+ };
+ myServices.dns.zones = with config.myServices.dns.helpers; {
+ "imsite.eu" = lib.mkMerge [
+ zoneHeader
+ (ips servers.eldiron.ips.main)
+ {
+ ns = [ "immae" ];
+ CAA = letsencrypt;
+ }
+ ];
+ "immae.dev" = lib.mkMerge [
+ {
+ extraConfig = ''
+ notify yes;
+ '';
+ slaves = [ "raito" ];
+ }
+ zoneHeader
+ (ips servers.eldiron.ips.integration)
+ {
+ ns = [ "immae" "raito" ];
+ CAA = letsencrypt;
+ }
+ ];
+ "immae.eu" = lib.mkMerge [
+ {
+ extraConfig = ''
+ notify yes;
+ '';
+ slaves = [ "raito" ];
+ }
+ zoneHeader
+ (ips servers.eldiron.ips.production)
+ {
+ ns = [ "immae" "raito" ];
+ CAA = letsencrypt;
+
+ # ns1 has glue records in gandi.net
+ subdomains.ns1 = ips servers.eldiron.ips.main;
+ # raito / kurisu.dual.lahfa.xyz ; replace with eldiron in case of problem
+ subdomains.ns2.A = builtins.map (address: { inherit address; ttl = 600; }) servers.eldiron.ips.main.ip4;
+ subdomains.ns2.AAAA = builtins.map (address: { inherit address; ttl = 600; }) servers.eldiron.ips.main.ip6;
+ }
+ {
+ # Machines local users
+ emailPolicies.localhost.receive = false;
+ subdomains.localhost = lib.mkMerge [ (mailCommon "immae.eu") mailSend ];
+ emailPolicies.eldiron.receive = true;
+ subdomains.eldiron = lib.mkMerge [ (mailCommon "immae.eu") mailSend ];
+ }
+ {
+ # For each server "server" and each server ip group "ipgroup",
+ # define ipgroup.server.immae.eu
+ # "main" is set as server.immae.eu instead
+ # if main has an "alias", it is duplicated with this alias.
+ # If the server is a vm, use the v.immae.eu namespace (only main is created)
+ subdomains = let
+ vms = lib.filterAttrs (n: v: v.isVm) servers;
+ bms = lib.filterAttrs (n: v: !v.isVm) servers;
+ toIps = type: builtins.mapAttrs (n: v: ips v.ips."${type}");
+ in
+ lib.mkMerge [
+ (toIps "main" bms)
+
+ { v.subdomains = toIps "main" vms; }
+
+ (lib.mapAttrs (_: v: {
+ subdomains = lib.mapAttrs'
+ (n': v': lib.nameValuePair "${if v'.alias == null then n' else v'.alias}" (ips v'))
+ (lib.filterAttrs (n': v': n' != "main" || v'.alias != null) v.ips);
+ }) bms)
+ ];
+ }
+ {
+ # Outils
+ subdomains = {
+ status = ips servers.monitoring-1.ips.main;
+ };
+ }
+ ];
+ };
+ networking.firewall.allowedUDPPorts = [ 53 ];
+ networking.firewall.allowedTCPPorts = [ 53 ];
+ users.users.named.extraGroups = [ "keys" ];
+ services.bind = {
+ enable = true;
+ cacheNetworks = ["any"];
+ extraOptions = ''
+ allow-recursion { 127.0.0.1; };
+ allow-transfer { none; };
+
+ notify-source ${lib.head config.myEnv.servers.eldiron.ips.main.ip4};
+ notify-source-v6 ${lib.head config.myEnv.servers.eldiron.ips.main.ip6};
+ version none;
+ hostname none;
+ server-id none;
+ '';
+ zones =
+ builtins.mapAttrs (name: v: {
+ master = true;
+ extraConfig = v.extraConfig;
+ masters = [];
+ slaves =
+ lib.flatten (map (n: builtins.attrValues config.myEnv.dns.ns.${n}) v.slaves);
+ file = pkgs.runCommand "${name}.zone" {
+ text = v;
+ passAsFile = [ "text" ];
+ # Automatically change the increment when relevant change
+ # happened (both serial and mta-sts)
+ } ''
+ mv "$textPath" $out
+ increment=$(( 100*($(date -u +%-H) * 60 + $(date -u +%-M))/1440 ))
+ sed -i -e "s/2022121902/$(date -u +%Y%m%d)$increment/g" $out
+ sed -i -e "s/20200109150200Z/$(date -u +%Y%m%d%H%M%SZ)/g" $out
+ '';
+ }) config.myServices.dns.zones;
+ };
+ myServices.monitoring.fromMasterActivatedPlugins = [ "dns" ];
+ myServices.monitoring.fromMasterObjects.service = lib.mkMerge (lib.mapAttrsToList (name: z:
+ lib.optional (builtins.elem "immae" z.ns) {
+ service_description = "eldiron dns is active and authoritative for ${name}";
+ host_name = config.hostEnv.fqdn;
+ use = "dns-service";
+ check_command = ["check_dns" name "-A"];
+
+ servicegroups = "webstatus-dns";
+ _webstatus_name = name;
+ } ++
+ lib.optional (builtins.elem "raito" z.ns) {
+ service_description = "raito dns is active and authoritative for ${name}";
+ host_name = config.hostEnv.fqdn;
+ use = "dns-service";
+ check_command = ["check_external_dns" "kurisu.dual.lahfa.xyz" name "-A"];
+
+ servicegroups = "webstatus-dns";
+ _webstatus_name = "${name} (Secondary DNS Raito)";
+ }
+ ) config.myServices.dns.zones);
+ };
+}