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