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>
6 ;;; This file is part of GNU Guix.
8 ;;; GNU Guix is free software; you can redistribute it and/or modify it
9 ;;; under the terms of the GNU General Public License as published by
10 ;;; the Free Software Foundation; either version 3 of the License, or (at
11 ;;; your option) any later version.
13 ;;; GNU Guix is distributed in the hope that it will be useful, but
14 ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
15 ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 ;;; GNU General Public License for more details.
18 ;;; You should have received a copy of the GNU General Public License
19 ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
21 (define-module (gnu services monitoring)
22 #:use-module (gnu services)
23 #:use-module (gnu services configuration)
24 #:use-module (gnu services shepherd)
25 #:use-module (gnu services web)
26 #:use-module (gnu packages admin)
27 #:use-module (gnu packages monitoring)
28 #:use-module (gnu system shadow)
29 #:use-module (guix gexp)
30 #:use-module (guix packages)
31 #:use-module (guix records)
32 #:use-module (guix utils)
33 #:use-module ((guix ui) #:select (display-hint G_))
34 #:use-module (ice-9 match)
35 #:use-module (ice-9 rdelim)
36 #:use-module (srfi srfi-26)
37 #:use-module (srfi srfi-35)
38 #:export (darkstat-configuration
41 prometheus-node-exporter-configuration
42 prometheus-node-exporter-configuration?
43 prometheus-node-exporter-configuration-package
44 prometheus-node-exporter-web-listen-address
45 prometheus-node-exporter-service-type
47 zabbix-server-configuration
48 zabbix-server-service-type
49 zabbix-agent-configuration
50 zabbix-agent-service-type
51 zabbix-front-end-configuration
52 zabbix-front-end-service-type
53 %zabbix-front-end-configuration-nginx))
60 (define-record-type* <darkstat-configuration>
61 darkstat-configuration make-darkstat-configuration darkstat-configuration?
62 (package darkstat-configuration-package
64 (interface darkstat-configuration-interface)
65 (port darkstat-configuration-port
67 (bind-address darkstat-configuration-bind-address
68 (default "127.0.0.1"))
69 (base darkstat-configuration-base
72 (define %darkstat-accounts
77 (comment "darkstat daemon user")
78 (home-directory "/var/lib/darkstat")
79 (shell (file-append shadow "/sbin/nologin")))
84 (define darkstat-shepherd-service
86 (($ <darkstat-configuration>
87 package interface port bind-address base)
89 (documentation "Network statistics gatherer.")
90 (provision '(darkstat))
91 (requirement '(networking))
92 (start #~(make-forkexec-constructor
93 (list #$(file-append package "/sbin/darkstat")
98 "--syslog" "--no-daemon"
99 "--chroot" "/var/lib/darkstat"
101 "--import" "darkstat.db"
102 "--export" "darkstat.db")))
103 (stop #~(make-kill-destructor))))))
105 (define darkstat-service-type
109 "Run @command{darkstat} to serve network traffic statistics reports over
112 (list (service-extension account-service-type
113 (const %darkstat-accounts))
114 (service-extension shepherd-root-service-type
115 (compose list darkstat-shepherd-service))))))
119 ;;; Prometheus node exporter
122 (define-record-type* <prometheus-node-exporter-configuration>
123 prometheus-node-exporter-configuration
124 make-prometheus-node-exporter-configuration
125 prometheus-node-exporter-configuration?
126 (package prometheus-node-exporter-configuration-package
127 (default go-github-com-prometheus-node-exporter))
128 (web-listen-address prometheus-node-exporter-web-listen-address
130 (textfile-directory prometheus-node-exporter-textfile-directory
131 (default "/var/lib/prometheus/node-exporter"))
132 (extra-options prometheus-node-exporter-extra-options
135 (define %prometheus-node-exporter-accounts
137 (name "prometheus-node-exporter")
138 (group "prometheus-node-exporter")
140 (comment "Prometheus node exporter daemon user")
141 (home-directory "/var/empty")
142 (shell (file-append shadow "/sbin/nologin")))
144 (name "prometheus-node-exporter")
147 (define prometheus-node-exporter-shepherd-service
149 (( $ <prometheus-node-exporter-configuration>
150 package web-listen-address textfile-directory extra-options)
153 (documentation "Prometheus node exporter.")
154 (provision '(prometheus-node-exporter))
155 (requirement '(networking))
156 (start #~(make-forkexec-constructor
157 (list #$(file-append package "/bin/node_exporter")
158 "--web.listen-address" #$web-listen-address
159 #$@(if textfile-directory
160 (list "--collector.textfile.directory"
164 #:user "prometheus-node-exporter"
165 #:group "prometheus-node-exporter"
166 #:log-file "/var/log/prometheus-node-exporter.log"))
167 (stop #~(make-kill-destructor)))))))
169 (define (prometheus-node-exporter-activation config)
170 (with-imported-modules '((guix build utils))
171 #~(let ((textfile-directory
172 #$(prometheus-node-exporter-textfile-directory config)))
173 (use-modules (guix build utils))
175 (when textfile-directory
176 (let ((user (getpw "prometheus-node-exporter")))
178 (mkdir-p textfile-directory)
179 (chown textfile-directory (passwd:uid user) (passwd:gid user))
180 (chmod textfile-directory #o775))))))
182 (define prometheus-node-exporter-service-type
184 (name 'prometheus-node-exporter)
186 "Run @command{node_exporter} to serve hardware and OS metrics to
190 (service-extension account-service-type
191 (const %prometheus-node-exporter-accounts))
192 (service-extension activation-service-type
193 prometheus-node-exporter-activation)
194 (service-extension shepherd-root-service-type
195 prometheus-node-exporter-shepherd-service)))
196 (default-value (prometheus-node-exporter-configuration))))
203 (define (uglify-field-name field-name)
206 (if (member (string->symbol str) '(ca db ssl))
208 (string-capitalize str)))
209 (string-split (string-delete #\?
210 (symbol->string field-name))
213 (define (serialize-field field-name val)
214 (format #t "~a=~a~%" (uglify-field-name field-name) val))
216 (define (serialize-number field-name val)
217 (serialize-field field-name (number->string val)))
219 (define (serialize-list field-name val)
220 (if (null? val) "" (serialize-field field-name (string-join val ","))))
222 (define (serialize-string field-name val)
223 (if (and (string? val) (string=? val ""))
225 (serialize-field field-name val)))
227 (define group? string?)
229 (define serialize-group
232 (define include-files? list?)
234 (define (serialize-include-files field-name val)
235 (if (null? val) "" (for-each (cut serialize-field 'include <>) val)))
237 (define extra-options? string?)
239 (define (serialize-extra-options field-name val)
240 (if (null? val) "" (display val)))
242 (define (nginx-server-configuration-list? val)
243 (and (list? val) (and-map nginx-server-configuration? val)))
245 (define (serialize-nginx-server-configuration-list field-name val)
248 (define-configuration zabbix-server-configuration
250 (package zabbix-server)
251 "The zabbix-server package.")
254 "User who will run the Zabbix server.")
255 (group ;for zabbix-server-account procedure
257 "Group who will run the Zabbix server.")
260 "Database host name.")
269 "Database password. Please, use @code{include-files} with
270 @code{DBPassword=SECRET} inside a specified file instead.")
276 "Specifies where log messages are written to:
278 @item @code{system} - syslog.
279 @item @code{file} - file specified with @code{log-file} parameter.
280 @item @code{console} - standard output.
283 (string "/var/log/zabbix/server.log")
284 "Log file name for @code{log-type} @code{file} parameter.")
286 (string "/var/run/zabbix/zabbix_server.pid")
289 (string "/etc/ssl/certs/ca-certificates.crt")
290 "The location of certificate authority (CA) files for SSL server
291 certificate verification.")
293 (string "/etc/ssl/certs")
294 "Location of SSL client certificates.")
297 "Extra options will be appended to Zabbix server configuration file.")
300 "You may include individual files or all files in a directory in the
301 configuration file."))
303 (define (zabbix-server-account config)
304 "Return the user accounts and user groups for CONFIG."
305 (let ((zabbix-user (zabbix-server-configuration-user config))
306 (zabbix-group (zabbix-server-configuration-group config)))
307 (list (user-group (name zabbix-group) (system? #t))
312 (comment "zabbix privilege separation user")
313 (home-directory (string-append "/var/run/" zabbix-user))
314 (shell (file-append shadow "/sbin/nologin"))))))
316 (define (zabbix-server-config-file config)
317 "Return the zabbix-server configuration file corresponding to CONFIG."
321 (call-with-output-file #$output
323 (display "# Generated by 'zabbix-server-service'.\n" port)
324 (display #$(with-output-to-string
326 (serialize-configuration
327 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)
338 (let ((user (getpw #$(zabbix-server-configuration-user config))))
339 (for-each (lambda (file)
340 (let ((directory (dirname file)))
342 (chown directory (passwd:uid user) (passwd:gid user))
343 (chmod directory #o755)))
344 (list #$(zabbix-server-configuration-log-file config)
345 #$(zabbix-server-configuration-pid-file config)
346 "/etc/zabbix/maintenance.inc.php"))))))
348 (define (zabbix-server-shepherd-service config)
349 "Return a <shepherd-service> for Zabbix server with CONFIG."
350 (list (shepherd-service
351 (provision '(zabbix-server))
352 (documentation "Run Zabbix server daemon.")
353 (start #~(make-forkexec-constructor
354 (list #$(file-append (zabbix-server-configuration-zabbix-server config)
355 "/sbin/zabbix_server")
356 "--config" #$(zabbix-server-config-file config)
358 #:user #$(zabbix-server-configuration-user config)
359 #:group #$(zabbix-server-configuration-group config)
360 #:pid-file #$(zabbix-server-configuration-pid-file config)
361 #:environment-variables
362 (list "SSL_CERT_DIR=/run/current-system/profile\
364 "SSL_CERT_FILE=/run/current-system/profile\
365 /etc/ssl/certs/ca-certificates.crt")))
366 (stop #~(make-kill-destructor)))))
368 (define zabbix-server-service-type
370 (name 'zabbix-server)
372 (list (service-extension shepherd-root-service-type
373 zabbix-server-shepherd-service)
374 (service-extension account-service-type
375 zabbix-server-account)
376 (service-extension activation-service-type
377 zabbix-server-activation)))
378 (default-value (zabbix-server-configuration))))
380 (define (generate-zabbix-server-documentation)
381 (generate-documentation
382 `((zabbix-server-configuration
383 ,zabbix-server-configuration-fields))
384 'zabbix-server-configuration))
386 (define-configuration zabbix-agent-configuration
388 (package zabbix-agentd)
389 "The zabbix-agent package.")
392 "User who will run the Zabbix agent.")
395 "Group who will run the Zabbix agent.")
398 "Unique, case sensitive hostname which is required for active checks and
399 must match hostname as configured on the server.")
402 "Specifies where log messages are written to:
404 @item @code{system} - syslog.
405 @item @code{file} - file specified with @code{log-file} parameter.
406 @item @code{console} - standard output.
409 (string "/var/log/zabbix/agent.log")
410 "Log file name for @code{log-type} @code{file} parameter.")
412 (string "/var/run/zabbix/zabbix_agent.pid")
415 (list '("127.0.0.1"))
416 "List of IP addresses, optionally in CIDR notation, or hostnames of Zabbix
417 servers and Zabbix proxies. Incoming connections will be accepted only from
418 the hosts listed here.")
420 (list '("127.0.0.1"))
421 "List of IP:port (or hostname:port) pairs of Zabbix servers and Zabbix
422 proxies for active checks. If port is not specified, default port is used.
423 If this parameter is not specified, active checks are disabled.")
426 "Extra options will be appended to Zabbix server configuration file.")
429 "You may include individual files or all files in a directory in the
430 configuration file."))
432 (define (zabbix-agent-account config)
433 "Return the user accounts and user groups for CONFIG."
434 (let ((zabbix-user "zabbix")
435 (zabbix-group "zabbix"))
436 (list (user-group (name zabbix-group) (system? #t))
441 (comment "zabbix privilege separation user")
442 (home-directory (string-append "/var/run/" zabbix-user))
443 (shell (file-append shadow "/sbin/nologin"))))))
445 (define (zabbix-agent-activation config)
446 "Return the activation gexp for CONFIG."
447 (with-imported-modules '((guix build utils))
449 (use-modules (guix build utils)
452 (getpw #$(zabbix-agent-configuration-user config))))
453 (for-each (lambda (file)
454 (let ((directory (dirname file)))
456 (chown directory (passwd:uid user) (passwd:gid user))
457 (chmod directory #o755)))
458 (list #$(zabbix-agent-configuration-log-file config)
459 #$(zabbix-agent-configuration-pid-file config)))))))
461 (define (zabbix-agent-config-file config)
462 "Return the zabbix-agent configuration file corresponding to CONFIG."
466 (call-with-output-file #$output
468 (display "# Generated by 'zabbix-agent-service'.\n" port)
469 (display #$(with-output-to-string
471 (serialize-configuration
472 config zabbix-agent-configuration-fields)))
476 (define (zabbix-agent-shepherd-service config)
477 "Return a <shepherd-service> for Zabbix agent with CONFIG."
478 (list (shepherd-service
479 (provision '(zabbix-agent))
480 (documentation "Run Zabbix agent daemon.")
481 (start #~(make-forkexec-constructor
482 (list #$(file-append (zabbix-agent-configuration-zabbix-agent config)
483 "/sbin/zabbix_agentd")
484 "--config" #$(zabbix-agent-config-file config)
486 #:user #$(zabbix-agent-configuration-user config)
487 #:group #$(zabbix-agent-configuration-group config)
488 #:pid-file #$(zabbix-agent-configuration-pid-file config)
489 #:environment-variables
490 (list "SSL_CERT_DIR=/run/current-system/profile\
492 "SSL_CERT_FILE=/run/current-system/profile\
493 /etc/ssl/certs/ca-certificates.crt")))
494 (stop #~(make-kill-destructor)))))
496 (define zabbix-agent-service-type
500 (list (service-extension shepherd-root-service-type
501 zabbix-agent-shepherd-service)
502 (service-extension account-service-type
503 zabbix-agent-account)
504 (service-extension activation-service-type
505 zabbix-agent-activation)))
506 (default-value (zabbix-agent-configuration))))
508 (define (generate-zabbix-agent-documentation)
509 (generate-documentation
510 `((zabbix-agent-configuration
511 ,zabbix-agent-configuration-fields))
512 'zabbix-agent-configuration))
514 (define %zabbix-front-end-configuration-nginx
515 (nginx-server-configuration
516 (root #~(string-append #$zabbix-server:front-end "/share/zabbix/php"))
517 (index '("index.php"))
519 (let ((php-location (nginx-php-location)))
520 (list (nginx-location-configuration
521 (inherit php-location)
522 (body (append (nginx-location-configuration-body php-location)
524 fastcgi_param PHP_VALUE \"post_max_size = 16M
525 max_execution_time = 300\";
529 (define-configuration zabbix-front-end-configuration
530 ;; TODO: Specify zabbix front-end package.
532 ;; (package zabbix-front-end)
533 ;; "The zabbix-front-end package.")
535 (nginx-server-configuration-list
536 (list %zabbix-front-end-configuration-nginx))
537 "NGINX configuration.")
540 "Database host name.")
552 "Database password. Please, use @code{db-secret-file} instead.")
555 "Secret file which will be appended to @file{zabbix.conf.php} file. This
556 file contains credentials for use by Zabbix front-end. You are expected to
557 create it manually.")
560 "Zabbix server hostname.")
563 "Zabbix server port."))
565 (define (zabbix-front-end-config config)
566 (match-record config <zabbix-front-end-configuration>
567 (%location db-host db-port db-name db-user db-password db-secret-file
568 zabbix-host zabbix-port)
569 (mixed-text-file "zabbix.conf.php"
572 // Zabbix GUI configuration file.
575 $DB['TYPE'] = 'POSTGRESQL';
576 $DB['SERVER'] = '" db-host "';
577 $DB['PORT'] = '" (number->string db-port) "';
578 $DB['DATABASE'] = '" db-name "';
579 $DB['USER'] = '" db-user "';
580 $DB['PASSWORD'] = '" (let ((file (location-file %location))
581 (line (location-line %location))
582 (column (location-column %location)))
583 (if (string-null? db-password)
584 (if (string-null? db-secret-file)
585 (raise (make-compound-condition
589 (format #f "no '~A' or '~A' field in your '~A' record"
590 'db-secret-file 'db-password
591 'zabbix-front-end-configuration))))
594 (location %location)))))
596 (with-input-from-file db-secret-file
599 (display-hint (format #f (G_ "~a:~a:~a: ~a:
600 Consider using @code{db-secret-file} instead of @code{db-password} for better
601 security.") file line column 'zabbix-front-end-configuration))
604 // Schema name. Used for IBM DB2 and PostgreSQL.
607 $ZBX_SERVER = '" zabbix-host "';
608 $ZBX_SERVER_PORT = '" (number->string zabbix-port) "';
609 $ZBX_SERVER_NAME = '';
611 $IMAGE_FORMAT_DEFAULT = IMAGE_FORMAT_PNG;
614 (define %maintenance.inc.php
615 ;; Empty php file to allow us move zabbix-frontend configs to ‘/etc/zabbix’
616 ;; directory. See ‘install-front-end’ phase in
617 ;; (@ (gnu packages monitoring) zabbix-server) package.
622 (define (zabbix-front-end-activation config)
623 "Return the activation gexp for CONFIG."
625 (use-modules (guix build utils))
626 (mkdir-p "/etc/zabbix")
627 (call-with-output-file "/etc/zabbix/maintenance.inc.php"
629 (display #$%maintenance.inc.php port)))
630 (copy-file #$(zabbix-front-end-config config)
631 "/etc/zabbix/zabbix.conf.php")))
633 (define zabbix-front-end-service-type
635 (name 'zabbix-front-end)
637 (list (service-extension activation-service-type
638 zabbix-front-end-activation)
639 (service-extension nginx-service-type
640 zabbix-front-end-configuration-nginx)
641 ;; Make sure php-fpm is instantiated.
642 (service-extension php-fpm-service-type
644 (default-value (zabbix-front-end-configuration))
646 "Run the zabbix-front-end web interface, which allows users to interact
647 with Zabbix server.")))
649 (define (generate-zabbix-front-end-documentation)
650 (generate-documentation
651 `((zabbix-front-end-configuration
652 ,zabbix-front-end-configuration-fields))
653 'zabbix-front-end-configuration))