at 23.11-pre 11 kB view raw
1{ config, pkgs, lib, options, ... }: 2 3let 4 inherit (lib) concatStrings foldl foldl' genAttrs literalExpression maintainers 5 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 "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 "nut" 55 "openldap" 56 "openvpn" 57 "pihole" 58 "postfix" 59 "postgres" 60 "process" 61 "pve" 62 "py-air-control" 63 "redis" 64 "rspamd" 65 "rtl_433" 66 "script" 67 "shelly" 68 "snmp" 69 "smartctl" 70 "smokeping" 71 "sql" 72 "statsd" 73 "surfboard" 74 "systemd" 75 "tor" 76 "unbound" 77 "unifi" 78 "unpoller" 79 "v2ray" 80 "varnish" 81 "wireguard" 82 "flow" 83 "zfs" 84 ] (name: 85 import (./. + "/exporters/${name}.nix") { inherit config lib pkgs options; } 86 ); 87 88 mkExporterOpts = ({ name, port }: { 89 enable = mkEnableOption (lib.mdDoc "the prometheus ${name} exporter"); 90 port = mkOption { 91 type = types.port; 92 default = port; 93 description = lib.mdDoc '' 94 Port to listen on. 95 ''; 96 }; 97 listenAddress = mkOption { 98 type = types.str; 99 default = "0.0.0.0"; 100 description = lib.mdDoc '' 101 Address to listen on. 102 ''; 103 }; 104 extraFlags = mkOption { 105 type = types.listOf types.str; 106 default = []; 107 description = lib.mdDoc '' 108 Extra commandline options to pass to the ${name} exporter. 109 ''; 110 }; 111 openFirewall = mkOption { 112 type = types.bool; 113 default = false; 114 description = lib.mdDoc '' 115 Open port in firewall for incoming connections. 116 ''; 117 }; 118 firewallFilter = mkOption { 119 type = types.nullOr types.str; 120 default = null; 121 example = literalExpression '' 122 "-i eth0 -p tcp -m tcp --dport ${toString port}" 123 ''; 124 description = lib.mdDoc '' 125 Specify a filter for iptables to use when 126 {option}`services.prometheus.exporters.${name}.openFirewall` 127 is true. It is used as `ip46tables -I nixos-fw firewallFilter -j nixos-fw-accept`. 128 ''; 129 }; 130 user = mkOption { 131 type = types.str; 132 default = "${name}-exporter"; 133 description = lib.mdDoc '' 134 User name under which the ${name} exporter shall be run. 135 ''; 136 }; 137 group = mkOption { 138 type = types.str; 139 default = "${name}-exporter"; 140 description = lib.mdDoc '' 141 Group under which the ${name} exporter shall be run. 142 ''; 143 }; 144 }); 145 146 mkSubModule = { name, port, extraOpts, imports }: { 147 ${name} = mkOption { 148 type = types.submodule [{ 149 inherit imports; 150 options = (mkExporterOpts { 151 inherit name port; 152 } // extraOpts); 153 } ({ config, ... }: mkIf config.openFirewall { 154 firewallFilter = mkDefault "-p tcp -m tcp --dport ${toString config.port}"; 155 })]; 156 internal = true; 157 default = {}; 158 }; 159 }; 160 161 mkSubModules = (foldl' (a: b: a//b) {} 162 (mapAttrsToList (name: opts: mkSubModule { 163 inherit name; 164 inherit (opts) port; 165 extraOpts = opts.extraOpts or {}; 166 imports = opts.imports or []; 167 }) exporterOpts) 168 ); 169 170 mkExporterConf = { name, conf, serviceOpts }: 171 let 172 enableDynamicUser = serviceOpts.serviceConfig.DynamicUser or true; 173 in 174 mkIf conf.enable { 175 warnings = conf.warnings or []; 176 users.users."${name}-exporter" = (mkIf (conf.user == "${name}-exporter" && !enableDynamicUser) { 177 description = "Prometheus ${name} exporter service user"; 178 isSystemUser = true; 179 inherit (conf) group; 180 }); 181 users.groups = (mkIf (conf.group == "${name}-exporter" && !enableDynamicUser) { 182 "${name}-exporter" = {}; 183 }); 184 networking.firewall.extraCommands = mkIf conf.openFirewall (concatStrings [ 185 "ip46tables -A nixos-fw ${conf.firewallFilter} " 186 "-m comment --comment ${name}-exporter -j nixos-fw-accept" 187 ]); 188 systemd.services."prometheus-${name}-exporter" = mkMerge ([{ 189 wantedBy = [ "multi-user.target" ]; 190 after = [ "network.target" ]; 191 serviceConfig.Restart = mkDefault "always"; 192 serviceConfig.PrivateTmp = mkDefault true; 193 serviceConfig.WorkingDirectory = mkDefault /tmp; 194 serviceConfig.DynamicUser = mkDefault enableDynamicUser; 195 serviceConfig.User = mkDefault conf.user; 196 serviceConfig.Group = conf.group; 197 # Hardening 198 serviceConfig.CapabilityBoundingSet = mkDefault [ "" ]; 199 serviceConfig.DeviceAllow = [ "" ]; 200 serviceConfig.LockPersonality = true; 201 serviceConfig.MemoryDenyWriteExecute = true; 202 serviceConfig.NoNewPrivileges = true; 203 serviceConfig.PrivateDevices = mkDefault true; 204 serviceConfig.ProtectClock = mkDefault true; 205 serviceConfig.ProtectControlGroups = true; 206 serviceConfig.ProtectHome = true; 207 serviceConfig.ProtectHostname = true; 208 serviceConfig.ProtectKernelLogs = true; 209 serviceConfig.ProtectKernelModules = true; 210 serviceConfig.ProtectKernelTunables = true; 211 serviceConfig.ProtectSystem = mkDefault "strict"; 212 serviceConfig.RemoveIPC = true; 213 serviceConfig.RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ]; 214 serviceConfig.RestrictNamespaces = true; 215 serviceConfig.RestrictRealtime = true; 216 serviceConfig.RestrictSUIDSGID = true; 217 serviceConfig.SystemCallArchitectures = "native"; 218 serviceConfig.UMask = "0077"; 219 } serviceOpts ]); 220 }; 221in 222{ 223 224 imports = (lib.forEach [ "blackboxExporter" "collectdExporter" "fritzboxExporter" 225 "jsonExporter" "minioExporter" "nginxExporter" "nodeExporter" 226 "snmpExporter" "unifiExporter" "varnishExporter" ] 227 (opt: lib.mkRemovedOptionModule [ "services" "prometheus" "${opt}" ] '' 228 The prometheus exporters are now configured using `services.prometheus.exporters'. 229 See the 18.03 release notes for more information. 230 '' )); 231 232 options.services.prometheus.exporters = mkOption { 233 type = types.submodule { 234 options = (mkSubModules); 235 imports = [ 236 ../../../misc/assertions.nix 237 (lib.mkRenamedOptionModule [ "unifi-poller" ] [ "unpoller" ]) 238 ]; 239 }; 240 description = lib.mdDoc "Prometheus exporter configuration"; 241 default = {}; 242 example = literalExpression '' 243 { 244 node = { 245 enable = true; 246 enabledCollectors = [ "systemd" ]; 247 }; 248 varnish.enable = true; 249 } 250 ''; 251 }; 252 253 config = mkMerge ([{ 254 assertions = [ { 255 assertion = cfg.ipmi.enable -> (cfg.ipmi.configFile != null) -> ( 256 !(lib.hasPrefix "/tmp/" cfg.ipmi.configFile) 257 ); 258 message = '' 259 Config file specified in `services.prometheus.exporters.ipmi.configFile' must 260 not reside within /tmp - it won't be visible to the systemd service. 261 ''; 262 } { 263 assertion = cfg.ipmi.enable -> (cfg.ipmi.webConfigFile != null) -> ( 264 !(lib.hasPrefix "/tmp/" cfg.ipmi.webConfigFile) 265 ); 266 message = '' 267 Config file specified in `services.prometheus.exporters.ipmi.webConfigFile' must 268 not reside within /tmp - it won't be visible to the systemd service. 269 ''; 270 } { 271 assertion = cfg.snmp.enable -> ( 272 (cfg.snmp.configurationPath == null) != (cfg.snmp.configuration == null) 273 ); 274 message = '' 275 Please ensure you have either `services.prometheus.exporters.snmp.configuration' 276 or `services.prometheus.exporters.snmp.configurationPath' set! 277 ''; 278 } { 279 assertion = cfg.mikrotik.enable -> ( 280 (cfg.mikrotik.configFile == null) != (cfg.mikrotik.configuration == null) 281 ); 282 message = '' 283 Please specify either `services.prometheus.exporters.mikrotik.configuration' 284 or `services.prometheus.exporters.mikrotik.configFile'. 285 ''; 286 } { 287 assertion = cfg.mail.enable -> ( 288 (cfg.mail.configFile == null) != (cfg.mail.configuration == null) 289 ); 290 message = '' 291 Please specify either 'services.prometheus.exporters.mail.configuration' 292 or 'services.prometheus.exporters.mail.configFile'. 293 ''; 294 } { 295 assertion = cfg.sql.enable -> ( 296 (cfg.sql.configFile == null) != (cfg.sql.configuration == null) 297 ); 298 message = '' 299 Please specify either 'services.prometheus.exporters.sql.configuration' or 300 'services.prometheus.exporters.sql.configFile' 301 ''; 302 } ] ++ (flip map (attrNames exporterOpts) (exporter: { 303 assertion = cfg.${exporter}.firewallFilter != null -> cfg.${exporter}.openFirewall; 304 message = '' 305 The `firewallFilter'-option of exporter ${exporter} doesn't have any effect unless 306 `openFirewall' is set to `true'! 307 ''; 308 })) ++ config.services.prometheus.exporters.assertions; 309 warnings = config.services.prometheus.exporters.warnings; 310 }] ++ [(mkIf config.services.minio.enable { 311 services.prometheus.exporters.minio.minioAddress = mkDefault "http://localhost:9000"; 312 services.prometheus.exporters.minio.minioAccessKey = mkDefault config.services.minio.accessKey; 313 services.prometheus.exporters.minio.minioAccessSecret = mkDefault config.services.minio.secretKey; 314 })] ++ [(mkIf config.services.prometheus.exporters.rtl_433.enable { 315 hardware.rtl-sdr.enable = mkDefault true; 316 })] ++ [(mkIf config.services.postfix.enable { 317 services.prometheus.exporters.postfix.group = mkDefault config.services.postfix.setgidGroup; 318 })] ++ (mapAttrsToList (name: conf: 319 mkExporterConf { 320 inherit name; 321 inherit (conf) serviceOpts; 322 conf = cfg.${name}; 323 }) exporterOpts) 324 ); 325 326 meta = { 327 doc = ./exporters.md; 328 maintainers = [ maintainers.willibutz ]; 329 }; 330}