1 { config, pkgs, lib, options, ... }:
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
15 # - extraOpts (types.attrs): extra configuration options to
16 # configure the exporter with, which
17 # are appended to the default options
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 [
81 import (./. + "/exporters/${name}.nix") { inherit config lib pkgs options; }
84 mkExporterOpts = ({ name, port }: {
85 enable = mkEnableOption (lib.mdDoc "the prometheus ${name} exporter");
89 description = lib.mdDoc ''
93 listenAddress = mkOption {
96 description = lib.mdDoc ''
100 extraFlags = mkOption {
101 type = types.listOf types.str;
103 description = lib.mdDoc ''
104 Extra commandline options to pass to the ${name} exporter.
107 openFirewall = mkOption {
110 description = lib.mdDoc ''
111 Open port in firewall for incoming connections.
114 firewallFilter = mkOption {
115 type = types.nullOr types.str;
117 example = literalExpression ''
118 "-i eth0 -p tcp -m tcp --dport ${toString port}"
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`.
128 default = "${name}-exporter";
129 description = lib.mdDoc ''
130 User name under which the ${name} exporter shall be run.
135 default = "${name}-exporter";
136 description = lib.mdDoc ''
137 Group under which the ${name} exporter shall be run.
142 mkSubModule = { name, port, extraOpts, imports }: {
144 type = types.submodule [{
146 options = (mkExporterOpts {
149 } ({ config, ... }: mkIf config.openFirewall {
150 firewallFilter = mkDefault "-p tcp -m tcp --dport ${toString config.port}";
157 mkSubModules = (foldl' (a: b: a//b) {}
158 (mapAttrsToList (name: opts: mkSubModule {
161 extraOpts = opts.extraOpts or {};
162 imports = opts.imports or [];
166 mkExporterConf = { name, conf, serviceOpts }:
168 enableDynamicUser = serviceOpts.serviceConfig.DynamicUser or true;
171 warnings = conf.warnings or [];
172 users.users."${name}-exporter" = (mkIf (conf.user == "${name}-exporter" && !enableDynamicUser) {
173 description = "Prometheus ${name} exporter service user";
175 inherit (conf) group;
177 users.groups = (mkIf (conf.group == "${name}-exporter" && !enableDynamicUser) {
178 "${name}-exporter" = {};
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"
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;
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";
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.
228 options.services.prometheus.exporters = mkOption {
229 type = types.submodule {
230 options = (mkSubModules);
232 description = lib.mdDoc "Prometheus exporter configuration";
234 example = literalExpression ''
238 enabledCollectors = [ "systemd" ];
240 varnish.enable = true;
247 assertion = cfg.ipmi.enable -> (cfg.ipmi.configFile != null) -> (
248 !(lib.hasPrefix "/tmp/" cfg.ipmi.configFile)
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.
255 assertion = cfg.ipmi.enable -> (cfg.ipmi.webConfigFile != null) -> (
256 !(lib.hasPrefix "/tmp/" cfg.ipmi.webConfigFile)
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.
263 assertion = cfg.snmp.enable -> (
264 (cfg.snmp.configurationPath == null) != (cfg.snmp.configuration == null)
267 Please ensure you have either `services.prometheus.exporters.snmp.configuration'
268 or `services.prometheus.exporters.snmp.configurationPath' set!
271 assertion = cfg.mikrotik.enable -> (
272 (cfg.mikrotik.configFile == null) != (cfg.mikrotik.configuration == null)
275 Please specify either `services.prometheus.exporters.mikrotik.configuration'
276 or `services.prometheus.exporters.mikrotik.configFile'.
279 assertion = cfg.mail.enable -> (
280 (cfg.mail.configFile == null) != (cfg.mail.configuration == null)
283 Please specify either 'services.prometheus.exporters.mail.configuration'
284 or 'services.prometheus.exporters.mail.configFile'.
287 assertion = cfg.sql.enable -> (
288 (cfg.sql.configFile == null) != (cfg.sql.configuration == null)
291 Please specify either 'services.prometheus.exporters.sql.configuration' or
292 'services.prometheus.exporters.sql.configFile'
294 } ] ++ (flip map (attrNames cfg) (exporter: {
295 assertion = cfg.${exporter}.firewallFilter != null -> cfg.${exporter}.openFirewall;
297 The `firewallFilter'-option of exporter ${exporter} doesn't have any effect unless
298 `openFirewall' is set to `true'!
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:
312 inherit (conf) serviceOpts;
318 doc = ./exporters.xml;
319 maintainers = [ maintainers.willibutz ];