1{
2 config,
3 lib,
4 pkgs,
5 ...
6}:
7
8let
9 cfg = config.services.librenms;
10 settingsFormat = pkgs.formats.json { };
11 configJson = settingsFormat.generate "librenms-config.json" cfg.settings;
12
13 package = cfg.package.override {
14 logDir = cfg.logDir;
15 dataDir = cfg.dataDir;
16 };
17
18 phpOptions = ''
19 log_errors = on
20 post_max_size = 100M
21 upload_max_filesize = 100M
22 memory_limit = ${toString cfg.settings.php_memory_limit}M
23 date.timezone = "${config.time.timeZone}"
24 '';
25 phpIni =
26 pkgs.runCommand "php.ini"
27 {
28 inherit (package) phpPackage;
29 inherit phpOptions;
30 preferLocalBuild = true;
31 passAsFile = [ "phpOptions" ];
32 }
33 ''
34 cat $phpPackage/etc/php.ini $phpOptionsPath > $out
35 '';
36
37 artisanWrapper = pkgs.writeShellScriptBin "librenms-artisan" ''
38 cd ${package}
39 sudo=exec
40 if [[ "$USER" != ${cfg.user} ]]; then
41 sudo='exec /run/wrappers/bin/sudo -u ${cfg.user}'
42 fi
43 $sudo ${package}/artisan "$@"
44 '';
45
46 lnmsWrapper = pkgs.writeShellScriptBin "lnms" ''
47 cd ${package}
48 sudo=exec
49 if [[ "$USER" != ${cfg.user} ]]; then
50 sudo='exec /run/wrappers/bin/sudo -u ${cfg.user}'
51 fi
52 $sudo ${package}/lnms "$@"
53 '';
54
55 configFile = pkgs.writeText "config.php" ''
56 <?php
57 $new_config = json_decode(file_get_contents("${cfg.dataDir}/config.json"), true);
58 $config = ($config == null) ? $new_config : array_merge($config, $new_config);
59
60 ${lib.optionalString (cfg.extraConfig != null) cfg.extraConfig}
61 '';
62
63in
64{
65 options.services.librenms = with lib; {
66 enable = mkEnableOption "LibreNMS network monitoring system";
67
68 package = lib.mkPackageOption pkgs "librenms" { };
69
70 finalPackage = lib.mkOption {
71 type = lib.types.package;
72 readOnly = true;
73 default = package;
74 defaultText = lib.literalExpression "package";
75 description = ''
76 The final package used by the module. This is the package that has all overrides.
77 '';
78 };
79
80 user = mkOption {
81 type = types.str;
82 default = "librenms";
83 description = ''
84 Name of the LibreNMS user.
85 '';
86 };
87
88 group = mkOption {
89 type = types.str;
90 default = "librenms";
91 description = ''
92 Name of the LibreNMS group.
93 '';
94 };
95
96 hostname = mkOption {
97 type = types.str;
98 default = config.networking.fqdnOrHostName;
99 defaultText = literalExpression "config.networking.fqdnOrHostName";
100 description = ''
101 The hostname to serve LibreNMS on.
102 '';
103 };
104
105 pollerThreads = mkOption {
106 type = types.int;
107 default = 16;
108 description = ''
109 Amount of threads of the cron-poller.
110 '';
111 };
112
113 enableOneMinutePolling = mkOption {
114 type = types.bool;
115 default = false;
116 description = ''
117 Enables the [1-Minute Polling](https://docs.librenms.org/Support/1-Minute-Polling/).
118 Changing this option will automatically convert your existing rrd files.
119 '';
120 };
121
122 enableLocalBilling = mkOption {
123 type = types.bool;
124 default = true;
125 description = ''
126 Enable billing Cron-Jobs on the local instance. Enabled by default, but you may disable it
127 on some nodes within a distributed poller setup. See [the docs](https://docs.librenms.org/Extensions/Distributed-Poller/#discovery)
128 for more informations about billing with distributed pollers.
129 '';
130 };
131
132 useDistributedPollers = mkOption {
133 type = types.bool;
134 default = false;
135 description = ''
136 Enables [distributed pollers](https://docs.librenms.org/Extensions/Distributed-Poller/)
137 for this LibreNMS instance. This will enable a local `rrdcached` and `memcached` server.
138
139 To use this feature, make sure to configure your firewall that the distributed pollers
140 can reach the local `mysql`, `rrdcached` and `memcached` ports.
141 '';
142 };
143
144 distributedPoller = {
145 enable = mkOption {
146 type = types.bool;
147 default = false;
148 description = ''
149 Configure this LibreNMS instance as a [distributed poller](https://docs.librenms.org/Extensions/Distributed-Poller/).
150 This will disable all web features and just configure the poller features.
151 Use the `mysql` database of your main LibreNMS instance in the database settings.
152 '';
153 };
154
155 name = mkOption {
156 type = types.nullOr types.str;
157 default = null;
158 description = ''
159 Custom name of this poller.
160 '';
161 };
162
163 group = mkOption {
164 type = types.str;
165 default = "0";
166 example = "1,2";
167 description = ''
168 Group(s) of this poller.
169 '';
170 };
171
172 distributedBilling = mkOption {
173 type = types.bool;
174 default = false;
175 description = ''
176 Enable distributed billing on this poller.
177
178 Note: according to [the docs](https://docs.librenms.org/Extensions/Distributed-Poller/#discovery),
179 billing should only be calculated on a single node per poller group. You can disable billing on
180 some nodes with the `services.librenms.enableLocalBilling` option.
181 '';
182 };
183
184 memcachedHost = mkOption {
185 type = types.str;
186 description = ''
187 Hostname or IP of the `memcached` server.
188 '';
189 };
190
191 memcachedPort = mkOption {
192 type = types.port;
193 default = 11211;
194 description = ''
195 Port of the `memcached` server.
196 '';
197 };
198
199 rrdcachedHost = mkOption {
200 type = types.str;
201 description = ''
202 Hostname or IP of the `rrdcached` server.
203 '';
204 };
205
206 rrdcachedPort = mkOption {
207 type = types.port;
208 default = 42217;
209 description = ''
210 Port of the `memcached` server.
211 '';
212 };
213 };
214
215 poolConfig = mkOption {
216 type =
217 with types;
218 attrsOf (oneOf [
219 str
220 int
221 bool
222 ]);
223 default = {
224 "pm" = "dynamic";
225 "pm.max_children" = 32;
226 "pm.start_servers" = 2;
227 "pm.min_spare_servers" = 2;
228 "pm.max_spare_servers" = 4;
229 "pm.max_requests" = 500;
230 };
231 description = ''
232 Options for the LibreNMS PHP pool. See the documentation on `php-fpm.conf`
233 for details on configuration directives.
234 '';
235 };
236
237 nginx = mkOption {
238 type = types.submodule (
239 recursiveUpdate (import ../web-servers/nginx/vhost-options.nix { inherit config lib; }) { }
240 );
241 default = { };
242 example = literalExpression ''
243 {
244 serverAliases = [
245 "librenms.''${config.networking.domain}"
246 ];
247 # To enable encryption and let let's encrypt take care of certificate
248 forceSSL = true;
249 enableACME = true;
250 # To set the LibreNMS virtualHost as the default virtualHost;
251 default = true;
252 }
253 '';
254 description = ''
255 With this option, you can customize the nginx virtualHost settings.
256 '';
257 };
258
259 dataDir = mkOption {
260 type = types.path;
261 default = "/var/lib/librenms";
262 description = ''
263 Path of the LibreNMS state directory.
264 '';
265 };
266
267 logDir = mkOption {
268 type = types.path;
269 default = "/var/log/librenms";
270 description = ''
271 Path of the LibreNMS logging directory.
272 '';
273 };
274
275 database = {
276 createLocally = mkOption {
277 type = types.bool;
278 default = false;
279 description = ''
280 Whether to create a local database automatically.
281 '';
282 };
283
284 host = mkOption {
285 default = "localhost";
286 description = ''
287 Hostname or IP of the MySQL/MariaDB server.
288 Ignored if 'socket' is defined.
289 '';
290 };
291
292 port = mkOption {
293 type = types.port;
294 default = 3306;
295 description = ''
296 Port of the MySQL/MariaDB server.
297 Ignored if 'socket' is defined.
298 '';
299 };
300
301 database = mkOption {
302 type = types.str;
303 default = "librenms";
304 description = ''
305 Name of the database on the MySQL/MariaDB server.
306 '';
307 };
308
309 username = mkOption {
310 type = types.str;
311 default = "librenms";
312 description = ''
313 Name of the user on the MySQL/MariaDB server.
314 Ignored if 'socket' is defined.
315 '';
316 };
317
318 passwordFile = mkOption {
319 type = types.nullOr types.path;
320 default = null;
321 example = "/run/secrets/mysql.pass";
322 description = ''
323 A file containing the password for the user of the MySQL/MariaDB server.
324 Must be readable for the LibreNMS user.
325 Ignored if 'socket' is defined, mandatory otherwise.
326 '';
327 };
328
329 socket = mkOption {
330 type = types.nullOr types.str;
331 default = null;
332 example = "/run/mysqld/mysqld.sock";
333 description = ''
334 A unix socket to mysql, accessible by the librenms user.
335 Useful when mysql is on the localhost.
336 '';
337 };
338 };
339
340 environmentFile = mkOption {
341 type = types.nullOr types.str;
342 default = null;
343 description = ''
344 File containing env-vars to be substituted into the final config. Useful for secrets.
345 Does not apply to settings defined in `extraConfig`.
346 '';
347 };
348
349 settings = mkOption {
350 type = types.submodule {
351 freeformType = settingsFormat.type;
352 options = { };
353 };
354 description = ''
355 Attrset of the LibreNMS configuration.
356 See <https://docs.librenms.org/Support/Configuration/> for reference.
357 All possible options are listed [here](https://github.com/librenms/librenms/blob/master/misc/config_definitions.json).
358 See <https://docs.librenms.org/Extensions/Authentication/> for setting other authentication methods.
359 '';
360 default = { };
361 example = {
362 base_url = "/librenms/";
363 top_devices = true;
364 top_ports = false;
365 };
366 };
367
368 extraConfig = mkOption {
369 type = types.nullOr types.str;
370 default = null;
371 description = ''
372 Additional config for LibreNMS that will be appended to the `config.php`. See
373 <https://github.com/librenms/librenms/blob/master/misc/config_definitions.json>
374 for possible options. Useful if you want to use PHP-Functions in your config.
375 '';
376 };
377 };
378
379 config = lib.mkIf cfg.enable {
380 assertions = [
381 {
382 assertion = config.time.timeZone != null;
383 message = "You must set `time.timeZone` to use the LibreNMS module.";
384 }
385 {
386 assertion = cfg.database.createLocally -> cfg.database.host == "localhost";
387 message = "The database host must be \"localhost\" if services.librenms.database.createLocally is set to true.";
388 }
389 {
390 assertion = !(cfg.useDistributedPollers && cfg.distributedPoller.enable);
391 message = "The LibreNMS instance can't be a distributed poller and a full instance at the same time.";
392 }
393 ];
394
395 users.users.${cfg.user} = {
396 group = "${cfg.group}";
397 isSystemUser = true;
398 };
399
400 users.groups.${cfg.group} = { };
401
402 services.librenms.settings =
403 {
404 # basic configs
405 "user" = cfg.user;
406 "own_hostname" = cfg.hostname;
407 "base_url" = lib.mkDefault "/";
408 "auth_mechanism" = lib.mkDefault "mysql";
409
410 # disable auto update function (won't work with NixOS)
411 "update" = false;
412
413 # enable fast ping by default
414 "ping_rrd_step" = 60;
415
416 # set default memory limit to 1G
417 "php_memory_limit" = lib.mkDefault 1024;
418
419 # one minute polling
420 "rrd.step" = if cfg.enableOneMinutePolling then 60 else 300;
421 "rrd.heartbeat" = if cfg.enableOneMinutePolling then 120 else 600;
422 }
423 // (lib.optionalAttrs cfg.distributedPoller.enable {
424 "distributed_poller" = true;
425 "distributed_poller_name" = lib.mkIf (
426 cfg.distributedPoller.name != null
427 ) cfg.distributedPoller.name;
428 "distributed_poller_group" = cfg.distributedPoller.group;
429 "distributed_billing" = cfg.distributedPoller.distributedBilling;
430 "distributed_poller_memcached_host" = cfg.distributedPoller.memcachedHost;
431 "distributed_poller_memcached_port" = cfg.distributedPoller.memcachedPort;
432 "rrdcached" =
433 "${cfg.distributedPoller.rrdcachedHost}:${toString cfg.distributedPoller.rrdcachedPort}";
434 })
435 // (lib.optionalAttrs cfg.useDistributedPollers {
436 "distributed_poller" = true;
437 # still enable a local poller with distributed polling
438 "distributed_poller_group" = lib.mkDefault "0";
439 "distributed_billing" = lib.mkDefault true;
440 "distributed_poller_memcached_host" = "localhost";
441 "distributed_poller_memcached_port" = 11211;
442 "rrdcached" = "localhost:42217";
443 });
444
445 services.memcached = lib.mkIf cfg.useDistributedPollers {
446 enable = true;
447 listen = "0.0.0.0";
448 };
449
450 systemd.services.rrdcached = lib.mkIf cfg.useDistributedPollers {
451 description = "rrdcached";
452 after = [ "librenms-setup.service" ];
453 wantedBy = [ "multi-user.target" ];
454 serviceConfig = {
455 Type = "forking";
456 User = cfg.user;
457 Group = cfg.group;
458 LimitNOFILE = 16384;
459 RuntimeDirectory = "rrdcached";
460 PidFile = "/run/rrdcached/rrdcached.pid";
461 # rrdcached params from https://docs.librenms.org/Extensions/Distributed-Poller/#config-sample
462 ExecStart = "${pkgs.rrdtool}/bin/rrdcached -l 0:42217 -R -j ${cfg.dataDir}/rrdcached-journal/ -F -b ${cfg.dataDir}/rrd -B -w 1800 -z 900 -p /run/rrdcached/rrdcached.pid";
463 };
464 };
465
466 services.mysql = lib.mkIf cfg.database.createLocally {
467 enable = true;
468 package = lib.mkDefault pkgs.mariadb;
469 settings.mysqld =
470 {
471 innodb_file_per_table = 1;
472 lower_case_table_names = 0;
473 }
474 // (lib.optionalAttrs cfg.useDistributedPollers {
475 bind-address = "0.0.0.0";
476 });
477 ensureDatabases = [ cfg.database.database ];
478 ensureUsers = [
479 {
480 name = cfg.database.username;
481 ensurePermissions = {
482 "${cfg.database.database}.*" = "ALL PRIVILEGES";
483 };
484 }
485 ];
486 initialScript = lib.mkIf cfg.useDistributedPollers (
487 pkgs.writeText "mysql-librenms-init" ''
488 CREATE USER IF NOT EXISTS '${cfg.database.username}'@'%';
489 GRANT ALL PRIVILEGES ON ${cfg.database.database}.* TO '${cfg.database.username}'@'%';
490 ''
491 );
492 };
493
494 services.nginx = lib.mkIf (!cfg.distributedPoller.enable) {
495 enable = true;
496 virtualHosts."${cfg.hostname}" = lib.mkMerge [
497 cfg.nginx
498 {
499 root = lib.mkForce "${package}/html";
500 locations."/" = {
501 index = "index.php";
502 tryFiles = "$uri $uri/ /index.php?$query_string";
503 };
504 locations."~ .php$".extraConfig = ''
505 fastcgi_pass unix:${config.services.phpfpm.pools."librenms".socket};
506 fastcgi_split_path_info ^(.+\.php)(/.+)$;
507 '';
508 }
509 ];
510 };
511
512 services.phpfpm.pools.librenms = lib.mkIf (!cfg.distributedPoller.enable) {
513 user = cfg.user;
514 group = cfg.group;
515 inherit (package) phpPackage;
516 inherit phpOptions;
517 settings = {
518 "listen.mode" = "0660";
519 "listen.owner" = config.services.nginx.user;
520 "listen.group" = config.services.nginx.group;
521 } // cfg.poolConfig;
522 };
523
524 systemd.services.librenms-scheduler = {
525 description = "LibreNMS Scheduler";
526 path = [ pkgs.unixtools.whereis ];
527 serviceConfig = {
528 Type = "oneshot";
529 WorkingDirectory = package;
530 User = cfg.user;
531 Group = cfg.group;
532 ExecStart = "${artisanWrapper}/bin/librenms-artisan schedule:run";
533 };
534 };
535
536 systemd.timers.librenms-scheduler = {
537 description = "LibreNMS Scheduler";
538 wantedBy = [ "timers.target" ];
539 timerConfig = {
540 OnCalendar = "minutely";
541 AccuracySec = "1second";
542 };
543 };
544
545 systemd.services.librenms-setup = {
546 description = "Preparation tasks for LibreNMS";
547 before = [ "phpfpm-librenms.service" ];
548 after = [
549 "systemd-tmpfiles-setup.service"
550 "network.target"
551 ] ++ (lib.optional (cfg.database.host == "localhost") "mysql.service");
552 wantedBy = [ "multi-user.target" ];
553 restartTriggers = [
554 package
555 configFile
556 ];
557 path = [
558 pkgs.mariadb
559 pkgs.unixtools.whereis
560 pkgs.gnused
561 ];
562 serviceConfig = {
563 Type = "oneshot";
564 RemainAfterExit = true;
565 EnvironmentFile = lib.mkIf (cfg.environmentFile != null) [ cfg.environmentFile ];
566 User = cfg.user;
567 Group = cfg.group;
568 ExecStartPre = lib.mkIf cfg.database.createLocally [
569 "!${
570 pkgs.writeShellScript "librenms-db-init" (
571 if !isNull cfg.database.socket then
572 ''
573 echo "ALTER USER '${cfg.database.username}'@'localhost' IDENTIFIED VIA unix_socket;" | ${pkgs.mariadb}/bin/mysql --socket='${cfg.database.socket}'
574 ${lib.optionalString cfg.useDistributedPollers ''
575 echo "ALTER USER '${cfg.database.username}'@'%' IDENTIFIED VIA unix_socket;" | ${pkgs.mariadb}/bin/mysql --socket='${cfg.database.socket}'
576 ''}
577 ''
578 else
579 ''
580 DB_PASSWORD=$(cat ${cfg.database.passwordFile} | tr -d '\n')
581 echo "ALTER USER '${cfg.database.username}'@'localhost' IDENTIFIED BY '$DB_PASSWORD';" | ${pkgs.mariadb}/bin/mysql
582 ${lib.optionalString cfg.useDistributedPollers ''
583 echo "ALTER USER '${cfg.database.username}'@'%' IDENTIFIED BY '$DB_PASSWORD';" | ${pkgs.mariadb}/bin/mysql
584 ''}
585 ''
586 )
587 }"
588 ];
589 };
590 script =
591 ''
592 set -euo pipefail
593
594 # config setup
595 ln -sf ${configFile} ${cfg.dataDir}/config.php
596 ${pkgs.envsubst}/bin/envsubst -i ${configJson} -o ${cfg.dataDir}/config.json
597 export PHPRC=${phpIni}
598
599 INIT=false
600 if [[ ! -s ${cfg.dataDir}/.env ]]; then
601 INIT=true
602 # init .env file
603 echo "APP_KEY=" > ${cfg.dataDir}/.env
604 ${artisanWrapper}/bin/librenms-artisan key:generate --ansi
605 ${artisanWrapper}/bin/librenms-artisan webpush:vapid
606 echo "" >> ${cfg.dataDir}/.env
607 echo -n "NODE_ID=" >> ${cfg.dataDir}/.env
608 ${package.phpPackage}/bin/php -r "echo uniqid();" >> ${cfg.dataDir}/.env
609 echo "" >> ${cfg.dataDir}/.env
610 else
611 # .env file already exists --> only update database and cache config
612 ${pkgs.gnused}/bin/sed -i /^DB_/d ${cfg.dataDir}/.env
613 ${pkgs.gnused}/bin/sed -i /^CACHE_DRIVER/d ${cfg.dataDir}/.env
614 fi
615 ${lib.optionalString (cfg.useDistributedPollers || cfg.distributedPoller.enable) ''
616 echo "CACHE_DRIVER=memcached" >> ${cfg.dataDir}/.env
617 ''}
618 echo "DB_DATABASE=${cfg.database.database}" >> ${cfg.dataDir}/.env
619 ''
620 + (
621 if !isNull cfg.database.socket then
622 ''
623 # use socket connection
624 echo "DB_SOCKET=${cfg.database.socket}" >> ${cfg.dataDir}/.env
625 echo "DB_PASSWORD=null" >> ${cfg.dataDir}/.env
626 ''
627 else
628 ''
629 # use TCP connection
630 echo "DB_HOST=${cfg.database.host}" >> ${cfg.dataDir}/.env
631 echo "DB_PORT=${toString cfg.database.port}" >> ${cfg.dataDir}/.env
632 echo "DB_USERNAME=${cfg.database.username}" >> ${cfg.dataDir}/.env
633 echo -n "DB_PASSWORD=" >> ${cfg.dataDir}/.env
634 cat ${cfg.database.passwordFile} >> ${cfg.dataDir}/.env
635 ''
636 )
637 + ''
638 # clear cache if package has changed (cache may contain cached paths
639 # to the old package)
640 OLD_PACKAGE=$(cat ${cfg.dataDir}/package)
641 if [[ $OLD_PACKAGE != "${package}" ]]; then
642 rm -r ${cfg.dataDir}/cache/*
643 fi
644
645 # convert rrd files when the oneMinutePolling option is changed
646 OLD_ENABLED=$(cat ${cfg.dataDir}/one_minute_enabled)
647 if [[ $OLD_ENABLED != "${lib.boolToString cfg.enableOneMinutePolling}" ]]; then
648 ${package}/scripts/rrdstep.php -h all
649 echo "${lib.boolToString cfg.enableOneMinutePolling}" > ${cfg.dataDir}/one_minute_enabled
650 fi
651
652 # migrate db if package version has changed
653 # not necessary for every package change
654 OLD_VERSION=$(cat ${cfg.dataDir}/version)
655 if [[ $OLD_VERSION != "${package.version}" ]]; then
656 ${artisanWrapper}/bin/librenms-artisan migrate --force --no-interaction
657 echo "${package.version}" > ${cfg.dataDir}/version
658 fi
659
660 if [[ $INIT == "true" ]]; then
661 ${artisanWrapper}/bin/librenms-artisan db:seed --force --no-interaction
662 fi
663
664 # regenerate cache if package has changed
665 if [[ $OLD_PACKAGE != "${package}" ]]; then
666 ${artisanWrapper}/bin/librenms-artisan view:clear
667 ${artisanWrapper}/bin/librenms-artisan optimize:clear
668 ${artisanWrapper}/bin/librenms-artisan view:cache
669 ${artisanWrapper}/bin/librenms-artisan optimize
670 echo "${package}" > ${cfg.dataDir}/package
671 fi
672 '';
673 };
674
675 programs.mtr.enable = true;
676
677 services.logrotate = {
678 enable = true;
679 settings."${cfg.logDir}/librenms.log" = {
680 su = "${cfg.user} ${cfg.group}";
681 create = "0640 ${cfg.user} ${cfg.group}";
682 rotate = 6;
683 frequency = "weekly";
684 compress = true;
685 delaycompress = true;
686 missingok = true;
687 notifempty = true;
688 };
689 };
690
691 services.cron = {
692 enable = true;
693 systemCronJobs =
694 let
695 env = "PHPRC=${phpIni}";
696 in
697 [
698 # based on crontab provided by LibreNMS
699 "33 */6 * * * ${cfg.user} ${env} ${package}/cronic ${package}/discovery-wrapper.py 1"
700 "*/5 * * * * ${cfg.user} ${env} ${package}/discovery.php -h new >> /dev/null 2>&1"
701
702 "${
703 if cfg.enableOneMinutePolling then "*" else "*/5"
704 } * * * * ${cfg.user} ${env} ${package}/cronic ${package}/poller-wrapper.py ${toString cfg.pollerThreads}"
705 "* * * * * ${cfg.user} ${env} ${package}/alerts.php >> /dev/null 2>&1"
706
707 "*/5 * * * * ${cfg.user} ${env} ${package}/check-services.php >> /dev/null 2>&1"
708
709 # extra: fast ping
710 "* * * * * ${cfg.user} ${env} ${package}/ping.php >> /dev/null 2>&1"
711
712 # daily.sh tasks are split to exclude update
713 "19 0 * * * ${cfg.user} ${env} ${package}/daily.sh cleanup >> /dev/null 2>&1"
714 "19 0 * * * ${cfg.user} ${env} ${package}/daily.sh notifications >> /dev/null 2>&1"
715 "19 0 * * * ${cfg.user} ${env} ${package}/daily.sh peeringdb >> /dev/null 2>&1"
716 "19 0 * * * ${cfg.user} ${env} ${package}/daily.sh mac_oui >> /dev/null 2>&1"
717 ]
718 ++ lib.optionals cfg.enableLocalBilling [
719 "*/5 * * * * ${cfg.user} ${env} ${package}/poll-billing.php >> /dev/null 2>&1"
720 "01 * * * * ${cfg.user} ${env} ${package}/billing-calculate.php >> /dev/null 2>&1"
721 ];
722 };
723
724 security.wrappers = {
725 fping = {
726 setuid = true;
727 owner = "root";
728 group = "root";
729 source = "${pkgs.fping}/bin/fping";
730 };
731 };
732
733 environment.systemPackages = [
734 artisanWrapper
735 lnmsWrapper
736 ];
737
738 systemd.tmpfiles.rules =
739 [
740 "d ${cfg.logDir} 0750 ${cfg.user} ${cfg.group} - -"
741 "f ${cfg.logDir}/librenms.log 0640 ${cfg.user} ${cfg.group} - -"
742 "d ${cfg.dataDir} 0750 ${cfg.user} ${cfg.group} - -"
743 "f ${cfg.dataDir}/.env 0600 ${cfg.user} ${cfg.group} - -"
744 "f ${cfg.dataDir}/version 0600 ${cfg.user} ${cfg.group} - -"
745 "f ${cfg.dataDir}/package 0600 ${cfg.user} ${cfg.group} - -"
746 "f ${cfg.dataDir}/one_minute_enabled 0600 ${cfg.user} ${cfg.group} - -"
747 "f ${cfg.dataDir}/config.json 0600 ${cfg.user} ${cfg.group} - -"
748 "d ${cfg.dataDir}/storage 0700 ${cfg.user} ${cfg.group} - -"
749 "d ${cfg.dataDir}/storage/app 0700 ${cfg.user} ${cfg.group} - -"
750 "d ${cfg.dataDir}/storage/debugbar 0700 ${cfg.user} ${cfg.group} - -"
751 "d ${cfg.dataDir}/storage/framework 0700 ${cfg.user} ${cfg.group} - -"
752 "d ${cfg.dataDir}/storage/framework/cache 0700 ${cfg.user} ${cfg.group} - -"
753 "d ${cfg.dataDir}/storage/framework/sessions 0700 ${cfg.user} ${cfg.group} - -"
754 "d ${cfg.dataDir}/storage/framework/views 0700 ${cfg.user} ${cfg.group} - -"
755 "d ${cfg.dataDir}/storage/logs 0700 ${cfg.user} ${cfg.group} - -"
756 "d ${cfg.dataDir}/rrd 0700 ${cfg.user} ${cfg.group} - -"
757 "d ${cfg.dataDir}/cache 0700 ${cfg.user} ${cfg.group} - -"
758 ]
759 ++ lib.optionals cfg.useDistributedPollers [
760 "d ${cfg.dataDir}/rrdcached-journal 0700 ${cfg.user} ${cfg.group} - -"
761 ];
762
763 };
764
765 meta.maintainers = with lib.maintainers; [ netali ] ++ lib.teams.wdz.members;
766}