services: Use 'file-append' for user account shells.
[jackhill/guix/guix.git] / gnu / services / monitoring.scm
1 ;;; GNU Guix --- Functional package management for GNU
2 ;;; Copyright © 2018 Sou Bunnbu <iyzsong@member.fsf.org>
3 ;;; Copyright © 2018 Gábor Boskovits <boskovits@gmail.com>
4 ;;; Copyright © 2018 Oleg Pykhalov <go.wigust@gmail.com>
5 ;;;
6 ;;; This file is part of GNU Guix.
7 ;;;
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.
12 ;;;
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.
17 ;;;
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/>.
20
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 ui) #:select (display-hint))
33 #:use-module (ice-9 match)
34 #:use-module (ice-9 rdelim)
35 #:use-module (srfi srfi-26)
36 #:use-module (srfi srfi-35)
37 #:export (darkstat-configuration
38 prometheus-node-exporter-configuration
39 darkstat-service-type
40 prometheus-node-exporter-service-type
41
42 zabbix-server-configuration
43 zabbix-server-service-type
44 zabbix-agent-configuration
45 zabbix-agent-service-type
46 zabbix-front-end-configuration
47 zabbix-front-end-service-type
48 %zabbix-front-end-configuration-nginx))
49
50 \f
51 ;;;
52 ;;; darkstat
53 ;;;
54
55 (define-record-type* <darkstat-configuration>
56 darkstat-configuration make-darkstat-configuration darkstat-configuration?
57 (package darkstat-configuration-package
58 (default darkstat))
59 (interface darkstat-configuration-interface)
60 (port darkstat-configuration-port
61 (default "667"))
62 (bind-address darkstat-configuration-bind-address
63 (default "127.0.0.1"))
64 (base darkstat-configuration-base
65 (default "/")))
66
67 (define %darkstat-accounts
68 (list (user-account
69 (name "darkstat")
70 (group "darkstat")
71 (system? #t)
72 (comment "darkstat daemon user")
73 (home-directory "/var/lib/darkstat")
74 (shell (file-append shadow "/sbin/nologin")))
75 (user-group
76 (name "darkstat")
77 (system? #t))))
78
79 (define darkstat-shepherd-service
80 (match-lambda
81 (($ <darkstat-configuration>
82 package interface port bind-address base)
83 (shepherd-service
84 (documentation "Network statistics gatherer.")
85 (provision '(darkstat))
86 (requirement '(networking))
87 (start #~(make-forkexec-constructor
88 (list #$(file-append package "/sbin/darkstat")
89 "-i" #$interface
90 "-p" #$port
91 "-b" #$bind-address
92 "--base" #$base
93 "--syslog" "--no-daemon"
94 "--chroot" "/var/lib/darkstat"
95 "--user" "darkstat"
96 "--import" "darkstat.db"
97 "--export" "darkstat.db")))
98 (stop #~(make-kill-destructor))))))
99
100 (define darkstat-service-type
101 (service-type
102 (name 'darkstat)
103 (description
104 "Run @command{darkstat} to serve network traffic statictics reports over
105 HTTP.")
106 (extensions
107 (list (service-extension account-service-type
108 (const %darkstat-accounts))
109 (service-extension shepherd-root-service-type
110 (compose list darkstat-shepherd-service))))))
111
112 (define-record-type* <prometheus-node-exporter-configuration>
113 prometheus-node-exporter-configuration
114 make-prometheus-node-exporter-configuration
115 prometheus-node-exporter-configuration?
116 (package prometheus-node-exporter-configuration-package
117 (default go-github-com-prometheus-node-exporter))
118 (web-listen-address prometheus-node-exporter-web-listen-address
119 (default ":9100")))
120
121 (define prometheus-node-exporter-shepherd-service
122 (match-lambda
123 (( $ <prometheus-node-exporter-configuration>
124 package web-listen-address)
125 (shepherd-service
126 (documentation "Prometheus node exporter.")
127 (provision '(prometheus-node-exporter))
128 (requirement '(networking))
129 (start #~(make-forkexec-constructor
130 (list #$(file-append package "/bin/node_exporter")
131 "--web.listen-address" #$web-listen-address)))
132 (stop #~(make-kill-destructor))))))
133
134 (define prometheus-node-exporter-service-type
135 (service-type
136 (name 'prometheus-node-exporter)
137 (description
138 "Run @command{node_exporter} to serve hardware and OS metrics to
139 prometheus.")
140 (extensions
141 (list (service-extension
142 shepherd-root-service-type
143 (compose list prometheus-node-exporter-shepherd-service))))))
144
145 \f
146 ;;;
147 ;;; Zabbix server
148 ;;;
149
150 (define (uglify-field-name field-name)
151 (apply string-append
152 (map (lambda (str)
153 (if (member (string->symbol str) '(ca db ssl))
154 (string-upcase str)
155 (string-capitalize str)))
156 (string-split (string-delete #\?
157 (symbol->string field-name))
158 #\-))))
159
160 (define (serialize-field field-name val)
161 (format #t "~a=~a~%" (uglify-field-name field-name) val))
162
163 (define (serialize-number field-name val)
164 (serialize-field field-name (number->string val)))
165
166 (define (serialize-list field-name val)
167 (if (null? val) "" (serialize-field field-name (string-join val ","))))
168
169 (define (serialize-string field-name val)
170 (if (and (string? val) (string=? val ""))
171 ""
172 (serialize-field field-name val)))
173
174 (define group? string?)
175
176 (define serialize-group
177 (const ""))
178
179 (define include-files? list?)
180
181 (define (serialize-include-files field-name val)
182 (if (null? val) "" (for-each (cut serialize-field 'include <>) val)))
183
184 (define extra-options? string?)
185
186 (define (serialize-extra-options field-name val)
187 (if (null? val) "" (display val)))
188
189 (define (nginx-server-configuration-list? val)
190 (and (list? val) (and-map nginx-server-configuration? val)))
191
192 (define (serialize-nginx-server-configuration-list field-name val)
193 "")
194
195 (define-configuration zabbix-server-configuration
196 (zabbix-server
197 (package zabbix-server)
198 "The zabbix-server package.")
199 (user
200 (string "zabbix")
201 "User who will run the Zabbix server.")
202 (group ;for zabbix-server-account procedure
203 (group "zabbix")
204 "Group who will run the Zabbix server.")
205 (db-host
206 (string "127.0.0.1")
207 "Database host name.")
208 (db-name
209 (string "zabbix")
210 "Database name.")
211 (db-user
212 (string "zabbix")
213 "Database user.")
214 (db-password
215 (string "")
216 "Database password. Please, use @code{include-files} with
217 @code{DBPassword=SECRET} inside a specified file instead.")
218 (db-port
219 (number 5432)
220 "Database port.")
221 (log-type
222 (string "")
223 "Specifies where log messages are written to:
224 @itemize
225 @item @code{system} - syslog.
226 @item @code{file} - file specified with @code{log-file} parameter.
227 @item @code{console} - standard output.
228 @end itemize\n")
229 (log-file
230 (string "/var/log/zabbix/server.log")
231 "Log file name for @code{log-type} @code{file} parameter.")
232 (pid-file
233 (string "/var/run/zabbix/zabbix_server.pid")
234 "Name of PID file.")
235 (ssl-ca-location
236 (string "/etc/ssl/certs/ca-certificates.crt")
237 "The location of certificate authority (CA) files for SSL server
238 certificate verification.")
239 (ssl-cert-location
240 (string "/etc/ssl/certs")
241 "Location of SSL client certificates.")
242 (extra-options
243 (extra-options "")
244 "Extra options will be appended to Zabbix server configuration file.")
245 (include-files
246 (include-files '())
247 "You may include individual files or all files in a directory in the
248 configuration file."))
249
250 (define (zabbix-server-account config)
251 "Return the user accounts and user groups for CONFIG."
252 (let ((zabbix-user (zabbix-server-configuration-user config))
253 (zabbix-group (zabbix-server-configuration-group config)))
254 (list (user-group (name zabbix-group) (system? #t))
255 (user-account
256 (name zabbix-user)
257 (system? #t)
258 (group zabbix-group)
259 (comment "zabbix privilege separation user")
260 (home-directory (string-append "/var/run/" zabbix-user))
261 (shell (file-append shadow "/sbin/nologin"))))))
262
263 (define (zabbix-server-config-file config)
264 "Return the zabbix-server configuration file corresponding to CONFIG."
265 (computed-file
266 "zabbix_server.conf"
267 #~(begin
268 (call-with-output-file #$output
269 (lambda (port)
270 (display "# Generated by 'zabbix-server-service'.\n" port)
271 (display #$(with-output-to-string
272 (lambda ()
273 (serialize-configuration
274 config zabbix-server-configuration-fields)))
275 port)
276 #t)))))
277
278 (define (zabbix-server-activation config)
279 "Return the activation gexp for CONFIG."
280 (with-imported-modules '((guix build utils)
281 (ice-9 rdelim))
282 #~(begin
283 (use-modules (guix build utils)
284 (ice-9 rdelim))
285 (let ((user (getpw #$(zabbix-server-configuration-user config))))
286 (for-each (lambda (file)
287 (let ((directory (dirname file)))
288 (mkdir-p directory)
289 (chown directory (passwd:uid user) (passwd:gid user))
290 (chmod directory #o755)))
291 (list #$(zabbix-server-configuration-log-file config)
292 #$(zabbix-server-configuration-pid-file config)
293 "/etc/zabbix/maintenance.inc.php"))))))
294
295 (define (zabbix-server-shepherd-service config)
296 "Return a <shepherd-service> for Zabbix server with CONFIG."
297 (list (shepherd-service
298 (provision '(zabbix-server))
299 (documentation "Run Zabbix server daemon.")
300 (start #~(make-forkexec-constructor
301 (list #$(file-append (zabbix-server-configuration-zabbix-server config)
302 "/sbin/zabbix_server")
303 "--config" #$(zabbix-server-config-file config)
304 "--foreground")
305 #:user #$(zabbix-server-configuration-user config)
306 #:group #$(zabbix-server-configuration-group config)
307 #:pid-file #$(zabbix-server-configuration-pid-file config)
308 #:environment-variables
309 (list "SSL_CERT_DIR=/run/current-system/profile\
310 /etc/ssl/certs"
311 "SSL_CERT_FILE=/run/current-system/profile\
312 /etc/ssl/certs/ca-certificates.crt")))
313 (stop #~(make-kill-destructor)))))
314
315 (define zabbix-server-service-type
316 (service-type
317 (name 'zabbix-server)
318 (extensions
319 (list (service-extension shepherd-root-service-type
320 zabbix-server-shepherd-service)
321 (service-extension account-service-type
322 zabbix-server-account)
323 (service-extension activation-service-type
324 zabbix-server-activation)))
325 (default-value (zabbix-server-configuration))))
326
327 (define (generate-zabbix-server-documentation)
328 (generate-documentation
329 `((zabbix-server-configuration
330 ,zabbix-server-configuration-fields))
331 'zabbix-server-configuration))
332
333 (define-configuration zabbix-agent-configuration
334 (zabbix-agent
335 (package zabbix-agentd)
336 "The zabbix-agent package.")
337 (user
338 (string "zabbix")
339 "User who will run the Zabbix agent.")
340 (group
341 (group "zabbix")
342 "Group who will run the Zabbix agent.")
343 (hostname
344 (string "Zabbix server")
345 "Unique, case sensitive hostname which is required for active checks and
346 must match hostname as configured on the server.")
347 (log-type
348 (string "")
349 "Specifies where log messages are written to:
350 @itemize
351 @item @code{system} - syslog.
352 @item @code{file} - file specified with @code{log-file} parameter.
353 @item @code{console} - standard output.
354 @end itemize\n")
355 (log-file
356 (string "/var/log/zabbix/agent.log")
357 "Log file name for @code{log-type} @code{file} parameter.")
358 (pid-file
359 (string "/var/run/zabbix/zabbix_agent.pid")
360 "Name of PID file.")
361 (server
362 (list '("127.0.0.1"))
363 "List of IP addresses, optionally in CIDR notation, or hostnames of Zabbix
364 servers and Zabbix proxies. Incoming connections will be accepted only from
365 the hosts listed here.")
366 (server-active
367 (list '("127.0.0.1"))
368 "List of IP:port (or hostname:port) pairs of Zabbix servers and Zabbix
369 proxies for active checks. If port is not specified, default port is used.
370 If this parameter is not specified, active checks are disabled.")
371 (extra-options
372 (extra-options "")
373 "Extra options will be appended to Zabbix server configuration file.")
374 (include-files
375 (include-files '())
376 "You may include individual files or all files in a directory in the
377 configuration file."))
378
379 (define (zabbix-agent-account config)
380 "Return the user accounts and user groups for CONFIG."
381 (let ((zabbix-user "zabbix")
382 (zabbix-group "zabbix"))
383 (list (user-group (name zabbix-group) (system? #t))
384 (user-account
385 (name zabbix-user)
386 (system? #t)
387 (group zabbix-group)
388 (comment "zabbix privilege separation user")
389 (home-directory (string-append "/var/run/" zabbix-user))
390 (shell (file-append shadow "/sbin/nologin"))))))
391
392 (define (zabbix-agent-activation config)
393 "Return the activation gexp for CONFIG."
394 (with-imported-modules '((guix build utils)
395 (ice-9 rdelim))
396 #~(begin
397 (use-modules (guix build utils)
398 (ice-9 rdelim))
399 (let ((user
400 (getpw #$(zabbix-agent-configuration-user config))))
401 (for-each (lambda (file)
402 (let ((directory (dirname file)))
403 (mkdir-p directory)
404 (chown directory (passwd:uid user) (passwd:gid user))
405 (chmod directory #o755)))
406 (list #$(zabbix-agent-configuration-log-file config)
407 #$(zabbix-agent-configuration-pid-file config)))))))
408
409 (define (zabbix-agent-config-file config)
410 "Return the zabbix-agent configuration file corresponding to CONFIG."
411 (computed-file
412 "zabbix_agent.conf"
413 #~(begin
414 (call-with-output-file #$output
415 (lambda (port)
416 (display "# Generated by 'zabbix-agent-service'.\n" port)
417 (display #$(with-output-to-string
418 (lambda ()
419 (serialize-configuration
420 config zabbix-agent-configuration-fields)))
421 port)
422 #t)))))
423
424 (define (zabbix-agent-shepherd-service config)
425 "Return a <shepherd-service> for Zabbix agent with CONFIG."
426 (list (shepherd-service
427 (provision '(zabbix-agent))
428 (documentation "Run Zabbix agent daemon.")
429 (start #~(make-forkexec-constructor
430 (list #$(file-append (zabbix-agent-configuration-zabbix-agent config)
431 "/sbin/zabbix_agentd")
432 "--config" #$(zabbix-agent-config-file config)
433 "--foreground")
434 #:user #$(zabbix-agent-configuration-user config)
435 #:group #$(zabbix-agent-configuration-group config)
436 #:pid-file #$(zabbix-agent-configuration-pid-file config)
437 #:environment-variables
438 (list "SSL_CERT_DIR=/run/current-system/profile\
439 /etc/ssl/certs"
440 "SSL_CERT_FILE=/run/current-system/profile\
441 /etc/ssl/certs/ca-certificates.crt")))
442 (stop #~(make-kill-destructor)))))
443
444 (define zabbix-agent-service-type
445 (service-type
446 (name 'zabbix-agent)
447 (extensions
448 (list (service-extension shepherd-root-service-type
449 zabbix-agent-shepherd-service)
450 (service-extension account-service-type
451 zabbix-agent-account)
452 (service-extension activation-service-type
453 zabbix-agent-activation)))
454 (default-value (zabbix-agent-configuration))))
455
456 (define (generate-zabbix-agent-documentation)
457 (generate-documentation
458 `((zabbix-agent-configuration
459 ,zabbix-agent-configuration-fields))
460 'zabbix-agent-configuration))
461
462 (define %zabbix-front-end-configuration-nginx
463 (nginx-server-configuration
464 (root #~(string-append #$zabbix-server:front-end "/share/zabbix/php"))
465 (index '("index.php"))
466 (locations
467 (let ((php-location (nginx-php-location)))
468 (list (nginx-location-configuration
469 (inherit php-location)
470 (body (append (nginx-location-configuration-body php-location)
471 (list "
472 fastcgi_param PHP_VALUE \"post_max_size = 16M
473 max_execution_time = 300\";
474 ")))))))))
475
476 (define-configuration zabbix-front-end-configuration
477 ;; TODO: Specify zabbix front-end package.
478 ;; (zabbix-
479 ;; (package zabbix-front-end)
480 ;; "The zabbix-front-end package.")
481 (nginx
482 (nginx-server-configuration-list
483 (list %zabbix-front-end-configuration-nginx))
484 "NGINX configuration.")
485 (db-host
486 (string "localhost")
487 "Database host name.")
488 (db-port
489 (number 5432)
490 "Database port.")
491 (db-name
492 (string "zabbix")
493 "Database name.")
494 (db-user
495 (string "zabbix")
496 "Database user.")
497 (db-password
498 (string "")
499 "Database password. Please, use @code{db-secret-file} instead.")
500 (db-secret-file
501 (string "")
502 "Secret file which will be appended to @file{zabbix.conf.php} file. This
503 file contains credentials for use by Zabbix front-end. You are expected to
504 create it manually.")
505 (zabbix-host
506 (string "localhost")
507 "Zabbix server hostname.")
508 (zabbix-port
509 (number 10051)
510 "Zabbix server port."))
511
512 (define zabbix-front-end-config
513 (match-lambda
514 (($ <zabbix-front-end-configuration>
515 _ db-host db-port db-name db-user db-password db-secret-file
516 zabbix-host zabbix-port)
517 (mixed-text-file "zabbix.conf.php"
518 "\
519 <?php
520 // Zabbix GUI configuration file.
521 global $DB;
522
523 $DB['TYPE'] = 'POSTGRESQL';
524 $DB['SERVER'] = '" db-host "';
525 $DB['PORT'] = '" (number->string db-port) "';
526 $DB['DATABASE'] = '" db-name "';
527 $DB['USER'] = '" db-user "';
528 $DB['PASSWORD'] = '" (if (string-null? db-password)
529 (if (string-null? db-secret-file)
530 (raise (condition
531 (&message
532 (message "\
533 you must provide either 'db-secret-file' or 'db-password'"))))
534 (string-trim-both
535 (with-input-from-file db-secret-file
536 read-string)))
537 (begin
538 (display-hint "\
539 Consider using @code{db-secret-file} instead of @code{db-password} and unset
540 @code{db-password} for security in @code{zabbix-front-end-configuration}.")
541 db-password)) "';
542
543 // Schema name. Used for IBM DB2 and PostgreSQL.
544 $DB['SCHEMA'] = '';
545
546 $ZBX_SERVER = '" zabbix-host "';
547 $ZBX_SERVER_PORT = '" (number->string zabbix-port) "';
548 $ZBX_SERVER_NAME = '';
549
550 $IMAGE_FORMAT_DEFAULT = IMAGE_FORMAT_PNG;
551 "))))
552
553 (define %maintenance.inc.php
554 ;; Empty php file to allow us move zabbix-frontend configs to ‘/etc/zabbix’
555 ;; directory. See ‘install-front-end’ phase in
556 ;; (@ (gnu packages monitoring) zabbix-server) package.
557 "\
558 <?php
559 ")
560
561 (define (zabbix-front-end-activation config)
562 "Return the activation gexp for CONFIG."
563 #~(begin
564 (use-modules (guix build utils))
565 (mkdir-p "/etc/zabbix")
566 (call-with-output-file "/etc/zabbix/maintenance.inc.php"
567 (lambda (port)
568 (display #$%maintenance.inc.php port)))
569 (copy-file #$(zabbix-front-end-config config)
570 "/etc/zabbix/zabbix.conf.php")))
571
572 (define zabbix-front-end-service-type
573 (service-type
574 (name 'zabbix-front-end)
575 (extensions
576 (list (service-extension activation-service-type
577 zabbix-front-end-activation)
578 (service-extension nginx-service-type
579 zabbix-front-end-configuration-nginx)
580 ;; Make sure php-fpm is instantiated.
581 (service-extension php-fpm-service-type
582 (const #t))))
583 (default-value (zabbix-front-end-configuration))
584 (description
585 "Run the zabbix-front-end web interface, which allows users to interact
586 with Zabbix server.")))
587
588 (define (generate-zabbix-front-end-documentation)
589 (generate-documentation
590 `((zabbix-front-end-configuration
591 ,zabbix-front-end-configuration-fields))
592 'zabbix-front-end-configuration))