aboutsummaryrefslogtreecommitdiff
path: root/systems/monitoring-1
diff options
context:
space:
mode:
Diffstat (limited to 'systems/monitoring-1')
-rw-r--r--systems/monitoring-1/base.nix75
-rw-r--r--systems/monitoring-1/flake.lock790
-rw-r--r--systems/monitoring-1/flake.nix45
-rw-r--r--systems/monitoring-1/monitoring-master.nix87
-rw-r--r--systems/monitoring-1/monitoring.nix61
-rw-r--r--systems/monitoring-1/monitoring/master.nix43
-rw-r--r--systems/monitoring-1/monitoring/phare.nix20
-rw-r--r--systems/monitoring-1/monitoring/ulminfo-fr.nix20
-rw-r--r--systems/monitoring-1/status.nix84
-rwxr-xr-xsystems/monitoring-1/status/app.py414
-rw-r--r--systems/monitoring-1/status_engine.nix123
11 files changed, 1762 insertions, 0 deletions
diff --git a/systems/monitoring-1/base.nix b/systems/monitoring-1/base.nix
new file mode 100644
index 0000000..8bfacc1
--- /dev/null
+++ b/systems/monitoring-1/base.nix
@@ -0,0 +1,75 @@
1{ config, pkgs, lib, nixpkgs, php, secrets, ... }:
2{
3 # ssh-keyscan monitoring-1 | nix-shell -p ssh-to-age --run ssh-to-age
4 secrets.ageKeys = [ "age1dn4lzhgxusqrpjjnzm7w8ml39ptf326htuzmpqdqs2gg3wq7cqzqxuvx8k" ];
5 boot.kernelPackages = pkgs.linuxPackages_latest;
6
7 imports = [
8 secrets.nixosModules.users-config-monitoring-1
9 (nixpkgs + "/nixos/modules/profiles/qemu-guest.nix")
10 ./monitoring-master.nix
11 ./monitoring.nix
12 ./status.nix
13 ./status_engine.nix
14 ];
15
16 nixpkgs.overlays = builtins.attrValues php.overlays;
17 nixpkgs.config.permittedInsecurePackages = [
18 "python-2.7.18.6" # for nagios-cli
19 ];
20
21 myServices.monitoring.enable = true;
22 myServices.monitoring.master = true;
23 myServices.status.enable = true;
24 networking = {
25 firewall.enable = true;
26 interfaces."ens3".ipv4.addresses = pkgs.lib.flatten (pkgs.lib.attrsets.mapAttrsToList
27 (n: ips: map (ip: { address = ip; prefixLength = 32; }) (ips.ip4 or []))
28 (pkgs.lib.attrsets.filterAttrs (n: v: n != "main") config.hostEnv.ips));
29 interfaces."ens3".ipv6.addresses = pkgs.lib.flatten (pkgs.lib.attrsets.mapAttrsToList
30 (n: ips: map (ip: { address = ip; prefixLength = (if n == "main" && ip == pkgs.lib.head ips.ip6 then 64 else 128); }) (ips.ip6 or []))
31 config.hostEnv.ips);
32 defaultGateway6 = { address = "fe80::1"; interface = "ens3"; };
33 };
34 boot.loader.grub.device = "nodev";
35 fileSystems."/" = { device = "/dev/sda1"; fsType = "ext4"; };
36 myServices.mailRelay.enable = true;
37
38 security.pki.certificateFiles = [
39 (pkgs.fetchurl {
40 url = "http://downloads.e.eriomem.net/eriomemca.pem";
41 sha256 = "1ixx4c6j3m26j8dp9a3dkvxc80v1nr5aqgmawwgs06bskasqkvvh";
42 })
43 ];
44
45 services.netdata.enable = true;
46 services.netdata.configDir."stream.conf" = config.secrets.fullPaths."netdata-stream.conf";
47 services.netdata.config.web."allow dashboard from" = "localhost";
48 services.netdata.config.web."allow badges from" = "*";
49 services.netdata.config.web."allow streaming from" = "*";
50 services.netdata.config.web."allow netdata.conf from" = "fd*";
51 services.netdata.config.web."allow management from" = "fd*";
52 networking.firewall.allowedTCPPorts = [ 19999 ];
53
54 secrets.keys = {
55 "netdata-stream.conf" = {
56 user = config.services.netdata.user;
57 group = config.services.netdata.group;
58 permissions = "0400";
59 text = builtins.concatStringsSep "\n" (pkgs.lib.mapAttrsToList (_: key: ''
60 [${key}]
61 enabled = yes
62 default history = 3600
63 default memory = ram
64 health enabled by default = auto
65 '') config.myEnv.monitoring.netdata_keys);
66 };
67 };
68 users.users."${config.services.netdata.user}".extraGroups = [ "keys" ];
69 # This value determines the NixOS release with which your system is
70 # to be compatible, in order to avoid breaking some software such as
71 # database servers. You should change this only after NixOS release
72 # notes say you should.
73 # https://nixos.org/nixos/manual/release-notes.html
74 system.stateVersion = "23.05"; # Did you read the comment?
75}
diff --git a/systems/monitoring-1/flake.lock b/systems/monitoring-1/flake.lock
new file mode 100644
index 0000000..d4a2c8d
--- /dev/null
+++ b/systems/monitoring-1/flake.lock
@@ -0,0 +1,790 @@
1{
2 "nodes": {
3 "backports": {
4 "inputs": {
5 "flake-utils": "flake-utils_3",
6 "nixpkgs": "nixpkgs_7"
7 },
8 "locked": {
9 "lastModified": 1,
10 "narHash": "sha256-VewHWeZvwLvWVm2bMQk5UQ0G/HyO8X87BssvmbLWbrY=",
11 "path": "../../backports",
12 "type": "path"
13 },
14 "original": {
15 "path": "../../backports",
16 "type": "path"
17 }
18 },
19 "chatons": {
20 "inputs": {
21 "environment": "environment"
22 },
23 "locked": {
24 "lastModified": 1,
25 "narHash": "sha256-UNkS/IZGHCdSX4hCzpTZwNBj9B8RGCMr9Za+G9Xdm4Y=",
26 "path": "../../flakes/private/chatons",
27 "type": "path"
28 },
29 "original": {
30 "path": "../../flakes/private/chatons",
31 "type": "path"
32 }
33 },
34 "colmena": {
35 "inputs": {
36 "flake-compat": "flake-compat",
37 "flake-utils": "flake-utils",
38 "nixpkgs": "nixpkgs",
39 "stable": "stable"
40 },
41 "locked": {
42 "lastModified": 1687954574,
43 "narHash": "sha256-YasVTaNXq2xqZdejyIhuyqvNypmx+K/Y1ZZ4+raeeII=",
44 "owner": "immae",
45 "repo": "colmena",
46 "rev": "e427171150a35e23204c4c15a2483358d22a0eff",
47 "type": "github"
48 },
49 "original": {
50 "owner": "immae",
51 "ref": "add-lib-get-flake",
52 "repo": "colmena",
53 "type": "github"
54 }
55 },
56 "disko": {
57 "inputs": {
58 "nixpkgs": "nixpkgs_2"
59 },
60 "locked": {
61 "lastModified": 1687968164,
62 "narHash": "sha256-L9jr2zCB6NIaBE3towusjGBigsnE2pMID8wBGkYbTS4=",
63 "owner": "nix-community",
64 "repo": "disko",
65 "rev": "8002e7cb899bc2a02a2ebfb7f999fcd7c18b92a1",
66 "type": "github"
67 },
68 "original": {
69 "owner": "nix-community",
70 "repo": "disko",
71 "type": "github"
72 }
73 },
74 "environment": {
75 "locked": {
76 "lastModified": 1,
77 "narHash": "sha256-rMKbM7fHqWQbI7y59BsPG8KwoDj2jyrvN2niPWB24uE=",
78 "path": "../environment",
79 "type": "path"
80 },
81 "original": {
82 "path": "../environment",
83 "type": "path"
84 }
85 },
86 "environment_2": {
87 "locked": {
88 "lastModified": 1,
89 "narHash": "sha256-rMKbM7fHqWQbI7y59BsPG8KwoDj2jyrvN2niPWB24uE=",
90 "path": "../../flakes/private/environment",
91 "type": "path"
92 },
93 "original": {
94 "path": "../../flakes/private/environment",
95 "type": "path"
96 }
97 },
98 "environment_3": {
99 "locked": {
100 "lastModified": 1,
101 "narHash": "sha256-rMKbM7fHqWQbI7y59BsPG8KwoDj2jyrvN2niPWB24uE=",
102 "path": "../environment",
103 "type": "path"
104 },
105 "original": {
106 "path": "../environment",
107 "type": "path"
108 }
109 },
110 "environment_4": {
111 "locked": {
112 "lastModified": 1,
113 "narHash": "sha256-rMKbM7fHqWQbI7y59BsPG8KwoDj2jyrvN2niPWB24uE=",
114 "path": "../environment",
115 "type": "path"
116 },
117 "original": {
118 "path": "../environment",
119 "type": "path"
120 }
121 },
122 "environment_5": {
123 "locked": {
124 "lastModified": 1,
125 "narHash": "sha256-rMKbM7fHqWQbI7y59BsPG8KwoDj2jyrvN2niPWB24uE=",
126 "path": "../environment",
127 "type": "path"
128 },
129 "original": {
130 "path": "../environment",
131 "type": "path"
132 }
133 },
134 "files-watcher": {
135 "locked": {
136 "lastModified": 1,
137 "narHash": "sha256-ZsdumUVoSPkV/DB6gO6dNDttjzalye0ToVBF9bl5W0k=",
138 "path": "../../flakes/files-watcher",
139 "type": "path"
140 },
141 "original": {
142 "path": "../../flakes/files-watcher",
143 "type": "path"
144 }
145 },
146 "flake-compat": {
147 "flake": false,
148 "locked": {
149 "lastModified": 1650374568,
150 "narHash": "sha256-Z+s0J8/r907g149rllvwhb4pKi8Wam5ij0st8PwAh+E=",
151 "owner": "edolstra",
152 "repo": "flake-compat",
153 "rev": "b4a34015c698c7793d592d66adbab377907a2be8",
154 "type": "github"
155 },
156 "original": {
157 "owner": "edolstra",
158 "repo": "flake-compat",
159 "type": "github"
160 }
161 },
162 "flake-parts": {
163 "inputs": {
164 "nixpkgs-lib": "nixpkgs-lib_2"
165 },
166 "locked": {
167 "lastModified": 1687762428,
168 "narHash": "sha256-DIf7mi45PKo+s8dOYF+UlXHzE0Wl/+k3tXUyAoAnoGE=",
169 "owner": "hercules-ci",
170 "repo": "flake-parts",
171 "rev": "37dd7bb15791c86d55c5121740a1887ab55ee836",
172 "type": "github"
173 },
174 "original": {
175 "owner": "hercules-ci",
176 "repo": "flake-parts",
177 "type": "github"
178 }
179 },
180 "flake-parts_2": {
181 "inputs": {
182 "nixpkgs-lib": "nixpkgs-lib_3"
183 },
184 "locked": {
185 "lastModified": 1675295133,
186 "narHash": "sha256-dU8fuLL98WFXG0VnRgM00bqKX6CEPBLybhiIDIgO45o=",
187 "owner": "hercules-ci",
188 "repo": "flake-parts",
189 "rev": "bf53492df08f3178ce85e0c9df8ed8d03c030c9f",
190 "type": "github"
191 },
192 "original": {
193 "owner": "hercules-ci",
194 "repo": "flake-parts",
195 "type": "github"
196 }
197 },
198 "flake-utils": {
199 "locked": {
200 "lastModified": 1659877975,
201 "narHash": "sha256-zllb8aq3YO3h8B/U0/J1WBgAL8EX5yWf5pMj3G0NAmc=",
202 "owner": "numtide",
203 "repo": "flake-utils",
204 "rev": "c0e246b9b83f637f4681389ecabcb2681b4f3af0",
205 "type": "github"
206 },
207 "original": {
208 "owner": "numtide",
209 "repo": "flake-utils",
210 "type": "github"
211 }
212 },
213 "flake-utils_2": {
214 "locked": {
215 "lastModified": 1648297722,
216 "narHash": "sha256-W+qlPsiZd8F3XkzXOzAoR+mpFqzm3ekQkJNa+PIh1BQ=",
217 "owner": "numtide",
218 "repo": "flake-utils",
219 "rev": "0f8662f1319ad6abf89b3380dd2722369fc51ade",
220 "type": "github"
221 },
222 "original": {
223 "owner": "numtide",
224 "repo": "flake-utils",
225 "type": "github"
226 }
227 },
228 "flake-utils_3": {
229 "locked": {
230 "lastModified": 1667395993,
231 "narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=",
232 "owner": "numtide",
233 "repo": "flake-utils",
234 "rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f",
235 "type": "github"
236 },
237 "original": {
238 "owner": "numtide",
239 "repo": "flake-utils",
240 "type": "github"
241 }
242 },
243 "loginctl-linger": {
244 "locked": {
245 "lastModified": 1,
246 "narHash": "sha256-TLlUOhiQzYo6SwH0E3oPCDfhgW249qPZTlVar1VmpKw=",
247 "path": "../../flakes/loginctl-linger",
248 "type": "path"
249 },
250 "original": {
251 "path": "../../flakes/loginctl-linger",
252 "type": "path"
253 }
254 },
255 "mail-relay": {
256 "inputs": {
257 "environment": "environment_3",
258 "secrets": "secrets"
259 },
260 "locked": {
261 "lastModified": 1,
262 "narHash": "sha256-xISja892g6YTu9YjGwaD36BBWi/1+IcuREw6iUDqfVw=",
263 "path": "../../flakes/private/mail-relay",
264 "type": "path"
265 },
266 "original": {
267 "path": "../../flakes/private/mail-relay",
268 "type": "path"
269 }
270 },
271 "monitoring": {
272 "inputs": {
273 "environment": "environment_4",
274 "naemon": "naemon",
275 "nixpkgs-lib": "nixpkgs-lib",
276 "secrets": "secrets_2"
277 },
278 "locked": {
279 "lastModified": 1,
280 "narHash": "sha256-K720bqCEHPK0F7GBaxo/ioJ3LVAyhjl/ZZobWwO4ebU=",
281 "path": "../../flakes/private/monitoring",
282 "type": "path"
283 },
284 "original": {
285 "path": "../../flakes/private/monitoring",
286 "type": "path"
287 }
288 },
289 "my-lib": {
290 "inputs": {
291 "colmena": "colmena",
292 "disko": "disko",
293 "flake-parts": "flake-parts",
294 "nixos-anywhere": "nixos-anywhere",
295 "nixpkgs": "nixpkgs_4"
296 },
297 "locked": {
298 "lastModified": 1,
299 "narHash": "sha256-wwpT+I5/zrln85BDzlZoEDC19GwYrcZSXbrJjyvC4jk=",
300 "path": "../../flakes/lib",
301 "type": "path"
302 },
303 "original": {
304 "path": "../../flakes/lib",
305 "type": "path"
306 }
307 },
308 "mypackages": {
309 "inputs": {
310 "flake-parts": "flake-parts_2",
311 "nixpkgs": "nixpkgs_8",
312 "webapps-ttrss": "webapps-ttrss"
313 },
314 "locked": {
315 "lastModified": 1,
316 "narHash": "sha256-C0plEL+g6kv5fo/VmTjMJK45RfFcGufqPKJVnviMyGY=",
317 "path": "../../mypackages",
318 "type": "path"
319 },
320 "original": {
321 "path": "../../mypackages",
322 "type": "path"
323 }
324 },
325 "myuids": {
326 "locked": {
327 "lastModified": 1,
328 "narHash": "sha256-HkW9YCLQCNBX3Em7J7MjraVEZO3I3PizkVV2QrUdULQ=",
329 "path": "../../flakes/myuids",
330 "type": "path"
331 },
332 "original": {
333 "path": "../../flakes/myuids",
334 "type": "path"
335 }
336 },
337 "myuids_2": {
338 "locked": {
339 "lastModified": 1,
340 "narHash": "sha256-HkW9YCLQCNBX3Em7J7MjraVEZO3I3PizkVV2QrUdULQ=",
341 "path": "../../myuids",
342 "type": "path"
343 },
344 "original": {
345 "path": "../../myuids",
346 "type": "path"
347 }
348 },
349 "naemon": {
350 "locked": {
351 "lastModified": 1,
352 "narHash": "sha256-6le57WLKj1HXdhe4cgYO6N0Z9nJZC+plQY8HhOwzEIk=",
353 "path": "../../naemon",
354 "type": "path"
355 },
356 "original": {
357 "path": "../../naemon",
358 "type": "path"
359 }
360 },
361 "nixos-2305": {
362 "locked": {
363 "lastModified": 1687938137,
364 "narHash": "sha256-Z00c0Pk3aE1aw9x44lVcqHmvx+oX7dxCXCvKcUuE150=",
365 "owner": "NixOS",
366 "repo": "nixpkgs",
367 "rev": "ba2ded3227a2992f2040fad4ba6f218a701884a5",
368 "type": "github"
369 },
370 "original": {
371 "owner": "NixOS",
372 "ref": "release-23.05",
373 "repo": "nixpkgs",
374 "type": "github"
375 }
376 },
377 "nixos-anywhere": {
378 "inputs": {
379 "disko": [
380 "my-lib",
381 "disko"
382 ],
383 "flake-parts": [
384 "my-lib",
385 "flake-parts"
386 ],
387 "nixos-2305": "nixos-2305",
388 "nixos-images": "nixos-images",
389 "nixpkgs": "nixpkgs_3",
390 "treefmt-nix": "treefmt-nix"
391 },
392 "locked": {
393 "lastModified": 1689945193,
394 "narHash": "sha256-+GPRt7ouE84A7GPNKnFYGU0cQL7skKxz0BAY0sUjUmw=",
395 "owner": "numtide",
396 "repo": "nixos-anywhere",
397 "rev": "27161266077a177ac116e2cb72cc70af5f145189",
398 "type": "github"
399 },
400 "original": {
401 "owner": "numtide",
402 "repo": "nixos-anywhere",
403 "type": "github"
404 }
405 },
406 "nixos-images": {
407 "inputs": {
408 "nixos-2305": [
409 "my-lib",
410 "nixos-anywhere",
411 "nixos-2305"
412 ],
413 "nixos-unstable": [
414 "my-lib",
415 "nixos-anywhere",
416 "nixpkgs"
417 ]
418 },
419 "locked": {
420 "lastModified": 1686819168,
421 "narHash": "sha256-IbRVStbKoMC2fUX6TxNO82KgpVfI8LL4Cq0bTgdYhnY=",
422 "owner": "nix-community",
423 "repo": "nixos-images",
424 "rev": "ccc1a2c08ce2fc38bcece85d2a6e7bf17bac9e37",
425 "type": "github"
426 },
427 "original": {
428 "owner": "nix-community",
429 "repo": "nixos-images",
430 "type": "github"
431 }
432 },
433 "nixpkgs": {
434 "locked": {
435 "lastModified": 1683408522,
436 "narHash": "sha256-9kcPh6Uxo17a3kK3XCHhcWiV1Yu1kYj22RHiymUhMkU=",
437 "owner": "NixOS",
438 "repo": "nixpkgs",
439 "rev": "897876e4c484f1e8f92009fd11b7d988a121a4e7",
440 "type": "github"
441 },
442 "original": {
443 "owner": "NixOS",
444 "ref": "nixos-unstable",
445 "repo": "nixpkgs",
446 "type": "github"
447 }
448 },
449 "nixpkgs-4": {
450 "flake": false,
451 "locked": {
452 "lastModified": 1646497237,
453 "narHash": "sha256-Ccpot1h/rV8MgcngDp5OrdmLTMaUTbStZTR5/sI7zW0=",
454 "owner": "NixOS",
455 "repo": "nixpkgs",
456 "rev": "062a0c5437b68f950b081bbfc8a699d57a4ee026",
457 "type": "github"
458 },
459 "original": {
460 "owner": "NixOS",
461 "repo": "nixpkgs",
462 "rev": "062a0c5437b68f950b081bbfc8a699d57a4ee026",
463 "type": "github"
464 }
465 },
466 "nixpkgs-lib": {
467 "locked": {
468 "dir": "lib",
469 "lastModified": 1691269286,
470 "narHash": "sha256-7cPTz1bPhwq8smt9rHDcFtJsd1tFDcBukzj5jOXqjfk=",
471 "owner": "NixOS",
472 "repo": "nixpkgs",
473 "rev": "85d4248a4f5aa6bc55dd2cea8131bb68b2d43804",
474 "type": "github"
475 },
476 "original": {
477 "dir": "lib",
478 "owner": "NixOS",
479 "repo": "nixpkgs",
480 "type": "github"
481 }
482 },
483 "nixpkgs-lib_2": {
484 "locked": {
485 "dir": "lib",
486 "lastModified": 1685564631,
487 "narHash": "sha256-8ywr3AkblY4++3lIVxmrWZFzac7+f32ZEhH/A8pNscI=",
488 "owner": "NixOS",
489 "repo": "nixpkgs",
490 "rev": "4f53efe34b3a8877ac923b9350c874e3dcd5dc0a",
491 "type": "github"
492 },
493 "original": {
494 "dir": "lib",
495 "owner": "NixOS",
496 "ref": "nixos-unstable",
497 "repo": "nixpkgs",
498 "type": "github"
499 }
500 },
501 "nixpkgs-lib_3": {
502 "locked": {
503 "dir": "lib",
504 "lastModified": 1675183161,
505 "narHash": "sha256-Zq8sNgAxDckpn7tJo7V1afRSk2eoVbu3OjI1QklGLNg=",
506 "owner": "NixOS",
507 "repo": "nixpkgs",
508 "rev": "e1e1b192c1a5aab2960bf0a0bd53a2e8124fa18e",
509 "type": "github"
510 },
511 "original": {
512 "dir": "lib",
513 "owner": "NixOS",
514 "ref": "nixos-unstable",
515 "repo": "nixpkgs",
516 "type": "github"
517 }
518 },
519 "nixpkgs_2": {
520 "locked": {
521 "lastModified": 1687701825,
522 "narHash": "sha256-aMC9hqsf+4tJL7aJWSdEUurW2TsjxtDcJBwM9Y4FIYM=",
523 "owner": "NixOS",
524 "repo": "nixpkgs",
525 "rev": "07059ee2fa34f1598758839b9af87eae7f7ae6ea",
526 "type": "github"
527 },
528 "original": {
529 "owner": "NixOS",
530 "ref": "nixpkgs-unstable",
531 "repo": "nixpkgs",
532 "type": "github"
533 }
534 },
535 "nixpkgs_3": {
536 "locked": {
537 "lastModified": 1687893427,
538 "narHash": "sha256-jJHj0Lxpvov1IPYQK441oLAKxxemHm16U9jf60bXAFU=",
539 "owner": "nixos",
540 "repo": "nixpkgs",
541 "rev": "4b14ab2a916508442e685089672681dff46805be",
542 "type": "github"
543 },
544 "original": {
545 "owner": "nixos",
546 "ref": "nixos-unstable-small",
547 "repo": "nixpkgs",
548 "type": "github"
549 }
550 },
551 "nixpkgs_4": {
552 "locked": {
553 "lastModified": 1648725829,
554 "narHash": "sha256-tXEzI38lLrzW2qCAIs0UAatE2xcsTsoKWaaXqAcF1NI=",
555 "owner": "NixOS",
556 "repo": "nixpkgs",
557 "rev": "72152ff5ad470ed1a5b97c0ba2737938c136c994",
558 "type": "github"
559 },
560 "original": {
561 "owner": "NixOS",
562 "repo": "nixpkgs",
563 "type": "github"
564 }
565 },
566 "nixpkgs_5": {
567 "locked": {
568 "lastModified": 1693158576,
569 "narHash": "sha256-aRTTXkYvhXosGx535iAFUaoFboUrZSYb1Ooih/auGp0=",
570 "owner": "nixos",
571 "repo": "nixpkgs",
572 "rev": "a999c1cc0c9eb2095729d5aa03e0d8f7ed256780",
573 "type": "github"
574 },
575 "original": {
576 "owner": "nixos",
577 "ref": "nixos-unstable",
578 "repo": "nixpkgs",
579 "type": "github"
580 }
581 },
582 "nixpkgs_6": {
583 "flake": false,
584 "locked": {
585 "lastModified": 1596265691,
586 "narHash": "sha256-9ofCzFqttTsGrvTaS4RrDSTNQO9PFOz5uyn8V+2eA5M=",
587 "owner": "NixOS",
588 "repo": "nixpkgs",
589 "rev": "840c782d507d60aaa49aa9e3f6d0b0e780912742",
590 "type": "github"
591 },
592 "original": {
593 "owner": "NixOS",
594 "repo": "nixpkgs",
595 "rev": "840c782d507d60aaa49aa9e3f6d0b0e780912742",
596 "type": "github"
597 }
598 },
599 "nixpkgs_7": {
600 "locked": {
601 "lastModified": 1687502512,
602 "narHash": "sha256-dBL/01TayOSZYxtY4cMXuNCBk8UMLoqRZA+94xiFpJA=",
603 "owner": "NixOS",
604 "repo": "nixpkgs",
605 "rev": "3ae20aa58a6c0d1ca95c9b11f59a2d12eebc511f",
606 "type": "github"
607 },
608 "original": {
609 "owner": "NixOS",
610 "ref": "nixos-unstable",
611 "repo": "nixpkgs",
612 "type": "github"
613 }
614 },
615 "nixpkgs_8": {
616 "locked": {
617 "lastModified": 1646497237,
618 "narHash": "sha256-Ccpot1h/rV8MgcngDp5OrdmLTMaUTbStZTR5/sI7zW0=",
619 "owner": "nixos",
620 "repo": "nixpkgs",
621 "rev": "062a0c5437b68f950b081bbfc8a699d57a4ee026",
622 "type": "github"
623 },
624 "original": {
625 "owner": "nixos",
626 "repo": "nixpkgs",
627 "rev": "062a0c5437b68f950b081bbfc8a699d57a4ee026",
628 "type": "github"
629 }
630 },
631 "php": {
632 "inputs": {
633 "flake-utils": "flake-utils_2",
634 "nixpkgs": "nixpkgs_6",
635 "nixpkgs-4": "nixpkgs-4"
636 },
637 "locked": {
638 "lastModified": 1,
639 "narHash": "sha256-Qs+O86L4sPArYWm7wMCFNKLCWfUwkz8STePsn5K9Xwk=",
640 "path": "../../flakes/private/php",
641 "type": "path"
642 },
643 "original": {
644 "path": "../../flakes/private/php",
645 "type": "path"
646 }
647 },
648 "root": {
649 "inputs": {
650 "chatons": "chatons",
651 "environment": "environment_2",
652 "files-watcher": "files-watcher",
653 "loginctl-linger": "loginctl-linger",
654 "mail-relay": "mail-relay",
655 "monitoring": "monitoring",
656 "my-lib": "my-lib",
657 "myuids": "myuids",
658 "nixpkgs": "nixpkgs_5",
659 "php": "php",
660 "secrets": "secrets_3",
661 "system": "system"
662 }
663 },
664 "secrets": {
665 "locked": {
666 "lastModified": 1,
667 "narHash": "sha256-5AakznhrJFmwCD7lr4JEh55MtdAJL6WA/YuBks6ISSE=",
668 "path": "../../secrets",
669 "type": "path"
670 },
671 "original": {
672 "path": "../../secrets",
673 "type": "path"
674 }
675 },
676 "secrets-public": {
677 "locked": {
678 "lastModified": 1,
679 "narHash": "sha256-5AakznhrJFmwCD7lr4JEh55MtdAJL6WA/YuBks6ISSE=",
680 "path": "../../secrets",
681 "type": "path"
682 },
683 "original": {
684 "path": "../../secrets",
685 "type": "path"
686 }
687 },
688 "secrets_2": {
689 "locked": {
690 "lastModified": 1,
691 "narHash": "sha256-5AakznhrJFmwCD7lr4JEh55MtdAJL6WA/YuBks6ISSE=",
692 "path": "../../secrets",
693 "type": "path"
694 },
695 "original": {
696 "path": "../../secrets",
697 "type": "path"
698 }
699 },
700 "secrets_3": {
701 "locked": {
702 "lastModified": 1,
703 "narHash": "sha256-5AakznhrJFmwCD7lr4JEh55MtdAJL6WA/YuBks6ISSE=",
704 "path": "../../flakes/secrets",
705 "type": "path"
706 },
707 "original": {
708 "path": "../../flakes/secrets",
709 "type": "path"
710 }
711 },
712 "stable": {
713 "locked": {
714 "lastModified": 1669735802,
715 "narHash": "sha256-qtG/o/i5ZWZLmXw108N2aPiVsxOcidpHJYNkT45ry9Q=",
716 "owner": "NixOS",
717 "repo": "nixpkgs",
718 "rev": "731cc710aeebecbf45a258e977e8b68350549522",
719 "type": "github"
720 },
721 "original": {
722 "owner": "NixOS",
723 "ref": "nixos-22.11",
724 "repo": "nixpkgs",
725 "type": "github"
726 }
727 },
728 "system": {
729 "inputs": {
730 "backports": "backports",
731 "environment": "environment_5",
732 "mypackages": "mypackages",
733 "myuids": "myuids_2",
734 "secrets-public": "secrets-public"
735 },
736 "locked": {
737 "lastModified": 1,
738 "narHash": "sha256-vOs7fcQVsOSl/gsyzFXfsWE7u0/O9mIKpHnwDwHxJTQ=",
739 "path": "../../flakes/private/system",
740 "type": "path"
741 },
742 "original": {
743 "path": "../../flakes/private/system",
744 "type": "path"
745 }
746 },
747 "treefmt-nix": {
748 "inputs": {
749 "nixpkgs": [
750 "my-lib",
751 "nixos-anywhere",
752 "nixpkgs"
753 ]
754 },
755 "locked": {
756 "lastModified": 1687940979,
757 "narHash": "sha256-D4ZFkgIG2s9Fyi78T3fVG9mqMD+/UnFDB62jS4gjZKY=",
758 "owner": "numtide",
759 "repo": "treefmt-nix",
760 "rev": "0a4f06c27610a99080b69433873885df82003aae",
761 "type": "github"
762 },
763 "original": {
764 "owner": "numtide",
765 "repo": "treefmt-nix",
766 "type": "github"
767 }
768 },
769 "webapps-ttrss": {
770 "flake": false,
771 "locked": {
772 "lastModified": 1546759381,
773 "narHash": "sha256-urjf4EoLWS7G0s0hRtaErrs2B8DUatNK/eoneuB0anY=",
774 "ref": "master",
775 "rev": "986ca251f995f7754a0470d3e0c44538a545081f",
776 "revCount": 9256,
777 "type": "git",
778 "url": "https://git.tt-rss.org/fox/tt-rss.git"
779 },
780 "original": {
781 "ref": "master",
782 "rev": "986ca251f995f7754a0470d3e0c44538a545081f",
783 "type": "git",
784 "url": "https://git.tt-rss.org/fox/tt-rss.git"
785 }
786 }
787 },
788 "root": "root",
789 "version": 7
790}
diff --git a/systems/monitoring-1/flake.nix b/systems/monitoring-1/flake.nix
new file mode 100644
index 0000000..e97cb05
--- /dev/null
+++ b/systems/monitoring-1/flake.nix
@@ -0,0 +1,45 @@
1{
2 inputs = {
3 nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
4
5 my-lib.url = "path:../../flakes/lib";
6
7 monitoring.url = "path:../../flakes/private/monitoring";
8 mail-relay.url = "path:../../flakes/private/mail-relay";
9 chatons.url = "path:../../flakes/private/chatons";
10 environment.url = "path:../../flakes/private/environment";
11 system.url = "path:../../flakes/private/system";
12 php.url = "path:../../flakes/private/php";
13
14 myuids.url = "path:../../flakes/myuids";
15 secrets.url = "path:../../flakes/secrets";
16 files-watcher.url = "path:../../flakes/files-watcher";
17 loginctl-linger.url = "path:../../flakes/loginctl-linger";
18 };
19 outputs = inputs@{ self, my-lib, nixpkgs, ...}:
20 my-lib.lib.mkColmenaFlake {
21 name = "monitoring-1";
22 inherit self nixpkgs;
23 system = "x86_64-linux";
24 targetHost = "95.216.164.150";
25 targetUser = "root";
26 nixosModules = {
27 base = ./base.nix;
28 system = inputs.system.nixosModule;
29 mail-relay = inputs.mail-relay.nixosModule;
30 chatons = inputs.chatons.nixosModule;
31 monitoring = inputs.monitoring.nixosModule;
32 environment = inputs.environment.nixosModule;
33
34 myuids = inputs.myuids.nixosModule;
35 secrets = inputs.secrets.nixosModule;
36 files-watcher = inputs.files-watcher.nixosModule;
37 loginctl-linger = inputs.loginctl-linger.nixosModule;
38 };
39 moduleArgs = {
40 nixpkgs = inputs.nixpkgs;
41 monitoring = inputs.monitoring;
42 php = inputs.php;
43 };
44 };
45}
diff --git a/systems/monitoring-1/monitoring-master.nix b/systems/monitoring-1/monitoring-master.nix
new file mode 100644
index 0000000..09319b8
--- /dev/null
+++ b/systems/monitoring-1/monitoring-master.nix
@@ -0,0 +1,87 @@
1{ config, pkgs, lib, nodes, name, monitoring, ... }:
2let
3 mlib = monitoring.lib;
4 nodesWithMonitoring = lib.filterAttrs (n: v: (v.config.myServices or {}) ? "monitoring") nodes;
5in
6{
7 imports = [
8 ./monitoring/master.nix
9 ./monitoring/phare.nix
10 ./monitoring/ulminfo-fr.nix
11 ];
12 myServices.monitoring.activatedPlugins = lib.flatten (lib.mapAttrsToList (_: n: n.config.myServices.monitoring.fromMasterActivatedPlugins) nodesWithMonitoring);
13 myServices.monitoring.objects = lib.mkMerge (
14 lib.mapAttrsToList (_: n:
15 mlib.toMasterPassiveObject "external-passive-service" 1.5 n.config.myServices.monitoring.objects
16 ) (lib.filterAttrs (n: v: n != name) nodesWithMonitoring)
17 ++
18 lib.mapAttrsToList (_: n: n.config.myServices.monitoring.fromMasterObjects) nodesWithMonitoring
19 );
20 myServices.chatonsProperties.hostings.monitoring = {
21 file.datetime = "2022-08-27T16:00:00";
22 hosting = {
23 name = "Monitoring";
24 description = "Website and server health monitoring";
25 website = "https://status.immae.eu";
26 logo = "https://www.naemon.io/favicon.ico";
27 status.level = "OK";
28 status.description = "OK";
29 registration.load = "OPEN";
30 install.type = "PACKAGE";
31 };
32 software = {
33 name = "naemon";
34 website = "https://www.naemon.io/";
35 license.url = "https://github.com/naemon/naemon-core/blob/master/COPYING";
36 license.name = "GNU General Public License v2.0";
37 version = config.services.naemon.package.version;
38 source.url = "https://github.com/naemon/naemon-core";
39 modules = "livestatus,status-engine";
40 };
41 };
42
43 services.nginx = {
44 virtualHosts."status.immae.eu".locations = {
45 "=/common/immae.cfg" = {
46 alias = pkgs.writeText "immae.cfg" ''
47 # put me for instance in /etc/naemon/module-conf.d/immae.cfg
48 # Make sure that you have include_dir=module-conf.d in
49 # naemon.cfg
50 log_initial_states=1
51 date_format=iso8601
52 admin_email=${config.myEnv.monitoring.email}
53 obsess_over_services=1
54 ocsp_command=notify-master
55 '';
56 };
57 "=/common/resource.cfg" = {
58 alias = pkgs.writeText "resource.cfg" ''
59 # Resource.cfg file
60 # Replace this with path to monitoring plugins
61 $USER1$=@@COMMON_PLUGINS@@
62 # Replace this with a path to scripts from
63 # https://git.immae.eu/cgit/perso/Immae/Config/Nix.git/tree/modules/private/monitoring/plugins
64 $USER2$=@@IMMAE_PLUGINS@@
65 $USER200$=https://status.immae.eu/
66 $USER201$=@@TOKEN@@
67 '';
68 };
69 };
70 };
71
72 secrets.keys = lib.mapAttrs' (k: v: lib.nameValuePair "${k}_access_key" {
73 user = "naemon";
74 group = "naemon";
75 permissions = "0400";
76 text = ''
77 export AWS_ACCESS_KEY_ID="${v.accessKeyId}"
78 export AWS_SECRET_ACCESS_KEY="${v.secretAccessKey}"
79 export BASE_URL="${v.remote "immae-eldiron"}"
80 '';
81 }) config.myEnv.backup.remotes;
82
83 services.naemon.extraConfig = ''
84 broker_module=${pkgs.naemon-livestatus}/lib/naemon-livestatus/livestatus.so ${config.services.naemon.runDir}/live
85 broker_module=${pkgs.status-engine-module}/lib/status-engine/naemon/statusengine-${pkgs.naemon.status_engine_version}.o use_service_perfdata=1 use_process_data=0 use_system_command_data=0 use_external_command_data=0 use_flapping_data=0 use_program_status_data=0 use_notification_data=0 use_contact_status_data=0 use_contact_notification_data=0 use_event_handler_data=0 use_object_data=0
86 '';
87}
diff --git a/systems/monitoring-1/monitoring.nix b/systems/monitoring-1/monitoring.nix
new file mode 100644
index 0000000..421c71f
--- /dev/null
+++ b/systems/monitoring-1/monitoring.nix
@@ -0,0 +1,61 @@
1{ config, pkgs, lib, name, monitoring, ... }:
2let
3 hostFQDN = config.hostEnv.fqdn;
4 emailCheck = monitoring.lib.emailCheck config.myEnv.monitoring.email_check;
5in
6{
7 config.myServices.monitoring.activatedPlugins = [ "memory" "command" "bandwidth" "emails" "ovh" "notify-primary" ];
8 config.myServices.monitoring.objects = lib.mkMerge [
9 (monitoring.lib.objectsCommon {
10 inherit hostFQDN;
11 hostName = name;
12 master = true;
13 processWarn = "70"; processAlert = "80";
14 loadWarn = "4.0"; loadAlert = "5.0";
15 load15Warn = "1.0"; load15Alert = "2.0";
16 interface = builtins.head (builtins.attrNames config.networking.interfaces);
17 })
18
19 {
20 service = [
21 (emailCheck "monitoring-1" hostFQDN)
22
23 {
24 service_description = "OVH account has enough sms";
25 host_name = hostFQDN;
26 use = "external-service";
27 check_command = "check_ovh_sms";
28
29 check_interval = 120;
30 notification_interval = "1440";
31 }
32
33 # Dummy service for testing
34 # {
35 # service_description = "Dummy failing test";
36 # host_name = "dummy-host";
37 # use = "local-service";
38 # check_interval = 0.3;
39 # max_check_attempts = "1";
40 # flap_detection_enabled = "0";
41 # notification_interval = "0.1";
42 # check_command = "check_critical";
43 # }
44 ];
45
46 host = {
47 # Dummy host for testing
48 # "dummy-host" = {
49 # alias = "dummy.host";
50 # check_interval = 0.3;
51 # max_check_attempts = "1";
52 # flap_detection_enabled = "0";
53 # notification_interval = "0.1";
54 # address = "dummy.host";
55 # use = "linux-server";
56 # check_command = "check_ok";
57 # };
58 };
59 }
60 ];
61}
diff --git a/systems/monitoring-1/monitoring/master.nix b/systems/monitoring-1/monitoring/master.nix
new file mode 100644
index 0000000..c8f52ea
--- /dev/null
+++ b/systems/monitoring-1/monitoring/master.nix
@@ -0,0 +1,43 @@
1{ config, ... }:
2{
3 myServices.monitoring.objects = {
4 contact = {
5 immae = {
6 alias = "Immae";
7 email = config.myEnv.monitoring.immae_contact;
8 use = "generic-contact";
9 contactgroups = "admins";
10 host_notification_commands = "notify-host-by-email,notify-host-by-apprise!$USER210$";
11 service_notification_commands = "notify-service-by-email,notify-service-by-apprise!$USER210$";
12 };
13 };
14 command = {
15 check_passive = "$USER1$/check_dummy 3 \"Service result are stale\"";
16 };
17 templates = {
18 service = {
19 external-passive-service = {
20 active_checks_enabled = "0";
21 check_freshness = "1";
22 check_period = "24x7";
23 contact_groups = "admins";
24 event_handler_enabled = "1";
25 flap_detection_enabled = "1";
26 is_volatile = "0";
27 max_check_attempts = "3";
28 notification_interval = "60";
29 notification_options = "w,u,c,r,f,s";
30 notification_period = "24x7";
31 notifications_enabled = "1";
32 passive_checks_enabled = "1";
33 process_perf_data = "1";
34 retain_nonstatus_information = "1";
35 retain_status_information = "1";
36 retry_interval = "2";
37 check_command = "check_passive";
38 _webstatus_namespace = "immae";
39 };
40 };
41 };
42 };
43}
diff --git a/systems/monitoring-1/monitoring/phare.nix b/systems/monitoring-1/monitoring/phare.nix
new file mode 100644
index 0000000..0ce9ffe
--- /dev/null
+++ b/systems/monitoring-1/monitoring/phare.nix
@@ -0,0 +1,20 @@
1{ monitoring, config, ... }:
2let
3 emailCheck = monitoring.lib.emailCheck config.myEnv.monitoring.email_check;
4in
5{
6 config.myServices.monitoring.activatedPlugins = [ "emails" ];
7 config.myServices.monitoring.objects.host = {
8 "phare.normalesup.org" = {
9 alias = "phare.normalesup.org";
10 address = "phare.normalesup.org";
11 use = "linux-server";
12 hostgroups = "webstatus-hosts";
13 _webstatus_name = "phare";
14 _webstatus_vhost = "status.immae.eu";
15 };
16 };
17 config.myServices.monitoring.objects.service = [
18 (emailCheck "phare" "phare.normalesup.org")
19 ];
20}
diff --git a/systems/monitoring-1/monitoring/ulminfo-fr.nix b/systems/monitoring-1/monitoring/ulminfo-fr.nix
new file mode 100644
index 0000000..b0c6657
--- /dev/null
+++ b/systems/monitoring-1/monitoring/ulminfo-fr.nix
@@ -0,0 +1,20 @@
1{ monitoring, config, ... }:
2let
3 emailCheck = monitoring.lib.emailCheck config.myEnv.monitoring.email_check;
4in
5{
6 config.myServices.monitoring.activatedPlugins = [ "emails" ];
7 config.myServices.monitoring.objects.host = {
8 "ulminfo.fr" = {
9 alias = "ulminfo.fr";
10 address = "ulminfo.fr";
11 use = "linux-server";
12 hostgroups = "webstatus-hosts";
13 _webstatus_name = "ulminfo";
14 _webstatus_vhost = "status.immae.eu";
15 };
16 };
17 config.myServices.monitoring.objects.service = [
18 (emailCheck "ulminfo" "ulminfo.fr")
19 ];
20}
diff --git a/systems/monitoring-1/status.nix b/systems/monitoring-1/status.nix
new file mode 100644
index 0000000..8b6615f
--- /dev/null
+++ b/systems/monitoring-1/status.nix
@@ -0,0 +1,84 @@
1{ config, pkgs, lib, name, ... }:
2{
3 options = {
4 myServices.status = {
5 enable = lib.mkOption {
6 type = lib.types.bool;
7 default = false;
8 description = ''
9 Whether to enable status app.
10 '';
11 };
12 };
13 };
14 config = lib.mkIf config.myServices.status.enable {
15 secrets.keys."naemon-status/environment" = {
16 user = "naemon";
17 group = "naemon";
18 permissions = "0400";
19 text = ''
20 TOKENS=${builtins.concatStringsSep " " config.myEnv.monitoring.nrdp_tokens}
21 '';
22 };
23 services.nginx = {
24 enable = true;
25 recommendedOptimisation = true;
26 recommendedGzipSettings = true;
27 recommendedProxySettings = true;
28 upstreams."netdata".servers = { "127.0.0.1:19999" = {}; };
29 upstreams."netdata".extraConfig = ''
30 keepalive 64;
31 '';
32 virtualHosts."status.immae.eu" = {
33 acmeRoot = config.security.acme.defaults.webroot;
34 useACMEHost = name;
35 forceSSL = true;
36 locations."/".proxyPass = "http://unix:/run/naemon-status/socket.sock:/";
37
38 locations."= /netdata".return = "301 /netdata/";
39 locations."~ /netdata/(?<ndpath>.*)".extraConfig = ''
40 proxy_redirect off;
41 proxy_set_header Host $host;
42
43 proxy_set_header X-Forwarded-Host $host;
44 proxy_set_header X-Forwarded-Server $host;
45 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
46 proxy_http_version 1.1;
47 proxy_pass_request_headers on;
48 proxy_set_header Connection "keep-alive";
49 proxy_store off;
50 proxy_pass http://netdata/$ndpath$is_args$args;
51
52 gzip on;
53 gzip_proxied any;
54 gzip_types *;
55 '';
56 };
57 };
58 security.acme.certs."${name}" = {
59 extraDomainNames = [ "status.immae.eu" ];
60 group = config.services.nginx.group;
61 };
62
63 networking.firewall.allowedTCPPorts = [ 80 443 ];
64 systemd.services.naemon-status = {
65 description = "Naemon status";
66 after = [ "network.target" ];
67 wantedBy = [ "multi-user.target" ];
68
69 serviceConfig = {
70 EnvironmentFile = config.secrets.fullPaths."naemon-status/environment";
71 Type = "simple";
72 WorkingDirectory = "${./status}";
73 ExecStart = let
74 python = pkgs.python3.withPackages (p: [ p.gunicorn p.flask p.flask_login ]);
75 in
76 "${python}/bin/gunicorn -w4 --bind unix:/run/naemon-status/socket.sock app:app";
77 User = "naemon";
78 RuntimeDirectory = "naemon-status";
79 StandardOutput = "journal";
80 StandardError = "inherit";
81 };
82 };
83 };
84}
diff --git a/systems/monitoring-1/status/app.py b/systems/monitoring-1/status/app.py
new file mode 100755
index 0000000..ff92891
--- /dev/null
+++ b/systems/monitoring-1/status/app.py
@@ -0,0 +1,414 @@
1from flask import Flask, request, render_template_string, jsonify, make_response
2from flask_login import LoginManager, UserMixin, login_required
3import socket
4import json
5import time
6import os
7
8login_manager = LoginManager()
9app = Flask(__name__)
10login_manager.init_app(app)
11
12STATUS = [
13 "ok",
14 "warning",
15 "error",
16 "unknown"
17 ]
18
19HOST_STATUS = [
20 "up",
21 "down",
22 "unreachable",
23 ]
24
25#### Push
26AUTHORIZED_KEYS = os.environ.get("TOKENS", "").split()
27COMMAND_FILE = "/var/run/naemon/naemon.cmd"
28
29ERROR_NO_REQUEST_HANDLER="NO REQUEST HANDLER"
30ERROR_NO_TOKEN_SUPPLIED="NO TOKEN"
31ERROR_BAD_TOKEN_SUPPLIED="BAD TOKEN"
32
33ERROR_BAD_COMMAND_FILE="BAD COMMAND FILE"
34ERROR_COMMAND_FILE_OPEN_WRITE="COMMAND FILE UNWRITEABLE"
35ERROR_COMMAND_FILE_OPEN="CANNOT OPEN COMMAND FILE"
36ERROR_BAD_WRITE="WRITE ERROR"
37
38ERROR_BAD_DATA="BAD DATA"
39ERROR_BAD_JSON="BAD JSON"
40
41ERROR_NO_CORRECT_STATUS="NO STATUS WAS CORRECT"
42#### /Push
43
44def get_lq(request):
45 # https://mathias-kettner.de/checkmk_livestatus.html
46 socket_path="/var/run/naemon/live"
47 s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
48 s.connect(socket_path)
49 s.send(request.encode())
50 s.shutdown(socket.SHUT_WR)
51 chunks = []
52 while len(chunks) == 0 or len(chunks[-1]) > 0:
53 chunks.append(s.recv(4096))
54 s.close()
55 return b"".join(chunks).decode()
56
57class Host:
58 def __init__(self, name, alias, status, webname, vhost):
59 self.name = name
60 self.alias = alias
61 self.webname = webname or alias
62 self.vhost = vhost
63 self.status = status
64 self.services = []
65
66 @classmethod
67 def parse_hosts(cls, payload, vhost):
68 parsed = filter(lambda x: x.vhost == vhost, [cls.parse(p) for p in json.loads(payload)])
69 return {p.name: p for p in parsed}
70
71 @classmethod
72 def parse(cls, payload):
73 return cls(payload[0], payload[1], HOST_STATUS[payload[2]], payload[3].get("WEBSTATUS_NAME"), payload[3].get("WEBSTATUS_VHOST"))
74
75 def __repr__(self):
76 return "Host {}: {} ({})".format(self.name, self.alias, self.webname)
77
78 @classmethod
79 def query(cls, vhost):
80 answer = get_lq("""GET hosts
81Filter: groups >= webstatus-hosts
82Columns: name alias state custom_variables
83OutputFormat: json
84""")
85 return cls.parse_hosts(answer, vhost)
86
87 def fill_services(self, services):
88 self.services = [service for service in services if service.host == self.name]
89
90class ServiceGroup:
91 def __init__(self, name, alias):
92 self.name = name
93 self.alias = alias
94 self.services = []
95
96 @classmethod
97 def parse_groups(cls, payload):
98 parsed = [cls.parse(p) for p in json.loads(payload)]
99 return {p.name: p for p in parsed}
100
101 @classmethod
102 def parse(cls, payload):
103 return cls(payload[0], payload[1])
104
105 @classmethod
106 def query(cls):
107 answer = get_lq("""GET servicegroups
108Filter: name ~ ^webstatus-
109Columns: name alias custom_variables
110OutputFormat: json
111""")
112 return cls.parse_groups(answer)
113
114 def fill_services(self, services, hosts):
115 self.services = [service for service in services if any([group == self.name for group in service.groups]) and service.host in hosts]
116
117 def __repr__(self):
118 return "ServiceGroup {}: {}".format(self.name, self.alias)
119
120class Service:
121 def __init__(self, name, host, groups, status, webname, url, description, infos):
122 self.name = name
123 self.host = host
124 self.groups = groups
125 self.status = status
126 self.webname = webname
127 self.url = url
128 self.description = description
129 self.infos = infos
130
131 @classmethod
132 def parse_services(cls, payload):
133 parsed = json.loads(payload)
134 return [cls.parse(p) for p in parsed if cls.valid(p[2])]
135
136 @staticmethod
137 def valid(groups):
138 return any([b.startswith("webstatus-") for b in groups])
139
140 @classmethod
141 def parse(cls, payload):
142 return cls(payload[0],
143 payload[1],
144 payload[2],
145 STATUS[payload[3]],
146 payload[4].get("WEBSTATUS_NAME"),
147 payload[4].get("WEBSTATUS_URL"),
148 payload[5],
149 payload[6])
150
151 @classmethod
152 def query(cls):
153 answer = get_lq("""GET services
154Columns: display_name host_name groups state custom_variables description plugin_output
155OutputFormat: json
156""")
157 return cls.parse_services(answer)
158
159 def __repr__(self):
160 return "Service {}: {}".format(self.name, self.webname)
161
162def get_infos(vhost):
163 hosts = Host.query(vhost)
164 servicegroups = ServiceGroup.query()
165 services = Service.query()
166
167 for host in hosts:
168 hosts[host].fill_services(services)
169 for group in servicegroups:
170 servicegroups[group].fill_services(services, hosts)
171 return (hosts, servicegroups, services)
172
173TEMPLATE='''<?xml version="1.0" encoding="UTF-8"?>
174<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
175<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
176 <head>
177 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
178 <meta name="viewport" content="width=device-width, initial-scale=1.0" />
179 <title>Status</title>
180 <meta name="referrer" content="no-referrer" />
181 <style type="text/css">
182 ul {
183 list-style: none;
184 margin: 0px;
185 }
186 ul li:nth-child(2n) {
187 background-color: rgb(240, 240, 240);
188 }
189 li.resource, li.service {
190 margin: 1px 0px;
191 }
192 span.status {
193 display: inline-block;
194 width: 150px;
195 text-align: center;
196 margin-right: 5px;
197 font-variant: small-caps;
198 font-size: 1.2em;
199 }
200 .status_ok,.status_up {
201 background-color: rgba(0, 255, 0, 0.5);;
202 }
203 .status_warning {
204 background-color: rgba(255, 255, 0, 0.5);;
205 }
206 .status_error,.status_down {
207 background-color: rgba(255, 0, 0, 0.5);;
208 }
209 .status_unknown,.status_unreachable {
210 background-color: rgba(0, 0, 255, 0.5);;
211 }
212 .infos {
213 margin-left: 40px;
214 color: rgb(100, 100, 100);
215 }
216 div#services {
217 column-count: auto;
218 column-width: 36em;
219 }
220 div.servicegroup {
221 -webkit-column-break-inside: avoid;
222 break-inside: avoid;
223 }
224 h3.servicegroup_title, h3.host_title {
225 margin: 1px 0px;
226 }
227 span.service_host, span.infos {
228 float: right;
229 display: inline-block;
230 color: rgb(100, 100, 100);
231 }
232 </style>
233 </head>
234 <body>
235 <h2>Hosts</h2>
236 {%- for host in hosts.values() %}
237 <h3 class="host_title">
238 <span class="status status_{{ host.status }}">{{ host.status }}</span>
239 <span class="host">{{ host.webname }}</span>
240 </h3>
241 {%- for service in servicegroups["webstatus-resources"].services if service.host == host.name -%}
242 {%- if loop.first %}
243 <ul class="resources">
244 {% endif %}
245
246 <li class="resource">
247 <span class="status status_{{ service.status }}">{{ service.status }}</span>
248 <span class="description">{{ service.description }}</span>
249 <span class="infos">{{ service.infos }}</span>
250 </li>
251
252 {%- if loop.last %}
253 </ul>
254 {% endif %}
255 {% endfor %}
256 {%- endfor %}
257
258 {%- for group in servicegroups.values() if group.services and group.name != "webstatus-resources" %}
259 {%- if loop.first %}
260 <h2>Services</h2>
261 <div id="services">
262 {%- endif %}
263 <div class="servicegroup">
264 <h3 class="servicegroup_title">{{ group.alias }}</h3>
265 {%- for service in group.services if service.host in hosts -%}
266 {%- if loop.first %}
267 <ul class="services">
268 {% endif %}
269
270 <li class="service" title="{{ service.infos }}">
271 <span class="status status_{{ service.status }}">{{ service.status }}</span>
272 <span class="description">
273 {% if service.url and service.url.startswith("https://") %}
274 <a href="{{ service.url }}">{{ service.webname or service.description }}</a>
275 {% else %}
276 {{ service.webname or service.description }}
277 {% endif %}
278 </span>
279 <span class="service_host">{{ hosts[service.host].webname }}</span>
280 </li>
281
282 {%- if loop.last %}
283 </ul>
284 {% endif %}
285 {%- endfor -%}
286 </div>
287 {%- if loop.last %}
288 </div>
289 {% endif %}
290 {%- endfor %}
291 </body>
292</html>
293'''
294
295@login_manager.request_loader
296def load_user_from_request(request):
297 api_key = request.headers.get('Token')
298 if api_key in AUTHORIZED_KEYS:
299 return UserMixin()
300 content = request.get_json(force=True, silent=True)
301 if content is not None and content.get("token") in AUTHORIZED_KEYS:
302 return UserMixin()
303
304@app.route("/live", methods=["POST"])
305@login_required
306def live():
307 query = request.get_data()
308 result = get_lq(query.decode() + "\n")
309 resp = make_response(result)
310 resp.content_type = "text/plain"
311 return resp
312
313@app.route("/", methods=["GET"])
314def get():
315 (hosts, servicegroups, services) = get_infos(request.host)
316 resp = make_response(render_template_string(TEMPLATE, hosts=hosts, servicegroups=servicegroups))
317 resp.content_type = "text/html"
318 return resp
319
320@app.route("/", methods=["POST"])
321@login_required
322def push():
323 content = request.get_json(force=True, silent=True)
324 if content is None:
325 return ERROR_BAD_JSON
326 if content.get("cmd") != "submitcheck":
327 return render_error(ERROR_NO_REQUEST_HANDLER)
328 if "checkresult" not in content or not isinstance(content["checkresult"], list):
329 return render_error(ERROR_BAD_DATA)
330
331 checks = 0
332 errors = 0
333 for check in map(lambda x: CheckResult.from_json(x), content["checkresult"]):
334 if check is None:
335 errors += 1
336 continue
337 try:
338 write_check_output(check)
339 except Exception as e:
340 return render_error(str(e))
341 checks += 1
342 return render_response(checks, errors)
343
344def write_check_output(check):
345 if check.type== "service":
346 command = "[{time}] PROCESS_SERVICE_CHECK_RESULT;{hostname};{servicename};{state};{output}";
347 else:
348 command = "[{time}] PROCESS_HOST_CHECK_RESULT;{hostname};{state};{output}";
349 formatted = command.format(
350 time=int(time.time()),
351 hostname=check.hostname,
352 state=check.state,
353 output=check.output,
354 servicename=check.servicename,
355 )
356
357 if not os.path.exists(COMMAND_FILE):
358 raise Exception(ERROR_BAD_COMMAND_FILE)
359 if not os.access(COMMAND_FILE, os.W_OK):
360 raise Exception(ERROR_COMMAND_FILE_OPEN_WRITE)
361 if not os.access(COMMAND_FILE, os.W_OK):
362 raise Exception(ERROR_COMMAND_FILE_OPEN_WRITE)
363 try:
364 with open(COMMAND_FILE, "w") as c:
365 c.write(formatted + "\n")
366 except Exception as e:
367 raise Exception(ERROR_BAD_WRITE)
368
369def render_error(error):
370 return jsonify({
371 "status": "error",
372 "message": error,
373 })
374
375def render_response(checks, errors):
376 if checks > 0:
377 return jsonify({
378 "status": "ok",
379 "result": {
380 "checks": checks,
381 "errors": errors,
382 }
383 })
384 else:
385 return jsonify({
386 "status": "error",
387 "message": ERROR_NO_CORRECT_STATUS,
388 })
389
390class CheckResult:
391 def __init__(self, hostname, state, output, servicename, checktype):
392 self.hostname = hostname
393 self.state = state
394 self.output = output
395 self.servicename = servicename
396 self.type = checktype
397
398 @classmethod
399 def from_json(klass, j):
400 if not isinstance(j, dict):
401 return None
402 for key in ["hostname", "state", "output"]:
403 if key not in j or not isinstance(j[key], str):
404 return None
405 for key in ["servicename", "type"]:
406 if key in j and not isinstance(j[key], str):
407 return None
408 return klass(
409 j["hostname"],
410 j["state"],
411 j["output"],
412 j.get("servicename", ""),
413 j.get("type", "host"))
414
diff --git a/systems/monitoring-1/status_engine.nix b/systems/monitoring-1/status_engine.nix
new file mode 100644
index 0000000..fc6afc0
--- /dev/null
+++ b/systems/monitoring-1/status_engine.nix
@@ -0,0 +1,123 @@
1{ config, pkgs, lib, name, ... }:
2let
3 package = pkgs.status-engine-worker.override { config_file = config.secrets.fullPaths."status_engine"; };
4 env = config.myEnv.tools.status_engine;
5in
6{
7 config = lib.mkIf config.myServices.status.enable {
8 systemd.services.gearmand = {
9 description = "Gearman daemon";
10 after = [ "network.target" ];
11 wantedBy = [ "multi-user.target" ];
12 serviceConfig = {
13 DynamicUser = true;
14 User = "gearmand";
15 Type = "simple";
16 ExecStart = "${pkgs.gearmand}/bin/gearmand --syslog -L 127.0.0.1 -q libsqlite3 --libsqlite3-db /var/lib/gearmand/gearmand.db --store-queue-on-shutdown -l stderr -P /run/gearmand/gearmand.pid";
17 RuntimeDirectory = "gearmand";
18 StateDirectory = "gearmand";
19 };
20 };
21
22 secrets.keys."status_engine" = {
23 permissions = "0400";
24 user = "naemon";
25 group = "naemon";
26 text = ''
27 node_name: ${name}
28 use_gearman: 1
29 gearman:
30 address: 127.0.0.1
31 port: 4730
32 timeout: 1000
33 use_rabbitmq: 0
34 use_redis: 1
35 redis:
36 address: 127.0.0.1
37 port: 6379
38 db: 0
39 store_live_data_in_archive_backend: 1
40 use_mysql: 1
41 mysql:
42 host: ${env.mysql.remoteHost}
43 port: ${builtins.toString env.mysql.port}
44 username: ${env.mysql.user}
45 password: ${env.mysql.password}
46 database: ${env.mysql.database}
47 use_crate: 0
48 number_of_bulk_records: 100
49 max_bulk_delay: 5
50 number_servicestatus_worker: 1
51 number_hoststatus_worker: 1
52 number_logentry_worker: 1
53 number_statechange_worker: 1
54 number_hostcheck_worker: 1
55 number_servicecheck_worker: 1
56 number_misc_worker: 1
57
58 process_perfdata: 1
59 number_perfdata_worker: 1
60 perfdata_backend:
61 - mysql
62
63 check_for_commands: 1
64 command_check_interval: 15
65 external_command_file: /run/naemon/naemon.cmd
66 query_handler: /run/naemon/naemon.qh
67 submit_method: qh
68
69 syslog_enabled: 1
70 syslog_tag: statusengine-worker
71
72 # Archive age
73 age_hostchecks: 5
74 age_host_acknowledgements: 60
75 age_host_notifications: 60
76 age_host_statehistory: 365
77 age_host_downtimes: 60
78 age_servicechecks: 5
79 age_service_acknowledgements: 60
80 age_service_notifications: 60
81 age_service_statehistory: 365
82 age_service_downtimes: 60
83 age_logentries: 5
84 age_tasks: 1
85 age_perfdata: 90
86
87 disable_http_proxy: 1
88 '';
89 };
90
91 services.redis.servers."" = rec {
92 enable = true;
93 bind = "127.0.0.1";
94 };
95
96 services.cron = {
97 mailto = "cron@immae.eu";
98 systemCronJobs = [
99 "0 0 * * * naemon cd ${package} && ./bin/Console.php cleanup"
100 ];
101 };
102
103 environment.systemPackages = [
104 pkgs.gearmand
105 (pkgs.writeScriptBin "status-engine-worker" ''
106 #! ${pkgs.stdenv.shell}
107 cd ${package}
108 exec sudo -E -u naemon ./bin/Console.php "$@"
109 '')
110 ];
111 systemd.services.status_engine_worker = {
112 description = "Status engine worker";
113 after = [ "network.target" ];
114 wantedBy = [ "multi-user.target" ];
115 serviceConfig = {
116 Type = "simple";
117 Restart = "on-failure";
118 User = "naemon";
119 ExecStart = "${package}/bin/StatusengineWorker.php";
120 };
121 };
122 };
123}