]> git.immae.eu Git - perso/Immae/Config/Nix.git/blob - systems/eldiron/dns.nix
Squash changes containing private information
[perso/Immae/Config/Nix.git] / systems / eldiron / dns.nix
1 { lib, pkgs, config, dns-nix, ... }:
2 {
3 options.myServices.dns = {
4 enable = lib.mkEnableOption "enable DNS resolver";
5 helpers = lib.mkOption {
6 readOnly = true;
7 description = ''
8 Some useful constants or functions for zones definition
9 '';
10 default = rec {
11 servers = config.myEnv.servers;
12 ips = i: { A = i.ip4; AAAA = i.ip6; };
13 letsencrypt = [ { tag = "issue"; value = "letsencrypt.org"; issuerCritical = false; } ];
14 toKV = a: builtins.concatStringsSep ";" (builtins.attrValues (builtins.mapAttrs (n: v: "${n}=${v}") a));
15 mailMX = {
16 hasEmail = true;
17 subdomains = let
18 mxes = lib.filterAttrs (n: v: v ? mx && v.mx.enable) servers;
19 in
20 lib.mapAttrs' (n: v: lib.nameValuePair v.mx.subdomain (ips v.ips.main)) mxes;
21 };
22 zoneHeader = {
23 TTL = 3*60*60;
24 SOA = {
25 # yyyymmdd?? (increment ?? at each change)
26 serial = 2022121902; # Don't change this value, it is replaced automatically!
27 refresh = 10800;
28 retry = 3600;
29 expire = 604800;
30 minimum = 10800; # negative cache ttl
31 adminEmail = "hostmaster@immae.eu"; #email-address s/@/./
32 nameServer = "ns1.immae.eu.";
33 };
34 };
35 mailSend = {
36 # DKIM
37 subdomains._domainkey.subdomains.eldiron.TXT = [
38 (toKV config.myEnv.mail.dkim.eldiron.public)
39 ];
40 # old key, may still be used by verifiers
41 subdomains._domainkey.subdomains.immae_eu.TXT = [
42 (toKV config.myEnv.mail.dkim.immae_eu.public)
43 ];
44 };
45 mailCommon = name: {
46 MX = let
47 mxes = lib.filterAttrs (n: v: v ? mx && v.mx.enable) servers;
48 in
49 lib.mapAttrsToList (n: v: { preference = v.mx.priority; exchange = "${v.mx.subdomain}.${name}."; }) mxes;
50
51 # https://tools.ietf.org/html/rfc6186
52 SRV = [
53 { service = "submission"; proto = "tcp"; priority = 0; weight = 1; port = 587; target = "smtp.immae.eu."; }
54 { service = "submissions"; proto = "tcp"; priority = 0; weight = 1; port = 465; target = "smtp.immae.eu."; }
55
56 { service = "imap"; proto = "tcp"; priority = 0; weight = 1; port = 143; target = "imap.immae.eu."; }
57 { service = "imaps"; proto = "tcp"; priority = 0; weight = 1; port = 993; target = "imap.immae.eu."; }
58 { service = "sieve"; proto = "tcp"; priority = 0; weight = 1; port = 4190; target = "imap.immae.eu."; }
59
60 { service = "pop3"; proto = "tcp"; priority = 10; weight = 1; port = 110; target = "pop3.immae.eu."; }
61 { service = "pop3s"; proto = "tcp"; priority = 10; weight = 1; port = 995; target = "pop3.immae.eu."; }
62 ];
63
64 subdomains = {
65 # MTA-STS
66 # https://blog.delouw.ch/2018/12/16/using-mta-sts-to-enhance-email-transport-security-and-privacy/
67 # https://support.google.com/a/answer/9261504
68 _mta-sts.TXT = [ (toKV { v = "STSv1"; id = "20200109150200Z"; }) ]; # Don't change this value, it is updated automatically!
69 _tls.subdomains._smtp.TXT = [ (toKV { v = "TLSRPTv1"; "rua" = "mailto:postmaster+mta-sts@immae.eu"; }) ];
70 mta-sts = ips servers.eldiron.ips.main;
71
72 # DMARC
73 _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"; }) ];
74 };
75
76 # SPF
77 TXT = [ (toKV { v = "spf1 mx ~all"; }) ];
78 };
79 };
80 };
81 zones = lib.mkOption {
82 type = lib.types.attrsOf (dns-nix.lib.types.zone.substSubModules (
83 dns-nix.lib.types.zone.getSubModules ++ [
84 ({ name, ... }: {
85 options = {
86 hasEmail = lib.mkEnableOption "This domain has e-mails configuration";
87 emailPolicies = lib.mkOption {
88 default = {};
89 type = lib.types.attrsOf (lib.types.submodule {
90 options = {
91 receive = lib.mkEnableOption "Configure this domain to receive e-mail";
92 };
93 });
94 apply = builtins.mapAttrs (n: v: v // {
95 domain = name;
96 fqdn = if n == "" then name else "${n}.${name}";
97 });
98 };
99 extraConfig = lib.mkOption {
100 type = lib.types.lines;
101 description = "Extra zone configuration for bind";
102 example = ''
103 notify yes;
104 '';
105 default = "";
106 };
107 slaves = lib.mkOption {
108 type = lib.types.listOf lib.types.str;
109 description = "NS slave groups of this zone";
110 default = [];
111 };
112 ns = lib.mkOption {
113 type = lib.types.listOf lib.types.str;
114 default = [];
115 };
116 };
117 })
118 ]));
119 apply = let
120 toNS = n: builtins.map (d: "${d}.") (builtins.concatMap (s: builtins.attrNames config.myEnv.dns.ns."${s}") n);
121 in
122 builtins.mapAttrs (n: v: v // { NS = v.NS or [] ++ toNS (v.ns); });
123 default = {};
124 description = ''
125 attrset of zones to configure
126 '';
127 };
128 };
129 config = let
130 cfg = config.services.bind;
131 in lib.mkIf config.myServices.dns.enable {
132 myServices.chatonsProperties.hostings.dns-secondaire = {
133 file.datetime = "2022-08-22T02:00:00";
134 hosting = {
135 name = "DNS secondaire";
136 description = "DNS secondaire";
137 website = "ns1.immae.eu";
138 status.level = "OK";
139 status.description = "OK";
140 registration.load = "OPEN";
141 install.type = "PACKAGE";
142 };
143 software = {
144 name = "bind9";
145 website = pkgs.bind.meta.homepage;
146 license.url = pkgs.bind.meta.license.url;
147 license.name = pkgs.bind.meta.license.fullName;
148 version = pkgs.bind.version;
149 source.url = "https://www.isc.org/download/";
150 };
151 };
152 myServices.dns.zones = with config.myServices.dns.helpers; {
153 "imsite.eu" = lib.mkMerge [
154 zoneHeader
155 (ips servers.eldiron.ips.main)
156 {
157 ns = [ "immae" ];
158 CAA = letsencrypt;
159 }
160 ];
161 "immae.dev" = lib.mkMerge [
162 {
163 extraConfig = ''
164 notify yes;
165 '';
166 slaves = [ "raito" ];
167 }
168 zoneHeader
169 (ips servers.eldiron.ips.integration)
170 {
171 ns = [ "immae" "raito" ];
172 CAA = letsencrypt;
173 }
174 ];
175 "immae.eu" = lib.mkMerge [
176 {
177 extraConfig = ''
178 notify yes;
179 '';
180 slaves = [ "raito" ];
181 }
182 zoneHeader
183 (ips servers.eldiron.ips.production)
184 {
185 ns = [ "immae" "raito" ];
186 CAA = letsencrypt;
187
188 # ns1 has glue records in gandi.net
189 subdomains.ns1 = ips servers.eldiron.ips.main;
190 # raito / kurisu.dual.lahfa.xyz ; replace with eldiron in case of problem
191 subdomains.ns2.A = builtins.map (address: { inherit address; ttl = 600; }) servers.eldiron.ips.main.ip4;
192 subdomains.ns2.AAAA = builtins.map (address: { inherit address; ttl = 600; }) servers.eldiron.ips.main.ip6;
193 }
194 {
195 # Machines local users
196 emailPolicies.localhost.receive = false;
197 subdomains.localhost = lib.mkMerge [ (mailCommon "immae.eu") mailSend ];
198 emailPolicies.eldiron.receive = true;
199 subdomains.eldiron = lib.mkMerge [ (mailCommon "immae.eu") mailSend ];
200 }
201 {
202 # For each server "server" and each server ip group "ipgroup",
203 # define ipgroup.server.immae.eu
204 # "main" is set as server.immae.eu instead
205 # if main has an "alias", it is duplicated with this alias.
206 # If the server is a vm, use the v.immae.eu namespace (only main is created)
207 subdomains = let
208 vms = lib.filterAttrs (n: v: v.isVm) servers;
209 bms = lib.filterAttrs (n: v: !v.isVm) servers;
210 toIps = type: builtins.mapAttrs (n: v: ips v.ips."${type}");
211 in
212 lib.mkMerge [
213 (toIps "main" bms)
214
215 { v.subdomains = toIps "main" vms; }
216
217 (lib.mapAttrs (_: v: {
218 subdomains = lib.mapAttrs'
219 (n': v': lib.nameValuePair "${if v'.alias == null then n' else v'.alias}" (ips v'))
220 (lib.filterAttrs (n': v': n' != "main" || v'.alias != null) v.ips);
221 }) bms)
222 ];
223 }
224 {
225 # Outils
226 subdomains = {
227 status = ips servers.monitoring-1.ips.main;
228 };
229 }
230 ];
231 };
232 networking.firewall.allowedUDPPorts = [ 53 ];
233 networking.firewall.allowedTCPPorts = [ 53 ];
234 users.users.named.extraGroups = [ "keys" ];
235 services.bind = {
236 enable = true;
237 cacheNetworks = ["any"];
238 extraOptions = ''
239 allow-recursion { 127.0.0.1; };
240 allow-transfer { none; };
241
242 notify-source ${lib.head config.myEnv.servers.eldiron.ips.main.ip4};
243 notify-source-v6 ${lib.head config.myEnv.servers.eldiron.ips.main.ip6};
244 version none;
245 hostname none;
246 server-id none;
247 '';
248 zones =
249 builtins.mapAttrs (name: v: {
250 master = true;
251 extraConfig = v.extraConfig;
252 masters = [];
253 slaves =
254 lib.flatten (map (n: builtins.attrValues config.myEnv.dns.ns.${n}) v.slaves);
255 file = pkgs.runCommand "${name}.zone" {
256 text = v;
257 passAsFile = [ "text" ];
258 # Automatically change the increment when relevant change
259 # happened (both serial and mta-sts)
260 } ''
261 mv "$textPath" $out
262 increment=$(( 100*($(date -u +%-H) * 60 + $(date -u +%-M))/1440 ))
263 sed -i -e "s/2022121902/$(date -u +%Y%m%d)$increment/g" $out
264 sed -i -e "s/20200109150200Z/$(date -u +%Y%m%d%H%M%SZ)/g" $out
265 '';
266 }) config.myServices.dns.zones;
267 };
268 myServices.monitoring.fromMasterActivatedPlugins = [ "dns" ];
269 myServices.monitoring.fromMasterObjects.service = lib.mkMerge (lib.mapAttrsToList (name: z:
270 lib.optional (builtins.elem "immae" z.ns) {
271 service_description = "eldiron dns is active and authoritative for ${name}";
272 host_name = config.hostEnv.fqdn;
273 use = "dns-service";
274 check_command = ["check_dns" name "-A"];
275
276 servicegroups = "webstatus-dns";
277 _webstatus_name = name;
278 } ++
279 lib.optional (builtins.elem "raito" z.ns) {
280 service_description = "raito dns is active and authoritative for ${name}";
281 host_name = config.hostEnv.fqdn;
282 use = "dns-service";
283 check_command = ["check_external_dns" "kurisu.dual.lahfa.xyz" name "-A"];
284
285 servicegroups = "webstatus-dns";
286 _webstatus_name = "${name} (Secondary DNS Raito)";
287 }
288 ) config.myServices.dns.zones);
289 };
290 }