at 24.11-pre 16 kB view raw
1{ config, pkgs, lib, options, utils, ... }: 2 3let 4 inherit (lib) concatStrings foldl foldl' genAttrs literalExpression maintainers 5 mapAttrs mapAttrsToList mkDefault mkEnableOption mkIf mkMerge mkOption 6 optional types mkOptionDefault flip attrNames; 7 8 cfg = config.services.prometheus.exporters; 9 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` 22 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 "dnssec" 35 "domain" 36 "dovecot" 37 "fastly" 38 "flow" 39 "fritz" 40 "fritzbox" 41 "graphite" 42 "idrac" 43 "imap-mailstat" 44 "influxdb" 45 "ipmi" 46 "jitsi" 47 "json" 48 "junos-czerwonk" 49 "kea" 50 "keylight" 51 "knot" 52 "lnd" 53 "mail" 54 "mikrotik" 55 "minio" 56 "modemmanager" 57 "mongodb" 58 "mysqld" 59 "nats" 60 "nextcloud" 61 "nginx" 62 "nginxlog" 63 "node" 64 "nut" 65 "openldap" 66 "pgbouncer" 67 "php-fpm" 68 "pihole" 69 "ping" 70 "postfix" 71 "postgres" 72 "process" 73 "pve" 74 "py-air-control" 75 "redis" 76 "restic" 77 "rspamd" 78 "rtl_433" 79 "sabnzbd" 80 "scaphandre" 81 "script" 82 "shelly" 83 "smartctl" 84 "smokeping" 85 "snmp" 86 "sql" 87 "statsd" 88 "surfboard" 89 "systemd" 90 "tor" 91 "unbound" 92 "unifi" 93 "unpoller" 94 "v2ray" 95 "varnish" 96 "wireguard" 97 "zfs" 98 ] 99 (name: 100 import (./. + "/exporters/${name}.nix") { inherit config lib pkgs options utils; } 101 )) // (mapAttrs 102 (name: params: 103 import (./. + "/exporters/${params.name}.nix") { inherit config lib pkgs options utils; type = params.type ; }) 104 { 105 exportarr-bazarr = { 106 name = "exportarr"; 107 type = "bazarr"; 108 }; 109 exportarr-lidarr = { 110 name = "exportarr"; 111 type = "lidarr"; 112 }; 113 exportarr-prowlarr = { 114 name = "exportarr"; 115 type = "prowlarr"; 116 }; 117 exportarr-radarr = { 118 name = "exportarr"; 119 type = "radarr"; 120 }; 121 exportarr-readarr = { 122 name = "exportarr"; 123 type = "readarr"; 124 }; 125 exportarr-sonarr = { 126 name = "exportarr"; 127 type = "sonarr"; 128 }; 129 } 130 ); 131 132 mkExporterOpts = ({ name, port }: { 133 enable = mkEnableOption "the prometheus ${name} exporter"; 134 port = mkOption { 135 type = types.port; 136 default = port; 137 description = '' 138 Port to listen on. 139 ''; 140 }; 141 listenAddress = mkOption { 142 type = types.str; 143 default = "0.0.0.0"; 144 description = '' 145 Address to listen on. 146 ''; 147 }; 148 extraFlags = mkOption { 149 type = types.listOf types.str; 150 default = []; 151 description = '' 152 Extra commandline options to pass to the ${name} exporter. 153 ''; 154 }; 155 openFirewall = mkOption { 156 type = types.bool; 157 default = false; 158 description = '' 159 Open port in firewall for incoming connections. 160 ''; 161 }; 162 firewallFilter = mkOption { 163 type = types.nullOr types.str; 164 default = null; 165 example = literalExpression '' 166 "-i eth0 -p tcp -m tcp --dport ${toString port}" 167 ''; 168 description = '' 169 Specify a filter for iptables to use when 170 {option}`services.prometheus.exporters.${name}.openFirewall` 171 is true. It is used as `ip46tables -I nixos-fw firewallFilter -j nixos-fw-accept`. 172 ''; 173 }; 174 firewallRules = mkOption { 175 type = types.nullOr types.lines; 176 default = null; 177 example = literalExpression '' 178 iifname "eth0" tcp dport ${toString port} counter accept 179 ''; 180 description = '' 181 Specify rules for nftables to add to the input chain 182 when {option}`services.prometheus.exporters.${name}.openFirewall` is true. 183 ''; 184 }; 185 user = mkOption { 186 type = types.str; 187 default = "${name}-exporter"; 188 description = '' 189 User name under which the ${name} exporter shall be run. 190 ''; 191 }; 192 group = mkOption { 193 type = types.str; 194 default = "${name}-exporter"; 195 description = '' 196 Group under which the ${name} exporter shall be run. 197 ''; 198 }; 199 }); 200 201 mkSubModule = { name, port, extraOpts, imports }: { 202 ${name} = mkOption { 203 type = types.submodule [{ 204 inherit imports; 205 options = (mkExporterOpts { 206 inherit name port; 207 } // extraOpts); 208 } ({ config, ... }: mkIf config.openFirewall { 209 firewallFilter = mkDefault "-p tcp -m tcp --dport ${toString config.port}"; 210 firewallRules = mkDefault ''tcp dport ${toString config.port} accept comment "${name}-exporter"''; 211 })]; 212 internal = true; 213 default = {}; 214 }; 215 }; 216 217 mkSubModules = (foldl' (a: b: a//b) {} 218 (mapAttrsToList (name: opts: mkSubModule { 219 inherit name; 220 inherit (opts) port; 221 extraOpts = opts.extraOpts or {}; 222 imports = opts.imports or []; 223 }) exporterOpts) 224 ); 225 226 mkExporterConf = { name, conf, serviceOpts }: 227 let 228 enableDynamicUser = serviceOpts.serviceConfig.DynamicUser or true; 229 nftables = config.networking.nftables.enable; 230 in 231 mkIf conf.enable { 232 warnings = conf.warnings or []; 233 users.users."${name}-exporter" = (mkIf (conf.user == "${name}-exporter" && !enableDynamicUser) { 234 description = "Prometheus ${name} exporter service user"; 235 isSystemUser = true; 236 inherit (conf) group; 237 }); 238 users.groups = (mkIf (conf.group == "${name}-exporter" && !enableDynamicUser) { 239 "${name}-exporter" = {}; 240 }); 241 networking.firewall.extraCommands = mkIf (conf.openFirewall && !nftables) (concatStrings [ 242 "ip46tables -A nixos-fw ${conf.firewallFilter} " 243 "-m comment --comment ${name}-exporter -j nixos-fw-accept" 244 ]); 245 networking.firewall.extraInputRules = mkIf (conf.openFirewall && nftables) conf.firewallRules; 246 systemd.services."prometheus-${name}-exporter" = mkMerge ([{ 247 wantedBy = [ "multi-user.target" ]; 248 after = [ "network.target" ]; 249 serviceConfig.Restart = mkDefault "always"; 250 serviceConfig.PrivateTmp = mkDefault true; 251 serviceConfig.WorkingDirectory = mkDefault /tmp; 252 serviceConfig.DynamicUser = mkDefault enableDynamicUser; 253 serviceConfig.User = mkDefault conf.user; 254 serviceConfig.Group = conf.group; 255 # Hardening 256 serviceConfig.CapabilityBoundingSet = mkDefault [ "" ]; 257 serviceConfig.DeviceAllow = [ "" ]; 258 serviceConfig.LockPersonality = true; 259 serviceConfig.MemoryDenyWriteExecute = true; 260 serviceConfig.NoNewPrivileges = true; 261 serviceConfig.PrivateDevices = mkDefault true; 262 serviceConfig.ProtectClock = mkDefault true; 263 serviceConfig.ProtectControlGroups = true; 264 serviceConfig.ProtectHome = true; 265 serviceConfig.ProtectHostname = true; 266 serviceConfig.ProtectKernelLogs = true; 267 serviceConfig.ProtectKernelModules = true; 268 serviceConfig.ProtectKernelTunables = true; 269 serviceConfig.ProtectSystem = mkDefault "strict"; 270 serviceConfig.RemoveIPC = true; 271 serviceConfig.RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ]; 272 serviceConfig.RestrictNamespaces = true; 273 serviceConfig.RestrictRealtime = true; 274 serviceConfig.RestrictSUIDSGID = true; 275 serviceConfig.SystemCallArchitectures = "native"; 276 serviceConfig.UMask = "0077"; 277 } serviceOpts ]); 278 }; 279in 280{ 281 282 imports = (lib.forEach [ "blackboxExporter" "collectdExporter" "fritzboxExporter" 283 "jsonExporter" "minioExporter" "nginxExporter" "nodeExporter" 284 "snmpExporter" "unifiExporter" "varnishExporter" ] 285 (opt: lib.mkRemovedOptionModule [ "services" "prometheus" "${opt}" ] '' 286 The prometheus exporters are now configured using `services.prometheus.exporters'. 287 See the 18.03 release notes for more information. 288 '' )); 289 290 options.services.prometheus.exporters = mkOption { 291 type = types.submodule { 292 options = (mkSubModules); 293 imports = [ 294 ../../../misc/assertions.nix 295 (lib.mkRenamedOptionModule [ "unifi-poller" ] [ "unpoller" ]) 296 ]; 297 }; 298 description = "Prometheus exporter configuration"; 299 default = {}; 300 example = literalExpression '' 301 { 302 node = { 303 enable = true; 304 enabledCollectors = [ "systemd" ]; 305 }; 306 varnish.enable = true; 307 } 308 ''; 309 }; 310 311 config = mkMerge ([{ 312 assertions = [ { 313 assertion = cfg.ipmi.enable -> (cfg.ipmi.configFile != null) -> ( 314 !(lib.hasPrefix "/tmp/" cfg.ipmi.configFile) 315 ); 316 message = '' 317 Config file specified in `services.prometheus.exporters.ipmi.configFile' must 318 not reside within /tmp - it won't be visible to the systemd service. 319 ''; 320 } { 321 assertion = cfg.ipmi.enable -> (cfg.ipmi.webConfigFile != null) -> ( 322 !(lib.hasPrefix "/tmp/" cfg.ipmi.webConfigFile) 323 ); 324 message = '' 325 Config file specified in `services.prometheus.exporters.ipmi.webConfigFile' must 326 not reside within /tmp - it won't be visible to the systemd service. 327 ''; 328 } { 329 assertion = cfg.snmp.enable -> ( 330 (cfg.snmp.configurationPath == null) != (cfg.snmp.configuration == null) 331 ); 332 message = '' 333 Please ensure you have either `services.prometheus.exporters.snmp.configuration' 334 or `services.prometheus.exporters.snmp.configurationPath' set! 335 ''; 336 } { 337 assertion = cfg.mikrotik.enable -> ( 338 (cfg.mikrotik.configFile == null) != (cfg.mikrotik.configuration == null) 339 ); 340 message = '' 341 Please specify either `services.prometheus.exporters.mikrotik.configuration' 342 or `services.prometheus.exporters.mikrotik.configFile'. 343 ''; 344 } { 345 assertion = cfg.mail.enable -> ( 346 (cfg.mail.configFile == null) != (cfg.mail.configuration == null) 347 ); 348 message = '' 349 Please specify either 'services.prometheus.exporters.mail.configuration' 350 or 'services.prometheus.exporters.mail.configFile'. 351 ''; 352 } { 353 assertion = cfg.mysqld.runAsLocalSuperUser -> config.services.mysql.enable; 354 message = '' 355 The exporter is configured to run as 'services.mysql.user', but 356 'services.mysql.enable' is set to false. 357 ''; 358 } { 359 assertion = cfg.nextcloud.enable -> ( 360 (cfg.nextcloud.passwordFile == null) != (cfg.nextcloud.tokenFile == null) 361 ); 362 message = '' 363 Please specify either 'services.prometheus.exporters.nextcloud.passwordFile' or 364 'services.prometheus.exporters.nextcloud.tokenFile' 365 ''; 366 } { 367 assertion = cfg.pgbouncer.enable -> ( 368 (cfg.pgbouncer.connectionStringFile != null || cfg.pgbouncer.connectionString != "") 369 ); 370 message = '' 371 PgBouncer exporter needs either connectionStringFile or connectionString configured" 372 ''; 373 } { 374 assertion = cfg.pgbouncer.enable -> ( 375 config.services.pgbouncer.ignoreStartupParameters != null && builtins.match ".*extra_float_digits.*" config.services.pgbouncer.ignoreStartupParameters != null 376 ); 377 message = '' 378 Prometheus PgBouncer exporter requires including `extra_float_digits` in services.pgbouncer.ignoreStartupParameters 379 380 Example: 381 services.pgbouncer.ignoreStartupParameters = extra_float_digits; 382 383 See https://github.com/prometheus-community/pgbouncer_exporter#pgbouncer-configuration 384 ''; 385 } { 386 assertion = cfg.sql.enable -> ( 387 (cfg.sql.configFile == null) != (cfg.sql.configuration == null) 388 ); 389 message = '' 390 Please specify either 'services.prometheus.exporters.sql.configuration' or 391 'services.prometheus.exporters.sql.configFile' 392 ''; 393 } { 394 assertion = cfg.scaphandre.enable -> (pkgs.stdenv.targetPlatform.isx86_64 == true); 395 message = '' 396 Scaphandre only support x86_64 architectures. 397 ''; 398 } { 399 assertion = cfg.scaphandre.enable -> ((lib.kernel.whenHelpers pkgs.linux.version).whenOlder "5.11" true).condition == false; 400 message = '' 401 Scaphandre requires a kernel version newer than '5.11', '${pkgs.linux.version}' given. 402 ''; 403 } { 404 assertion = cfg.scaphandre.enable -> (builtins.elem "intel_rapl_common" config.boot.kernelModules); 405 message = '' 406 Scaphandre needs 'intel_rapl_common' kernel module to be enabled. Please add it in 'boot.kernelModules'. 407 ''; 408 } { 409 assertion = cfg.idrac.enable -> ( 410 (cfg.idrac.configurationPath == null) != (cfg.idrac.configuration == null) 411 ); 412 message = '' 413 Please ensure you have either `services.prometheus.exporters.idrac.configuration' 414 or `services.prometheus.exporters.idrac.configurationPath' set! 415 ''; 416 } ] ++ (flip map (attrNames exporterOpts) (exporter: { 417 assertion = cfg.${exporter}.firewallFilter != null -> cfg.${exporter}.openFirewall; 418 message = '' 419 The `firewallFilter'-option of exporter ${exporter} doesn't have any effect unless 420 `openFirewall' is set to `true'! 421 ''; 422 })) ++ config.services.prometheus.exporters.assertions; 423 warnings = [ 424 (mkIf (config.services.prometheus.exporters.idrac.enable && config.services.prometheus.exporters.idrac.configurationPath != null) '' 425 Configuration file in `services.prometheus.exporters.idrac.configurationPath` may override 426 `services.prometheus.exporters.idrac.listenAddress` and/or `services.prometheus.exporters.idrac.port`. 427 Consider using `services.prometheus.exporters.idrac.configuration` instead. 428 '' 429 ) 430 (mkIf 431 (cfg.pgbouncer.enable && cfg.pgbouncer.connectionString != "") '' 432 config.services.prometheus.exporters.pgbouncer.connectionString is insecure. Use connectionStringFile instead. 433 '' 434 ) 435 (mkIf 436 (cfg.pgbouncer.enable && config.services.pgbouncer.authType != "any") '' 437 Admin user (with password or passwordless) MUST exist in the services.pgbouncer.authFile if authType other than any is used. 438 '' 439 ) 440 ] ++ config.services.prometheus.exporters.warnings; 441 }] ++ [(mkIf config.services.minio.enable { 442 services.prometheus.exporters.minio.minioAddress = mkDefault "http://localhost:9000"; 443 services.prometheus.exporters.minio.minioAccessKey = mkDefault config.services.minio.accessKey; 444 services.prometheus.exporters.minio.minioAccessSecret = mkDefault config.services.minio.secretKey; 445 })] ++ [(mkIf config.services.prometheus.exporters.rtl_433.enable { 446 hardware.rtl-sdr.enable = mkDefault true; 447 })] ++ [(mkIf config.services.postfix.enable { 448 services.prometheus.exporters.postfix.group = mkDefault config.services.postfix.setgidGroup; 449 })] ++ (mapAttrsToList (name: conf: 450 mkExporterConf { 451 inherit name; 452 inherit (conf) serviceOpts; 453 conf = cfg.${name}; 454 }) exporterOpts) 455 ); 456 457 meta = { 458 doc = ./exporters.md; 459 maintainers = [ maintainers.willibutz ]; 460 }; 461}