diff options
author | Ismaël Bouya <ismael.bouya@normalesup.org> | 2023-10-04 01:35:06 +0200 |
---|---|---|
committer | Ismaël Bouya <ismael.bouya@normalesup.org> | 2023-10-04 02:11:48 +0200 |
commit | 1a64deeb894dc95e2645a75771732c6cc53a79ad (patch) | |
tree | 1b9df4838f894577a09b9b260151756272efeb53 /systems/eldiron/dns.nix | |
parent | fa25ffd4583cc362075cd5e1b4130f33306103f0 (diff) | |
download | Nix-1a64deeb894dc95e2645a75771732c6cc53a79ad.tar.gz Nix-1a64deeb894dc95e2645a75771732c6cc53a79ad.tar.zst Nix-1a64deeb894dc95e2645a75771732c6cc53a79ad.zip |
Squash changes containing private information
There were a lot of changes since the previous commit, but a lot of them
contained personnal information about users. All thos changes got
stashed into a single commit (history is kept in a different place) and
private information was moved in a separate private repository
Diffstat (limited to 'systems/eldiron/dns.nix')
-rw-r--r-- | systems/eldiron/dns.nix | 290 |
1 files changed, 290 insertions, 0 deletions
diff --git a/systems/eldiron/dns.nix b/systems/eldiron/dns.nix new file mode 100644 index 0000000..486fcc1 --- /dev/null +++ b/systems/eldiron/dns.nix | |||
@@ -0,0 +1,290 @@ | |||
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 | } | ||