1 ;;; GNU Guix --- Functional package management for GNU
2 ;;; Copyright © 2018 Sou Bunnbu <iyzsong@member.fsf.org>
3 ;;; Copyright © 2018, 2019 Gábor Boskovits <boskovits@gmail.com>
4 ;;; Copyright © 2018, 2019, 2020 Oleg Pykhalov <go.wigust@gmail.com>
5 ;;; Copyright © 2022 Marius Bakke <marius@gnu.org>
7 ;;; This file is part of GNU Guix.
9 ;;; GNU Guix is free software; you can redistribute it and/or modify it
10 ;;; under the terms of the GNU General Public License as published by
11 ;;; the Free Software Foundation; either version 3 of the License, or (at
12 ;;; your option) any later version.
14 ;;; GNU Guix is distributed in the hope that it will be useful, but
15 ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
16 ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 ;;; GNU General Public License for more details.
19 ;;; You should have received a copy of the GNU General Public License
20 ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
22 (define-module (gnu services monitoring)
23 #:use-module (gnu services)
24 #:use-module (gnu services configuration)
25 #:use-module (gnu services shepherd)
26 #:use-module (gnu services web)
27 #:use-module (gnu packages admin)
28 #:use-module (gnu packages monitoring)
29 #:use-module (gnu system shadow)
30 #:use-module (guix gexp)
31 #:use-module (guix packages)
32 #:use-module (guix records)
33 #:use-module (guix utils)
34 #:use-module ((guix ui) #:select (display-hint G_))
35 #:use-module (ice-9 match)
36 #:use-module (ice-9 rdelim)
37 #:use-module (srfi srfi-26)
38 #:use-module (srfi srfi-35)
39 #:export (darkstat-configuration
42 prometheus-node-exporter-configuration
43 prometheus-node-exporter-configuration?
44 prometheus-node-exporter-configuration-package
45 prometheus-node-exporter-web-listen-address
46 prometheus-node-exporter-service-type
48 zabbix-server-configuration
49 zabbix-server-service-type
50 zabbix-agent-configuration
51 zabbix-agent-service-type
52 zabbix-front-end-configuration
53 zabbix-front-end-service-type
54 %zabbix-front-end-configuration-nginx))
61 (define-record-type* <darkstat-configuration>
62 darkstat-configuration make-darkstat-configuration darkstat-configuration?
63 (package darkstat-configuration-package
65 (interface darkstat-configuration-interface)
66 (port darkstat-configuration-port
68 (bind-address darkstat-configuration-bind-address
69 (default "127.0.0.1"))
70 (base darkstat-configuration-base
73 (define %darkstat-accounts
78 (comment "darkstat daemon user")
79 (home-directory "/var/lib/darkstat")
80 (shell (file-append shadow "/sbin/nologin")))
85 (define darkstat-shepherd-service
87 (($ <darkstat-configuration>
88 package interface port bind-address base)
90 (documentation "Network statistics gatherer.")
91 (provision '(darkstat))
92 (requirement '(networking))
93 (start #~(make-forkexec-constructor
94 (list #$(file-append package "/sbin/darkstat")
99 "--syslog" "--no-daemon"
100 "--chroot" "/var/lib/darkstat"
102 "--import" "darkstat.db"
103 "--export" "darkstat.db")))
104 (stop #~(make-kill-destructor))))))
106 (define darkstat-service-type
110 "Run @command{darkstat} to serve network traffic statistics reports over
113 (list (service-extension account-service-type
114 (const %darkstat-accounts))
115 (service-extension shepherd-root-service-type
116 (compose list darkstat-shepherd-service))))))
120 ;;; Prometheus node exporter
123 (define-record-type* <prometheus-node-exporter-configuration>
124 prometheus-node-exporter-configuration
125 make-prometheus-node-exporter-configuration
126 prometheus-node-exporter-configuration?
127 (package prometheus-node-exporter-configuration-package
128 (default go-github-com-prometheus-node-exporter))
129 (web-listen-address prometheus-node-exporter-web-listen-address
131 (textfile-directory prometheus-node-exporter-textfile-directory
132 (default "/var/lib/prometheus/node-exporter"))
133 (extra-options prometheus-node-exporter-extra-options
136 (define %prometheus-node-exporter-accounts
138 (name "prometheus-node-exporter")
139 (group "prometheus-node-exporter")
141 (comment "Prometheus node exporter daemon user")
142 (home-directory "/var/empty")
143 (shell (file-append shadow "/sbin/nologin")))
145 (name "prometheus-node-exporter")
148 (define prometheus-node-exporter-shepherd-service
150 (( $ <prometheus-node-exporter-configuration>
151 package web-listen-address textfile-directory extra-options)
154 (documentation "Prometheus node exporter.")
155 (provision '(prometheus-node-exporter))
156 (requirement '(networking))
157 (start #~(make-forkexec-constructor
158 (list #$(file-append package "/bin/node_exporter")
159 "--web.listen-address" #$web-listen-address
160 #$@(if textfile-directory
161 (list "--collector.textfile.directory"
165 #:user "prometheus-node-exporter"
166 #:group "prometheus-node-exporter"
167 #:log-file "/var/log/prometheus-node-exporter.log"))
168 (stop #~(make-kill-destructor)))))))
170 (define (prometheus-node-exporter-activation config)
171 (with-imported-modules '((guix build utils))
172 #~(let ((textfile-directory
173 #$(prometheus-node-exporter-textfile-directory config)))
174 (use-modules (guix build utils))
176 (when textfile-directory
177 (let ((user (getpw "prometheus-node-exporter")))
179 (mkdir-p textfile-directory)
180 (chown textfile-directory (passwd:uid user) (passwd:gid user))
181 (chmod textfile-directory #o775))))))
183 (define prometheus-node-exporter-service-type
185 (name 'prometheus-node-exporter)
187 "Run @command{node_exporter} to serve hardware and OS metrics to
191 (service-extension account-service-type
192 (const %prometheus-node-exporter-accounts))
193 (service-extension activation-service-type
194 prometheus-node-exporter-activation)
195 (service-extension shepherd-root-service-type
196 prometheus-node-exporter-shepherd-service)))
197 (default-value (prometheus-node-exporter-configuration))))
204 (define (uglify-field-name field-name)
207 (if (member (string->symbol str) '(ca db ssl))
209 (string-capitalize str)))
210 (string-split (string-delete #\?
211 (symbol->string field-name))
214 (define (serialize-field field-name val)
215 #~(format #f "~a=~a~%" #$(uglify-field-name field-name) #$val))
217 (define (serialize-number field-name val)
218 (serialize-field field-name (number->string val)))
220 (define (serialize-list field-name val)
223 #$(serialize-field field-name (string-join val ","))))
226 (define (serialize-string field-name val)
227 (if (and (string? val) (string=? val ""))
229 (serialize-field field-name val)))
231 (define group? string?)
233 (define serialize-group
236 (define include-files? list?)
238 (define (serialize-include-files field-name val)
239 #~(string-append #$@(map (cut serialize-field 'include <>) val)))
241 (define extra-options? string?)
243 (define (serialize-extra-options field-name val)
244 #~(if (= 0 (string-length #$val)) "" #$(format #f "~a~%" val)))
246 (define (nginx-server-configuration-list? val)
247 (and (list? val) (and-map nginx-server-configuration? val)))
249 (define (serialize-nginx-server-configuration-list field-name val)
252 (define-configuration zabbix-server-configuration
254 (file-like zabbix-server)
255 "The zabbix-server package.")
258 "User who will run the Zabbix server.")
259 (group ;for zabbix-server-account procedure
261 "Group who will run the Zabbix server.")
264 "Database host name.")
273 "Database password. Please, use @code{include-files} with
274 @code{DBPassword=SECRET} inside a specified file instead.")
280 "Specifies where log messages are written to:
282 @item @code{system} - syslog.
283 @item @code{file} - file specified with @code{log-file} parameter.
284 @item @code{console} - standard output.
287 (string "/var/log/zabbix/server.log")
288 "Log file name for @code{log-type} @code{file} parameter.")
290 (string "/var/run/zabbix/zabbix_server.pid")
293 (string "/etc/ssl/certs/ca-certificates.crt")
294 "The location of certificate authority (CA) files for SSL server
295 certificate verification.")
297 (string "/etc/ssl/certs")
298 "Location of SSL client certificates.")
301 "Extra options will be appended to Zabbix server configuration file.")
304 "You may include individual files or all files in a directory in the
305 configuration file."))
307 (define (zabbix-server-account config)
308 "Return the user accounts and user groups for CONFIG."
309 (let ((zabbix-user (zabbix-server-configuration-user config))
310 (zabbix-group (zabbix-server-configuration-group config)))
311 (list (user-group (name zabbix-group) (system? #t))
316 (comment "zabbix privilege separation user")
317 (home-directory (string-append "/var/run/" zabbix-user))
318 (shell (file-append shadow "/sbin/nologin"))))))
320 (define (zabbix-server-config-file config)
321 "Return the zabbix-server configuration file corresponding to CONFIG."
325 (call-with-output-file #$output
327 (format port "# Generated by 'zabbix-server-service'.~%")
328 (format port #$(serialize-configuration
329 config zabbix-server-configuration-fields)))))))
331 (define (zabbix-server-activation config)
332 "Return the activation gexp for CONFIG."
333 (with-imported-modules '((guix build utils))
335 (use-modules (guix build utils)
337 (let ((user (getpw #$(zabbix-server-configuration-user config))))
338 (for-each (lambda (file)
339 (let ((directory (dirname file)))
341 (chown directory (passwd:uid user) (passwd:gid user))
342 (chmod directory #o755)))
343 (list #$(zabbix-server-configuration-log-file config)
344 #$(zabbix-server-configuration-pid-file config)
345 "/etc/zabbix/maintenance.inc.php"))))))
347 (define (zabbix-server-runtime-control-procedure zabbix-server config command)
348 ;; XXX: This is duplicated from mcron; factorize.
350 ;; Run 'zabbix_server' in a pipe so we can explicitly redirect its output
351 ;; to 'current-output-port', which at this stage is bound to the client
353 (let ((pipe (apply open-pipe* OPEN_READ #$zabbix-server
355 "-R" #$command args)))
357 (match (read-line pipe 'concat)
361 (zero? (close-pipe pipe)))
363 ;; There's a race with the SIGCHLD handler, which could
364 ;; call 'waitpid' before 'close-pipe' above does. If we
365 ;; get ECHILD, that means we lost the race; in that case, we
366 ;; cannot tell what the exit code was (FIXME).
367 (or (= ECHILD (system-error-errno args))
368 (apply throw args)))))
373 ;; Provide shepherd actions for common "zabbix_server -R" commands
374 ;; mainly for a convenient way to use the correct configuration file.
375 (define (zabbix-server-actions zabbix-server config)
376 (list (shepherd-action
377 (name 'reload-config-cache)
378 (documentation "Reload the configuration cache.")
379 (procedure (zabbix-server-runtime-control-procedure
380 zabbix-server config "config_cache_reload")))
382 (name 'reload-snmp-cache)
383 (documentation "Reload SNMP cache.")
384 (procedure (zabbix-server-runtime-control-procedure
385 zabbix-server config "snmp_cache_reload")))))
387 (define (zabbix-server-shepherd-service config)
388 "Return a <shepherd-service> for Zabbix server with CONFIG."
390 (file-append (zabbix-server-configuration-zabbix-server config)
391 "/sbin/zabbix_server"))
392 (config-file (zabbix-server-config-file config)))
393 (list (shepherd-service
394 (provision '(zabbix-server))
395 (requirement '(user-processes))
396 (documentation "Run the Zabbix server daemon.")
397 (actions (zabbix-server-actions zabbix-server config-file))
398 (start #~(make-forkexec-constructor
399 (list #$zabbix-server
400 "--config" #$config-file
402 #:user #$(zabbix-server-configuration-user config)
403 #:group #$(zabbix-server-configuration-group config)
404 #:pid-file #$(zabbix-server-configuration-pid-file config)
405 #:environment-variables
406 (list "SSL_CERT_DIR=/run/current-system/profile\
408 "SSL_CERT_FILE=/run/current-system/profile\
409 /etc/ssl/certs/ca-certificates.crt")))
410 (stop #~(make-kill-destructor))))))
412 (define zabbix-server-service-type
414 (name 'zabbix-server)
416 (list (service-extension shepherd-root-service-type
417 zabbix-server-shepherd-service)
418 (service-extension account-service-type
419 zabbix-server-account)
420 (service-extension activation-service-type
421 zabbix-server-activation)))
422 (default-value (zabbix-server-configuration))
423 (description "Run the Zabbix server, a high-performance monitoring system
424 that can collect data about machines from a variety of sources and provide the
425 results in a Web interface.")))
427 (define (generate-zabbix-server-documentation)
428 (generate-documentation
429 `((zabbix-server-configuration
430 ,zabbix-server-configuration-fields))
431 'zabbix-server-configuration))
433 (define-configuration zabbix-agent-configuration
435 (file-like zabbix-agentd)
436 "The zabbix-agent package.")
439 "User who will run the Zabbix agent.")
442 "Group who will run the Zabbix agent.")
445 "Unique, case sensitive hostname which is required for active checks and
446 must match hostname as configured on the server.")
449 "Specifies where log messages are written to:
451 @item @code{system} - syslog.
452 @item @code{file} - file specified with @code{log-file} parameter.
453 @item @code{console} - standard output.
456 (string "/var/log/zabbix/agent.log")
457 "Log file name for @code{log-type} @code{file} parameter.")
459 (string "/var/run/zabbix/zabbix_agent.pid")
462 (list '("127.0.0.1"))
463 "List of IP addresses, optionally in CIDR notation, or hostnames of Zabbix
464 servers and Zabbix proxies. Incoming connections will be accepted only from
465 the hosts listed here.")
467 (list '("127.0.0.1"))
468 "List of IP:port (or hostname:port) pairs of Zabbix servers and Zabbix
469 proxies for active checks. If port is not specified, default port is used.
470 If this parameter is not specified, active checks are disabled.")
473 "Extra options will be appended to Zabbix server configuration file.")
476 "You may include individual files or all files in a directory in the
477 configuration file."))
479 (define (zabbix-agent-account config)
480 "Return the user accounts and user groups for CONFIG."
481 (let ((zabbix-user (zabbix-agent-configuration-user config))
482 (zabbix-group (zabbix-agent-configuration-group config)))
483 (list (user-group (name zabbix-group) (system? #t))
488 (comment "zabbix privilege separation user")
489 (home-directory (string-append "/var/run/" zabbix-user))
490 (shell (file-append shadow "/sbin/nologin"))))))
492 (define (zabbix-agent-activation config)
493 "Return the activation gexp for CONFIG."
494 (with-imported-modules '((guix build utils))
496 (use-modules (guix build utils)
499 (getpw #$(zabbix-agent-configuration-user config))))
500 (for-each (lambda (file)
501 (let ((directory (dirname file)))
503 (chown directory (passwd:uid user) (passwd:gid user))
504 (chmod directory #o755)))
505 (list #$(zabbix-agent-configuration-log-file config)
506 #$(zabbix-agent-configuration-pid-file config)))))))
508 (define (zabbix-agent-config-file config)
509 "Return the zabbix-agent configuration file corresponding to CONFIG."
513 (call-with-output-file #$output
515 (format port "# Generated by 'zabbix-agent-service'.~%")
516 (format port #$(serialize-configuration
517 config zabbix-agent-configuration-fields)))))))
519 (define (zabbix-agent-shepherd-service config)
520 "Return a <shepherd-service> for Zabbix agent with CONFIG."
521 (list (shepherd-service
522 (provision '(zabbix-agent))
523 (requirement '(user-processes))
524 (documentation "Run Zabbix agent daemon.")
525 (start #~(make-forkexec-constructor
526 (list #$(file-append (zabbix-agent-configuration-zabbix-agent config)
527 "/sbin/zabbix_agentd")
528 "--config" #$(zabbix-agent-config-file config)
530 #:user #$(zabbix-agent-configuration-user config)
531 #:group #$(zabbix-agent-configuration-group config)
532 #:pid-file #$(zabbix-agent-configuration-pid-file config)
533 #:environment-variables
534 (list "SSL_CERT_DIR=/run/current-system/profile\
536 "SSL_CERT_FILE=/run/current-system/profile\
537 /etc/ssl/certs/ca-certificates.crt"
538 "PATH=/run/setuid-programs:\
539 /run/current-system/profile/bin:/run/current-system/profile/sbin")))
540 (stop #~(make-kill-destructor)))))
542 (define zabbix-agent-service-type
546 (list (service-extension shepherd-root-service-type
547 zabbix-agent-shepherd-service)
548 (service-extension account-service-type
549 zabbix-agent-account)
550 (service-extension activation-service-type
551 zabbix-agent-activation)))
552 (default-value (zabbix-agent-configuration))
553 (description "Run the Zabbix agent, @command{zabbix_agentd}, which gathers
554 information about the running system for the Zabbix monitoring server.")))
556 (define (generate-zabbix-agent-documentation)
557 (generate-documentation
558 `((zabbix-agent-configuration
559 ,zabbix-agent-configuration-fields))
560 'zabbix-agent-configuration))
562 (define %zabbix-front-end-configuration-nginx
563 (nginx-server-configuration
564 (root #~(string-append #$zabbix-server:front-end "/share/zabbix/php"))
565 (index '("index.php"))
567 (let ((php-location (nginx-php-location)))
568 (list (nginx-location-configuration
569 (inherit php-location)
570 (body (append (nginx-location-configuration-body php-location)
572 fastcgi_param PHP_VALUE \"post_max_size = 16M
573 max_execution_time = 300\";
577 (define (zabbix-front-end-nginx-extension config)
579 (($ <zabbix-front-end-configuration> _ server nginx)
582 (nginx-server-configuration
583 (inherit %zabbix-front-end-configuration-nginx)
584 (root #~(string-append #$server:front-end "/share/zabbix/php"))))
587 (define-configuration zabbix-front-end-configuration
589 (file-like zabbix-server)
590 "The Zabbix server package to use.")
593 "List of @ref{nginx-server-configuration,@code{nginx-server-configuration}}
594 blocks for the Zabbix front-end. When empty, a default that listens on port 80
598 "Database host name.")
610 "Database password. Please, use @code{db-secret-file} instead.")
613 "Secret file which will be appended to @file{zabbix.conf.php} file. This
614 file contains credentials for use by Zabbix front-end. You are expected to
615 create it manually.")
618 "Zabbix server hostname.")
621 "Zabbix server port."))
623 (define (zabbix-front-end-config config)
624 (match-record config <zabbix-front-end-configuration>
625 (%location db-host db-port db-name db-user db-password db-secret-file
626 zabbix-host zabbix-port)
627 (mixed-text-file "zabbix.conf.php"
630 // Zabbix GUI configuration file.
633 $DB['TYPE'] = 'POSTGRESQL';
634 $DB['SERVER'] = '" db-host "';
635 $DB['PORT'] = '" (number->string db-port) "';
636 $DB['DATABASE'] = '" db-name "';
637 $DB['USER'] = '" db-user "';
638 $DB['PASSWORD'] = " (let ((file (location-file %location))
639 (line (location-line %location))
640 (column (location-column %location)))
641 (if (string-null? db-password)
642 (if (string-null? db-secret-file)
643 (raise (make-compound-condition
647 (format #f "no '~A' or '~A' field in your '~A' record"
648 'db-secret-file 'db-password
649 'zabbix-front-end-configuration))))
652 (location %location)))))
653 (string-append "trim(file_get_contents('"
654 db-secret-file "'));\n"))
656 (display-hint (format #f (G_ "~a:~a:~a: ~a:
657 Consider using @code{db-secret-file} instead of @code{db-password} for better
658 security.") file line column 'zabbix-front-end-configuration))
659 (format #f "'~a';~%" db-password))))
661 // Schema name. Used for IBM DB2 and PostgreSQL.
664 // Use IEEE754 compatible value range for 64-bit Numeric (float) history values.
665 $DB['DOUBLE_IEEE754'] = true;
667 $ZBX_SERVER = '" zabbix-host "';
668 $ZBX_SERVER_PORT = '" (number->string zabbix-port) "';
669 $ZBX_SERVER_NAME = '';
671 $IMAGE_FORMAT_DEFAULT = IMAGE_FORMAT_PNG;
674 (define %maintenance.inc.php
675 ;; Empty php file to allow us move zabbix-frontend configs to ‘/etc/zabbix’
676 ;; directory. See ‘install-front-end’ phase in
677 ;; (@ (gnu packages monitoring) zabbix-server) package.
682 (define (zabbix-front-end-activation config)
683 "Return the activation gexp for CONFIG."
685 (use-modules (guix build utils))
686 (mkdir-p "/etc/zabbix")
687 (call-with-output-file "/etc/zabbix/maintenance.inc.php"
689 (display #$%maintenance.inc.php port)))
690 (copy-file #$(zabbix-front-end-config config)
691 "/etc/zabbix/zabbix.conf.php")))
693 (define zabbix-front-end-service-type
695 (name 'zabbix-front-end)
697 (list (service-extension activation-service-type
698 zabbix-front-end-activation)
699 (service-extension nginx-service-type
700 zabbix-front-end-nginx-extension)
701 ;; Make sure php-fpm is instantiated.
702 (service-extension php-fpm-service-type
704 (default-value (zabbix-front-end-configuration))
706 "Run the zabbix-front-end web interface, which allows users to interact
707 with Zabbix server.")))
709 (define (generate-zabbix-front-end-documentation)
710 (generate-documentation
711 `((zabbix-front-end-configuration
712 ,zabbix-front-end-configuration-fields))
713 'zabbix-front-end-configuration))