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