python3Packages.orjson: Disable failing tests on 32 bit
[NixPkgs.git] / nixos / modules / services / monitoring / prometheus / exporters.nix
blobd9e380d42749b3a3278f0206a06a2d662ef9239b
1 { config, pkgs, lib, options, ... }:
3 let
4   inherit (lib) concatStrings foldl foldl' genAttrs literalExpression maintainers
5                 mapAttrsToList mkDefault mkEnableOption mkIf mkMerge mkOption
6                 optional types mkOptionDefault flip attrNames;
8   cfg = config.services.prometheus.exporters;
10   # each attribute in `exporterOpts` is expected to have specified:
11   #   - port        (types.int):   port on which the exporter listens
12   #   - serviceOpts (types.attrs): config that is merged with the
13   #                                default definition of the exporter's
14   #                                systemd service
15   #   - extraOpts   (types.attrs): extra configuration options to
16   #                                configure the exporter with, which
17   #                                are appended to the default options
18   #
19   #  Note that `extraOpts` is optional, but a script for the exporter's
20   #  systemd service must be provided by specifying either
21   #  `serviceOpts.script` or `serviceOpts.serviceConfig.ExecStart`
23   exporterOpts = genAttrs [
24     "apcupsd"
25     "artifactory"
26     "bind"
27     "bird"
28     "bitcoin"
29     "blackbox"
30     "buildkite-agent"
31     "collectd"
32     "dmarc"
33     "dnsmasq"
34     "domain"
35     "dovecot"
36     "fastly"
37     "fritzbox"
38     "influxdb"
39     "ipmi"
40     "json"
41     "jitsi"
42     "kea"
43     "keylight"
44     "knot"
45     "lnd"
46     "mail"
47     "mikrotik"
48     "minio"
49     "modemmanager"
50     "nextcloud"
51     "nginx"
52     "nginxlog"
53     "node"
54     "openldap"
55     "openvpn"
56     "pihole"
57     "postfix"
58     "postgres"
59     "process"
60     "pve"
61     "py-air-control"
62     "redis"
63     "rspamd"
64     "rtl_433"
65     "script"
66     "snmp"
67     "smartctl"
68     "smokeping"
69     "sql"
70     "surfboard"
71     "systemd"
72     "tor"
73     "unbound"
74     "unifi"
75     "unifi-poller"
76     "v2ray"
77     "varnish"
78     "wireguard"
79     "flow"
80   ] (name:
81     import (./. + "/exporters/${name}.nix") { inherit config lib pkgs options; }
82   );
84   mkExporterOpts = ({ name, port }: {
85     enable = mkEnableOption (lib.mdDoc "the prometheus ${name} exporter");
86     port = mkOption {
87       type = types.port;
88       default = port;
89       description = lib.mdDoc ''
90         Port to listen on.
91       '';
92     };
93     listenAddress = mkOption {
94       type = types.str;
95       default = "0.0.0.0";
96       description = lib.mdDoc ''
97         Address to listen on.
98       '';
99     };
100     extraFlags = mkOption {
101       type = types.listOf types.str;
102       default = [];
103       description = lib.mdDoc ''
104         Extra commandline options to pass to the ${name} exporter.
105       '';
106     };
107     openFirewall = mkOption {
108       type = types.bool;
109       default = false;
110       description = lib.mdDoc ''
111         Open port in firewall for incoming connections.
112       '';
113     };
114     firewallFilter = mkOption {
115       type = types.nullOr types.str;
116       default = null;
117       example = literalExpression ''
118         "-i eth0 -p tcp -m tcp --dport ${toString port}"
119       '';
120       description = lib.mdDoc ''
121         Specify a filter for iptables to use when
122         {option}`services.prometheus.exporters.${name}.openFirewall`
123         is true. It is used as `ip46tables -I nixos-fw firewallFilter -j nixos-fw-accept`.
124       '';
125     };
126     user = mkOption {
127       type = types.str;
128       default = "${name}-exporter";
129       description = lib.mdDoc ''
130         User name under which the ${name} exporter shall be run.
131       '';
132     };
133     group = mkOption {
134       type = types.str;
135       default = "${name}-exporter";
136       description = lib.mdDoc ''
137         Group under which the ${name} exporter shall be run.
138       '';
139     };
140   });
142   mkSubModule = { name, port, extraOpts, imports }: {
143     ${name} = mkOption {
144       type = types.submodule [{
145         inherit imports;
146         options = (mkExporterOpts {
147           inherit name port;
148         } // extraOpts);
149       } ({ config, ... }: mkIf config.openFirewall {
150         firewallFilter = mkDefault "-p tcp -m tcp --dport ${toString config.port}";
151       })];
152       internal = true;
153       default = {};
154     };
155   };
157   mkSubModules = (foldl' (a: b: a//b) {}
158     (mapAttrsToList (name: opts: mkSubModule {
159       inherit name;
160       inherit (opts) port;
161       extraOpts = opts.extraOpts or {};
162       imports = opts.imports or [];
163     }) exporterOpts)
164   );
166   mkExporterConf = { name, conf, serviceOpts }:
167     let
168       enableDynamicUser = serviceOpts.serviceConfig.DynamicUser or true;
169     in
170     mkIf conf.enable {
171       warnings = conf.warnings or [];
172       users.users."${name}-exporter" = (mkIf (conf.user == "${name}-exporter" && !enableDynamicUser) {
173         description = "Prometheus ${name} exporter service user";
174         isSystemUser = true;
175         inherit (conf) group;
176       });
177       users.groups = (mkIf (conf.group == "${name}-exporter" && !enableDynamicUser) {
178         "${name}-exporter" = {};
179       });
180       networking.firewall.extraCommands = mkIf conf.openFirewall (concatStrings [
181         "ip46tables -A nixos-fw ${conf.firewallFilter} "
182         "-m comment --comment ${name}-exporter -j nixos-fw-accept"
183       ]);
184       systemd.services."prometheus-${name}-exporter" = mkMerge ([{
185         wantedBy = [ "multi-user.target" ];
186         after = [ "network.target" ];
187         serviceConfig.Restart = mkDefault "always";
188         serviceConfig.PrivateTmp = mkDefault true;
189         serviceConfig.WorkingDirectory = mkDefault /tmp;
190         serviceConfig.DynamicUser = mkDefault enableDynamicUser;
191         serviceConfig.User = mkDefault conf.user;
192         serviceConfig.Group = conf.group;
193         # Hardening
194         serviceConfig.CapabilityBoundingSet = mkDefault [ "" ];
195         serviceConfig.DeviceAllow = [ "" ];
196         serviceConfig.LockPersonality = true;
197         serviceConfig.MemoryDenyWriteExecute = true;
198         serviceConfig.NoNewPrivileges = true;
199         serviceConfig.PrivateDevices = true;
200         serviceConfig.ProtectClock = mkDefault true;
201         serviceConfig.ProtectControlGroups = true;
202         serviceConfig.ProtectHome = true;
203         serviceConfig.ProtectHostname = true;
204         serviceConfig.ProtectKernelLogs = true;
205         serviceConfig.ProtectKernelModules = true;
206         serviceConfig.ProtectKernelTunables = true;
207         serviceConfig.ProtectSystem = mkDefault "strict";
208         serviceConfig.RemoveIPC = true;
209         serviceConfig.RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ];
210         serviceConfig.RestrictNamespaces = true;
211         serviceConfig.RestrictRealtime = true;
212         serviceConfig.RestrictSUIDSGID = true;
213         serviceConfig.SystemCallArchitectures = "native";
214         serviceConfig.UMask = "0077";
215       } serviceOpts ]);
216   };
220   imports = (lib.forEach [ "blackboxExporter" "collectdExporter" "fritzboxExporter"
221                    "jsonExporter" "minioExporter" "nginxExporter" "nodeExporter"
222                    "snmpExporter" "unifiExporter" "varnishExporter" ]
223        (opt: lib.mkRemovedOptionModule [ "services" "prometheus" "${opt}" ] ''
224          The prometheus exporters are now configured using `services.prometheus.exporters'.
225          See the 18.03 release notes for more information.
226        '' ));
228   options.services.prometheus.exporters = mkOption {
229     type = types.submodule {
230       options = (mkSubModules);
231     };
232     description = lib.mdDoc "Prometheus exporter configuration";
233     default = {};
234     example = literalExpression ''
235       {
236         node = {
237           enable = true;
238           enabledCollectors = [ "systemd" ];
239         };
240         varnish.enable = true;
241       }
242     '';
243   };
245   config = mkMerge ([{
246     assertions = [ {
247       assertion = cfg.ipmi.enable -> (cfg.ipmi.configFile != null) -> (
248         !(lib.hasPrefix "/tmp/" cfg.ipmi.configFile)
249       );
250       message = ''
251         Config file specified in `services.prometheus.exporters.ipmi.configFile' must
252           not reside within /tmp - it won't be visible to the systemd service.
253       '';
254     } {
255       assertion = cfg.ipmi.enable -> (cfg.ipmi.webConfigFile != null) -> (
256         !(lib.hasPrefix "/tmp/" cfg.ipmi.webConfigFile)
257       );
258       message = ''
259         Config file specified in `services.prometheus.exporters.ipmi.webConfigFile' must
260           not reside within /tmp - it won't be visible to the systemd service.
261       '';
262     } {
263       assertion = cfg.snmp.enable -> (
264         (cfg.snmp.configurationPath == null) != (cfg.snmp.configuration == null)
265       );
266       message = ''
267         Please ensure you have either `services.prometheus.exporters.snmp.configuration'
268           or `services.prometheus.exporters.snmp.configurationPath' set!
269       '';
270     } {
271       assertion = cfg.mikrotik.enable -> (
272         (cfg.mikrotik.configFile == null) != (cfg.mikrotik.configuration == null)
273       );
274       message = ''
275         Please specify either `services.prometheus.exporters.mikrotik.configuration'
276           or `services.prometheus.exporters.mikrotik.configFile'.
277       '';
278     } {
279       assertion = cfg.mail.enable -> (
280         (cfg.mail.configFile == null) != (cfg.mail.configuration == null)
281       );
282       message = ''
283         Please specify either 'services.prometheus.exporters.mail.configuration'
284           or 'services.prometheus.exporters.mail.configFile'.
285       '';
286     } {
287       assertion = cfg.sql.enable -> (
288         (cfg.sql.configFile == null) != (cfg.sql.configuration == null)
289       );
290       message = ''
291         Please specify either 'services.prometheus.exporters.sql.configuration' or
292           'services.prometheus.exporters.sql.configFile'
293       '';
294     } ] ++ (flip map (attrNames cfg) (exporter: {
295       assertion = cfg.${exporter}.firewallFilter != null -> cfg.${exporter}.openFirewall;
296       message = ''
297         The `firewallFilter'-option of exporter ${exporter} doesn't have any effect unless
298         `openFirewall' is set to `true'!
299       '';
300     }));
301   }] ++ [(mkIf config.services.minio.enable {
302     services.prometheus.exporters.minio.minioAddress  = mkDefault "http://localhost:9000";
303     services.prometheus.exporters.minio.minioAccessKey = mkDefault config.services.minio.accessKey;
304     services.prometheus.exporters.minio.minioAccessSecret = mkDefault config.services.minio.secretKey;
305   })] ++ [(mkIf config.services.prometheus.exporters.rtl_433.enable {
306     hardware.rtl-sdr.enable = mkDefault true;
307   })] ++ [(mkIf config.services.postfix.enable {
308     services.prometheus.exporters.postfix.group = mkDefault config.services.postfix.setgidGroup;
309   })] ++ (mapAttrsToList (name: conf:
310     mkExporterConf {
311       inherit name;
312       inherit (conf) serviceOpts;
313       conf = cfg.${name};
314     }) exporterOpts)
315   );
317   meta = {
318     doc = ./exporters.xml;
319     maintainers = [ maintainers.willibutz ];
320   };