]>
Commit | Line | Data |
---|---|---|
1a64deeb IB |
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 | } |