diff options
Diffstat (limited to 'nixpkgs/nixos/modules/services/monitoring/prometheus')
80 files changed, 7492 insertions, 0 deletions
diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/alertmanager-irc-relay.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/alertmanager-irc-relay.nix new file mode 100644 index 000000000000..9b9bafa09441 --- /dev/null +++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/alertmanager-irc-relay.nix @@ -0,0 +1,102 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.prometheus.alertmanagerIrcRelay; + + configFormat = pkgs.formats.yaml { }; + configFile = configFormat.generate "alertmanager-irc-relay.yml" cfg.settings; +in +{ + options.services.prometheus.alertmanagerIrcRelay = { + enable = mkEnableOption (mdDoc "Alertmanager IRC Relay"); + + package = mkPackageOption pkgs "alertmanager-irc-relay" { }; + + extraFlags = mkOption { + type = types.listOf types.str; + default = []; + description = mdDoc "Extra command line options to pass to alertmanager-irc-relay."; + }; + + settings = mkOption { + type = configFormat.type; + example = literalExpression '' + { + http_host = "localhost"; + http_port = 8000; + + irc_host = "irc.example.com"; + irc_port = 7000; + irc_nickname = "myalertbot"; + + irc_channels = [ + { name = "#mychannel"; } + ]; + } + ''; + description = mdDoc '' + Configuration for Alertmanager IRC Relay as a Nix attribute set. + For a reference, check out the + [example configuration](https://github.com/google/alertmanager-irc-relay#configuring-and-running-the-bot) + and the + [source code](https://github.com/google/alertmanager-irc-relay/blob/master/config.go). + + Note: The webhook's URL MUST point to the IRC channel where the message + should be posted. For `#mychannel` from the example, this would be + `http://localhost:8080/mychannel`. + ''; + }; + }; + + config = mkIf cfg.enable { + systemd.services.alertmanager-irc-relay = { + description = "Alertmanager IRC Relay"; + + wantedBy = [ "multi-user.target" ]; + after = [ "network-online.target" ]; + + serviceConfig = { + ExecStart = '' + ${cfg.package}/bin/alertmanager-irc-relay \ + -config ${configFile} \ + ${escapeShellArgs cfg.extraFlags} + ''; + + DynamicUser = true; + NoNewPrivileges = true; + + ProtectProc = "invisible"; + ProtectSystem = "strict"; + ProtectHome = "tmpfs"; + + PrivateTmp = true; + PrivateDevices = true; + PrivateIPC = true; + + ProtectHostname = true; + ProtectClock = true; + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectKernelLogs = true; + ProtectControlGroups = true; + + RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ]; + RestrictRealtime = true; + RestrictSUIDSGID = true; + + SystemCallFilter = [ + "@system-service" + "~@cpu-emulation" + "~@privileged" + "~@reboot" + "~@setuid" + "~@swap" + ]; + }; + }; + }; + + meta.maintainers = [ maintainers.oxzi ]; +} diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/alertmanager.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/alertmanager.nix new file mode 100644 index 000000000000..4fd630015f35 --- /dev/null +++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/alertmanager.nix @@ -0,0 +1,196 @@ +{ config, pkgs, lib, ... }: + +with lib; + +let + cfg = config.services.prometheus.alertmanager; + mkConfigFile = pkgs.writeText "alertmanager.yml" (builtins.toJSON cfg.configuration); + + checkedConfig = file: + if cfg.checkConfig then + pkgs.runCommand "checked-config" { nativeBuildInputs = [ cfg.package ]; } '' + ln -s ${file} $out + amtool check-config $out + '' else file; + + alertmanagerYml = let + yml = if cfg.configText != null then + pkgs.writeText "alertmanager.yml" cfg.configText + else mkConfigFile; + in checkedConfig yml; + + cmdlineArgs = cfg.extraFlags ++ [ + "--config.file /tmp/alert-manager-substituted.yaml" + "--web.listen-address ${cfg.listenAddress}:${toString cfg.port}" + "--log.level ${cfg.logLevel}" + "--storage.path /var/lib/alertmanager" + (toString (map (peer: "--cluster.peer ${peer}:9094") cfg.clusterPeers)) + ] ++ (optional (cfg.webExternalUrl != null) + "--web.external-url ${cfg.webExternalUrl}" + ) ++ (optional (cfg.logFormat != null) + "--log.format ${cfg.logFormat}" + ); +in { + imports = [ + (mkRemovedOptionModule [ "services" "prometheus" "alertmanager" "user" ] "The alertmanager service is now using systemd's DynamicUser mechanism which obviates a user setting.") + (mkRemovedOptionModule [ "services" "prometheus" "alertmanager" "group" ] "The alertmanager service is now using systemd's DynamicUser mechanism which obviates a group setting.") + (mkRemovedOptionModule [ "services" "prometheus" "alertmanagerURL" ] '' + Due to incompatibility, the alertmanagerURL option has been removed, + please use 'services.prometheus.alertmanagers' instead. + '') + ]; + + options = { + services.prometheus.alertmanager = { + enable = mkEnableOption (lib.mdDoc "Prometheus Alertmanager"); + + package = mkPackageOption pkgs "prometheus-alertmanager" { }; + + configuration = mkOption { + type = types.nullOr types.attrs; + default = null; + description = lib.mdDoc '' + Alertmanager configuration as nix attribute set. + ''; + }; + + configText = mkOption { + type = types.nullOr types.lines; + default = null; + description = lib.mdDoc '' + Alertmanager configuration as YAML text. If non-null, this option + defines the text that is written to alertmanager.yml. If null, the + contents of alertmanager.yml is generated from the structured config + options. + ''; + }; + + checkConfig = mkOption { + type = types.bool; + default = true; + description = lib.mdDoc '' + Check configuration with `amtool check-config`. The call to `amtool` is + subject to sandboxing by Nix. + + If you use credentials stored in external files + (`environmentFile`, etc), + they will not be visible to `amtool` + and it will report errors, despite a correct configuration. + ''; + }; + + logFormat = mkOption { + type = types.nullOr types.str; + default = null; + description = lib.mdDoc '' + If set use a syslog logger or JSON logging. + ''; + }; + + logLevel = mkOption { + type = types.enum ["debug" "info" "warn" "error" "fatal"]; + default = "warn"; + description = lib.mdDoc '' + Only log messages with the given severity or above. + ''; + }; + + webExternalUrl = mkOption { + type = types.nullOr types.str; + default = null; + description = lib.mdDoc '' + The URL under which Alertmanager is externally reachable (for example, if Alertmanager is served via a reverse proxy). + Used for generating relative and absolute links back to Alertmanager itself. + If the URL has a path portion, it will be used to prefix all HTTP endoints served by Alertmanager. + If omitted, relevant URL components will be derived automatically. + ''; + }; + + listenAddress = mkOption { + type = types.str; + default = ""; + description = lib.mdDoc '' + Address to listen on for the web interface and API. Empty string will listen on all interfaces. + "localhost" will listen on 127.0.0.1 (but not ::1). + ''; + }; + + port = mkOption { + type = types.port; + default = 9093; + description = lib.mdDoc '' + Port to listen on for the web interface and API. + ''; + }; + + openFirewall = mkOption { + type = types.bool; + default = false; + description = lib.mdDoc '' + Open port in firewall for incoming connections. + ''; + }; + + clusterPeers = mkOption { + type = types.listOf types.str; + default = []; + description = lib.mdDoc '' + Initial peers for HA cluster. + ''; + }; + + extraFlags = mkOption { + type = types.listOf types.str; + default = []; + description = lib.mdDoc '' + Extra commandline options when launching the Alertmanager. + ''; + }; + + environmentFile = mkOption { + type = types.nullOr types.path; + default = null; + example = "/root/alertmanager.env"; + description = lib.mdDoc '' + File to load as environment file. Environment variables + from this file will be interpolated into the config file + using envsubst with this syntax: + `$ENVIRONMENT ''${VARIABLE}` + ''; + }; + }; + }; + + config = mkMerge [ + (mkIf cfg.enable { + assertions = singleton { + assertion = cfg.configuration != null || cfg.configText != null; + message = "Can not enable alertmanager without a configuration. " + + "Set either the `configuration` or `configText` attribute."; + }; + }) + (mkIf cfg.enable { + networking.firewall.allowedTCPPorts = optional cfg.openFirewall cfg.port; + + systemd.services.alertmanager = { + wantedBy = [ "multi-user.target" ]; + after = [ "network-online.target" ]; + preStart = '' + ${lib.getBin pkgs.envsubst}/bin/envsubst -o "/tmp/alert-manager-substituted.yaml" \ + -i "${alertmanagerYml}" + ''; + serviceConfig = { + Restart = "always"; + StateDirectory = "alertmanager"; + DynamicUser = true; # implies PrivateTmp + EnvironmentFile = lib.mkIf (cfg.environmentFile != null) cfg.environmentFile; + WorkingDirectory = "/tmp"; + ExecStart = "${cfg.package}/bin/alertmanager" + + optionalString (length cmdlineArgs != 0) (" \\\n " + + concatStringsSep " \\\n " cmdlineArgs); + ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; + }; + }; + }) + ]; +} diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/default.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/default.nix new file mode 100644 index 000000000000..b4ac8e21451a --- /dev/null +++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/default.nix @@ -0,0 +1,1863 @@ +{ config, pkgs, lib, ... }: + +with lib; + +let + yaml = pkgs.formats.yaml { }; + cfg = config.services.prometheus; + checkConfigEnabled = + (lib.isBool cfg.checkConfig && cfg.checkConfig) + || cfg.checkConfig == "syntax-only"; + + workingDir = "/var/lib/" + cfg.stateDir; + + triggerReload = pkgs.writeShellScriptBin "trigger-reload-prometheus" '' + PATH="${makeBinPath (with pkgs; [ systemd ])}" + if systemctl -q is-active prometheus.service; then + systemctl reload prometheus.service + fi + ''; + + reload = pkgs.writeShellScriptBin "reload-prometheus" '' + PATH="${makeBinPath (with pkgs; [ systemd coreutils gnugrep ])}" + cursor=$(journalctl --show-cursor -n0 | grep -oP "cursor: \K.*") + kill -HUP $MAINPID + journalctl -u prometheus.service --after-cursor="$cursor" -f \ + | grep -m 1 "Completed loading of configuration file" > /dev/null + ''; + + # a wrapper that verifies that the configuration is valid + promtoolCheck = what: name: file: + if checkConfigEnabled then + pkgs.runCommandLocal + "${name}-${replaceStrings [" "] [""] what}-checked" + { nativeBuildInputs = [ cfg.package.cli ]; } '' + ln -s ${file} $out + promtool ${what} $out + '' else file; + + generatedPrometheusYml = yaml.generate "prometheus.yml" promConfig; + + # This becomes the main config file for Prometheus + promConfig = { + global = filterValidPrometheus cfg.globalConfig; + scrape_configs = filterValidPrometheus cfg.scrapeConfigs; + remote_write = filterValidPrometheus cfg.remoteWrite; + remote_read = filterValidPrometheus cfg.remoteRead; + rule_files = optionals (!(cfg.enableAgentMode)) (map (promtoolCheck "check rules" "rules") (cfg.ruleFiles ++ [ + (pkgs.writeText "prometheus.rules" (concatStringsSep "\n" cfg.rules)) + ])); + alerting = { + inherit (cfg) alertmanagers; + }; + }; + + prometheusYml = + let + yml = + if cfg.configText != null then + pkgs.writeText "prometheus.yml" cfg.configText + else generatedPrometheusYml; + in + promtoolCheck "check config ${lib.optionalString (cfg.checkConfig == "syntax-only") "--syntax-only"}" "prometheus.yml" yml; + + cmdlineArgs = cfg.extraFlags ++ [ + "--config.file=${ + if cfg.enableReload + then "/etc/prometheus/prometheus.yaml" + else prometheusYml + }" + "--web.listen-address=${cfg.listenAddress}:${builtins.toString cfg.port}" + ] ++ ( + if (cfg.enableAgentMode) then [ + "--enable-feature=agent" + ] else [ + "--alertmanager.notification-queue-capacity=${toString cfg.alertmanagerNotificationQueueCapacity }" + "--storage.tsdb.path=${workingDir}/data/" + ]) + ++ optional (cfg.webExternalUrl != null) "--web.external-url=${cfg.webExternalUrl}" + ++ optional (cfg.retentionTime != null) "--storage.tsdb.retention.time=${cfg.retentionTime}" + ++ optional (cfg.webConfigFile != null) "--web.config.file=${cfg.webConfigFile}"; + + filterValidPrometheus = filterAttrsListRecursive (n: v: !(n == "_module" || v == null)); + filterAttrsListRecursive = pred: x: + if isAttrs x then + listToAttrs + ( + concatMap + (name: + let v = x.${name}; in + if pred name v then [ + (nameValuePair name (filterAttrsListRecursive pred v)) + ] else [ ] + ) + (attrNames x) + ) + else if isList x then + map (filterAttrsListRecursive pred) x + else x; + + # + # Config types: helper functions + # + + mkDefOpt = type: defaultStr: description: mkOpt type (description + '' + + Defaults to ````${defaultStr}```` in prometheus + when set to `null`. + ''); + + mkOpt = type: description: mkOption { + type = types.nullOr type; + default = null; + description = lib.mdDoc description; + }; + + mkSdConfigModule = extraOptions: types.submodule { + options = { + basic_auth = mkOpt promTypes.basic_auth '' + Optional HTTP basic authentication information. + ''; + + authorization = mkOpt + (types.submodule { + options = { + type = mkDefOpt types.str "Bearer" '' + Sets the authentication type. + ''; + + credentials = mkOpt types.str '' + Sets the credentials. It is mutually exclusive with `credentials_file`. + ''; + + credentials_file = mkOpt types.str '' + Sets the credentials to the credentials read from the configured file. + It is mutually exclusive with `credentials`. + ''; + }; + }) '' + Optional `Authorization` header configuration. + ''; + + oauth2 = mkOpt promtypes.oauth2 '' + Optional OAuth 2.0 configuration. + Cannot be used at the same time as basic_auth or authorization. + ''; + + proxy_url = mkOpt types.str '' + Optional proxy URL. + ''; + + follow_redirects = mkDefOpt types.bool "true" '' + Configure whether HTTP requests follow HTTP 3xx redirects. + ''; + + tls_config = mkOpt promTypes.tls_config '' + TLS configuration. + ''; + } // extraOptions; + }; + + # + # Config types: general + # + + promTypes.globalConfig = types.submodule { + options = { + scrape_interval = mkDefOpt types.str "1m" '' + How frequently to scrape targets by default. + ''; + + scrape_timeout = mkDefOpt types.str "10s" '' + How long until a scrape request times out. + ''; + + evaluation_interval = mkDefOpt types.str "1m" '' + How frequently to evaluate rules by default. + ''; + + external_labels = mkOpt (types.attrsOf types.str) '' + The labels to add to any time series or alerts when + communicating with external systems (federation, remote + storage, Alertmanager). + ''; + }; + }; + + promTypes.basic_auth = types.submodule { + options = { + username = mkOption { + type = types.str; + description = lib.mdDoc '' + HTTP username + ''; + }; + password = mkOpt types.str "HTTP password"; + password_file = mkOpt types.str "HTTP password file"; + }; + }; + + promTypes.tls_config = types.submodule { + options = { + ca_file = mkOpt types.str '' + CA certificate to validate API server certificate with. + ''; + + cert_file = mkOpt types.str '' + Certificate file for client cert authentication to the server. + ''; + + key_file = mkOpt types.str '' + Key file for client cert authentication to the server. + ''; + + server_name = mkOpt types.str '' + ServerName extension to indicate the name of the server. + http://tools.ietf.org/html/rfc4366#section-3.1 + ''; + + insecure_skip_verify = mkOpt types.bool '' + Disable validation of the server certificate. + ''; + }; + }; + + promtypes.oauth2 = types.submodule { + options = { + client_id = mkOpt types.str '' + OAuth client ID. + ''; + + client_secret = mkOpt types.str '' + OAuth client secret. + ''; + + client_secret_file = mkOpt types.str '' + Read the client secret from a file. It is mutually exclusive with `client_secret`. + ''; + + scopes = mkOpt (types.listOf types.str) '' + Scopes for the token request. + ''; + + token_url = mkOpt types.str '' + The URL to fetch the token from. + ''; + + endpoint_params = mkOpt (types.attrsOf types.str) '' + Optional parameters to append to the token URL. + ''; + }; + }; + + promTypes.scrape_config = types.submodule { + options = { + authorization = mkOption { + type = types.nullOr types.attrs; + default = null; + description = lib.mdDoc '' + Sets the `Authorization` header on every scrape request with the configured credentials. + ''; + }; + job_name = mkOption { + type = types.str; + description = lib.mdDoc '' + The job name assigned to scraped metrics by default. + ''; + }; + scrape_interval = mkOpt types.str '' + How frequently to scrape targets from this job. Defaults to the + globally configured default. + ''; + + scrape_timeout = mkOpt types.str '' + Per-target timeout when scraping this job. Defaults to the + globally configured default. + ''; + + metrics_path = mkDefOpt types.str "/metrics" '' + The HTTP resource path on which to fetch metrics from targets. + ''; + + honor_labels = mkDefOpt types.bool "false" '' + Controls how Prometheus handles conflicts between labels + that are already present in scraped data and labels that + Prometheus would attach server-side ("job" and "instance" + labels, manually configured target labels, and labels + generated by service discovery implementations). + + If honor_labels is set to "true", label conflicts are + resolved by keeping label values from the scraped data and + ignoring the conflicting server-side labels. + + If honor_labels is set to "false", label conflicts are + resolved by renaming conflicting labels in the scraped data + to "exported_\<original-label\>" (for example + "exported_instance", "exported_job") and then attaching + server-side labels. This is useful for use cases such as + federation, where all labels specified in the target should + be preserved. + ''; + + honor_timestamps = mkDefOpt types.bool "true" '' + honor_timestamps controls whether Prometheus respects the timestamps present + in scraped data. + + If honor_timestamps is set to `true`, the timestamps of the metrics exposed + by the target will be used. + + If honor_timestamps is set to `false`, the timestamps of the metrics exposed + by the target will be ignored. + ''; + + scheme = mkDefOpt (types.enum [ "http" "https" ]) "http" '' + The URL scheme with which to fetch metrics from targets. + ''; + + params = mkOpt (types.attrsOf (types.listOf types.str)) '' + Optional HTTP URL parameters. + ''; + + basic_auth = mkOpt promTypes.basic_auth '' + Sets the `Authorization` header on every scrape request with the + configured username and password. + password and password_file are mutually exclusive. + ''; + + bearer_token = mkOpt types.str '' + Sets the `Authorization` header on every scrape request with + the configured bearer token. It is mutually exclusive with + {option}`bearer_token_file`. + ''; + + bearer_token_file = mkOpt types.str '' + Sets the `Authorization` header on every scrape request with + the bearer token read from the configured file. It is mutually + exclusive with {option}`bearer_token`. + ''; + + tls_config = mkOpt promTypes.tls_config '' + Configures the scrape request's TLS settings. + ''; + + proxy_url = mkOpt types.str '' + Optional proxy URL. + ''; + + azure_sd_configs = mkOpt (types.listOf promTypes.azure_sd_config) '' + List of Azure service discovery configurations. + ''; + + consul_sd_configs = mkOpt (types.listOf promTypes.consul_sd_config) '' + List of Consul service discovery configurations. + ''; + + digitalocean_sd_configs = mkOpt (types.listOf promTypes.digitalocean_sd_config) '' + List of DigitalOcean service discovery configurations. + ''; + + docker_sd_configs = mkOpt (types.listOf promTypes.docker_sd_config) '' + List of Docker service discovery configurations. + ''; + + dockerswarm_sd_configs = mkOpt (types.listOf promTypes.dockerswarm_sd_config) '' + List of Docker Swarm service discovery configurations. + ''; + + dns_sd_configs = mkOpt (types.listOf promTypes.dns_sd_config) '' + List of DNS service discovery configurations. + ''; + + ec2_sd_configs = mkOpt (types.listOf promTypes.ec2_sd_config) '' + List of EC2 service discovery configurations. + ''; + + eureka_sd_configs = mkOpt (types.listOf promTypes.eureka_sd_config) '' + List of Eureka service discovery configurations. + ''; + + file_sd_configs = mkOpt (types.listOf promTypes.file_sd_config) '' + List of file service discovery configurations. + ''; + + gce_sd_configs = mkOpt (types.listOf promTypes.gce_sd_config) '' + List of Google Compute Engine service discovery configurations. + + See [the relevant Prometheus configuration docs](https://prometheus.io/docs/prometheus/latest/configuration/configuration/#gce_sd_config) + for more detail. + ''; + + hetzner_sd_configs = mkOpt (types.listOf promTypes.hetzner_sd_config) '' + List of Hetzner service discovery configurations. + ''; + + http_sd_configs = mkOpt (types.listOf promTypes.http_sd_config) '' + List of HTTP service discovery configurations. + ''; + + kubernetes_sd_configs = mkOpt (types.listOf promTypes.kubernetes_sd_config) '' + List of Kubernetes service discovery configurations. + ''; + + kuma_sd_configs = mkOpt (types.listOf promTypes.kuma_sd_config) '' + List of Kuma service discovery configurations. + ''; + + lightsail_sd_configs = mkOpt (types.listOf promTypes.lightsail_sd_config) '' + List of Lightsail service discovery configurations. + ''; + + linode_sd_configs = mkOpt (types.listOf promTypes.linode_sd_config) '' + List of Linode service discovery configurations. + ''; + + marathon_sd_configs = mkOpt (types.listOf promTypes.marathon_sd_config) '' + List of Marathon service discovery configurations. + ''; + + nerve_sd_configs = mkOpt (types.listOf promTypes.nerve_sd_config) '' + List of AirBnB's Nerve service discovery configurations. + ''; + + openstack_sd_configs = mkOpt (types.listOf promTypes.openstack_sd_config) '' + List of OpenStack service discovery configurations. + ''; + + puppetdb_sd_configs = mkOpt (types.listOf promTypes.puppetdb_sd_config) '' + List of PuppetDB service discovery configurations. + ''; + + scaleway_sd_configs = mkOpt (types.listOf promTypes.scaleway_sd_config) '' + List of Scaleway service discovery configurations. + ''; + + serverset_sd_configs = mkOpt (types.listOf promTypes.serverset_sd_config) '' + List of Zookeeper Serverset service discovery configurations. + ''; + + triton_sd_configs = mkOpt (types.listOf promTypes.triton_sd_config) '' + List of Triton Serverset service discovery configurations. + ''; + + uyuni_sd_configs = mkOpt (types.listOf promTypes.uyuni_sd_config) '' + List of Uyuni Serverset service discovery configurations. + ''; + + static_configs = mkOpt (types.listOf promTypes.static_config) '' + List of labeled target groups for this job. + ''; + + relabel_configs = mkOpt (types.listOf promTypes.relabel_config) '' + List of relabel configurations. + ''; + + metric_relabel_configs = mkOpt (types.listOf promTypes.relabel_config) '' + List of metric relabel configurations. + ''; + + body_size_limit = mkDefOpt types.str "0" '' + An uncompressed response body larger than this many bytes will cause the + scrape to fail. 0 means no limit. Example: 100MB. + This is an experimental feature, this behaviour could + change or be removed in the future. + ''; + + sample_limit = mkDefOpt types.int "0" '' + Per-scrape limit on number of scraped samples that will be accepted. + If more than this number of samples are present after metric relabelling + the entire scrape will be treated as failed. 0 means no limit. + ''; + + label_limit = mkDefOpt types.int "0" '' + Per-scrape limit on number of labels that will be accepted for a sample. If + more than this number of labels are present post metric-relabeling, the + entire scrape will be treated as failed. 0 means no limit. + ''; + + label_name_length_limit = mkDefOpt types.int "0" '' + Per-scrape limit on length of labels name that will be accepted for a sample. + If a label name is longer than this number post metric-relabeling, the entire + scrape will be treated as failed. 0 means no limit. + ''; + + label_value_length_limit = mkDefOpt types.int "0" '' + Per-scrape limit on length of labels value that will be accepted for a sample. + If a label value is longer than this number post metric-relabeling, the + entire scrape will be treated as failed. 0 means no limit. + ''; + + target_limit = mkDefOpt types.int "0" '' + Per-scrape config limit on number of unique targets that will be + accepted. If more than this number of targets are present after target + relabeling, Prometheus will mark the targets as failed without scraping them. + 0 means no limit. This is an experimental feature, this behaviour could + change in the future. + ''; + }; + }; + + # + # Config types: service discovery + # + + # For this one, the docs actually define all types needed to use mkSdConfigModule, but a bunch + # of them are marked with 'currently not support by Azure' so we don't bother adding them in + # here. + promTypes.azure_sd_config = types.submodule { + options = { + environment = mkDefOpt types.str "AzurePublicCloud" '' + The Azure environment. + ''; + + authentication_method = mkDefOpt (types.enum [ "OAuth" "ManagedIdentity" ]) "OAuth" '' + The authentication method, either OAuth or ManagedIdentity. + See https://docs.microsoft.com/en-us/azure/active-directory/managed-identities-azure-resources/overview + ''; + + subscription_id = mkOption { + type = types.str; + description = lib.mdDoc '' + The subscription ID. + ''; + }; + + tenant_id = mkOpt types.str '' + Optional tenant ID. Only required with authentication_method OAuth. + ''; + + client_id = mkOpt types.str '' + Optional client ID. Only required with authentication_method OAuth. + ''; + + client_secret = mkOpt types.str '' + Optional client secret. Only required with authentication_method OAuth. + ''; + + refresh_interval = mkDefOpt types.str "300s" '' + Refresh interval to re-read the instance list. + ''; + + port = mkDefOpt types.int "80" '' + The port to scrape metrics from. If using the public IP + address, this must instead be specified in the relabeling + rule. + ''; + + proxy_url = mkOpt types.str '' + Optional proxy URL. + ''; + + follow_redirects = mkDefOpt types.bool "true" '' + Configure whether HTTP requests follow HTTP 3xx redirects. + ''; + + tls_config = mkOpt promTypes.tls_config '' + TLS configuration. + ''; + }; + }; + + promTypes.consul_sd_config = mkSdConfigModule { + server = mkDefOpt types.str "localhost:8500" '' + Consul server to query. + ''; + + token = mkOpt types.str "Consul token"; + + datacenter = mkOpt types.str "Consul datacenter"; + + scheme = mkDefOpt types.str "http" "Consul scheme"; + + username = mkOpt types.str "Consul username"; + + password = mkOpt types.str "Consul password"; + + tls_config = mkOpt promTypes.tls_config '' + Configures the Consul request's TLS settings. + ''; + + services = mkOpt (types.listOf types.str) '' + A list of services for which targets are retrieved. + ''; + + tags = mkOpt (types.listOf types.str) '' + An optional list of tags used to filter nodes for a given + service. Services must contain all tags in the list. + ''; + + node_meta = mkOpt (types.attrsOf types.str) '' + Node metadata used to filter nodes for a given service. + ''; + + tag_separator = mkDefOpt types.str "," '' + The string by which Consul tags are joined into the tag label. + ''; + + allow_stale = mkOpt types.bool '' + Allow stale Consul results + (see <https://www.consul.io/api/index.html#consistency-modes>). + + Will reduce load on Consul. + ''; + + refresh_interval = mkDefOpt types.str "30s" '' + The time after which the provided names are refreshed. + + On large setup it might be a good idea to increase this value + because the catalog will change all the time. + ''; + }; + + promTypes.digitalocean_sd_config = mkSdConfigModule { + port = mkDefOpt types.int "80" '' + The port to scrape metrics from. + ''; + + refresh_interval = mkDefOpt types.str "60s" '' + The time after which the droplets are refreshed. + ''; + }; + + mkDockerSdConfigModule = extraOptions: mkSdConfigModule ({ + host = mkOption { + type = types.str; + description = lib.mdDoc '' + Address of the Docker daemon. + ''; + }; + + port = mkDefOpt types.int "80" '' + The port to scrape metrics from, when `role` is nodes, and for discovered + tasks and services that don't have published ports. + ''; + + filters = mkOpt + (types.listOf (types.submodule { + options = { + name = mkOption { + type = types.str; + description = lib.mdDoc '' + Name of the filter. The available filters are listed in the upstream documentation: + Services: <https://docs.docker.com/engine/api/v1.40/#operation/ServiceList> + Tasks: <https://docs.docker.com/engine/api/v1.40/#operation/TaskList> + Nodes: <https://docs.docker.com/engine/api/v1.40/#operation/NodeList> + ''; + }; + values = mkOption { + type = types.str; + description = lib.mdDoc '' + Value for the filter. + ''; + }; + }; + })) '' + Optional filters to limit the discovery process to a subset of available resources. + ''; + + refresh_interval = mkDefOpt types.str "60s" '' + The time after which the containers are refreshed. + ''; + } // extraOptions); + + promTypes.docker_sd_config = mkDockerSdConfigModule { + host_networking_host = mkDefOpt types.str "localhost" '' + The host to use if the container is in host networking mode. + ''; + }; + + promTypes.dockerswarm_sd_config = mkDockerSdConfigModule { + role = mkOption { + type = types.enum [ "services" "tasks" "nodes" ]; + description = lib.mdDoc '' + Role of the targets to retrieve. Must be `services`, `tasks`, or `nodes`. + ''; + }; + }; + + promTypes.dns_sd_config = types.submodule { + options = { + names = mkOption { + type = types.listOf types.str; + description = lib.mdDoc '' + A list of DNS SRV record names to be queried. + ''; + }; + + type = mkDefOpt (types.enum [ "SRV" "A" "AAAA" ]) "SRV" '' + The type of DNS query to perform. One of SRV, A, or AAAA. + ''; + + port = mkOpt types.int '' + The port number used if the query type is not SRV. + ''; + + refresh_interval = mkDefOpt types.str "30s" '' + The time after which the provided names are refreshed. + ''; + }; + }; + + promTypes.ec2_sd_config = types.submodule { + options = { + region = mkOption { + type = types.str; + description = lib.mdDoc '' + The AWS Region. If blank, the region from the instance metadata is used. + ''; + }; + endpoint = mkOpt types.str '' + Custom endpoint to be used. + ''; + + access_key = mkOpt types.str '' + The AWS API key id. If blank, the environment variable + `AWS_ACCESS_KEY_ID` is used. + ''; + + secret_key = mkOpt types.str '' + The AWS API key secret. If blank, the environment variable + `AWS_SECRET_ACCESS_KEY` is used. + ''; + + profile = mkOpt types.str '' + Named AWS profile used to connect to the API. + ''; + + role_arn = mkOpt types.str '' + AWS Role ARN, an alternative to using AWS API keys. + ''; + + refresh_interval = mkDefOpt types.str "60s" '' + Refresh interval to re-read the instance list. + ''; + + port = mkDefOpt types.int "80" '' + The port to scrape metrics from. If using the public IP + address, this must instead be specified in the relabeling + rule. + ''; + + filters = mkOpt + (types.listOf (types.submodule { + options = { + name = mkOption { + type = types.str; + description = lib.mdDoc '' + See [this list](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeInstances.html) + for the available filters. + ''; + }; + + values = mkOption { + type = types.listOf types.str; + default = [ ]; + description = lib.mdDoc '' + Value of the filter. + ''; + }; + }; + })) '' + Filters can be used optionally to filter the instance list by other criteria. + ''; + }; + }; + + promTypes.eureka_sd_config = mkSdConfigModule { + server = mkOption { + type = types.str; + description = lib.mdDoc '' + The URL to connect to the Eureka server. + ''; + }; + }; + + promTypes.file_sd_config = types.submodule { + options = { + files = mkOption { + type = types.listOf types.str; + description = lib.mdDoc '' + Patterns for files from which target groups are extracted. Refer + to the Prometheus documentation for permitted filename patterns + and formats. + ''; + }; + + refresh_interval = mkDefOpt types.str "5m" '' + Refresh interval to re-read the files. + ''; + }; + }; + + promTypes.gce_sd_config = types.submodule { + options = { + # Use `mkOption` instead of `mkOpt` for project and zone because they are + # required configuration values for `gce_sd_config`. + project = mkOption { + type = types.str; + description = lib.mdDoc '' + The GCP Project. + ''; + }; + + zone = mkOption { + type = types.str; + description = lib.mdDoc '' + The zone of the scrape targets. If you need multiple zones use multiple + gce_sd_configs. + ''; + }; + + filter = mkOpt types.str '' + Filter can be used optionally to filter the instance list by other + criteria Syntax of this filter string is described here in the filter + query parameter section: <https://cloud.google.com/compute/docs/reference/latest/instances/list>. + ''; + + refresh_interval = mkDefOpt types.str "60s" '' + Refresh interval to re-read the cloud instance list. + ''; + + port = mkDefOpt types.port "80" '' + The port to scrape metrics from. If using the public IP address, this + must instead be specified in the relabeling rule. + ''; + + tag_separator = mkDefOpt types.str "," '' + The tag separator used to separate concatenated GCE instance network tags. + + See the GCP documentation on network tags for more information: + <https://cloud.google.com/vpc/docs/add-remove-network-tags> + ''; + }; + }; + + promTypes.hetzner_sd_config = mkSdConfigModule { + role = mkOption { + type = types.enum [ "robot" "hcloud" ]; + description = lib.mdDoc '' + The Hetzner role of entities that should be discovered. + One of `robot` or `hcloud`. + ''; + }; + + port = mkDefOpt types.int "80" '' + The port to scrape metrics from. + ''; + + refresh_interval = mkDefOpt types.str "60s" '' + The time after which the servers are refreshed. + ''; + }; + + promTypes.http_sd_config = types.submodule { + options = { + url = mkOption { + type = types.str; + description = lib.mdDoc '' + URL from which the targets are fetched. + ''; + }; + + refresh_interval = mkDefOpt types.str "60s" '' + Refresh interval to re-query the endpoint. + ''; + + basic_auth = mkOpt promTypes.basic_auth '' + Authentication information used to authenticate to the API server. + password and password_file are mutually exclusive. + ''; + + proxy_url = mkOpt types.str '' + Optional proxy URL. + ''; + + follow_redirects = mkDefOpt types.bool "true" '' + Configure whether HTTP requests follow HTTP 3xx redirects. + ''; + + tls_config = mkOpt promTypes.tls_config '' + Configures the scrape request's TLS settings. + ''; + }; + }; + + promTypes.kubernetes_sd_config = mkSdConfigModule { + api_server = mkOpt types.str '' + The API server addresses. If left empty, Prometheus is assumed to run inside + of the cluster and will discover API servers automatically and use the pod's + CA certificate and bearer token file at /var/run/secrets/kubernetes.io/serviceaccount/. + ''; + + role = mkOption { + type = types.enum [ "endpoints" "service" "pod" "node" "ingress" ]; + description = lib.mdDoc '' + The Kubernetes role of entities that should be discovered. + One of endpoints, service, pod, node, or ingress. + ''; + }; + + kubeconfig_file = mkOpt types.str '' + Optional path to a kubeconfig file. + Note that api_server and kube_config are mutually exclusive. + ''; + + namespaces = mkOpt + ( + types.submodule { + options = { + names = mkOpt (types.listOf types.str) '' + Namespace name. + ''; + }; + } + ) '' + Optional namespace discovery. If omitted, all namespaces are used. + ''; + + selectors = mkOpt + ( + types.listOf ( + types.submodule { + options = { + role = mkOption { + type = types.str; + description = lib.mdDoc '' + Selector role + ''; + }; + + label = mkOpt types.str '' + Selector label + ''; + + field = mkOpt types.str '' + Selector field + ''; + }; + } + ) + ) '' + Optional label and field selectors to limit the discovery process to a subset of available resources. + See https://kubernetes.io/docs/concepts/overview/working-with-objects/field-selectors/ + and https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ to learn more about the possible + filters that can be used. Endpoints role supports pod, service and endpoints selectors, other roles + only support selectors matching the role itself (e.g. node role can only contain node selectors). + + Note: When making decision about using field/label selector make sure that this + is the best approach - it will prevent Prometheus from reusing single list/watch + for all scrape configs. This might result in a bigger load on the Kubernetes API, + because per each selector combination there will be additional LIST/WATCH. On the other hand, + if you just want to monitor small subset of pods in large cluster it's recommended to use selectors. + Decision, if selectors should be used or not depends on the particular situation. + ''; + }; + + promTypes.kuma_sd_config = mkSdConfigModule { + server = mkOption { + type = types.str; + description = lib.mdDoc '' + Address of the Kuma Control Plane's MADS xDS server. + ''; + }; + + refresh_interval = mkDefOpt types.str "30s" '' + The time to wait between polling update requests. + ''; + + fetch_timeout = mkDefOpt types.str "2m" '' + The time after which the monitoring assignments are refreshed. + ''; + }; + + promTypes.lightsail_sd_config = types.submodule { + options = { + region = mkOpt types.str '' + The AWS region. If blank, the region from the instance metadata is used. + ''; + + endpoint = mkOpt types.str '' + Custom endpoint to be used. + ''; + + access_key = mkOpt types.str '' + The AWS API keys. If blank, the environment variable `AWS_ACCESS_KEY_ID` is used. + ''; + + secret_key = mkOpt types.str '' + The AWS API keys. If blank, the environment variable `AWS_SECRET_ACCESS_KEY` is used. + ''; + + profile = mkOpt types.str '' + Named AWS profile used to connect to the API. + ''; + + role_arn = mkOpt types.str '' + AWS Role ARN, an alternative to using AWS API keys. + ''; + + refresh_interval = mkDefOpt types.str "60s" '' + Refresh interval to re-read the instance list. + ''; + + port = mkDefOpt types.int "80" '' + The port to scrape metrics from. If using the public IP address, this must + instead be specified in the relabeling rule. + ''; + }; + }; + + promTypes.linode_sd_config = mkSdConfigModule { + port = mkDefOpt types.int "80" '' + The port to scrape metrics from. + ''; + + tag_separator = mkDefOpt types.str "," '' + The string by which Linode Instance tags are joined into the tag label. + ''; + + refresh_interval = mkDefOpt types.str "60s" '' + The time after which the linode instances are refreshed. + ''; + }; + + promTypes.marathon_sd_config = mkSdConfigModule { + servers = mkOption { + type = types.listOf types.str; + description = lib.mdDoc '' + List of URLs to be used to contact Marathon servers. You need to provide at least one server URL. + ''; + }; + + refresh_interval = mkDefOpt types.str "30s" '' + Polling interval. + ''; + + auth_token = mkOpt types.str '' + Optional authentication information for token-based authentication: + <https://docs.mesosphere.com/1.11/security/ent/iam-api/#passing-an-authentication-token> + It is mutually exclusive with `auth_token_file` and other authentication mechanisms. + ''; + + auth_token_file = mkOpt types.str '' + Optional authentication information for token-based authentication: + <https://docs.mesosphere.com/1.11/security/ent/iam-api/#passing-an-authentication-token> + It is mutually exclusive with `auth_token` and other authentication mechanisms. + ''; + }; + + promTypes.nerve_sd_config = types.submodule { + options = { + servers = mkOption { + type = types.listOf types.str; + description = lib.mdDoc '' + The Zookeeper servers. + ''; + }; + + paths = mkOption { + type = types.listOf types.str; + description = lib.mdDoc '' + Paths can point to a single service, or the root of a tree of services. + ''; + }; + + timeout = mkDefOpt types.str "10s" '' + Timeout value. + ''; + }; + }; + + promTypes.openstack_sd_config = types.submodule { + options = + let + userDescription = '' + username is required if using Identity V2 API. Consult with your provider's + control panel to discover your account's username. In Identity V3, either + userid or a combination of username and domain_id or domain_name are needed. + ''; + + domainDescription = '' + At most one of domain_id and domain_name must be provided if using username + with Identity V3. Otherwise, either are optional. + ''; + + projectDescription = '' + The project_id and project_name fields are optional for the Identity V2 API. + Some providers allow you to specify a project_name instead of the project_id. + Some require both. Your provider's authentication policies will determine + how these fields influence authentication. + ''; + + applicationDescription = '' + The application_credential_id or application_credential_name fields are + required if using an application credential to authenticate. Some providers + allow you to create an application credential to authenticate rather than a + password. + ''; + in + { + role = mkOption { + type = types.str; + description = lib.mdDoc '' + The OpenStack role of entities that should be discovered. + ''; + }; + + region = mkOption { + type = types.str; + description = lib.mdDoc '' + The OpenStack Region. + ''; + }; + + identity_endpoint = mkOpt types.str '' + identity_endpoint specifies the HTTP endpoint that is required to work with + the Identity API of the appropriate version. While it's ultimately needed by + all of the identity services, it will often be populated by a provider-level + function. + ''; + + username = mkOpt types.str userDescription; + userid = mkOpt types.str userDescription; + + password = mkOpt types.str '' + password for the Identity V2 and V3 APIs. Consult with your provider's + control panel to discover your account's preferred method of authentication. + ''; + + domain_name = mkOpt types.str domainDescription; + domain_id = mkOpt types.str domainDescription; + + project_name = mkOpt types.str projectDescription; + project_id = mkOpt types.str projectDescription; + + application_credential_name = mkOpt types.str applicationDescription; + application_credential_id = mkOpt types.str applicationDescription; + + application_credential_secret = mkOpt types.str '' + The application_credential_secret field is required if using an application + credential to authenticate. + ''; + + all_tenants = mkDefOpt types.bool "false" '' + Whether the service discovery should list all instances for all projects. + It is only relevant for the 'instance' role and usually requires admin permissions. + ''; + + refresh_interval = mkDefOpt types.str "60s" '' + Refresh interval to re-read the instance list. + ''; + + port = mkDefOpt types.int "80" '' + The port to scrape metrics from. If using the public IP address, this must + instead be specified in the relabeling rule. + ''; + + availability = mkDefOpt (types.enum [ "public" "admin" "internal" ]) "public" '' + The availability of the endpoint to connect to. Must be one of public, admin or internal. + ''; + + tls_config = mkOpt promTypes.tls_config '' + TLS configuration. + ''; + }; + }; + + promTypes.puppetdb_sd_config = mkSdConfigModule { + url = mkOption { + type = types.str; + description = lib.mdDoc '' + The URL of the PuppetDB root query endpoint. + ''; + }; + + query = mkOption { + type = types.str; + description = lib.mdDoc '' + Puppet Query Language (PQL) query. Only resources are supported. + https://puppet.com/docs/puppetdb/latest/api/query/v4/pql.html + ''; + }; + + include_parameters = mkDefOpt types.bool "false" '' + Whether to include the parameters as meta labels. + Due to the differences between parameter types and Prometheus labels, + some parameters might not be rendered. The format of the parameters might + also change in future releases. + + Note: Enabling this exposes parameters in the Prometheus UI and API. Make sure + that you don't have secrets exposed as parameters if you enable this. + ''; + + refresh_interval = mkDefOpt types.str "60s" '' + Refresh interval to re-read the resources list. + ''; + + port = mkDefOpt types.int "80" '' + The port to scrape metrics from. + ''; + }; + + promTypes.scaleway_sd_config = types.submodule { + options = { + access_key = mkOption { + type = types.str; + description = lib.mdDoc '' + Access key to use. https://console.scaleway.com/project/credentials + ''; + }; + + secret_key = mkOpt types.str '' + Secret key to use when listing targets. https://console.scaleway.com/project/credentials + It is mutually exclusive with `secret_key_file`. + ''; + + secret_key_file = mkOpt types.str '' + Sets the secret key with the credentials read from the configured file. + It is mutually exclusive with `secret_key`. + ''; + + project_id = mkOption { + type = types.str; + description = lib.mdDoc '' + Project ID of the targets. + ''; + }; + + role = mkOption { + type = types.enum [ "instance" "baremetal" ]; + description = lib.mdDoc '' + Role of the targets to retrieve. Must be `instance` or `baremetal`. + ''; + }; + + port = mkDefOpt types.int "80" '' + The port to scrape metrics from. + ''; + + api_url = mkDefOpt types.str "https://api.scaleway.com" '' + API URL to use when doing the server listing requests. + ''; + + zone = mkDefOpt types.str "fr-par-1" '' + Zone is the availability zone of your targets (e.g. fr-par-1). + ''; + + name_filter = mkOpt types.str '' + Specify a name filter (works as a LIKE) to apply on the server listing request. + ''; + + tags_filter = mkOpt (types.listOf types.str) '' + Specify a tag filter (a server needs to have all defined tags to be listed) to apply on the server listing request. + ''; + + refresh_interval = mkDefOpt types.str "60s" '' + Refresh interval to re-read the managed targets list. + ''; + + proxy_url = mkOpt types.str '' + Optional proxy URL. + ''; + + follow_redirects = mkDefOpt types.bool "true" '' + Configure whether HTTP requests follow HTTP 3xx redirects. + ''; + + tls_config = mkOpt promTypes.tls_config '' + TLS configuration. + ''; + }; + }; + + # These are exactly the same. + promTypes.serverset_sd_config = promTypes.nerve_sd_config; + + promTypes.triton_sd_config = types.submodule { + options = { + account = mkOption { + type = types.str; + description = lib.mdDoc '' + The account to use for discovering new targets. + ''; + }; + + role = mkDefOpt (types.enum [ "container" "cn" ]) "container" '' + The type of targets to discover, can be set to: + - "container" to discover virtual machines (SmartOS zones, lx/KVM/bhyve branded zones) running on Triton + - "cn" to discover compute nodes (servers/global zones) making up the Triton infrastructure + ''; + + dns_suffix = mkOption { + type = types.str; + description = lib.mdDoc '' + The DNS suffix which should be applied to target. + ''; + }; + + endpoint = mkOption { + type = types.str; + description = lib.mdDoc '' + The Triton discovery endpoint (e.g. `cmon.us-east-3b.triton.zone`). This is + often the same value as dns_suffix. + ''; + }; + + groups = mkOpt (types.listOf types.str) '' + A list of groups for which targets are retrieved, only supported when targeting the `container` role. + If omitted all containers owned by the requesting account are scraped. + ''; + + port = mkDefOpt types.int "9163" '' + The port to use for discovery and metric scraping. + ''; + + refresh_interval = mkDefOpt types.str "60s" '' + The interval which should be used for refreshing targets. + ''; + + version = mkDefOpt types.int "1" '' + The Triton discovery API version. + ''; + + tls_config = mkOpt promTypes.tls_config '' + TLS configuration. + ''; + }; + }; + + promTypes.uyuni_sd_config = mkSdConfigModule { + server = mkOption { + type = types.str; + description = lib.mdDoc '' + The URL to connect to the Uyuni server. + ''; + }; + + username = mkOption { + type = types.str; + description = lib.mdDoc '' + Credentials are used to authenticate the requests to Uyuni API. + ''; + }; + + password = mkOption { + type = types.str; + description = lib.mdDoc '' + Credentials are used to authenticate the requests to Uyuni API. + ''; + }; + + entitlement = mkDefOpt types.str "monitoring_entitled" '' + The entitlement string to filter eligible systems. + ''; + + separator = mkDefOpt types.str "," '' + The string by which Uyuni group names are joined into the groups label + ''; + + refresh_interval = mkDefOpt types.str "60s" '' + Refresh interval to re-read the managed targets list. + ''; + }; + + promTypes.static_config = types.submodule { + options = { + targets = mkOption { + type = types.listOf types.str; + description = lib.mdDoc '' + The targets specified by the target group. + ''; + }; + labels = mkOption { + type = types.attrsOf types.str; + default = { }; + description = lib.mdDoc '' + Labels assigned to all metrics scraped from the targets. + ''; + }; + }; + }; + + # + # Config types: relabling + # + + promTypes.relabel_config = types.submodule { + options = { + source_labels = mkOpt (types.listOf types.str) '' + The source labels select values from existing labels. Their content + is concatenated using the configured separator and matched against + the configured regular expression. + ''; + + separator = mkDefOpt types.str ";" '' + Separator placed between concatenated source label values. + ''; + + target_label = mkOpt types.str '' + Label to which the resulting value is written in a replace action. + It is mandatory for replace actions. + ''; + + regex = mkDefOpt types.str "(.*)" '' + Regular expression against which the extracted value is matched. + ''; + + modulus = mkOpt types.int '' + Modulus to take of the hash of the source label values. + ''; + + replacement = mkDefOpt types.str "$1" '' + Replacement value against which a regex replace is performed if the + regular expression matches. + ''; + + action = + mkDefOpt (types.enum [ "replace" "lowercase" "uppercase" "keep" "drop" "hashmod" "labelmap" "labeldrop" "labelkeep" ]) "replace" '' + Action to perform based on regex matching. + ''; + }; + }; + + # + # Config types : remote read / write + # + + promTypes.remote_write = types.submodule { + options = { + url = mkOption { + type = types.str; + description = lib.mdDoc '' + ServerName extension to indicate the name of the server. + http://tools.ietf.org/html/rfc4366#section-3.1 + ''; + }; + remote_timeout = mkOpt types.str '' + Timeout for requests to the remote write endpoint. + ''; + headers = mkOpt (types.attrsOf types.str) '' + Custom HTTP headers to be sent along with each remote write request. + Be aware that headers that are set by Prometheus itself can't be overwritten. + ''; + write_relabel_configs = mkOpt (types.listOf promTypes.relabel_config) '' + List of remote write relabel configurations. + ''; + name = mkOpt types.str '' + Name of the remote write config, which if specified must be unique among remote write configs. + The name will be used in metrics and logging in place of a generated value to help users distinguish between + remote write configs. + ''; + basic_auth = mkOpt promTypes.basic_auth '' + Sets the `Authorization` header on every remote write request with the + configured username and password. + password and password_file are mutually exclusive. + ''; + bearer_token = mkOpt types.str '' + Sets the `Authorization` header on every remote write request with + the configured bearer token. It is mutually exclusive with `bearer_token_file`. + ''; + bearer_token_file = mkOpt types.str '' + Sets the `Authorization` header on every remote write request with the bearer token + read from the configured file. It is mutually exclusive with `bearer_token`. + ''; + tls_config = mkOpt promTypes.tls_config '' + Configures the remote write request's TLS settings. + ''; + proxy_url = mkOpt types.str "Optional Proxy URL."; + queue_config = mkOpt + (types.submodule { + options = { + capacity = mkOpt types.int '' + Number of samples to buffer per shard before we block reading of more + samples from the WAL. It is recommended to have enough capacity in each + shard to buffer several requests to keep throughput up while processing + occasional slow remote requests. + ''; + max_shards = mkOpt types.int '' + Maximum number of shards, i.e. amount of concurrency. + ''; + min_shards = mkOpt types.int '' + Minimum number of shards, i.e. amount of concurrency. + ''; + max_samples_per_send = mkOpt types.int '' + Maximum number of samples per send. + ''; + batch_send_deadline = mkOpt types.str '' + Maximum time a sample will wait in buffer. + ''; + min_backoff = mkOpt types.str '' + Initial retry delay. Gets doubled for every retry. + ''; + max_backoff = mkOpt types.str '' + Maximum retry delay. + ''; + }; + }) '' + Configures the queue used to write to remote storage. + ''; + metadata_config = mkOpt + (types.submodule { + options = { + send = mkOpt types.bool '' + Whether metric metadata is sent to remote storage or not. + ''; + send_interval = mkOpt types.str '' + How frequently metric metadata is sent to remote storage. + ''; + }; + }) '' + Configures the sending of series metadata to remote storage. + Metadata configuration is subject to change at any point + or be removed in future releases. + ''; + }; + }; + + promTypes.remote_read = types.submodule { + options = { + url = mkOption { + type = types.str; + description = lib.mdDoc '' + ServerName extension to indicate the name of the server. + http://tools.ietf.org/html/rfc4366#section-3.1 + ''; + }; + name = mkOpt types.str '' + Name of the remote read config, which if specified must be unique among remote read configs. + The name will be used in metrics and logging in place of a generated value to help users distinguish between + remote read configs. + ''; + required_matchers = mkOpt (types.attrsOf types.str) '' + An optional list of equality matchers which have to be + present in a selector to query the remote read endpoint. + ''; + remote_timeout = mkOpt types.str '' + Timeout for requests to the remote read endpoint. + ''; + headers = mkOpt (types.attrsOf types.str) '' + Custom HTTP headers to be sent along with each remote read request. + Be aware that headers that are set by Prometheus itself can't be overwritten. + ''; + read_recent = mkOpt types.bool '' + Whether reads should be made for queries for time ranges that + the local storage should have complete data for. + ''; + basic_auth = mkOpt promTypes.basic_auth '' + Sets the `Authorization` header on every remote read request with the + configured username and password. + password and password_file are mutually exclusive. + ''; + bearer_token = mkOpt types.str '' + Sets the `Authorization` header on every remote read request with + the configured bearer token. It is mutually exclusive with `bearer_token_file`. + ''; + bearer_token_file = mkOpt types.str '' + Sets the `Authorization` header on every remote read request with the bearer token + read from the configured file. It is mutually exclusive with `bearer_token`. + ''; + tls_config = mkOpt promTypes.tls_config '' + Configures the remote read request's TLS settings. + ''; + proxy_url = mkOpt types.str "Optional Proxy URL."; + }; + }; + +in +{ + + imports = [ + (mkRenamedOptionModule [ "services" "prometheus2" ] [ "services" "prometheus" ]) + (mkRemovedOptionModule [ "services" "prometheus" "environmentFile" ] + "It has been removed since it was causing issues (https://github.com/NixOS/nixpkgs/issues/126083) and Prometheus now has native support for secret files, i.e. `basic_auth.password_file` and `authorization.credentials_file`.") + (mkRemovedOptionModule [ "services" "prometheus" "alertmanagerTimeout" ] + "Deprecated upstream and no longer had any effect") + ]; + + options.services.prometheus = { + + enable = mkEnableOption (lib.mdDoc "Prometheus monitoring daemon"); + + package = mkPackageOption pkgs "prometheus" { }; + + port = mkOption { + type = types.port; + default = 9090; + description = lib.mdDoc '' + Port to listen on. + ''; + }; + + listenAddress = mkOption { + type = types.str; + default = "0.0.0.0"; + description = lib.mdDoc '' + Address to listen on for the web interface, API, and telemetry. + ''; + }; + + stateDir = mkOption { + type = types.str; + default = "prometheus2"; + description = lib.mdDoc '' + Directory below `/var/lib` to store Prometheus metrics data. + This directory will be created automatically using systemd's StateDirectory mechanism. + ''; + }; + + extraFlags = mkOption { + type = types.listOf types.str; + default = [ ]; + description = lib.mdDoc '' + Extra commandline options when launching Prometheus. + ''; + }; + + enableReload = mkOption { + default = false; + type = types.bool; + description = lib.mdDoc '' + Reload prometheus when configuration file changes (instead of restart). + + The following property holds: switching to a configuration + (`switch-to-configuration`) that changes the prometheus + configuration only finishes successfully when prometheus has finished + loading the new configuration. + ''; + }; + + enableAgentMode = mkEnableOption (lib.mdDoc "agent mode"); + + configText = mkOption { + type = types.nullOr types.lines; + default = null; + description = lib.mdDoc '' + If non-null, this option defines the text that is written to + prometheus.yml. If null, the contents of prometheus.yml is generated + from the structured config options. + ''; + }; + + globalConfig = mkOption { + type = promTypes.globalConfig; + default = { }; + description = lib.mdDoc '' + Parameters that are valid in all configuration contexts. They + also serve as defaults for other configuration sections + ''; + }; + + remoteRead = mkOption { + type = types.listOf promTypes.remote_read; + default = [ ]; + description = lib.mdDoc '' + Parameters of the endpoints to query from. + See [the official documentation](https://prometheus.io/docs/prometheus/latest/configuration/configuration/#remote_read) for more information. + ''; + }; + + remoteWrite = mkOption { + type = types.listOf promTypes.remote_write; + default = [ ]; + description = lib.mdDoc '' + Parameters of the endpoints to send samples to. + See [the official documentation](https://prometheus.io/docs/prometheus/latest/configuration/configuration/#remote_write) for more information. + ''; + }; + + rules = mkOption { + type = types.listOf types.str; + default = [ ]; + description = lib.mdDoc '' + Alerting and/or Recording rules to evaluate at runtime. + ''; + }; + + ruleFiles = mkOption { + type = types.listOf types.path; + default = [ ]; + description = lib.mdDoc '' + Any additional rules files to include in this configuration. + ''; + }; + + scrapeConfigs = mkOption { + type = types.listOf promTypes.scrape_config; + default = [ ]; + description = lib.mdDoc '' + A list of scrape configurations. + ''; + }; + + alertmanagers = mkOption { + type = types.listOf types.attrs; + example = literalExpression '' + [ { + scheme = "https"; + path_prefix = "/alertmanager"; + static_configs = [ { + targets = [ + "prometheus.domain.tld" + ]; + } ]; + } ] + ''; + default = [ ]; + description = lib.mdDoc '' + A list of alertmanagers to send alerts to. + See [the official documentation](https://prometheus.io/docs/prometheus/latest/configuration/configuration/#alertmanager_config) for more information. + ''; + }; + + alertmanagerNotificationQueueCapacity = mkOption { + type = types.int; + default = 10000; + description = lib.mdDoc '' + The capacity of the queue for pending alert manager notifications. + ''; + }; + + webExternalUrl = mkOption { + type = types.nullOr types.str; + default = null; + example = "https://example.com/"; + description = lib.mdDoc '' + The URL under which Prometheus is externally reachable (for example, + if Prometheus is served via a reverse proxy). + ''; + }; + + webConfigFile = mkOption { + type = types.nullOr types.path; + default = null; + description = lib.mdDoc '' + Specifies which file should be used as web.config.file and be passed on startup. + See https://prometheus.io/docs/prometheus/latest/configuration/https/ for valid options. + ''; + }; + + checkConfig = mkOption { + type = with types; either bool (enum [ "syntax-only" ]); + default = true; + example = "syntax-only"; + description = lib.mdDoc '' + Check configuration with `promtool check`. The call to `promtool` is + subject to sandboxing by Nix. + + If you use credentials stored in external files + (`password_file`, `bearer_token_file`, etc), + they will not be visible to `promtool` + and it will report errors, despite a correct configuration. + To resolve this, you may set this option to `"syntax-only"` + in order to only syntax check the Prometheus configuration. + ''; + }; + + retentionTime = mkOption { + type = types.nullOr types.str; + default = null; + example = "15d"; + description = lib.mdDoc '' + How long to retain samples in storage. + ''; + }; + }; + + config = mkIf cfg.enable { + assertions = [ + ( + let + # Match something with dots (an IPv4 address) or something ending in + # a square bracket (an IPv6 addresses) followed by a port number. + legacy = builtins.match "(.*\\..*|.*]):([[:digit:]]+)" cfg.listenAddress; + in + { + assertion = legacy == null; + message = '' + Do not specify the port for Prometheus to listen on in the + listenAddress option; use the port option instead: + services.prometheus.listenAddress = ${builtins.elemAt legacy 0}; + services.prometheus.port = ${builtins.elemAt legacy 1}; + ''; + } + ) + ]; + + users.groups.prometheus.gid = config.ids.gids.prometheus; + users.users.prometheus = { + description = "Prometheus daemon user"; + uid = config.ids.uids.prometheus; + group = "prometheus"; + }; + environment.etc."prometheus/prometheus.yaml" = mkIf cfg.enableReload { + source = prometheusYml; + }; + systemd.services.prometheus = { + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + serviceConfig = { + ExecStart = "${cfg.package}/bin/prometheus" + + optionalString (length cmdlineArgs != 0) (" \\\n " + + concatStringsSep " \\\n " cmdlineArgs); + ExecReload = mkIf cfg.enableReload "+${reload}/bin/reload-prometheus"; + User = "prometheus"; + Restart = "always"; + RuntimeDirectory = "prometheus"; + RuntimeDirectoryMode = "0700"; + WorkingDirectory = workingDir; + StateDirectory = cfg.stateDir; + StateDirectoryMode = "0700"; + # Hardening + AmbientCapabilities = lib.mkIf (cfg.port < 1024) [ "CAP_NET_BIND_SERVICE" ]; + CapabilityBoundingSet = if (cfg.port < 1024) then [ "CAP_NET_BIND_SERVICE" ] else [ "" ]; + DeviceAllow = [ "/dev/null rw" ]; + DevicePolicy = "strict"; + LockPersonality = true; + MemoryDenyWriteExecute = true; + NoNewPrivileges = true; + PrivateDevices = true; + PrivateTmp = true; + PrivateUsers = true; + ProtectClock = true; + ProtectControlGroups = true; + ProtectHome = true; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + ProtectProc = "invisible"; + ProtectSystem = "full"; + RemoveIPC = true; + RestrictAddressFamilies = [ "AF_INET" "AF_INET6" "AF_UNIX" ]; + RestrictNamespaces = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + SystemCallArchitectures = "native"; + SystemCallFilter = [ "@system-service" "~@privileged" ]; + }; + }; + # prometheus-config-reload will activate after prometheus. However, what we + # don't want is that on startup it immediately reloads prometheus because + # prometheus itself might have just started. + # + # Instead we only want to reload prometheus when the config file has + # changed. So on startup prometheus-config-reload will just output a + # harmless message and then stay active (RemainAfterExit). + # + # Then, when the config file has changed, switch-to-configuration notices + # that this service has changed (restartTriggers) and needs to be reloaded + # (reloadIfChanged). The reload command then reloads prometheus. + systemd.services.prometheus-config-reload = mkIf cfg.enableReload { + wantedBy = [ "prometheus.service" ]; + after = [ "prometheus.service" ]; + reloadIfChanged = true; + restartTriggers = [ prometheusYml ]; + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + TimeoutSec = 60; + ExecStart = "${pkgs.logger}/bin/logger 'prometheus-config-reload will only reload prometheus when reloaded itself.'"; + ExecReload = [ "${triggerReload}/bin/trigger-reload-prometheus" ]; + }; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters.md b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters.md new file mode 100644 index 000000000000..34fadecadc74 --- /dev/null +++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters.md @@ -0,0 +1,180 @@ +# Prometheus exporters {#module-services-prometheus-exporters} + +Prometheus exporters provide metrics for the +[prometheus monitoring system](https://prometheus.io). + +## Configuration {#module-services-prometheus-exporters-configuration} + +One of the most common exporters is the +[node exporter](https://github.com/prometheus/node_exporter), +it provides hardware and OS metrics from the host it's +running on. The exporter could be configured as follows: +``` + services.prometheus.exporters.node = { + enable = true; + port = 9100; + enabledCollectors = [ + "logind" + "systemd" + ]; + disabledCollectors = [ + "textfile" + ]; + openFirewall = true; + firewallFilter = "-i br0 -p tcp -m tcp --dport 9100"; + }; +``` +It should now serve all metrics from the collectors that are explicitly +enabled and the ones that are +[enabled by default](https://github.com/prometheus/node_exporter#enabled-by-default), +via http under `/metrics`. In this +example the firewall should just allow incoming connections to the +exporter's port on the bridge interface `br0` (this would +have to be configured separately of course). For more information about +configuration see `man configuration.nix` or search through +the [available options](https://nixos.org/nixos/options.html#prometheus.exporters). + +Prometheus can now be configured to consume the metrics produced by the exporter: +``` + services.prometheus = { + # ... + + scrapeConfigs = [ + { + job_name = "node"; + static_configs = [{ + targets = [ "localhost:${toString config.services.prometheus.exporters.node.port}" ]; + }]; + } + ]; + + # ... + } +``` + +## Adding a new exporter {#module-services-prometheus-exporters-new-exporter} + +To add a new exporter, it has to be packaged first (see +`nixpkgs/pkgs/servers/monitoring/prometheus/` for +examples), then a module can be added. The postfix exporter is used in this +example: + + - Some default options for all exporters are provided by + `nixpkgs/nixos/modules/services/monitoring/prometheus/exporters.nix`: + + - `enable` + - `port` + - `listenAddress` + - `extraFlags` + - `openFirewall` + - `firewallFilter` + - `user` + - `group` + - As there is already a package available, the module can now be added. This + is accomplished by adding a new file to the + `nixos/modules/services/monitoring/prometheus/exporters/` + directory, which will be called postfix.nix and contains all exporter + specific options and configuration: + ``` + # nixpkgs/nixos/modules/services/prometheus/exporters/postfix.nix + { config, lib, pkgs, options }: + + with lib; + + let + # for convenience we define cfg here + cfg = config.services.prometheus.exporters.postfix; + in + { + port = 9154; # The postfix exporter listens on this port by default + + # `extraOpts` is an attribute set which contains additional options + # (and optional overrides for default options). + # Note that this attribute is optional. + extraOpts = { + telemetryPath = mkOption { + type = types.str; + default = "/metrics"; + description = '' + Path under which to expose metrics. + ''; + }; + logfilePath = mkOption { + type = types.path; + default = /var/log/postfix_exporter_input.log; + example = /var/log/mail.log; + description = '' + Path where Postfix writes log entries. + This file will be truncated by this exporter! + ''; + }; + showqPath = mkOption { + type = types.path; + default = /var/spool/postfix/public/showq; + example = /var/lib/postfix/queue/public/showq; + description = '' + Path at which Postfix places its showq socket. + ''; + }; + }; + + # `serviceOpts` is an attribute set which contains configuration + # for the exporter's systemd service. One of + # `serviceOpts.script` and `serviceOpts.serviceConfig.ExecStart` + # has to be specified here. This will be merged with the default + # service configuration. + # Note that by default 'DynamicUser' is 'true'. + serviceOpts = { + serviceConfig = { + DynamicUser = false; + ExecStart = '' + ${pkgs.prometheus-postfix-exporter}/bin/postfix_exporter \ + --web.listen-address ${cfg.listenAddress}:${toString cfg.port} \ + --web.telemetry-path ${cfg.telemetryPath} \ + ${concatStringsSep " \\\n " cfg.extraFlags} + ''; + }; + }; + } + ``` + - This should already be enough for the postfix exporter. Additionally one + could now add assertions and conditional default values. This can be done + in the 'meta-module' that combines all exporter definitions and generates + the submodules: + `nixpkgs/nixos/modules/services/prometheus/exporters.nix` + +## Updating an exporter module {#module-services-prometheus-exporters-update-exporter-module} + +Should an exporter option change at some point, it is possible to add +information about the change to the exporter definition similar to +`nixpkgs/nixos/modules/rename.nix`: +``` +{ config, lib, pkgs, options }: + +with lib; + +let + cfg = config.services.prometheus.exporters.nginx; +in +{ + port = 9113; + extraOpts = { + # additional module options + # ... + }; + serviceOpts = { + # service configuration + # ... + }; + imports = [ + # 'services.prometheus.exporters.nginx.telemetryEndpoint' -> 'services.prometheus.exporters.nginx.telemetryPath' + (mkRenamedOptionModule [ "telemetryEndpoint" ] [ "telemetryPath" ]) + + # removed option 'services.prometheus.exporters.nginx.insecure' + (mkRemovedOptionModule [ "insecure" ] '' + This option was replaced by 'prometheus.exporters.nginx.sslVerify' which defaults to true. + '') + ({ options.warnings = options.warnings; }) + ]; +} +``` diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters.nix new file mode 100644 index 000000000000..35db8a7376b1 --- /dev/null +++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters.nix @@ -0,0 +1,444 @@ +{ config, pkgs, lib, options, ... }: + +let + inherit (lib) concatStrings foldl foldl' genAttrs literalExpression maintainers + mapAttrs mapAttrsToList mkDefault mkEnableOption mkIf mkMerge mkOption + optional types mkOptionDefault flip attrNames; + + cfg = config.services.prometheus.exporters; + + # each attribute in `exporterOpts` is expected to have specified: + # - port (types.int): port on which the exporter listens + # - serviceOpts (types.attrs): config that is merged with the + # default definition of the exporter's + # systemd service + # - extraOpts (types.attrs): extra configuration options to + # configure the exporter with, which + # are appended to the default options + # + # Note that `extraOpts` is optional, but a script for the exporter's + # systemd service must be provided by specifying either + # `serviceOpts.script` or `serviceOpts.serviceConfig.ExecStart` + + exporterOpts = (genAttrs [ + "apcupsd" + "artifactory" + "bind" + "bird" + "bitcoin" + "blackbox" + "buildkite-agent" + "collectd" + "dmarc" + "dnsmasq" + "domain" + "dovecot" + "fastly" + "flow" + "fritzbox" + "graphite" + "idrac" + "imap-mailstat" + "influxdb" + "ipmi" + "jitsi" + "json" + "junos-czerwonk" + "kea" + "keylight" + "knot" + "lnd" + "mail" + "mikrotik" + "minio" + "modemmanager" + "mongodb" + "mysqld" + "nextcloud" + "nginx" + "nginxlog" + "node" + "nut" + "openldap" + "openvpn" + "pgbouncer" + "php-fpm" + "pihole" + "ping" + "postfix" + "postgres" + "process" + "pve" + "py-air-control" + "redis" + "rspamd" + "rtl_433" + "sabnzbd" + "scaphandre" + "script" + "shelly" + "smartctl" + "smokeping" + "snmp" + "sql" + "statsd" + "surfboard" + "systemd" + "tor" + "unbound" + "unifi" + "unpoller" + "v2ray" + "varnish" + "wireguard" + "zfs" + ] + (name: + import (./. + "/exporters/${name}.nix") { inherit config lib pkgs options; } + )) // (mapAttrs + (name: params: + import (./. + "/exporters/${params.name}.nix") { inherit config lib pkgs options; type = params.type ; }) + { + exportarr-bazarr = { + name = "exportarr"; + type = "bazarr"; + }; + exportarr-lidarr = { + name = "exportarr"; + type = "lidarr"; + }; + exportarr-prowlarr = { + name = "exportarr"; + type = "prowlarr"; + }; + exportarr-radarr = { + name = "exportarr"; + type = "radarr"; + }; + exportarr-readarr = { + name = "exportarr"; + type = "readarr"; + }; + exportarr-sonarr = { + name = "exportarr"; + type = "sonarr"; + }; + } + ); + + mkExporterOpts = ({ name, port }: { + enable = mkEnableOption (lib.mdDoc "the prometheus ${name} exporter"); + port = mkOption { + type = types.port; + default = port; + description = lib.mdDoc '' + Port to listen on. + ''; + }; + listenAddress = mkOption { + type = types.str; + default = "0.0.0.0"; + description = lib.mdDoc '' + Address to listen on. + ''; + }; + extraFlags = mkOption { + type = types.listOf types.str; + default = []; + description = lib.mdDoc '' + Extra commandline options to pass to the ${name} exporter. + ''; + }; + openFirewall = mkOption { + type = types.bool; + default = false; + description = lib.mdDoc '' + Open port in firewall for incoming connections. + ''; + }; + firewallFilter = mkOption { + type = types.nullOr types.str; + default = null; + example = literalExpression '' + "-i eth0 -p tcp -m tcp --dport ${toString port}" + ''; + description = lib.mdDoc '' + Specify a filter for iptables to use when + {option}`services.prometheus.exporters.${name}.openFirewall` + is true. It is used as `ip46tables -I nixos-fw firewallFilter -j nixos-fw-accept`. + ''; + }; + user = mkOption { + type = types.str; + default = "${name}-exporter"; + description = lib.mdDoc '' + User name under which the ${name} exporter shall be run. + ''; + }; + group = mkOption { + type = types.str; + default = "${name}-exporter"; + description = lib.mdDoc '' + Group under which the ${name} exporter shall be run. + ''; + }; + }); + + mkSubModule = { name, port, extraOpts, imports }: { + ${name} = mkOption { + type = types.submodule [{ + inherit imports; + options = (mkExporterOpts { + inherit name port; + } // extraOpts); + } ({ config, ... }: mkIf config.openFirewall { + firewallFilter = mkDefault "-p tcp -m tcp --dport ${toString config.port}"; + })]; + internal = true; + default = {}; + }; + }; + + mkSubModules = (foldl' (a: b: a//b) {} + (mapAttrsToList (name: opts: mkSubModule { + inherit name; + inherit (opts) port; + extraOpts = opts.extraOpts or {}; + imports = opts.imports or []; + }) exporterOpts) + ); + + mkExporterConf = { name, conf, serviceOpts }: + let + enableDynamicUser = serviceOpts.serviceConfig.DynamicUser or true; + in + mkIf conf.enable { + warnings = conf.warnings or []; + users.users."${name}-exporter" = (mkIf (conf.user == "${name}-exporter" && !enableDynamicUser) { + description = "Prometheus ${name} exporter service user"; + isSystemUser = true; + inherit (conf) group; + }); + users.groups = (mkIf (conf.group == "${name}-exporter" && !enableDynamicUser) { + "${name}-exporter" = {}; + }); + networking.firewall.extraCommands = mkIf conf.openFirewall (concatStrings [ + "ip46tables -A nixos-fw ${conf.firewallFilter} " + "-m comment --comment ${name}-exporter -j nixos-fw-accept" + ]); + systemd.services."prometheus-${name}-exporter" = mkMerge ([{ + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + serviceConfig.Restart = mkDefault "always"; + serviceConfig.PrivateTmp = mkDefault true; + serviceConfig.WorkingDirectory = mkDefault /tmp; + serviceConfig.DynamicUser = mkDefault enableDynamicUser; + serviceConfig.User = mkDefault conf.user; + serviceConfig.Group = conf.group; + # Hardening + serviceConfig.CapabilityBoundingSet = mkDefault [ "" ]; + serviceConfig.DeviceAllow = [ "" ]; + serviceConfig.LockPersonality = true; + serviceConfig.MemoryDenyWriteExecute = true; + serviceConfig.NoNewPrivileges = true; + serviceConfig.PrivateDevices = mkDefault true; + serviceConfig.ProtectClock = mkDefault true; + serviceConfig.ProtectControlGroups = true; + serviceConfig.ProtectHome = true; + serviceConfig.ProtectHostname = true; + serviceConfig.ProtectKernelLogs = true; + serviceConfig.ProtectKernelModules = true; + serviceConfig.ProtectKernelTunables = true; + serviceConfig.ProtectSystem = mkDefault "strict"; + serviceConfig.RemoveIPC = true; + serviceConfig.RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ]; + serviceConfig.RestrictNamespaces = true; + serviceConfig.RestrictRealtime = true; + serviceConfig.RestrictSUIDSGID = true; + serviceConfig.SystemCallArchitectures = "native"; + serviceConfig.UMask = "0077"; + } serviceOpts ]); + }; +in +{ + + imports = (lib.forEach [ "blackboxExporter" "collectdExporter" "fritzboxExporter" + "jsonExporter" "minioExporter" "nginxExporter" "nodeExporter" + "snmpExporter" "unifiExporter" "varnishExporter" ] + (opt: lib.mkRemovedOptionModule [ "services" "prometheus" "${opt}" ] '' + The prometheus exporters are now configured using `services.prometheus.exporters'. + See the 18.03 release notes for more information. + '' )); + + options.services.prometheus.exporters = mkOption { + type = types.submodule { + options = (mkSubModules); + imports = [ + ../../../misc/assertions.nix + (lib.mkRenamedOptionModule [ "unifi-poller" ] [ "unpoller" ]) + ]; + }; + description = lib.mdDoc "Prometheus exporter configuration"; + default = {}; + example = literalExpression '' + { + node = { + enable = true; + enabledCollectors = [ "systemd" ]; + }; + varnish.enable = true; + } + ''; + }; + + config = mkMerge ([{ + assertions = [ { + assertion = cfg.ipmi.enable -> (cfg.ipmi.configFile != null) -> ( + !(lib.hasPrefix "/tmp/" cfg.ipmi.configFile) + ); + message = '' + Config file specified in `services.prometheus.exporters.ipmi.configFile' must + not reside within /tmp - it won't be visible to the systemd service. + ''; + } { + assertion = cfg.ipmi.enable -> (cfg.ipmi.webConfigFile != null) -> ( + !(lib.hasPrefix "/tmp/" cfg.ipmi.webConfigFile) + ); + message = '' + Config file specified in `services.prometheus.exporters.ipmi.webConfigFile' must + not reside within /tmp - it won't be visible to the systemd service. + ''; + } { + assertion = cfg.snmp.enable -> ( + (cfg.snmp.configurationPath == null) != (cfg.snmp.configuration == null) + ); + message = '' + Please ensure you have either `services.prometheus.exporters.snmp.configuration' + or `services.prometheus.exporters.snmp.configurationPath' set! + ''; + } { + assertion = cfg.mikrotik.enable -> ( + (cfg.mikrotik.configFile == null) != (cfg.mikrotik.configuration == null) + ); + message = '' + Please specify either `services.prometheus.exporters.mikrotik.configuration' + or `services.prometheus.exporters.mikrotik.configFile'. + ''; + } { + assertion = cfg.mail.enable -> ( + (cfg.mail.configFile == null) != (cfg.mail.configuration == null) + ); + message = '' + Please specify either 'services.prometheus.exporters.mail.configuration' + or 'services.prometheus.exporters.mail.configFile'. + ''; + } { + assertion = cfg.mysqld.runAsLocalSuperUser -> config.services.mysql.enable; + message = '' + The exporter is configured to run as 'services.mysql.user', but + 'services.mysql.enable' is set to false. + ''; + } { + assertion = cfg.nextcloud.enable -> ( + (cfg.nextcloud.passwordFile == null) != (cfg.nextcloud.tokenFile == null) + ); + message = '' + Please specify either 'services.prometheus.exporters.nextcloud.passwordFile' or + 'services.prometheus.exporters.nextcloud.tokenFile' + ''; + } { + assertion = cfg.pgbouncer.enable -> ( + (cfg.pgbouncer.connectionStringFile != null || cfg.pgbouncer.connectionString != "") + ); + message = '' + PgBouncer exporter needs either connectionStringFile or connectionString configured" + ''; + } { + assertion = cfg.pgbouncer.enable -> ( + config.services.pgbouncer.ignoreStartupParameters != null && builtins.match ".*extra_float_digits.*" config.services.pgbouncer.ignoreStartupParameters != null + ); + message = '' + Prometheus PgBouncer exporter requires including `extra_float_digits` in services.pgbouncer.ignoreStartupParameters + + Example: + services.pgbouncer.ignoreStartupParameters = extra_float_digits; + + See https://github.com/prometheus-community/pgbouncer_exporter#pgbouncer-configuration + ''; + } { + assertion = cfg.sql.enable -> ( + (cfg.sql.configFile == null) != (cfg.sql.configuration == null) + ); + message = '' + Please specify either 'services.prometheus.exporters.sql.configuration' or + 'services.prometheus.exporters.sql.configFile' + ''; + } { + assertion = cfg.scaphandre.enable -> (pkgs.stdenv.targetPlatform.isx86_64 == true); + message = '' + Scaphandre only support x86_64 architectures. + ''; + } { + assertion = cfg.scaphandre.enable -> ((lib.kernel.whenHelpers pkgs.linux.version).whenOlder "5.11" true).condition == false; + message = '' + Scaphandre requires a kernel version newer than '5.11', '${pkgs.linux.version}' given. + ''; + } { + assertion = cfg.scaphandre.enable -> (builtins.elem "intel_rapl_common" config.boot.kernelModules); + message = '' + Scaphandre needs 'intel_rapl_common' kernel module to be enabled. Please add it in 'boot.kernelModules'. + ''; + } { + assertion = cfg.idrac.enable -> ( + (cfg.idrac.configurationPath == null) != (cfg.idrac.configuration == null) + ); + message = '' + Please ensure you have either `services.prometheus.exporters.idrac.configuration' + or `services.prometheus.exporters.idrac.configurationPath' set! + ''; + } ] ++ (flip map (attrNames exporterOpts) (exporter: { + assertion = cfg.${exporter}.firewallFilter != null -> cfg.${exporter}.openFirewall; + message = '' + The `firewallFilter'-option of exporter ${exporter} doesn't have any effect unless + `openFirewall' is set to `true'! + ''; + })) ++ config.services.prometheus.exporters.assertions; + warnings = [ + (mkIf (config.services.prometheus.exporters.idrac.enable && config.services.prometheus.exporters.idrac.configurationPath != null) '' + Configuration file in `services.prometheus.exporters.idrac.configurationPath` may override + `services.prometheus.exporters.idrac.listenAddress` and/or `services.prometheus.exporters.idrac.port`. + Consider using `services.prometheus.exporters.idrac.configuration` instead. + '' + ) + (mkIf + (cfg.pgbouncer.enable && cfg.pgbouncer.connectionString != "") '' + config.services.prometheus.exporters.pgbouncer.connectionString is insecure. Use connectionStringFile instead. + '' + ) + (mkIf + (cfg.pgbouncer.enable && config.services.pgbouncer.authType != "any") '' + Admin user (with password or passwordless) MUST exist in the services.pgbouncer.authFile if authType other than any is used. + '' + ) + ] ++ config.services.prometheus.exporters.warnings; + }] ++ [(mkIf config.services.minio.enable { + services.prometheus.exporters.minio.minioAddress = mkDefault "http://localhost:9000"; + services.prometheus.exporters.minio.minioAccessKey = mkDefault config.services.minio.accessKey; + services.prometheus.exporters.minio.minioAccessSecret = mkDefault config.services.minio.secretKey; + })] ++ [(mkIf config.services.prometheus.exporters.rtl_433.enable { + hardware.rtl-sdr.enable = mkDefault true; + })] ++ [(mkIf config.services.postfix.enable { + services.prometheus.exporters.postfix.group = mkDefault config.services.postfix.setgidGroup; + })] ++ (mapAttrsToList (name: conf: + mkExporterConf { + inherit name; + inherit (conf) serviceOpts; + conf = cfg.${name}; + }) exporterOpts) + ); + + meta = { + doc = ./exporters.md; + maintainers = [ maintainers.willibutz ]; + }; +} diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/apcupsd.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/apcupsd.nix new file mode 100644 index 000000000000..a8a9f84ea8ea --- /dev/null +++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/apcupsd.nix @@ -0,0 +1,38 @@ +{ config, lib, pkgs, options }: + +with lib; + +let + cfg = config.services.prometheus.exporters.apcupsd; +in +{ + port = 9162; + extraOpts = { + apcupsdAddress = mkOption { + type = types.str; + default = ":3551"; + description = lib.mdDoc '' + Address of the apcupsd Network Information Server (NIS). + ''; + }; + + apcupsdNetwork = mkOption { + type = types.enum ["tcp" "tcp4" "tcp6"]; + default = "tcp"; + description = lib.mdDoc '' + Network of the apcupsd Network Information Server (NIS): one of "tcp", "tcp4", or "tcp6". + ''; + }; + }; + serviceOpts = { + serviceConfig = { + ExecStart = '' + ${pkgs.prometheus-apcupsd-exporter}/bin/apcupsd_exporter \ + -telemetry.addr ${cfg.listenAddress}:${toString cfg.port} \ + -apcupsd.addr ${cfg.apcupsdAddress} \ + -apcupsd.network ${cfg.apcupsdNetwork} \ + ${concatStringsSep " \\\n " cfg.extraFlags} + ''; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/artifactory.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/artifactory.nix new file mode 100644 index 000000000000..bc67fe59b3b8 --- /dev/null +++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/artifactory.nix @@ -0,0 +1,59 @@ +{ config, lib, pkgs, options }: + +with lib; + +let + cfg = config.services.prometheus.exporters.artifactory; +in +{ + port = 9531; + extraOpts = { + scrapeUri = mkOption { + type = types.str; + default = "http://localhost:8081/artifactory"; + description = lib.mdDoc '' + URI on which to scrape JFrog Artifactory. + ''; + }; + + artiUsername = mkOption { + type = types.str; + description = lib.mdDoc '' + Username for authentication against JFrog Artifactory API. + ''; + }; + + artiPassword = mkOption { + type = types.str; + default = ""; + description = lib.mdDoc '' + Password for authentication against JFrog Artifactory API. + One of the password or access token needs to be set. + ''; + }; + + artiAccessToken = mkOption { + type = types.str; + default = ""; + description = lib.mdDoc '' + Access token for authentication against JFrog Artifactory API. + One of the password or access token needs to be set. + ''; + }; + }; + serviceOpts = { + serviceConfig = { + ExecStart = '' + ${pkgs.prometheus-artifactory-exporter}/bin/artifactory_exporter \ + --web.listen-address ${cfg.listenAddress}:${toString cfg.port} \ + --artifactory.scrape-uri ${cfg.scrapeUri} \ + ${concatStringsSep " \\\n " cfg.extraFlags} + ''; + Environment = [ + "ARTI_USERNAME=${cfg.artiUsername}" + "ARTI_PASSWORD=${cfg.artiPassword}" + "ARTI_ACCESS_TOKEN=${cfg.artiAccessToken}" + ]; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/bind.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/bind.nix new file mode 100644 index 000000000000..bd2003f06504 --- /dev/null +++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/bind.nix @@ -0,0 +1,54 @@ +{ config, lib, pkgs, options }: + +with lib; + +let + cfg = config.services.prometheus.exporters.bind; +in +{ + port = 9119; + extraOpts = { + bindURI = mkOption { + type = types.str; + default = "http://localhost:8053/"; + description = lib.mdDoc '' + HTTP XML API address of an Bind server. + ''; + }; + bindTimeout = mkOption { + type = types.str; + default = "10s"; + description = lib.mdDoc '' + Timeout for trying to get stats from Bind. + ''; + }; + bindVersion = mkOption { + type = types.enum [ "xml.v2" "xml.v3" "auto" ]; + default = "auto"; + description = lib.mdDoc '' + BIND statistics version. Can be detected automatically. + ''; + }; + bindGroups = mkOption { + type = types.listOf (types.enum [ "server" "view" "tasks" ]); + default = [ "server" "view" ]; + description = lib.mdDoc '' + List of statistics to collect. Available: [server, view, tasks] + ''; + }; + }; + serviceOpts = { + serviceConfig = { + ExecStart = '' + ${pkgs.prometheus-bind-exporter}/bin/bind_exporter \ + --web.listen-address ${cfg.listenAddress}:${toString cfg.port} \ + --bind.pid-file /var/run/named/named.pid \ + --bind.timeout ${toString cfg.bindTimeout} \ + --bind.stats-url ${cfg.bindURI} \ + --bind.stats-version ${cfg.bindVersion} \ + --bind.stats-groups ${concatStringsSep "," cfg.bindGroups} \ + ${concatStringsSep " \\\n " cfg.extraFlags} + ''; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/bird.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/bird.nix new file mode 100644 index 000000000000..5f6c36f4c567 --- /dev/null +++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/bird.nix @@ -0,0 +1,50 @@ +{ config, lib, pkgs, options }: + +with lib; + +let + cfg = config.services.prometheus.exporters.bird; +in +{ + port = 9324; + extraOpts = { + birdVersion = mkOption { + type = types.enum [ 1 2 ]; + default = 2; + description = lib.mdDoc '' + Specifies whether BIRD1 or BIRD2 is in use. + ''; + }; + birdSocket = mkOption { + type = types.path; + default = "/run/bird/bird.ctl"; + description = lib.mdDoc '' + Path to BIRD2 (or BIRD1 v4) socket. + ''; + }; + newMetricFormat = mkOption { + type = types.bool; + default = true; + description = lib.mdDoc '' + Enable the new more-generic metric format. + ''; + }; + }; + serviceOpts = { + serviceConfig = { + SupplementaryGroups = singleton (if cfg.birdVersion == 1 then "bird" else "bird2"); + ExecStart = '' + ${pkgs.prometheus-bird-exporter}/bin/bird_exporter \ + -web.listen-address ${cfg.listenAddress}:${toString cfg.port} \ + -bird.socket ${cfg.birdSocket} \ + -bird.v2=${if cfg.birdVersion == 2 then "true" else "false"} \ + -format.new=${if cfg.newMetricFormat then "true" else "false"} \ + ${concatStringsSep " \\\n " cfg.extraFlags} + ''; + RestrictAddressFamilies = [ + # Need AF_UNIX to collect data + "AF_UNIX" + ]; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/bitcoin.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/bitcoin.nix new file mode 100644 index 000000000000..330d54126448 --- /dev/null +++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/bitcoin.nix @@ -0,0 +1,82 @@ +{ config, lib, pkgs, options }: + +with lib; + +let + cfg = config.services.prometheus.exporters.bitcoin; +in +{ + port = 9332; + extraOpts = { + rpcUser = mkOption { + type = types.str; + default = "bitcoinrpc"; + description = lib.mdDoc '' + RPC user name. + ''; + }; + + rpcPasswordFile = mkOption { + type = types.path; + description = lib.mdDoc '' + File containing RPC password. + ''; + }; + + rpcScheme = mkOption { + type = types.enum [ "http" "https" ]; + default = "http"; + description = lib.mdDoc '' + Whether to connect to bitcoind over http or https. + ''; + }; + + rpcHost = mkOption { + type = types.str; + default = "localhost"; + description = lib.mdDoc '' + RPC host. + ''; + }; + + rpcPort = mkOption { + type = types.port; + default = 8332; + description = lib.mdDoc '' + RPC port number. + ''; + }; + + refreshSeconds = mkOption { + type = types.ints.unsigned; + default = 300; + description = lib.mdDoc '' + How often to ask bitcoind for metrics. + ''; + }; + + extraEnv = mkOption { + type = types.attrsOf types.str; + default = {}; + description = lib.mdDoc '' + Extra environment variables for the exporter. + ''; + }; + }; + serviceOpts = { + script = '' + export BITCOIN_RPC_PASSWORD=$(cat ${cfg.rpcPasswordFile}) + exec ${pkgs.prometheus-bitcoin-exporter}/bin/bitcoind-monitor.py + ''; + + environment = { + BITCOIN_RPC_USER = cfg.rpcUser; + BITCOIN_RPC_SCHEME = cfg.rpcScheme; + BITCOIN_RPC_HOST = cfg.rpcHost; + BITCOIN_RPC_PORT = toString cfg.rpcPort; + METRICS_ADDR = cfg.listenAddress; + METRICS_PORT = toString cfg.port; + REFRESH_SECONDS = toString cfg.refreshSeconds; + } // cfg.extraEnv; + }; +} diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/blackbox.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/blackbox.nix new file mode 100644 index 000000000000..ce2c391de523 --- /dev/null +++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/blackbox.nix @@ -0,0 +1,70 @@ +{ config, lib, pkgs, options }: + +with lib; + +let + logPrefix = "services.prometheus.exporter.blackbox"; + cfg = config.services.prometheus.exporters.blackbox; + + # This ensures that we can deal with string paths, path types and + # store-path strings with context. + coerceConfigFile = file: + if (builtins.isPath file) || (lib.isStorePath file) then + file + else + (lib.warn '' + ${logPrefix}: configuration file "${file}" is being copied to the nix-store. + If you would like to avoid that, please set enableConfigCheck to false. + '' /. + file); + checkConfigLocation = file: + if lib.hasPrefix "/tmp/" file then + throw + "${logPrefix}: configuration file must not reside within /tmp - it won't be visible to the systemd service." + else + file; + checkConfig = file: + pkgs.runCommand "checked-blackbox-exporter.conf" { + preferLocalBuild = true; + nativeBuildInputs = [ pkgs.buildPackages.prometheus-blackbox-exporter ]; + } '' + ln -s ${coerceConfigFile file} $out + blackbox_exporter --config.check --config.file $out + ''; +in { + port = 9115; + extraOpts = { + configFile = mkOption { + type = types.path; + description = lib.mdDoc '' + Path to configuration file. + ''; + }; + enableConfigCheck = mkOption { + type = types.bool; + default = true; + description = lib.mdDoc '' + Whether to run a correctness check for the configuration file. This depends + on the configuration file residing in the nix-store. Paths passed as string will + be copied to the store. + ''; + }; + }; + + serviceOpts = let + adjustedConfigFile = if cfg.enableConfigCheck then + checkConfig cfg.configFile + else + checkConfigLocation cfg.configFile; + in { + serviceConfig = { + AmbientCapabilities = [ "CAP_NET_RAW" ]; # for ping probes + ExecStart = '' + ${pkgs.prometheus-blackbox-exporter}/bin/blackbox_exporter \ + --web.listen-address ${cfg.listenAddress}:${toString cfg.port} \ + --config.file ${escapeShellArg adjustedConfigFile} \ + ${concatStringsSep " \\\n " cfg.extraFlags} + ''; + ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/buildkite-agent.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/buildkite-agent.nix new file mode 100644 index 000000000000..0515b72b13f9 --- /dev/null +++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/buildkite-agent.nix @@ -0,0 +1,64 @@ +{ config, lib, pkgs, options }: + +with lib; + +let + cfg = config.services.prometheus.exporters.buildkite-agent; +in +{ + port = 9876; + extraOpts = { + tokenPath = mkOption { + type = types.nullOr types.path; + apply = final: if final == null then null else toString final; + description = lib.mdDoc '' + The token from your Buildkite "Agents" page. + + A run-time path to the token file, which is supposed to be provisioned + outside of Nix store. + ''; + }; + interval = mkOption { + type = types.str; + default = "30s"; + example = "1min"; + description = lib.mdDoc '' + How often to update metrics. + ''; + }; + endpoint = mkOption { + type = types.str; + default = "https://agent.buildkite.com/v3"; + description = lib.mdDoc '' + The Buildkite Agent API endpoint. + ''; + }; + queues = mkOption { + type = with types; nullOr (listOf str); + default = null; + example = literalExpression ''[ "my-queue1" "my-queue2" ]''; + description = lib.mdDoc '' + Which specific queues to process. + ''; + }; + }; + serviceOpts = { + script = + let + queues = concatStringsSep " " (map (q: "-queue ${q}") cfg.queues); + in + '' + export BUILDKITE_AGENT_TOKEN="$(cat ${toString cfg.tokenPath})" + exec ${pkgs.buildkite-agent-metrics}/bin/buildkite-agent-metrics \ + -backend prometheus \ + -interval ${cfg.interval} \ + -endpoint ${cfg.endpoint} \ + ${optionalString (cfg.queues != null) queues} \ + -prometheus-addr "${cfg.listenAddress}:${toString cfg.port}" ${concatStringsSep " " cfg.extraFlags} + ''; + serviceConfig = { + DynamicUser = false; + RuntimeDirectory = "buildkite-agent-metrics"; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/collectd.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/collectd.nix new file mode 100644 index 000000000000..f67596f05a3a --- /dev/null +++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/collectd.nix @@ -0,0 +1,77 @@ +{ config, lib, pkgs, options }: + +with lib; + +let + cfg = config.services.prometheus.exporters.collectd; +in +{ + port = 9103; + extraOpts = { + collectdBinary = { + enable = mkEnableOption (lib.mdDoc "collectd binary protocol receiver"); + + authFile = mkOption { + default = null; + type = types.nullOr types.path; + description = lib.mdDoc "File mapping user names to pre-shared keys (passwords)."; + }; + + port = mkOption { + type = types.port; + default = 25826; + description = lib.mdDoc "Network address on which to accept collectd binary network packets."; + }; + + listenAddress = mkOption { + type = types.str; + default = "0.0.0.0"; + description = lib.mdDoc '' + Address to listen on for binary network packets. + ''; + }; + + securityLevel = mkOption { + type = types.enum ["None" "Sign" "Encrypt"]; + default = "None"; + description = lib.mdDoc '' + Minimum required security level for accepted packets. + ''; + }; + }; + + logFormat = mkOption { + type = types.enum [ "logfmt" "json" ]; + default = "logfmt"; + example = "json"; + description = lib.mdDoc '' + Set the log format. + ''; + }; + + logLevel = mkOption { + type = types.enum ["debug" "info" "warn" "error" "fatal"]; + default = "info"; + description = lib.mdDoc '' + Only log messages with the given severity or above. + ''; + }; + }; + serviceOpts = let + collectSettingsArgs = optionalString (cfg.collectdBinary.enable) '' + --collectd.listen-address ${cfg.collectdBinary.listenAddress}:${toString cfg.collectdBinary.port} \ + --collectd.security-level ${cfg.collectdBinary.securityLevel} \ + ''; + in { + serviceConfig = { + ExecStart = '' + ${pkgs.prometheus-collectd-exporter}/bin/collectd_exporter \ + --log.format ${escapeShellArg cfg.logFormat} \ + --log.level ${cfg.logLevel} \ + --web.listen-address ${cfg.listenAddress}:${toString cfg.port} \ + ${collectSettingsArgs} \ + ${concatStringsSep " \\\n " cfg.extraFlags} + ''; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/dmarc.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/dmarc.nix new file mode 100644 index 000000000000..437cece588a7 --- /dev/null +++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/dmarc.nix @@ -0,0 +1,117 @@ +{ config, lib, pkgs, options }: + +with lib; + +let + cfg = config.services.prometheus.exporters.dmarc; + + json = builtins.toJSON { + inherit (cfg) folders port; + listen_addr = cfg.listenAddress; + storage_path = "$STATE_DIRECTORY"; + imap = (builtins.removeAttrs cfg.imap [ "passwordFile" ]) // { password = "$IMAP_PASSWORD"; use_ssl = true; }; + poll_interval_seconds = cfg.pollIntervalSeconds; + deduplication_max_seconds = cfg.deduplicationMaxSeconds; + logging = { + version = 1; + disable_existing_loggers = false; + }; + }; +in { + port = 9797; + extraOpts = { + imap = { + host = mkOption { + type = types.str; + default = "localhost"; + description = lib.mdDoc '' + Hostname of IMAP server to connect to. + ''; + }; + port = mkOption { + type = types.port; + default = 993; + description = lib.mdDoc '' + Port of the IMAP server to connect to. + ''; + }; + username = mkOption { + type = types.str; + example = "postmaster@example.org"; + description = lib.mdDoc '' + Login username for the IMAP connection. + ''; + }; + passwordFile = mkOption { + type = types.str; + example = "/run/secrets/dovecot_pw"; + description = lib.mdDoc '' + File containing the login password for the IMAP connection. + ''; + }; + }; + folders = { + inbox = mkOption { + type = types.str; + default = "INBOX"; + description = lib.mdDoc '' + IMAP mailbox that is checked for incoming DMARC aggregate reports + ''; + }; + done = mkOption { + type = types.str; + default = "Archive"; + description = lib.mdDoc '' + IMAP mailbox that successfully processed reports are moved to. + ''; + }; + error = mkOption { + type = types.str; + default = "Invalid"; + description = lib.mdDoc '' + IMAP mailbox that emails are moved to that could not be processed. + ''; + }; + }; + pollIntervalSeconds = mkOption { + type = types.ints.unsigned; + default = 60; + description = lib.mdDoc '' + How often to poll the IMAP server in seconds. + ''; + }; + deduplicationMaxSeconds = mkOption { + type = types.ints.unsigned; + default = 604800; + defaultText = "7 days (in seconds)"; + description = lib.mdDoc '' + How long individual report IDs will be remembered to avoid + counting double delivered reports twice. + ''; + }; + debug = mkOption { + type = types.bool; + default = false; + description = lib.mdDoc '' + Whether to declare enable `--debug`. + ''; + }; + }; + serviceOpts = { + path = with pkgs; [ envsubst coreutils ]; + serviceConfig = { + StateDirectory = "prometheus-dmarc-exporter"; + WorkingDirectory = "/var/lib/prometheus-dmarc-exporter"; + ExecStart = "${pkgs.writeShellScript "setup-cfg" '' + export IMAP_PASSWORD="$(<${cfg.imap.passwordFile})" + envsubst \ + -i ${pkgs.writeText "dmarc-exporter.json.template" json} \ + -o ''${STATE_DIRECTORY}/dmarc-exporter.json + + exec ${pkgs.dmarc-metrics-exporter}/bin/dmarc-metrics-exporter \ + --configuration /var/lib/prometheus-dmarc-exporter/dmarc-exporter.json \ + ${optionalString cfg.debug "--debug"} + ''}"; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/dnsmasq.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/dnsmasq.nix new file mode 100644 index 000000000000..ece42a34cb06 --- /dev/null +++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/dnsmasq.nix @@ -0,0 +1,38 @@ +{ config, lib, pkgs, options }: + +with lib; + +let + cfg = config.services.prometheus.exporters.dnsmasq; +in +{ + port = 9153; + extraOpts = { + dnsmasqListenAddress = mkOption { + type = types.str; + default = "localhost:53"; + description = lib.mdDoc '' + Address on which dnsmasq listens. + ''; + }; + leasesPath = mkOption { + type = types.path; + default = "/var/lib/misc/dnsmasq.leases"; + example = "/var/lib/dnsmasq/dnsmasq.leases"; + description = lib.mdDoc '' + Path to the `dnsmasq.leases` file. + ''; + }; + }; + serviceOpts = { + serviceConfig = { + ExecStart = '' + ${pkgs.prometheus-dnsmasq-exporter}/bin/dnsmasq_exporter \ + --listen ${cfg.listenAddress}:${toString cfg.port} \ + --dnsmasq ${cfg.dnsmasqListenAddress} \ + --leases_path ${escapeShellArg cfg.leasesPath} \ + ${concatStringsSep " \\\n " cfg.extraFlags} + ''; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/domain.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/domain.nix new file mode 100644 index 000000000000..61e2fc80afde --- /dev/null +++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/domain.nix @@ -0,0 +1,19 @@ +{ config, lib, pkgs, options }: + +with lib; + +let + cfg = config.services.prometheus.exporters.domain; +in +{ + port = 9222; + serviceOpts = { + serviceConfig = { + ExecStart = '' + ${pkgs.prometheus-domain-exporter}/bin/domain_exporter \ + --bind ${cfg.listenAddress}:${toString cfg.port} \ + ${concatStringsSep " \\\n " cfg.extraFlags} + ''; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/dovecot.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/dovecot.nix new file mode 100644 index 000000000000..6fb438353a4c --- /dev/null +++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/dovecot.nix @@ -0,0 +1,92 @@ +{ config, lib, pkgs, options }: + +with lib; + +let + cfg = config.services.prometheus.exporters.dovecot; +in +{ + port = 9166; + extraOpts = { + telemetryPath = mkOption { + type = types.str; + default = "/metrics"; + description = lib.mdDoc '' + Path under which to expose metrics. + ''; + }; + socketPath = mkOption { + type = types.path; + default = "/var/run/dovecot/stats"; + example = "/var/run/dovecot2/old-stats"; + description = lib.mdDoc '' + Path under which the stats socket is placed. + The user/group under which the exporter runs, + should be able to access the socket in order + to scrape the metrics successfully. + + Please keep in mind that the stats module has changed in + [Dovecot 2.3+](https://wiki2.dovecot.org/Upgrading/2.3) which + is not [compatible with this exporter](https://github.com/kumina/dovecot_exporter/issues/8). + + The following extra config has to be passed to Dovecot to ensure that recent versions + work with this exporter: + ``` + { + services.prometheus.exporters.dovecot.enable = true; + services.prometheus.exporters.dovecot.socketPath = "/var/run/dovecot2/old-stats"; + services.dovecot2.mailPlugins.globally.enable = [ "old_stats" ]; + services.dovecot2.extraConfig = ''' + service old-stats { + unix_listener old-stats { + user = dovecot-exporter + group = dovecot-exporter + mode = 0660 + } + fifo_listener old-stats-mail { + mode = 0660 + user = dovecot + group = dovecot + } + fifo_listener old-stats-user { + mode = 0660 + user = dovecot + group = dovecot + } + } + plugin { + old_stats_refresh = 30 secs + old_stats_track_cmds = yes + } + '''; + } + ``` + ''; + }; + scopes = mkOption { + type = types.listOf types.str; + default = [ "user" ]; + example = [ "user" "global" ]; + description = lib.mdDoc '' + Stats scopes to query. + ''; + }; + }; + serviceOpts = { + serviceConfig = { + DynamicUser = false; + ExecStart = '' + ${pkgs.prometheus-dovecot-exporter}/bin/dovecot_exporter \ + --web.listen-address ${cfg.listenAddress}:${toString cfg.port} \ + --web.telemetry-path ${cfg.telemetryPath} \ + --dovecot.socket-path ${escapeShellArg cfg.socketPath} \ + --dovecot.scopes ${concatStringsSep "," cfg.scopes} \ + ${concatStringsSep " \\\n " cfg.extraFlags} + ''; + RestrictAddressFamilies = [ + # Need AF_UNIX to collect data + "AF_UNIX" + ]; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/exportarr.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/exportarr.nix new file mode 100644 index 000000000000..8511abbee1bd --- /dev/null +++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/exportarr.nix @@ -0,0 +1,55 @@ +{ config, lib, pkgs, options, type }: + +let + cfg = config.services.prometheus.exporters."exportarr-${type}"; + exportarrEnvironment = ( + lib.mapAttrs (_: toString) cfg.environment + ) // { + PORT = toString cfg.port; + URL = cfg.url; + API_KEY_FILE = lib.mkIf (cfg.apiKeyFile != null) "%d/api-key"; + }; +in +{ + port = 9708; + extraOpts = { + url = lib.mkOption { + type = lib.types.str; + default = "http://127.0.0.1"; + description = lib.mdDoc '' + The full URL to Sonarr, Radarr, or Lidarr. + ''; + }; + + apiKeyFile = lib.mkOption { + type = lib.types.nullOr lib.types.path; + default = null; + description = lib.mdDoc '' + File containing the api-key. + ''; + }; + + package = lib.mkPackageOption pkgs "exportarr" { }; + + environment = lib.mkOption { + type = lib.types.attrsOf lib.types.str; + default = { }; + description = lib.mdDoc '' + See [the configuration guide](https://github.com/onedr0p/exportarr#configuration) for available options. + ''; + example = { + PROWLARR__BACKFILL = true; + }; + }; + }; + serviceOpts = { + serviceConfig = { + LoadCredential = lib.optionalString (cfg.apiKeyFile != null) "api-key:${cfg.apiKeyFile}"; + ExecStart = ''${cfg.package}/bin/exportarr ${type} "$@"''; + ProcSubset = "pid"; + ProtectProc = "invisible"; + SystemCallFilter = ["@system-service" "~@privileged"]; + }; + environment = exportarrEnvironment; + }; +} diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/fastly.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/fastly.nix new file mode 100644 index 000000000000..36409caccf2e --- /dev/null +++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/fastly.nix @@ -0,0 +1,41 @@ +{ config, lib, pkgs, options }: + +with lib; + +let cfg = config.services.prometheus.exporters.fastly; +in +{ + port = 9118; + extraOpts = { + debug = mkEnableOption (lib.mdDoc "Debug logging mode for fastly-exporter"); + + configFile = mkOption { + type = types.nullOr types.path; + default = null; + description = lib.mdDoc '' + Path to a fastly-exporter configuration file. + Example one can be generated with `fastly-exporter --config-file-example`. + ''; + example = "./fastly-exporter-config.txt"; + }; + + tokenPath = mkOption { + type = types.nullOr types.path; + apply = final: if final == null then null else toString final; + description = lib.mdDoc '' + A run-time path to the token file, which is supposed to be provisioned + outside of Nix store. + ''; + }; + }; + serviceOpts = { + script = '' + ${optionalString (cfg.tokenPath != null) + "export FASTLY_API_TOKEN=$(cat ${toString cfg.tokenPath})"} + ${pkgs.prometheus-fastly-exporter}/bin/fastly-exporter \ + -listen http://${cfg.listenAddress}:${toString cfg.port} + ${optionalString cfg.debug "-debug true"} \ + ${optionalString (cfg.configFile != null) "-config-file ${cfg.configFile}"} + ''; + }; +} diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/flow.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/flow.nix new file mode 100644 index 000000000000..81099aaf1704 --- /dev/null +++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/flow.nix @@ -0,0 +1,50 @@ +{ config, lib, pkgs, options }: + +with lib; + +let + cfg = config.services.prometheus.exporters.flow; +in { + port = 9590; + extraOpts = { + brokers = mkOption { + type = types.listOf types.str; + example = literalExpression ''[ "kafka.example.org:19092" ]''; + description = lib.mdDoc "List of Kafka brokers to connect to."; + }; + + asn = mkOption { + type = types.ints.positive; + example = 65542; + description = lib.mdDoc "The ASN being monitored."; + }; + + partitions = mkOption { + type = types.listOf types.int; + default = []; + description = lib.mdDoc '' + The number of the partitions to consume, none means all. + ''; + }; + + topic = mkOption { + type = types.str; + example = "pmacct.acct"; + description = lib.mdDoc "The Kafka topic to consume from."; + }; + }; + + serviceOpts = { + serviceConfig = { + DynamicUser = true; + ExecStart = '' + ${pkgs.prometheus-flow-exporter}/bin/flow-exporter \ + -asn ${toString cfg.asn} \ + -topic ${cfg.topic} \ + -brokers ${concatStringsSep "," cfg.brokers} \ + ${optionalString (cfg.partitions != []) "-partitions ${concatStringsSep "," cfg.partitions}"} \ + -addr ${cfg.listenAddress}:${toString cfg.port} ${concatStringsSep " " cfg.extraFlags} + ''; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/fritzbox.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/fritzbox.nix new file mode 100644 index 000000000000..dc53d21406ff --- /dev/null +++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/fritzbox.nix @@ -0,0 +1,38 @@ +{ config, lib, pkgs, options }: + +with lib; + +let + cfg = config.services.prometheus.exporters.fritzbox; +in +{ + port = 9133; + extraOpts = { + gatewayAddress = mkOption { + type = types.str; + default = "fritz.box"; + description = lib.mdDoc '' + The hostname or IP of the FRITZ!Box. + ''; + }; + + gatewayPort = mkOption { + type = types.int; + default = 49000; + description = lib.mdDoc '' + The port of the FRITZ!Box UPnP service. + ''; + }; + }; + serviceOpts = { + serviceConfig = { + ExecStart = '' + ${pkgs.prometheus-fritzbox-exporter}/bin/exporter \ + -listen-address ${cfg.listenAddress}:${toString cfg.port} \ + -gateway-address ${cfg.gatewayAddress} \ + -gateway-port ${toString cfg.gatewayPort} \ + ${concatStringsSep " \\\n " cfg.extraFlags} + ''; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/graphite.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/graphite.nix new file mode 100644 index 000000000000..34a887104212 --- /dev/null +++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/graphite.nix @@ -0,0 +1,41 @@ +{ config, lib, pkgs, options }: + +let + cfg = config.services.prometheus.exporters.graphite; + format = pkgs.formats.yaml { }; +in +{ + port = 9108; + extraOpts = { + graphitePort = lib.mkOption { + type = lib.types.port; + default = 9109; + description = lib.mdDoc '' + Port to use for the graphite server. + ''; + }; + mappingSettings = lib.mkOption { + type = lib.types.submodule { + freeformType = format.type; + options = { }; + }; + default = { }; + description = lib.mdDoc '' + Mapping configuration for the exporter, see + <https://github.com/prometheus/graphite_exporter#yaml-config> for + available options. + ''; + }; + }; + serviceOpts = { + serviceConfig = { + ExecStart = '' + ${pkgs.prometheus-graphite-exporter}/bin/graphite_exporter \ + --web.listen-address ${cfg.listenAddress}:${toString cfg.port} \ + --graphite.listen-address ${cfg.listenAddress}:${toString cfg.graphitePort} \ + --graphite.mapping-config ${format.generate "mapping.yml" cfg.mappingSettings} \ + ${lib.concatStringsSep " \\\n " cfg.extraFlags} + ''; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/idrac.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/idrac.nix new file mode 100644 index 000000000000..f5604bc00ee0 --- /dev/null +++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/idrac.nix @@ -0,0 +1,69 @@ +{ config, lib, pkgs, options }: + +with lib; +let + cfg = config.services.prometheus.exporters.idrac; + + configFile = if cfg.configurationPath != null + then cfg.configurationPath + else pkgs.writeText "idrac.yml" (builtins.toJSON cfg.configuration); +in +{ + port = 9348; + extraOpts = { + configurationPath = mkOption { + type = with types; nullOr path; + default = null; + example = "/etc/prometheus-idrac-exporter/idrac.yml"; + description = lib.mdDoc '' + Path to the service's config file. This path can either be a computed path in /nix/store or a path in the local filesystem. + + The config file should NOT be stored in /nix/store as it will contain passwords and/or keys in plain text. + + Mutually exclusive with `configuration` option. + + Configuration reference: https://github.com/mrlhansen/idrac_exporter/#configuration + ''; + }; + configuration = mkOption { + type = types.nullOr types.attrs; + description = lib.mdDoc '' + Configuration for iDRAC exporter, as a nix attribute set. + + Configuration reference: https://github.com/mrlhansen/idrac_exporter/#configuration + + Mutually exclusive with `configurationPath` option. + ''; + default = null; + example = { + timeout = 10; + retries = 1; + hosts = { + default = { + username = "username"; + password = "password"; + }; + }; + metrics = { + system = true; + sensors = true; + power = true; + sel = true; + storage = true; + memory = true; + }; + }; + }; + }; + + serviceOpts = { + serviceConfig = { + LoadCredential = "configFile:${configFile}"; + ExecStart = "${pkgs.prometheus-idrac-exporter}/bin/idrac_exporter -config %d/configFile"; + Environment = [ + "IDRAC_EXPORTER_LISTEN_ADDRESS=${cfg.listenAddress}" + "IDRAC_EXPORTER_LISTEN_PORT=${toString cfg.port}" + ]; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/imap-mailstat.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/imap-mailstat.nix new file mode 100644 index 000000000000..c5024a258e71 --- /dev/null +++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/imap-mailstat.nix @@ -0,0 +1,71 @@ +{ config, lib, pkgs, options }: + +with lib; + +let + cfg = config.services.prometheus.exporters.imap-mailstat; + valueToString = value: + if (builtins.typeOf value == "string") then "\"${value}\"" + else ( + if (builtins.typeOf value == "int") then "${toString value}" + else ( + if (builtins.typeOf value == "bool") then (if value then "true" else "false") + else "XXX ${toString value}" + ) + ); + createConfigFile = accounts: + # unfortunately on toTOML yet + # https://github.com/NixOS/nix/issues/3929 + pkgs.writeText "imap-mailstat-exporter.conf" '' + ${concatStrings (attrValues (mapAttrs (name: config: "[[Accounts]]\nname = \"${name}\"\n${concatStrings (attrValues (mapAttrs (k: v: "${k} = ${valueToString v}\n") config))}") accounts))} + ''; + mkOpt = type: description: mkOption { + type = types.nullOr type; + default = null; + description = lib.mdDoc description; + }; + accountOptions.options = { + mailaddress = mkOpt types.str "Your email address (at the moment used as login name)"; + username = mkOpt types.str "If empty string mailaddress value is used"; + password = mkOpt types.str ""; + serveraddress = mkOpt types.str "mailserver name or address"; + serverport = mkOpt types.int "imap port number (at the moment only tls connection is supported)"; + starttls = mkOpt types.bool "set to true for using STARTTLS to start a TLS connection"; + }; +in +{ + port = 8081; + extraOpts = { + oldestUnseenDate = mkOption { + type = types.bool; + default = false; + description = lib.mdDoc '' + Enable metric with timestamp of oldest unseen mail + ''; + }; + accounts = mkOption { + type = types.attrsOf (types.submodule accountOptions); + default = {}; + description = lib.mdDoc '' + Accounts to monitor + ''; + }; + configurationFile = mkOption { + type = types.path; + example = "/path/to/config-file"; + description = lib.mdDoc '' + File containing the configuration + ''; + }; + }; + serviceOpts = { + serviceConfig = { + ExecStart = '' + ${pkgs.prometheus-imap-mailstat-exporter}/bin/imap-mailstat-exporter \ + -config ${createConfigFile cfg.accounts} \ + ${optionalString cfg.oldestUnseenDate "-oldestunseendate"} \ + ${concatStringsSep " \\\n " cfg.extraFlags} + ''; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/influxdb.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/influxdb.nix new file mode 100644 index 000000000000..61c0c08d2250 --- /dev/null +++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/influxdb.nix @@ -0,0 +1,34 @@ +{ config, lib, pkgs, options }: + +with lib; + +let + cfg = config.services.prometheus.exporters.influxdb; +in +{ + port = 9122; + extraOpts = { + sampleExpiry = mkOption { + type = types.str; + default = "5m"; + example = "10m"; + description = lib.mdDoc "How long a sample is valid for"; + }; + udpBindAddress = mkOption { + type = types.str; + default = ":9122"; + example = "192.0.2.1:9122"; + description = lib.mdDoc "Address on which to listen for udp packets"; + }; + }; + serviceOpts = { + serviceConfig = { + RuntimeDirectory = "prometheus-influxdb-exporter"; + ExecStart = '' + ${pkgs.prometheus-influxdb-exporter}/bin/influxdb_exporter \ + --web.listen-address ${cfg.listenAddress}:${toString cfg.port} \ + --influxdb.sample-expiry ${cfg.sampleExpiry} ${concatStringsSep " " cfg.extraFlags} + ''; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/ipmi.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/ipmi.nix new file mode 100644 index 000000000000..9adbe31d84d6 --- /dev/null +++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/ipmi.nix @@ -0,0 +1,42 @@ +{ config, lib, pkgs, options }: + +with lib; + +let + logPrefix = "services.prometheus.exporter.ipmi"; + cfg = config.services.prometheus.exporters.ipmi; +in { + port = 9290; + + extraOpts = { + configFile = mkOption { + type = types.nullOr types.path; + default = null; + description = lib.mdDoc '' + Path to configuration file. + ''; + }; + + webConfigFile = mkOption { + type = types.nullOr types.path; + default = null; + description = lib.mdDoc '' + Path to configuration file that can enable TLS or authentication. + ''; + }; + }; + + serviceOpts.serviceConfig = { + ExecStart = with cfg; concatStringsSep " " ([ + "${pkgs.prometheus-ipmi-exporter}/bin/ipmi_exporter" + "--web.listen-address ${listenAddress}:${toString port}" + ] ++ optionals (cfg.webConfigFile != null) [ + "--web.config.file ${escapeShellArg cfg.webConfigFile}" + ] ++ optionals (cfg.configFile != null) [ + "--config.file ${escapeShellArg cfg.configFile}" + ] ++ extraFlags); + + ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; + RestrictAddressFamilies = [ "AF_INET" "AF_INET6" "AF_UNIX" ]; + }; +} diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/jitsi.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/jitsi.nix new file mode 100644 index 000000000000..024602718602 --- /dev/null +++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/jitsi.nix @@ -0,0 +1,40 @@ +{ config, lib, pkgs, options }: + +with lib; + +let + cfg = config.services.prometheus.exporters.jitsi; +in +{ + port = 9700; + extraOpts = { + url = mkOption { + type = types.str; + default = "http://localhost:8080/colibri/stats"; + description = lib.mdDoc '' + Jitsi Videobridge metrics URL to monitor. + This is usually /colibri/stats on port 8080 of the jitsi videobridge host. + ''; + }; + interval = mkOption { + type = types.str; + default = "30s"; + example = "1min"; + description = lib.mdDoc '' + How often to scrape new data + ''; + }; + }; + serviceOpts = { + serviceConfig = { + ExecStart = '' + ${pkgs.prometheus-jitsi-exporter}/bin/jitsiexporter \ + -url ${escapeShellArg cfg.url} \ + -host ${cfg.listenAddress} \ + -port ${toString cfg.port} \ + -interval ${toString cfg.interval} \ + ${concatStringsSep " \\\n " cfg.extraFlags} + ''; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/json.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/json.nix new file mode 100644 index 000000000000..473f3a7e47e3 --- /dev/null +++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/json.nix @@ -0,0 +1,43 @@ +{ config, lib, pkgs, options }: + +with lib; + +let + cfg = config.services.prometheus.exporters.json; +in +{ + port = 7979; + extraOpts = { + configFile = mkOption { + type = types.path; + description = lib.mdDoc '' + Path to configuration file. + ''; + }; + }; + serviceOpts = { + serviceConfig = { + ExecStart = '' + ${pkgs.prometheus-json-exporter}/bin/json_exporter \ + --config.file ${escapeShellArg cfg.configFile} \ + --web.listen-address="${cfg.listenAddress}:${toString cfg.port}" \ + ${concatStringsSep " \\\n " cfg.extraFlags} + ''; + }; + }; + imports = [ + (mkRemovedOptionModule [ "url" ] '' + This option was removed. The URL of the endpoint serving JSON + must now be provided to the exporter by prometheus via the url + parameter `target'. + + In prometheus a scrape URL would look like this: + + http://some.json-exporter.host:7979/probe?target=https://example.com/some/json/endpoint + + For more information, take a look at the official documentation + (https://github.com/prometheus-community/json_exporter) of the json_exporter. + '') + ({ options.warnings = options.warnings; options.assertions = options.assertions; }) + ]; +} diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/junos-czerwonk.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/junos-czerwonk.nix new file mode 100644 index 000000000000..15e0c9ecb177 --- /dev/null +++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/junos-czerwonk.nix @@ -0,0 +1,72 @@ +{ config, lib, pkgs, options }: + +with lib; + +let + cfg = config.services.prometheus.exporters.junos-czerwonk; + + configFile = if cfg.configuration != null then configurationFile else (escapeShellArg cfg.configurationFile); + + configurationFile = pkgs.writeText "prometheus-junos-czerwonk-exporter.conf" (builtins.toJSON (cfg.configuration)); +in +{ + port = 9326; + extraOpts = { + environmentFile = mkOption { + type = types.nullOr types.str; + default = null; + description = lib.mdDoc '' + File containing env-vars to be substituted into the exporter's config. + ''; + }; + configurationFile = mkOption { + type = types.nullOr types.path; + default = null; + description = lib.mdDoc '' + Specify the JunOS exporter configuration file to use. + ''; + }; + configuration = mkOption { + type = types.nullOr types.attrs; + default = null; + description = lib.mdDoc '' + JunOS exporter configuration as nix attribute set. Mutually exclusive with the `configurationFile` option. + ''; + example = { + devices = [ + { + host = "router1"; + key_file = "/path/to/key"; + } + ]; + }; + }; + telemetryPath = mkOption { + type = types.str; + default = "/metrics"; + description = lib.mdDoc '' + Path under which to expose metrics. + ''; + }; + }; + serviceOpts = { + serviceConfig = { + DynamicUser = false; + EnvironmentFile = mkIf (cfg.environmentFile != null) [ cfg.environmentFile ]; + RuntimeDirectory = "prometheus-junos-czerwonk-exporter"; + ExecStartPre = [ + "${pkgs.writeShellScript "subst-secrets-junos-czerwonk-exporter" '' + umask 0077 + ${pkgs.envsubst}/bin/envsubst -i ${configFile} -o ''${RUNTIME_DIRECTORY}/junos-exporter.json + ''}" + ]; + ExecStart = '' + ${pkgs.prometheus-junos-czerwonk-exporter}/bin/junos_exporter \ + -web.listen-address ${cfg.listenAddress}:${toString cfg.port} \ + -web.telemetry-path ${cfg.telemetryPath} \ + -config.file ''${RUNTIME_DIRECTORY}/junos-exporter.json \ + ${concatStringsSep " \\\n " cfg.extraFlags} + ''; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/kea.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/kea.nix new file mode 100644 index 000000000000..3abb6ff6bdf8 --- /dev/null +++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/kea.nix @@ -0,0 +1,49 @@ +{ config +, lib +, pkgs +, options +}: + +with lib; + +let + cfg = config.services.prometheus.exporters.kea; +in { + port = 9547; + extraOpts = { + controlSocketPaths = mkOption { + type = types.listOf types.str; + example = literalExpression '' + [ + "/run/kea/kea-dhcp4.socket" + "/run/kea/kea-dhcp6.socket" + ] + ''; + description = lib.mdDoc '' + Paths to kea control sockets + ''; + }; + }; + serviceOpts = { + after = [ + "kea-dhcp4-server.service" + "kea-dhcp6-server.service" + ]; + serviceConfig = { + User = "kea"; + DynamicUser = true; + ExecStart = '' + ${pkgs.prometheus-kea-exporter}/bin/kea-exporter \ + --address ${cfg.listenAddress} \ + --port ${toString cfg.port} \ + ${concatStringsSep " " cfg.controlSocketPaths} + ''; + RuntimeDirectory = "kea"; + RuntimeDirectoryPreserve = true; + RestrictAddressFamilies = [ + # Need AF_UNIX to collect data + "AF_UNIX" + ]; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/keylight.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/keylight.nix new file mode 100644 index 000000000000..dfa56343b871 --- /dev/null +++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/keylight.nix @@ -0,0 +1,19 @@ +{ config, lib, pkgs, options }: + +with lib; + +let + cfg = config.services.prometheus.exporters.keylight; +in +{ + port = 9288; + serviceOpts = { + serviceConfig = { + ExecStart = '' + ${pkgs.prometheus-keylight-exporter}/bin/keylight_exporter \ + -metrics.addr ${cfg.listenAddress}:${toString cfg.port} \ + ${concatStringsSep " \\\n " cfg.extraFlags} + ''; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/knot.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/knot.nix new file mode 100644 index 000000000000..775848750803 --- /dev/null +++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/knot.nix @@ -0,0 +1,58 @@ +{ config, lib, pkgs, options }: + +with lib; + +let + cfg = config.services.prometheus.exporters.knot; +in { + port = 9433; + extraOpts = { + knotLibraryPath = mkOption { + type = types.nullOr types.str; + default = null; + example = literalExpression ''"''${pkgs.knot-dns.out}/lib/libknot.so"''; + description = lib.mdDoc '' + Path to the library of `knot-dns`. + ''; + }; + + knotSocketPath = mkOption { + type = types.str; + default = "/run/knot/knot.sock"; + description = lib.mdDoc '' + Socket path of {manpage}`knotd(8)`. + ''; + }; + + knotSocketTimeout = mkOption { + type = types.ints.positive; + default = 2000; + description = lib.mdDoc '' + Timeout in seconds. + ''; + }; + }; + serviceOpts = { + path = with pkgs; [ + procps + ]; + serviceConfig = { + ExecStart = '' + ${pkgs.prometheus-knot-exporter}/bin/knot-exporter \ + --web-listen-addr ${cfg.listenAddress} \ + --web-listen-port ${toString cfg.port} \ + --knot-socket-path ${cfg.knotSocketPath} \ + --knot-socket-timeout ${toString cfg.knotSocketTimeout} \ + ${lib.optionalString (cfg.knotLibraryPath != null) "--knot-library-path ${cfg.knotLibraryPath}"} \ + ${concatStringsSep " \\\n " cfg.extraFlags} + ''; + SupplementaryGroups = [ + "knot" + ]; + RestrictAddressFamilies = [ + # Need AF_UNIX to collect data + "AF_UNIX" + ]; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/lnd.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/lnd.nix new file mode 100644 index 000000000000..9f914b1dc146 --- /dev/null +++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/lnd.nix @@ -0,0 +1,46 @@ +{ config, lib, pkgs, options }: + +with lib; + +let + cfg = config.services.prometheus.exporters.lnd; +in +{ + port = 9092; + extraOpts = { + lndHost = mkOption { + type = types.str; + default = "localhost:10009"; + description = lib.mdDoc '' + lnd instance gRPC address:port. + ''; + }; + + lndTlsPath = mkOption { + type = types.path; + description = lib.mdDoc '' + Path to lnd TLS certificate. + ''; + }; + + lndMacaroonDir = mkOption { + type = types.path; + description = lib.mdDoc '' + Path to lnd macaroons. + ''; + }; + }; + serviceOpts.serviceConfig = { + ExecStart = '' + ${pkgs.prometheus-lnd-exporter}/bin/lndmon \ + --prometheus.listenaddr=${cfg.listenAddress}:${toString cfg.port} \ + --prometheus.logdir=/var/log/prometheus-lnd-exporter \ + --lnd.host=${cfg.lndHost} \ + --lnd.tlspath=${cfg.lndTlsPath} \ + --lnd.macaroondir=${cfg.lndMacaroonDir} \ + ${concatStringsSep " \\\n " cfg.extraFlags} + ''; + LogsDirectory = "prometheus-lnd-exporter"; + ReadOnlyPaths = [ cfg.lndTlsPath cfg.lndMacaroonDir ]; + }; +} diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/mail.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/mail.nix new file mode 100644 index 000000000000..15079f5841f4 --- /dev/null +++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/mail.nix @@ -0,0 +1,190 @@ +{ config, lib, pkgs, options }: + +with lib; + +let + cfg = config.services.prometheus.exporters.mail; + + configFile = if cfg.configuration != null then configurationFile else (escapeShellArg cfg.configFile); + + configurationFile = pkgs.writeText "prometheus-mail-exporter.conf" (builtins.toJSON ( + # removes the _module attribute, null values and converts attrNames to lowercase + mapAttrs' (name: value: + if name == "servers" + then nameValuePair (toLower name) + ((map (srv: (mapAttrs' (n: v: nameValuePair (toLower n) v) + (filterAttrs (n: v: !(n == "_module" || v == null)) srv) + ))) value) + else nameValuePair (toLower name) value + ) (filterAttrs (n: _: !(n == "_module")) cfg.configuration) + )); + + serverOptions.options = { + name = mkOption { + type = types.str; + description = lib.mdDoc '' + Value for label 'configname' which will be added to all metrics. + ''; + }; + server = mkOption { + type = types.str; + description = lib.mdDoc '' + Hostname of the server that should be probed. + ''; + }; + port = mkOption { + type = types.port; + example = 587; + description = lib.mdDoc '' + Port to use for SMTP. + ''; + }; + from = mkOption { + type = types.str; + example = "exporteruser@domain.tld"; + description = lib.mdDoc '' + Content of 'From' Header for probing mails. + ''; + }; + to = mkOption { + type = types.str; + example = "exporteruser@domain.tld"; + description = lib.mdDoc '' + Content of 'To' Header for probing mails. + ''; + }; + detectionDir = mkOption { + type = types.path; + example = "/var/spool/mail/exporteruser/new"; + description = lib.mdDoc '' + Directory in which new mails for the exporter user are placed. + Note that this needs to exist when the exporter starts. + ''; + }; + login = mkOption { + type = types.nullOr types.str; + default = null; + example = "exporteruser@domain.tld"; + description = lib.mdDoc '' + Username to use for SMTP authentication. + ''; + }; + passphrase = mkOption { + type = types.nullOr types.str; + default = null; + description = lib.mdDoc '' + Password to use for SMTP authentication. + ''; + }; + }; + + exporterOptions.options = { + monitoringInterval = mkOption { + type = types.str; + example = "10s"; + description = lib.mdDoc '' + Time interval between two probe attempts. + ''; + }; + mailCheckTimeout = mkOption { + type = types.str; + description = lib.mdDoc '' + Timeout until mails are considered "didn't make it". + ''; + }; + disableFileDeletion = mkOption { + type = types.bool; + default = false; + description = lib.mdDoc '' + Disables the exporter's function to delete probing mails. + ''; + }; + servers = mkOption { + type = types.listOf (types.submodule serverOptions); + default = []; + example = literalExpression '' + [ { + name = "testserver"; + server = "smtp.domain.tld"; + port = 587; + from = "exporteruser@domain.tld"; + to = "exporteruser@domain.tld"; + detectionDir = "/path/to/Maildir/new"; + } ] + ''; + description = lib.mdDoc '' + List of servers that should be probed. + + *Note:* if your mailserver has {manpage}`rspamd(8)` configured, + it can happen that emails from this exporter are marked as spam. + + It's possible to work around the issue with a config like this: + ``` + { + services.rspamd.locals."multimap.conf".text = ''' + ALLOWLIST_PROMETHEUS { + filter = "email:domain:tld"; + type = "from"; + map = "''${pkgs.writeText "allowmap" "domain.tld"}"; + score = -100.0; + } + '''; + } + ``` + ''; + }; + }; +in +{ + port = 9225; + extraOpts = { + environmentFile = mkOption { + type = types.nullOr types.str; + default = null; + description = lib.mdDoc '' + File containing env-vars to be substituted into the exporter's config. + ''; + }; + configFile = mkOption { + type = types.nullOr types.path; + default = null; + description = lib.mdDoc '' + Specify the mailexporter configuration file to use. + ''; + }; + configuration = mkOption { + type = types.nullOr (types.submodule exporterOptions); + default = null; + description = lib.mdDoc '' + Specify the mailexporter configuration file to use. + ''; + }; + telemetryPath = mkOption { + type = types.str; + default = "/metrics"; + description = lib.mdDoc '' + Path under which to expose metrics. + ''; + }; + }; + serviceOpts = { + serviceConfig = { + DynamicUser = false; + EnvironmentFile = mkIf (cfg.environmentFile != null) [ cfg.environmentFile ]; + RuntimeDirectory = "prometheus-mail-exporter"; + ExecStartPre = [ + "${pkgs.writeShellScript "subst-secrets-mail-exporter" '' + umask 0077 + ${pkgs.envsubst}/bin/envsubst -i ${configFile} -o ''${RUNTIME_DIRECTORY}/mail-exporter.json + ''}" + ]; + ExecStart = '' + ${pkgs.prometheus-mail-exporter}/bin/mailexporter \ + --web.listen-address ${cfg.listenAddress}:${toString cfg.port} \ + --web.telemetry-path ${cfg.telemetryPath} \ + --config.file ''${RUNTIME_DIRECTORY}/mail-exporter.json \ + ${concatStringsSep " \\\n " cfg.extraFlags} + ''; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/mikrotik.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/mikrotik.nix new file mode 100644 index 000000000000..54dab4b5581a --- /dev/null +++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/mikrotik.nix @@ -0,0 +1,66 @@ +{ config, lib, pkgs, options }: + +with lib; + +let + cfg = config.services.prometheus.exporters.mikrotik; +in +{ + port = 9436; + extraOpts = { + configFile = mkOption { + type = types.nullOr types.path; + default = null; + description = lib.mdDoc '' + Path to a mikrotik exporter configuration file. Mutually exclusive with + {option}`configuration` option. + ''; + example = literalExpression "./mikrotik.yml"; + }; + + configuration = mkOption { + type = types.nullOr types.attrs; + default = null; + description = lib.mdDoc '' + Mikrotik exporter configuration as nix attribute set. Mutually exclusive with + {option}`configFile` option. + + See <https://github.com/nshttpd/mikrotik-exporter/blob/master/README.md> + for the description of the configuration file format. + ''; + example = literalExpression '' + { + devices = [ + { + name = "my_router"; + address = "10.10.0.1"; + user = "prometheus"; + password = "changeme"; + } + ]; + features = { + bgp = true; + dhcp = true; + routes = true; + optics = true; + }; + } + ''; + }; + }; + serviceOpts = let + configFile = if cfg.configFile != null + then cfg.configFile + else "${pkgs.writeText "mikrotik-exporter.yml" (builtins.toJSON cfg.configuration)}"; + in { + serviceConfig = { + # -port is misleading name, it actually accepts address too + ExecStart = '' + ${pkgs.prometheus-mikrotik-exporter}/bin/mikrotik-exporter \ + -config-file=${escapeShellArg configFile} \ + -port=${cfg.listenAddress}:${toString cfg.port} \ + ${concatStringsSep " \\\n " cfg.extraFlags} + ''; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/minio.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/minio.nix new file mode 100644 index 000000000000..82cc3fc314f2 --- /dev/null +++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/minio.nix @@ -0,0 +1,64 @@ +{ config, lib, pkgs, options }: + +with lib; + +let + cfg = config.services.prometheus.exporters.minio; +in +{ + port = 9290; + extraOpts = { + minioAddress = mkOption { + type = types.str; + example = "https://10.0.0.1:9000"; + description = lib.mdDoc '' + The URL of the minio server. + Use HTTPS if Minio accepts secure connections only. + By default this connects to the local minio server if enabled. + ''; + }; + + minioAccessKey = mkOption { + type = types.str; + example = "yourMinioAccessKey"; + description = lib.mdDoc '' + The value of the Minio access key. + It is required in order to connect to the server. + By default this uses the one from the local minio server if enabled + and `config.services.minio.accessKey`. + ''; + }; + + minioAccessSecret = mkOption { + type = types.str; + description = lib.mdDoc '' + The value of the Minio access secret. + It is required in order to connect to the server. + By default this uses the one from the local minio server if enabled + and `config.services.minio.secretKey`. + ''; + }; + + minioBucketStats = mkOption { + type = types.bool; + default = false; + description = lib.mdDoc '' + Collect statistics about the buckets and files in buckets. + It requires more computation, use it carefully in case of large buckets.. + ''; + }; + }; + serviceOpts = { + serviceConfig = { + ExecStart = '' + ${pkgs.prometheus-minio-exporter}/bin/minio-exporter \ + -web.listen-address ${cfg.listenAddress}:${toString cfg.port} \ + -minio.server ${cfg.minioAddress} \ + -minio.access-key ${escapeShellArg cfg.minioAccessKey} \ + -minio.access-secret ${escapeShellArg cfg.minioAccessSecret} \ + ${optionalString cfg.minioBucketStats "-minio.bucket-stats"} \ + ${concatStringsSep " \\\n " cfg.extraFlags} + ''; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/modemmanager.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/modemmanager.nix new file mode 100644 index 000000000000..222ea3e5384f --- /dev/null +++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/modemmanager.nix @@ -0,0 +1,37 @@ +{ config, lib, pkgs, options }: + +with lib; + +let + cfg = config.services.prometheus.exporters.modemmanager; +in +{ + port = 9539; + extraOpts = { + refreshRate = mkOption { + type = types.str; + default = "5s"; + description = lib.mdDoc '' + How frequently ModemManager will refresh the extended signal quality + information for each modem. The duration should be specified in seconds + ("5s"), minutes ("1m"), or hours ("1h"). + ''; + }; + }; + serviceOpts = { + serviceConfig = { + # Required in order to authenticate with ModemManager via D-Bus. + SupplementaryGroups = "networkmanager"; + ExecStart = '' + ${pkgs.prometheus-modemmanager-exporter}/bin/modemmanager_exporter \ + -addr ${cfg.listenAddress}:${toString cfg.port} \ + -rate ${cfg.refreshRate} \ + ${concatStringsSep " \\\n " cfg.extraFlags} + ''; + RestrictAddressFamilies = [ + # Need AF_UNIX to collect data + "AF_UNIX" + ]; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/mongodb.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/mongodb.nix new file mode 100644 index 000000000000..b36a09c60920 --- /dev/null +++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/mongodb.nix @@ -0,0 +1,68 @@ +{ config, lib, pkgs, options }: + +with lib; + +let + cfg = config.services.prometheus.exporters.mongodb; +in +{ + port = 9216; + extraOpts = { + uri = mkOption { + type = types.str; + default = "mongodb://localhost:27017/test"; + example = "mongodb://localhost:27017/test"; + description = lib.mdDoc "MongoDB URI to connect to."; + }; + collStats = mkOption { + type = types.listOf types.str; + default = [ ]; + example = [ "db1.coll1" "db2" ]; + description = lib.mdDoc '' + List of comma separared databases.collections to get $collStats + ''; + }; + indexStats = mkOption { + type = types.listOf types.str; + default = [ ]; + example = [ "db1.coll1" "db2" ]; + description = lib.mdDoc '' + List of comma separared databases.collections to get $indexStats + ''; + }; + collector = mkOption { + type = types.listOf types.str; + default = [ ]; + example = [ "diagnosticdata" "replicasetstatus" "dbstats" "topmetrics" "currentopmetrics" "indexstats" "dbstats" "profile" ]; + description = lib.mdDoc "Enabled collectors"; + }; + collectAll = mkOption { + type = types.bool; + default = false; + description = lib.mdDoc '' + Enable all collectors. Same as specifying all --collector.<name> + ''; + }; + telemetryPath = mkOption { + type = types.str; + default = "/metrics"; + example = "/metrics"; + description = lib.mdDoc "Metrics expose path"; + }; + }; + serviceOpts = { + serviceConfig = { + RuntimeDirectory = "prometheus-mongodb-exporter"; + ExecStart = '' + ${getExe pkgs.prometheus-mongodb-exporter} \ + --mongodb.uri="${cfg.uri}" \ + ${if cfg.collectAll then "--collect-all" else concatMapStringsSep " " (x: "--collect.${x}") cfg.collector} \ + ${optionalString (length cfg.collStats > 0) "--mongodb.collstats-colls=${concatStringsSep "," cfg.collStats}"} \ + ${optionalString (length cfg.indexStats > 0) "--mongodb.indexstats-colls=${concatStringsSep "," cfg.indexStats}"} \ + --web.listen-address="${cfg.listenAddress}:${toString cfg.port}" \ + --web.telemetry-path="${cfg.telemetryPath}" \ + ${escapeShellArgs cfg.extraFlags} + ''; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/mysqld.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/mysqld.nix new file mode 100644 index 000000000000..849c514de681 --- /dev/null +++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/mysqld.nix @@ -0,0 +1,60 @@ +{ config, lib, pkgs, options }: +let + cfg = config.services.prometheus.exporters.mysqld; + inherit (lib) types mkOption mdDoc mkIf mkForce cli concatStringsSep optionalString escapeShellArgs; +in { + port = 9104; + extraOpts = { + telemetryPath = mkOption { + type = types.str; + default = "/metrics"; + description = mdDoc '' + Path under which to expose metrics. + ''; + }; + + runAsLocalSuperUser = mkOption { + type = types.bool; + default = false; + description = mdDoc '' + Whether to run the exporter as {option}`services.mysql.user`. + ''; + }; + + configFile = mkOption { + type = types.path; + example = "/var/lib/prometheus-mysqld-exporter.cnf"; + description = mdDoc '' + Path to the services config file. + + See <https://github.com/prometheus/mysqld_exporter#running> for more information about + the available options. + + ::: {.warn} + Please do not store this file in the nix store if you choose to include any credentials here, + as it would be world-readable. + ::: + ''; + }; + }; + + serviceOpts = { + serviceConfig = { + DynamicUser = !cfg.runAsLocalSuperUser; + User = mkIf cfg.runAsLocalSuperUser (mkForce config.services.mysql.user); + LoadCredential = mkIf (cfg.configFile != null) (mkForce ("config:" + cfg.configFile)); + ExecStart = concatStringsSep " " [ + "${pkgs.prometheus-mysqld-exporter}/bin/mysqld_exporter" + "--web.listen-address=${cfg.listenAddress}:${toString cfg.port}" + "--web.telemetry-path=${cfg.telemetryPath}" + (optionalString (cfg.configFile != null) ''--config.my-cnf=''${CREDENTIALS_DIRECTORY}/config'') + (escapeShellArgs cfg.extraFlags) + ]; + RestrictAddressFamilies = [ + # The exporter can be configured to talk to a local mysql server via a unix socket. + "AF_UNIX" + ]; + }; + }; +} + diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/nextcloud.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/nextcloud.nix new file mode 100644 index 000000000000..28a3eb6a134c --- /dev/null +++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/nextcloud.nix @@ -0,0 +1,72 @@ +{ config, lib, pkgs, options }: + +with lib; + +let + cfg = config.services.prometheus.exporters.nextcloud; +in +{ + port = 9205; + extraOpts = { + url = mkOption { + type = types.str; + example = "https://domain.tld"; + description = lib.mdDoc '' + URL to the Nextcloud serverinfo page. + Adding the path to the serverinfo API is optional, it defaults + to `/ocs/v2.php/apps/serverinfo/api/v1/info`. + ''; + }; + username = mkOption { + type = types.str; + default = "nextcloud-exporter"; + description = lib.mdDoc '' + Username for connecting to Nextcloud. + Note that this account needs to have admin privileges in Nextcloud. + Unused when using token authentication. + ''; + }; + passwordFile = mkOption { + type = types.nullOr types.path; + default = null; + example = "/path/to/password-file"; + description = lib.mdDoc '' + File containing the password for connecting to Nextcloud. + Make sure that this file is readable by the exporter user. + ''; + }; + tokenFile = mkOption { + type = types.nullOr types.path; + default = null; + example = "/path/to/token-file"; + description = lib.mdDoc '' + File containing the token for connecting to Nextcloud. + Make sure that this file is readable by the exporter user. + ''; + }; + timeout = mkOption { + type = types.str; + default = "5s"; + description = lib.mdDoc '' + Timeout for getting server info document. + ''; + }; + }; + serviceOpts = { + serviceConfig = { + DynamicUser = false; + ExecStart = '' + ${pkgs.prometheus-nextcloud-exporter}/bin/nextcloud-exporter \ + --addr ${cfg.listenAddress}:${toString cfg.port} \ + --timeout ${cfg.timeout} \ + --server ${cfg.url} \ + ${if cfg.passwordFile != null then '' + --username ${cfg.username} \ + --password ${escapeShellArg "@${cfg.passwordFile}"} \ + '' else '' + --auth-token ${escapeShellArg "@${cfg.tokenFile}"} \ + ''} \ + ${concatStringsSep " \\\n " cfg.extraFlags}''; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/nginx.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/nginx.nix new file mode 100644 index 000000000000..88dc79fc2503 --- /dev/null +++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/nginx.nix @@ -0,0 +1,68 @@ +{ config, lib, pkgs, options }: + +with lib; + +let + cfg = config.services.prometheus.exporters.nginx; +in +{ + port = 9113; + extraOpts = { + scrapeUri = mkOption { + type = types.str; + default = "http://localhost/nginx_status"; + description = lib.mdDoc '' + Address to access the nginx status page. + Can be enabled with services.nginx.statusPage = true. + ''; + }; + telemetryPath = mkOption { + type = types.str; + default = "/metrics"; + description = lib.mdDoc '' + Path under which to expose metrics. + ''; + }; + sslVerify = mkOption { + type = types.bool; + default = true; + description = lib.mdDoc '' + Whether to perform certificate verification for https. + ''; + }; + constLabels = mkOption { + type = types.listOf types.str; + default = []; + example = [ + "label1=value1" + "label2=value2" + ]; + description = lib.mdDoc '' + A list of constant labels that will be used in every metric. + ''; + }; + }; + serviceOpts = mkMerge ([{ + environment.CONST_LABELS = concatStringsSep "," cfg.constLabels; + serviceConfig = { + ExecStart = '' + ${pkgs.prometheus-nginx-exporter}/bin/nginx-prometheus-exporter \ + --nginx.scrape-uri='${cfg.scrapeUri}' \ + --${lib.optionalString (!cfg.sslVerify) "no-"}nginx.ssl-verify \ + --web.listen-address=${cfg.listenAddress}:${toString cfg.port} \ + --web.telemetry-path=${cfg.telemetryPath} \ + ${concatStringsSep " \\\n " cfg.extraFlags} + ''; + }; + }] ++ [(mkIf config.services.nginx.enable { + after = [ "nginx.service" ]; + requires = [ "nginx.service" ]; + })]); + imports = [ + (mkRenamedOptionModule [ "telemetryEndpoint" ] [ "telemetryPath" ]) + (mkRemovedOptionModule [ "insecure" ] '' + This option was replaced by 'prometheus.exporters.nginx.sslVerify'. + '') + ({ options.warnings = options.warnings; options.assertions = options.assertions; }) + ]; +} diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/nginxlog.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/nginxlog.nix new file mode 100644 index 000000000000..674dc9dd4158 --- /dev/null +++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/nginxlog.nix @@ -0,0 +1,51 @@ +{ config, lib, pkgs, options }: + +with lib; + +let + cfg = config.services.prometheus.exporters.nginxlog; +in { + port = 9117; + extraOpts = { + settings = mkOption { + type = types.attrs; + default = {}; + description = lib.mdDoc '' + All settings of nginxlog expressed as an Nix attrset. + + Check the official documentation for the corresponding YAML + settings that can all be used here: https://github.com/martin-helmich/prometheus-nginxlog-exporter + + The `listen` object is already generated by `port`, `listenAddress` and `metricsEndpoint` and + will be merged with the value of `settings` before writing it as JSON. + ''; + }; + + metricsEndpoint = mkOption { + type = types.str; + default = "/metrics"; + description = lib.mdDoc '' + Path under which to expose metrics. + ''; + }; + }; + + serviceOpts = let + listenConfig = { + listen = { + port = cfg.port; + address = cfg.listenAddress; + metrics_endpoint = cfg.metricsEndpoint; + }; + }; + completeConfig = pkgs.writeText "nginxlog-exporter.yaml" (builtins.toJSON (lib.recursiveUpdate listenConfig cfg.settings)); + in { + serviceConfig = { + ExecStart = '' + ${pkgs.prometheus-nginxlog-exporter}/bin/prometheus-nginxlog-exporter -config-file ${completeConfig} + ''; + Restart="always"; + ProtectSystem="full"; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/node.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/node.nix new file mode 100644 index 000000000000..dd8602e2c63d --- /dev/null +++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/node.nix @@ -0,0 +1,53 @@ +{ config, lib, pkgs, options }: + +with lib; + +let + cfg = config.services.prometheus.exporters.node; + collectorIsEnabled = final: any (collector: (final == collector)) cfg.enabledCollectors; + collectorIsDisabled = final: any (collector: (final == collector)) cfg.disabledCollectors; +in +{ + port = 9100; + extraOpts = { + enabledCollectors = mkOption { + type = types.listOf types.str; + default = []; + example = [ "systemd" ]; + description = lib.mdDoc '' + Collectors to enable. The collectors listed here are enabled in addition to the default ones. + ''; + }; + disabledCollectors = mkOption { + type = types.listOf types.str; + default = []; + example = [ "timex" ]; + description = lib.mdDoc '' + Collectors to disable which are enabled by default. + ''; + }; + }; + serviceOpts = { + serviceConfig = { + DynamicUser = false; + RuntimeDirectory = "prometheus-node-exporter"; + ExecStart = '' + ${pkgs.prometheus-node-exporter}/bin/node_exporter \ + ${concatMapStringsSep " " (x: "--collector." + x) cfg.enabledCollectors} \ + ${concatMapStringsSep " " (x: "--no-collector." + x) cfg.disabledCollectors} \ + --web.listen-address ${cfg.listenAddress}:${toString cfg.port} ${concatStringsSep " " cfg.extraFlags} + ''; + RestrictAddressFamilies = optionals (collectorIsEnabled "logind" || collectorIsEnabled "systemd") [ + # needs access to dbus via unix sockets (logind/systemd) + "AF_UNIX" + ] ++ optionals (collectorIsEnabled "network_route" || collectorIsEnabled "wifi" || ! collectorIsDisabled "netdev") [ + # needs netlink sockets for wireless collector + "AF_NETLINK" + ]; + # The timex collector needs to access clock APIs + ProtectClock = collectorIsDisabled "timex"; + # Allow space monitoring under /home + ProtectHome = true; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/nut.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/nut.nix new file mode 100644 index 000000000000..1c86b48b4509 --- /dev/null +++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/nut.nix @@ -0,0 +1,50 @@ +{ config, lib, pkgs, options }: + +with lib; + +let + cfg = config.services.prometheus.exporters.nut; +in +{ + port = 9199; + extraOpts = { + nutServer = mkOption { + type = types.str; + default = "127.0.0.1"; + description = lib.mdDoc '' + Hostname or address of the NUT server + ''; + }; + nutUser = mkOption { + type = types.str; + default = ""; + example = "nut"; + description = lib.mdDoc '' + The user to log in into NUT server. If set, passwordPath should + also be set. + + Default NUT configs usually permit reading variables without + authentication. + ''; + }; + passwordPath = mkOption { + type = types.nullOr types.path; + default = null; + apply = final: if final == null then null else toString final; + description = lib.mdDoc '' + A run-time path to the nutUser password file, which should be + provisioned outside of Nix store. + ''; + }; + }; + serviceOpts = { + script = '' + ${optionalString (cfg.passwordPath != null) + "export NUT_EXPORTER_PASSWORD=$(cat ${toString cfg.passwordPath})"} + ${pkgs.prometheus-nut-exporter}/bin/nut_exporter \ + --nut.server=${cfg.nutServer} \ + --web.listen-address="${cfg.listenAddress}:${toString cfg.port}" \ + ${optionalString (cfg.nutUser != "") "--nut.username=${cfg.nutUser}"} + ''; + }; +} diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/openldap.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/openldap.nix new file mode 100644 index 000000000000..aee3ae5bb2d4 --- /dev/null +++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/openldap.nix @@ -0,0 +1,67 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.prometheus.exporters.openldap; +in { + port = 9330; + extraOpts = { + ldapCredentialFile = mkOption { + type = types.path; + example = "/run/keys/ldap_pass"; + description = lib.mdDoc '' + Environment file to contain the credentials to authenticate against + `openldap`. + + The file should look like this: + ``` + --- + ldapUser: "cn=monitoring,cn=Monitor" + ldapPass: "secret" + ``` + ''; + }; + protocol = mkOption { + default = "tcp"; + example = "udp"; + type = types.str; + description = lib.mdDoc '' + Which protocol to use to connect against `openldap`. + ''; + }; + ldapAddr = mkOption { + default = "localhost:389"; + type = types.str; + description = lib.mdDoc '' + Address of the `openldap`-instance. + ''; + }; + metricsPath = mkOption { + default = "/metrics"; + type = types.str; + description = lib.mdDoc '' + URL path where metrics should be exposed. + ''; + }; + interval = mkOption { + default = "30s"; + type = types.str; + example = "1m"; + description = lib.mdDoc '' + Scrape interval of the exporter. + ''; + }; + }; + serviceOpts.serviceConfig = { + ExecStart = '' + ${pkgs.prometheus-openldap-exporter}/bin/openldap_exporter \ + --promAddr ${cfg.listenAddress}:${toString cfg.port} \ + --metrPath ${cfg.metricsPath} \ + --ldapNet ${cfg.protocol} \ + --interval ${cfg.interval} \ + --config ${cfg.ldapCredentialFile} \ + ${concatStringsSep " \\\n " cfg.extraFlags} + ''; + }; +} diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/openvpn.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/openvpn.nix new file mode 100644 index 000000000000..5b54dad99805 --- /dev/null +++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/openvpn.nix @@ -0,0 +1,39 @@ +{ config, pkgs, lib, ... }: + +with lib; + +let + cfg = config.services.prometheus.exporters.openvpn; +in { + port = 9176; + extraOpts = { + statusPaths = mkOption { + type = types.listOf types.str; + description = lib.mdDoc '' + Paths to OpenVPN status files. Please configure the OpenVPN option + `status` accordingly. + ''; + }; + telemetryPath = mkOption { + type = types.str; + default = "/metrics"; + description = lib.mdDoc '' + Path under which to expose metrics. + ''; + }; + }; + + serviceOpts = { + serviceConfig = { + PrivateDevices = true; + ProtectKernelModules = true; + NoNewPrivileges = true; + ExecStart = '' + ${pkgs.prometheus-openvpn-exporter}/bin/openvpn_exporter \ + -openvpn.status_paths "${concatStringsSep "," cfg.statusPaths}" \ + -web.listen-address ${cfg.listenAddress}:${toString cfg.port} \ + -web.telemetry-path ${cfg.telemetryPath} + ''; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/pgbouncer.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/pgbouncer.nix new file mode 100644 index 000000000000..9e55cadae523 --- /dev/null +++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/pgbouncer.nix @@ -0,0 +1,145 @@ +{ config, lib, pkgs, options }: + +with lib; + +let + cfg = config.services.prometheus.exporters.pgbouncer; +in +{ + port = 9127; + extraOpts = { + + telemetryPath = mkOption { + type = types.str; + default = "/metrics"; + description = lib.mdDoc '' + Path under which to expose metrics. + ''; + }; + + connectionString = mkOption { + type = types.str; + default = ""; + example = "postgres://admin:@localhost:6432/pgbouncer?sslmode=require"; + description = lib.mdDoc '' + Connection string for accessing pgBouncer. + + NOTE: You MUST keep pgbouncer as database name (special internal db)!!! + + NOTE: Admin user (with password or passwordless) MUST exist + in the services.pgbouncer.authFile if authType other than any is used. + + WARNING: this secret is stored in the world-readable Nix store! + Use {option}`connectionStringFile` instead. + ''; + }; + + connectionStringFile = mkOption { + type = types.nullOr types.path; + default = null; + example = "/run/keys/pgBouncer-connection-string"; + description = lib.mdDoc '' + File that contains pgBouncer connection string in format: + postgres://admin:@localhost:6432/pgbouncer?sslmode=require + + NOTE: You MUST keep pgbouncer as database name (special internal db)!!! + + NOTE: Admin user (with password or passwordless) MUST exist + in the services.pgbouncer.authFile if authType other than any is used. + + {option}`connectionStringFile` takes precedence over {option}`connectionString` + ''; + }; + + pidFile = mkOption { + type = types.nullOr types.str; + default = null; + description = lib.mdDoc '' + Path to PgBouncer pid file. + + If provided, the standard process metrics get exported for the PgBouncer + process, prefixed with 'pgbouncer_process_...'. The pgbouncer_process exporter + needs to have read access to files owned by the PgBouncer process. Depends on + the availability of /proc. + + https://prometheus.io/docs/instrumenting/writing_clientlibs/#process-metrics. + + ''; + }; + + webSystemdSocket = mkOption { + type = types.bool; + default = false; + description = lib.mdDoc '' + Use systemd socket activation listeners instead of port listeners (Linux only). + ''; + }; + + logLevel = mkOption { + type = types.enum ["debug" "info" "warn" "error" ]; + default = "info"; + description = lib.mdDoc '' + Only log messages with the given severity or above. + ''; + }; + + logFormat = mkOption { + type = types.enum ["logfmt" "json"]; + default = "logfmt"; + description = lib.mdDoc '' + Output format of log messages. One of: [logfmt, json] + ''; + }; + + webConfigFile = mkOption { + type = types.nullOr types.path; + default = null; + description = lib.mdDoc '' + Path to configuration file that can enable TLS or authentication. + ''; + }; + + extraFlags = mkOption { + type = types.listOf types.str; + default = [ ]; + description = lib.mdDoc '' + Extra commandline options when launching Prometheus. + ''; + }; + + }; + + serviceOpts = { + after = [ "pgbouncer.service" ]; + serviceConfig = let + startScript = pkgs.writeShellScriptBin "pgbouncer-start" "${concatStringsSep " " ([ + "${pkgs.prometheus-pgbouncer-exporter}/bin/pgbouncer_exporter" + "--web.listen-address ${cfg.listenAddress}:${toString cfg.port}" + "--pgBouncer.connectionString ${if cfg.connectionStringFile != null then + "$(head -n1 ${cfg.connectionStringFile})" else "${escapeShellArg cfg.connectionString}"}" + ] + ++ optionals (cfg.telemetryPath != null) [ + "--web.telemetry-path ${escapeShellArg cfg.telemetryPath}" + ] + ++ optionals (cfg.pidFile != null) [ + "--pgBouncer.pid-file= ${escapeShellArg cfg.pidFile}" + ] + ++ optionals (cfg.logLevel != null) [ + "--log.level ${escapeShellArg cfg.logLevel}" + ] + ++ optionals (cfg.logFormat != null) [ + "--log.format ${escapeShellArg cfg.logFormat}" + ] + ++ optionals (cfg.webSystemdSocket != false) [ + "--web.systemd-socket ${escapeShellArg cfg.webSystemdSocket}" + ] + ++ optionals (cfg.webConfigFile != null) [ + "--web.config.file ${escapeShellArg cfg.webConfigFile}" + ] + ++ cfg.extraFlags)}"; + in + { + ExecStart = "${startScript}/bin/pgbouncer-start"; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/php-fpm.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/php-fpm.nix new file mode 100644 index 000000000000..8238f1ac1856 --- /dev/null +++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/php-fpm.nix @@ -0,0 +1,65 @@ +{ config +, lib +, pkgs +, options +}: + +let + logPrefix = "services.prometheus.exporter.php-fpm"; + cfg = config.services.prometheus.exporters.php-fpm; +in { + port = 9253; + extraOpts = { + package = lib.mkPackageOption pkgs "prometheus-php-fpm-exporter" {}; + + telemetryPath = lib.mkOption { + type = lib.types.str; + default = "/metrics"; + description = lib.mdDoc '' + Path under which to expose metrics. + ''; + }; + + environmentFile = lib.mkOption { + type = lib.types.nullOr lib.types.path; + default = null; + example = "/root/prometheus-php-fpm-exporter.env"; + description = lib.mdDoc '' + Environment file as defined in {manpage}`systemd.exec(5)`. + + Secrets may be passed to the service without adding them to the + world-readable Nix store, by specifying placeholder variables as + the option value in Nix and setting these variables accordingly in the + environment file. + + Environment variables from this file will be interpolated into the + config file using envsubst with this syntax: + `$ENVIRONMENT ''${VARIABLE}` + + For variables to use see [options and defaults](https://github.com/hipages/php-fpm_exporter#options-and-defaults). + + The main use is to set the PHP_FPM_SCRAPE_URI that indicate how to connect to PHP-FPM process. + + ``` + # Content of the environment file + PHP_FPM_SCRAPE_URI="unix:///tmp/php.sock;/status" + ``` + + Note that this file needs to be available on the host on which + this exporter is running. + ''; + }; + }; + + serviceOpts = { + serviceConfig = { + EnvironmentFile = lib.mkIf (cfg.environmentFile != null) [ cfg.environmentFile ]; + ExecStart = '' + ${lib.getExe cfg.package} server \ + --web.listen-address ${cfg.listenAddress}:${toString cfg.port} \ + --web.telemetry-path ${cfg.telemetryPath} \ + ${lib.concatStringsSep " \\\n " cfg.extraFlags} + ''; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/pihole.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/pihole.nix new file mode 100644 index 000000000000..6f403b3e58c8 --- /dev/null +++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/pihole.nix @@ -0,0 +1,78 @@ +{ config, lib, pkgs, options }: + +with lib; + +let + cfg = config.services.prometheus.exporters.pihole; +in +{ + imports = [ + (mkRemovedOptionModule [ "interval"] "This option has been removed.") + ({ options.warnings = options.warnings; options.assertions = options.assertions; }) + ]; + + port = 9617; + extraOpts = { + apiToken = mkOption { + type = types.str; + default = ""; + example = "580a770cb40511eb85290242ac130003580a770cb40511eb85290242ac130003"; + description = lib.mdDoc '' + Pi-Hole API token which can be used instead of a password + ''; + }; + password = mkOption { + type = types.str; + default = ""; + example = "password"; + description = lib.mdDoc '' + The password to login into Pi-Hole. An api token can be used instead. + ''; + }; + piholeHostname = mkOption { + type = types.str; + default = "pihole"; + example = "127.0.0.1"; + description = lib.mdDoc '' + Hostname or address where to find the Pi-Hole webinterface + ''; + }; + piholePort = mkOption { + type = types.port; + default = 80; + example = 443; + description = lib.mdDoc '' + The port Pi-Hole webinterface is reachable on + ''; + }; + protocol = mkOption { + type = types.enum [ "http" "https" ]; + default = "http"; + example = "https"; + description = lib.mdDoc '' + The protocol which is used to connect to Pi-Hole + ''; + }; + timeout = mkOption { + type = types.str; + default = "5s"; + description = lib.mdDoc '' + Controls the timeout to connect to a Pi-Hole instance + ''; + }; + }; + serviceOpts = { + serviceConfig = { + ExecStart = '' + ${pkgs.prometheus-pihole-exporter}/bin/pihole-exporter \ + ${optionalString (cfg.apiToken != "") "-pihole_api_token ${cfg.apiToken}"} \ + -pihole_hostname ${cfg.piholeHostname} \ + ${optionalString (cfg.password != "") "-pihole_password ${cfg.password}"} \ + -pihole_port ${toString cfg.piholePort} \ + -pihole_protocol ${cfg.protocol} \ + -port ${toString cfg.port} \ + -timeout ${cfg.timeout} + ''; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/ping.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/ping.nix new file mode 100644 index 000000000000..af78b6bef625 --- /dev/null +++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/ping.nix @@ -0,0 +1,48 @@ +{ config, lib, pkgs, options }: + +with lib; + +let + cfg = config.services.prometheus.exporters.ping; + + settingsFormat = pkgs.formats.yaml {}; + configFile = settingsFormat.generate "config.yml" cfg.settings; +in +{ + port = 9427; + extraOpts = { + telemetryPath = mkOption { + type = types.str; + default = "/metrics"; + description = '' + Path under which to expose metrics. + ''; + }; + + settings = mkOption { + type = settingsFormat.type; + default = {}; + + description = lib.mdDoc '' + Configuration for ping_exporter, see + <https://github.com/czerwonk/ping_exporter> + for supported values. + ''; + }; + }; + + serviceOpts = { + serviceConfig = { + # ping-exporter needs `CAP_NET_RAW` to run as non root https://github.com/czerwonk/ping_exporter#running-as-non-root-user + CapabilityBoundingSet = [ "CAP_NET_RAW" ]; + AmbientCapabilities = [ "CAP_NET_RAW" ]; + ExecStart = '' + ${pkgs.prometheus-ping-exporter}/bin/ping_exporter \ + --web.listen-address ${cfg.listenAddress}:${toString cfg.port} \ + --web.telemetry-path ${cfg.telemetryPath} \ + --config.path="${configFile}" \ + ${concatStringsSep " \\\n " cfg.extraFlags} + ''; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/postfix.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/postfix.nix new file mode 100644 index 000000000000..9f402b123110 --- /dev/null +++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/postfix.nix @@ -0,0 +1,100 @@ +{ config, lib, pkgs, options }: + +with lib; + +let + cfg = config.services.prometheus.exporters.postfix; +in +{ + port = 9154; + extraOpts = { + group = mkOption { + type = types.str; + description = lib.mdDoc '' + Group under which the postfix exporter shall be run. + It should match the group that is allowed to access the + `showq` socket in the `queue/public/` directory. + Defaults to `services.postfix.setgidGroup` when postfix is enabled. + ''; + }; + telemetryPath = mkOption { + type = types.str; + default = "/metrics"; + description = lib.mdDoc '' + Path under which to expose metrics. + ''; + }; + logfilePath = mkOption { + type = types.path; + default = "/var/log/postfix_exporter_input.log"; + example = "/var/log/mail.log"; + description = lib.mdDoc '' + Path where Postfix writes log entries. + This file will be truncated by this exporter! + ''; + }; + showqPath = mkOption { + type = types.path; + default = "/var/lib/postfix/queue/public/showq"; + example = "/var/spool/postfix/public/showq"; + description = lib.mdDoc '' + Path where Postfix places its showq socket. + ''; + }; + systemd = { + enable = mkOption { + type = types.bool; + default = true; + description = lib.mdDoc '' + Whether to enable reading metrics from the systemd journal instead of from a logfile + ''; + }; + unit = mkOption { + type = types.str; + default = "postfix.service"; + description = lib.mdDoc '' + Name of the postfix systemd unit. + ''; + }; + slice = mkOption { + type = types.nullOr types.str; + default = null; + description = lib.mdDoc '' + Name of the postfix systemd slice. + This overrides the {option}`systemd.unit`. + ''; + }; + journalPath = mkOption { + type = types.nullOr types.path; + default = null; + description = lib.mdDoc '' + Path to the systemd journal. + ''; + }; + }; + }; + serviceOpts = { + after = mkIf cfg.systemd.enable [ cfg.systemd.unit ]; + serviceConfig = { + DynamicUser = false; + # By default, each prometheus exporter only gets AF_INET & AF_INET6, + # but AF_UNIX is needed to read from the `showq`-socket. + RestrictAddressFamilies = [ "AF_UNIX" ]; + SupplementaryGroups = mkIf cfg.systemd.enable [ "systemd-journal" ]; + ExecStart = '' + ${pkgs.prometheus-postfix-exporter}/bin/postfix_exporter \ + --web.listen-address ${cfg.listenAddress}:${toString cfg.port} \ + --web.telemetry-path ${cfg.telemetryPath} \ + --postfix.showq_path ${escapeShellArg cfg.showqPath} \ + ${concatStringsSep " \\\n " (cfg.extraFlags + ++ optional cfg.systemd.enable "--systemd.enable" + ++ optional cfg.systemd.enable (if cfg.systemd.slice != null + then "--systemd.slice ${cfg.systemd.slice}" + else "--systemd.unit ${cfg.systemd.unit}") + ++ optional (cfg.systemd.enable && (cfg.systemd.journalPath != null)) + "--systemd.journal_path ${escapeShellArg cfg.systemd.journalPath}" + ++ optional (!cfg.systemd.enable) "--postfix.logfile_path ${escapeShellArg cfg.logfilePath}")} + ''; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/postgres.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/postgres.nix new file mode 100644 index 000000000000..755d771ecdff --- /dev/null +++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/postgres.nix @@ -0,0 +1,86 @@ +{ config, lib, pkgs, options }: + +with lib; + +let + cfg = config.services.prometheus.exporters.postgres; +in +{ + port = 9187; + extraOpts = { + telemetryPath = mkOption { + type = types.str; + default = "/metrics"; + description = lib.mdDoc '' + Path under which to expose metrics. + ''; + }; + dataSourceName = mkOption { + type = types.str; + default = "user=postgres database=postgres host=/run/postgresql sslmode=disable"; + example = "postgresql://username:password@localhost:5432/postgres?sslmode=disable"; + description = lib.mdDoc '' + Accepts PostgreSQL URI form and key=value form arguments. + ''; + }; + runAsLocalSuperUser = mkOption { + type = types.bool; + default = false; + description = lib.mdDoc '' + Whether to run the exporter as the local 'postgres' super user. + ''; + }; + + # TODO perhaps LoadCredential would be more appropriate + environmentFile = mkOption { + type = types.nullOr types.path; + default = null; + example = "/root/prometheus-postgres-exporter.env"; + description = lib.mdDoc '' + Environment file as defined in {manpage}`systemd.exec(5)`. + + Secrets may be passed to the service without adding them to the + world-readable Nix store, by specifying placeholder variables as + the option value in Nix and setting these variables accordingly in the + environment file. + + Environment variables from this file will be interpolated into the + config file using envsubst with this syntax: + `$ENVIRONMENT ''${VARIABLE}` + + The main use is to set the DATA_SOURCE_NAME that contains the + postgres password + + note that contents from this file will override dataSourceName + if you have set it from nix. + + ``` + # Content of the environment file + DATA_SOURCE_NAME=postgresql://username:password@localhost:5432/postgres?sslmode=disable + ``` + + Note that this file needs to be available on the host on which + this exporter is running. + ''; + }; + + }; + serviceOpts = { + environment.DATA_SOURCE_NAME = cfg.dataSourceName; + serviceConfig = { + DynamicUser = false; + User = mkIf cfg.runAsLocalSuperUser (mkForce "postgres"); + EnvironmentFile = mkIf (cfg.environmentFile != null) [ cfg.environmentFile ]; + ExecStart = '' + ${pkgs.prometheus-postgres-exporter}/bin/postgres_exporter \ + --web.listen-address ${cfg.listenAddress}:${toString cfg.port} \ + --web.telemetry-path ${cfg.telemetryPath} \ + ${concatStringsSep " \\\n " cfg.extraFlags} + ''; + RestrictAddressFamilies = [ + # Need AF_UNIX to collect data + "AF_UNIX" + ]; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/process.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/process.nix new file mode 100644 index 000000000000..278d6cd78074 --- /dev/null +++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/process.nix @@ -0,0 +1,46 @@ +{ config, lib, pkgs, options }: + +with lib; + +let + cfg = config.services.prometheus.exporters.process; + configFile = pkgs.writeText "process-exporter.yaml" (builtins.toJSON cfg.settings); +in +{ + port = 9256; + extraOpts = { + settings.process_names = mkOption { + type = types.listOf types.anything; + default = []; + example = literalExpression '' + [ + # Remove nix store path from process name + { name = "{{.Matches.Wrapped}} {{ .Matches.Args }}"; cmdline = [ "^/nix/store[^ ]*/(?P<Wrapped>[^ /]*) (?P<Args>.*)" ]; } + ] + ''; + description = lib.mdDoc '' + All settings expressed as an Nix attrset. + + Check the official documentation for the corresponding YAML + settings that can all be used here: <https://github.com/ncabatoff/process-exporter> + ''; + }; + }; + serviceOpts = { + serviceConfig = { + DynamicUser = false; + ExecStart = '' + ${pkgs.prometheus-process-exporter}/bin/process-exporter \ + --web.listen-address ${cfg.listenAddress}:${toString cfg.port} \ + --config.path ${configFile} \ + ${concatStringsSep " \\\n " cfg.extraFlags} + ''; + NoNewPrivileges = true; + ProtectHome = true; + ProtectSystem = true; + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectControlGroups = true; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/pve.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/pve.nix new file mode 100644 index 000000000000..20ee2e4b3238 --- /dev/null +++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/pve.nix @@ -0,0 +1,112 @@ +{ config, lib, pkgs, options }: + +with lib; +let + cfg = config.services.prometheus.exporters.pve; + + # pve exporter requires a config file so create an empty one if configFile is not provided + emptyConfigFile = pkgs.writeTextFile { + name = "pve.yml"; + text = "default:"; + }; + + computedConfigFile = if cfg.configFile == null then emptyConfigFile else cfg.configFile; +in +{ + port = 9221; + extraOpts = { + package = mkPackageOption pkgs "prometheus-pve-exporter" { }; + + environmentFile = mkOption { + type = with types; nullOr path; + default = null; + example = "/etc/prometheus-pve-exporter/pve.env"; + description = lib.mdDoc '' + Path to the service's environment file. This path can either be a computed path in /nix/store or a path in the local filesystem. + + The environment file should NOT be stored in /nix/store as it contains passwords and/or keys in plain text. + + Environment reference: https://github.com/prometheus-pve/prometheus-pve-exporter#authentication + ''; + }; + + configFile = mkOption { + type = with types; nullOr path; + default = null; + example = "/etc/prometheus-pve-exporter/pve.yml"; + description = lib.mdDoc '' + Path to the service's config file. This path can either be a computed path in /nix/store or a path in the local filesystem. + + The config file should NOT be stored in /nix/store as it will contain passwords and/or keys in plain text. + + If both configFile and environmentFile are provided, the configFile option will be ignored. + + Configuration reference: https://github.com/prometheus-pve/prometheus-pve-exporter/#authentication + ''; + }; + + collectors = { + status = mkOption { + type = types.bool; + default = true; + description = lib.mdDoc '' + Collect Node/VM/CT status + ''; + }; + version = mkOption { + type = types.bool; + default = true; + description = lib.mdDoc '' + Collect PVE version info + ''; + }; + node = mkOption { + type = types.bool; + default = true; + description = lib.mdDoc '' + Collect PVE node info + ''; + }; + cluster = mkOption { + type = types.bool; + default = true; + description = lib.mdDoc '' + Collect PVE cluster info + ''; + }; + resources = mkOption { + type = types.bool; + default = true; + description = lib.mdDoc '' + Collect PVE resources info + ''; + }; + config = mkOption { + type = types.bool; + default = true; + description = lib.mdDoc '' + Collect PVE onboot status + ''; + }; + }; + }; + serviceOpts = { + serviceConfig = { + DynamicUser = cfg.environmentFile == null; + LoadCredential = "configFile:${computedConfigFile}"; + ExecStart = '' + ${cfg.package}/bin/pve_exporter \ + --${optionalString (!cfg.collectors.status) "no-"}collector.status \ + --${optionalString (!cfg.collectors.version) "no-"}collector.version \ + --${optionalString (!cfg.collectors.node) "no-"}collector.node \ + --${optionalString (!cfg.collectors.cluster) "no-"}collector.cluster \ + --${optionalString (!cfg.collectors.resources) "no-"}collector.resources \ + --${optionalString (!cfg.collectors.config) "no-"}collector.config \ + %d/configFile \ + ${toString cfg.port} ${cfg.listenAddress} + ''; + } // optionalAttrs (cfg.environmentFile != null) { + EnvironmentFile = cfg.environmentFile; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/py-air-control.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/py-air-control.nix new file mode 100644 index 000000000000..f03b3c4df916 --- /dev/null +++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/py-air-control.nix @@ -0,0 +1,53 @@ +{ config, lib, pkgs, options }: + +with lib; + +let + cfg = config.services.prometheus.exporters.py-air-control; + + workingDir = "/var/lib/${cfg.stateDir}"; + +in +{ + port = 9896; + extraOpts = { + deviceHostname = mkOption { + type = types.str; + example = "192.168.1.123"; + description = lib.mdDoc '' + The hostname of the air purification device from which to scrape the metrics. + ''; + }; + protocol = mkOption { + type = types.str; + default = "http"; + description = lib.mdDoc '' + The protocol to use when communicating with the air purification device. + Available: [http, coap, plain_coap] + ''; + }; + stateDir = mkOption { + type = types.str; + default = "prometheus-py-air-control-exporter"; + description = lib.mdDoc '' + Directory below `/var/lib` to store runtime data. + This directory will be created automatically using systemd's StateDirectory mechanism. + ''; + }; + }; + serviceOpts = { + serviceConfig = { + DynamicUser = false; + StateDirectory = cfg.stateDir; + WorkingDirectory = workingDir; + ExecStart = '' + ${pkgs.python3Packages.py-air-control-exporter}/bin/py-air-control-exporter \ + --host ${cfg.deviceHostname} \ + --protocol ${cfg.protocol} \ + --listen-port ${toString cfg.port} \ + --listen-address ${cfg.listenAddress} + ''; + Environment = [ "HOME=${workingDir}" ]; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/redis.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/redis.nix new file mode 100644 index 000000000000..befbcb21f766 --- /dev/null +++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/redis.nix @@ -0,0 +1,19 @@ +{ config, lib, pkgs, options }: + +with lib; + +let + cfg = config.services.prometheus.exporters.redis; +in +{ + port = 9121; + serviceOpts = { + serviceConfig = { + ExecStart = '' + ${pkgs.prometheus-redis-exporter}/bin/redis_exporter \ + -web.listen-address ${cfg.listenAddress}:${toString cfg.port} \ + ${concatStringsSep " \\\n " cfg.extraFlags} + ''; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/rspamd.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/rspamd.nix new file mode 100644 index 000000000000..f9dcfad07d30 --- /dev/null +++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/rspamd.nix @@ -0,0 +1,97 @@ +{ config, lib, pkgs, options }: + +with lib; + +let + cfg = config.services.prometheus.exporters.rspamd; + + mkFile = conf: + pkgs.writeText "rspamd-exporter-config.yml" (builtins.toJSON conf); + + generateConfig = extraLabels: { + modules.default.metrics = (map (path: { + name = "rspamd_${replaceStrings [ "[" "." " " "]" "\\" "'" ] [ "_" "_" "_" "" "" "" ] path}"; + path = "{ .${path} }"; + labels = extraLabels; + }) [ + "actions['add\\ header']" + "actions['no\\ action']" + "actions['rewrite\\ subject']" + "actions['soft\\ reject']" + "actions.greylist" + "actions.reject" + "bytes_allocated" + "chunks_allocated" + "chunks_freed" + "chunks_oversized" + "connections" + "control_connections" + "ham_count" + "learned" + "pools_allocated" + "pools_freed" + "read_only" + "scanned" + "shared_chunks_allocated" + "spam_count" + "total_learns" + ]) ++ [{ + name = "rspamd_statfiles"; + type = "object"; + path = "{.statfiles[*]}"; + labels = recursiveUpdate { + symbol = "{.symbol}"; + type = "{.type}"; + } extraLabels; + values = { + revision = "{.revision}"; + size = "{.size}"; + total = "{.total}"; + used = "{.used}"; + languages = "{.languages}"; + users = "{.users}"; + }; + }]; + }; +in +{ + port = 7980; + extraOpts = { + extraLabels = mkOption { + type = types.attrsOf types.str; + default = { + host = config.networking.hostName; + }; + defaultText = literalExpression "{ host = config.networking.hostName; }"; + example = literalExpression '' + { + host = config.networking.hostName; + custom_label = "some_value"; + } + ''; + description = lib.mdDoc "Set of labels added to each metric."; + }; + }; + serviceOpts.serviceConfig.ExecStart = '' + ${pkgs.prometheus-json-exporter}/bin/json_exporter \ + --config.file ${mkFile (generateConfig cfg.extraLabels)} \ + --web.listen-address "${cfg.listenAddress}:${toString cfg.port}" \ + ${concatStringsSep " \\\n " cfg.extraFlags} + ''; + + imports = [ + (mkRemovedOptionModule [ "url" ] '' + This option was removed. The URL of the rspamd metrics endpoint + must now be provided to the exporter by prometheus via the url + parameter `target'. + + In prometheus a scrape URL would look like this: + + http://some.rspamd-exporter.host:7980/probe?target=http://some.rspamd.host:11334/stat + + For more information, take a look at the official documentation + (https://github.com/prometheus-community/json_exporter) of the json_exporter. + '') + ({ options.warnings = options.warnings; options.assertions = options.assertions; }) + ]; +} diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/rtl_433.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/rtl_433.nix new file mode 100644 index 000000000000..1f7235cb7830 --- /dev/null +++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/rtl_433.nix @@ -0,0 +1,83 @@ +{ config, lib, pkgs, options }: + +let + cfg = config.services.prometheus.exporters.rtl_433; +in +{ + port = 9550; + + extraOpts = let + mkMatcherOptionType = field: description: with lib.types; + listOf (submodule { + options = { + name = lib.mkOption { + type = str; + description = lib.mdDoc "Name to match."; + }; + "${field}" = lib.mkOption { + type = int; + description = lib.mdDoc description; + }; + location = lib.mkOption { + type = str; + description = lib.mdDoc "Location to match."; + }; + }; + }); + in + { + rtl433Flags = lib.mkOption { + type = lib.types.str; + default = "-C si"; + example = "-C si -R 19"; + description = lib.mdDoc '' + Flags passed verbatim to rtl_433 binary. + Having `-C si` (the default) is recommended since only Celsius temperatures are parsed. + ''; + }; + channels = lib.mkOption { + type = mkMatcherOptionType "channel" "Channel to match."; + default = []; + example = [ + { name = "Acurite"; channel = 6543; location = "Kitchen"; } + ]; + description = lib.mdDoc '' + List of channel matchers to export. + ''; + }; + ids = lib.mkOption { + type = mkMatcherOptionType "id" "ID to match."; + default = []; + example = [ + { name = "Nexus"; id = 1; location = "Bedroom"; } + ]; + description = lib.mdDoc '' + List of ID matchers to export. + ''; + }; + }; + + serviceOpts = { + serviceConfig = { + # rtl-sdr udev rules make supported USB devices +rw by plugdev. + SupplementaryGroups = "plugdev"; + # rtl_433 needs rw access to the USB radio. + PrivateDevices = lib.mkForce false; + DeviceAllow = lib.mkForce "char-usb_device rw"; + RestrictAddressFamilies = [ "AF_NETLINK" ]; + + ExecStart = let + matchers = (map (m: + "--channel_matcher '${m.name},${toString m.channel},${m.location}'" + ) cfg.channels) ++ (map (m: + "--id_matcher '${m.name},${toString m.id},${m.location}'" + ) cfg.ids); in '' + ${pkgs.prometheus-rtl_433-exporter}/bin/rtl_433_prometheus \ + -listen ${cfg.listenAddress}:${toString cfg.port} \ + -subprocess "${pkgs.rtl_433}/bin/rtl_433 -F json ${cfg.rtl433Flags}" \ + ${lib.concatStringsSep " \\\n " matchers} \ + ${lib.concatStringsSep " \\\n " cfg.extraFlags} + ''; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/sabnzbd.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/sabnzbd.nix new file mode 100644 index 000000000000..b9ab305f7c08 --- /dev/null +++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/sabnzbd.nix @@ -0,0 +1,57 @@ +{ config, lib, pkgs, options }: + +let + inherit (lib) mkOption types; + cfg = config.services.prometheus.exporters.sabnzbd; +in +{ + port = 9387; + + extraOpts = { + servers = mkOption { + description = "List of sabnzbd servers to connect to."; + type = types.listOf (types.submodule { + options = { + baseUrl = mkOption { + type = types.str; + description = "Base URL of the sabnzbd server."; + example = "http://localhost:8080/sabnzbd"; + }; + apiKeyFile = mkOption { + type = types.str; + description = '' + The path to a file containing the API key. + The file is securely passed to the service by leveraging systemd credentials. + No special permissions need to be set on this file. + ''; + example = "/run/secrets/sabnzbd_apikey"; + }; + }; + }); + }; + }; + + serviceOpts = + let + servers = lib.zipAttrs cfg.servers; + credentials = lib.imap0 (i: v: { name = "apikey-${toString i}"; path = v; }) servers.apiKeyFile; + in + { + serviceConfig.LoadCredential = builtins.map ({ name, path }: "${name}:${path}") credentials; + + environment = { + METRICS_PORT = toString cfg.port; + METRICS_ADDR = cfg.listenAddress; + SABNZBD_BASEURLS = lib.concatStringsSep "," servers.baseUrl; + }; + + script = + let + apiKeys = lib.concatStringsSep "," (builtins.map (cred: "$(< $CREDENTIALS_DIRECTORY/${cred.name})") credentials); + in + '' + export SABNZBD_APIKEYS="${apiKeys}" + exec ${lib.getExe pkgs.prometheus-sabnzbd-exporter} + ''; + }; +} diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/scaphandre.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/scaphandre.nix new file mode 100644 index 000000000000..3b6ebf65b090 --- /dev/null +++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/scaphandre.nix @@ -0,0 +1,33 @@ +{ config +, lib +, pkgs +, options +}: + +let + logPrefix = "services.prometheus.exporter.scaphandre"; + cfg = config.services.prometheus.exporters.scaphandre; +in { + port = 8080; + extraOpts = { + telemetryPath = lib.mkOption { + type = lib.types.str; + default = "/metrics"; + description = lib.mdDoc '' + Path under which to expose metrics. + ''; + }; + }; + + serviceOpts = { + serviceConfig = { + ExecStart = '' + ${pkgs.scaphandre}/bin/scaphandre prometheus \ + --address ${cfg.listenAddress} \ + --port ${toString cfg.port} \ + --suffix ${cfg.telemetryPath} \ + ${lib.concatStringsSep " \\\n " cfg.extraFlags} + ''; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/script.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/script.nix new file mode 100644 index 000000000000..eab0e1d8a6b5 --- /dev/null +++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/script.nix @@ -0,0 +1,64 @@ +{ config, lib, pkgs, options }: + +with lib; + +let + cfg = config.services.prometheus.exporters.script; + configFile = pkgs.writeText "script-exporter.yaml" (builtins.toJSON cfg.settings); +in +{ + port = 9172; + extraOpts = { + settings.scripts = mkOption { + type = with types; listOf (submodule { + options = { + name = mkOption { + type = str; + example = "sleep"; + description = lib.mdDoc "Name of the script."; + }; + script = mkOption { + type = str; + example = "sleep 5"; + description = lib.mdDoc "Shell script to execute when metrics are requested."; + }; + timeout = mkOption { + type = nullOr int; + default = null; + example = 60; + description = lib.mdDoc "Optional timeout for the script in seconds."; + }; + }; + }); + example = literalExpression '' + { + scripts = [ + { name = "sleep"; script = "sleep 5"; } + ]; + } + ''; + description = lib.mdDoc '' + All settings expressed as an Nix attrset. + + Check the official documentation for the corresponding YAML + settings that can all be used here: <https://github.com/adhocteam/script_exporter#sample-configuration> + ''; + }; + }; + serviceOpts = { + serviceConfig = { + ExecStart = '' + ${pkgs.prometheus-script-exporter}/bin/script_exporter \ + --web.listen-address ${cfg.listenAddress}:${toString cfg.port} \ + --config.file ${configFile} \ + ${concatStringsSep " \\\n " cfg.extraFlags} + ''; + NoNewPrivileges = true; + ProtectHome = true; + ProtectSystem = "strict"; + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectControlGroups = true; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/shelly.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/shelly.nix new file mode 100644 index 000000000000..b9cfd1b1e84a --- /dev/null +++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/shelly.nix @@ -0,0 +1,27 @@ +{ config, lib, pkgs, options }: + +with lib; + +let + cfg = config.services.prometheus.exporters.shelly; +in +{ + port = 9784; + extraOpts = { + metrics-file = mkOption { + type = types.path; + description = lib.mdDoc '' + Path to the JSON file with the metric definitions + ''; + }; + }; + serviceOpts = { + serviceConfig = { + ExecStart = '' + ${pkgs.prometheus-shelly-exporter}/bin/shelly_exporter \ + -metrics-file ${cfg.metrics-file} \ + -listen-address ${cfg.listenAddress}:${toString cfg.port} + ''; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/smartctl.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/smartctl.nix new file mode 100644 index 000000000000..50e1321a1e9c --- /dev/null +++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/smartctl.nix @@ -0,0 +1,64 @@ +{ config, lib, pkgs, options }: + +with lib; + +let + cfg = config.services.prometheus.exporters.smartctl; + args = lib.escapeShellArgs ([ + "--web.listen-address=${cfg.listenAddress}:${toString cfg.port}" + "--smartctl.path=${pkgs.smartmontools}/bin/smartctl" + "--smartctl.interval=${cfg.maxInterval}" + ] ++ map (device: "--smartctl.device=${device}") cfg.devices + ++ cfg.extraFlags); +in { + port = 9633; + + extraOpts = { + devices = mkOption { + type = types.listOf types.str; + default = []; + example = literalExpression '' + [ "/dev/sda", "/dev/nvme0n1" ]; + ''; + description = lib.mdDoc '' + Paths to the disks that will be monitored. Will autodiscover + all disks if none given. + ''; + }; + maxInterval = mkOption { + type = types.str; + default = "60s"; + example = "2m"; + description = lib.mdDoc '' + Interval that limits how often a disk can be queried. + ''; + }; + }; + + serviceOpts = { + serviceConfig = { + AmbientCapabilities = [ + "CAP_SYS_RAWIO" + "CAP_SYS_ADMIN" + ]; + CapabilityBoundingSet = [ + "CAP_SYS_RAWIO" + "CAP_SYS_ADMIN" + ]; + DevicePolicy = "closed"; + DeviceAllow = lib.mkOverride 50 [ + "block-blkext rw" + "block-sd rw" + "char-nvme rw" + ]; + ExecStart = '' + ${pkgs.prometheus-smartctl-exporter}/bin/smartctl_exporter ${args} + ''; + PrivateDevices = lib.mkForce false; + ProtectProc = "invisible"; + ProcSubset = "pid"; + SupplementaryGroups = [ "disk" ]; + SystemCallFilter = [ "@system-service" "~@privileged" ]; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/smokeping.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/smokeping.nix new file mode 100644 index 000000000000..459f5842f546 --- /dev/null +++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/smokeping.nix @@ -0,0 +1,61 @@ +{ config, lib, pkgs, options }: + +with lib; + +let + cfg = config.services.prometheus.exporters.smokeping; + goDuration = types.mkOptionType { + name = "goDuration"; + description = "Go duration (https://golang.org/pkg/time/#ParseDuration)"; + check = x: types.str.check x && builtins.match "(-?[0-9]+(\.[0-9]+)?(ns|us|µs|ms|s|m|h))+" x != null; + inherit (types.str) merge; + }; +in +{ + port = 9374; + extraOpts = { + telemetryPath = mkOption { + type = types.str; + default = "/metrics"; + description = lib.mdDoc '' + Path under which to expose metrics. + ''; + }; + pingInterval = mkOption { + type = goDuration; + default = "1s"; + description = lib.mdDoc '' + Interval between pings. + ''; + }; + buckets = mkOption { + type = types.commas; + default = "5e-05,0.0001,0.0002,0.0004,0.0008,0.0016,0.0032,0.0064,0.0128,0.0256,0.0512,0.1024,0.2048,0.4096,0.8192,1.6384,3.2768,6.5536,13.1072,26.2144"; + description = lib.mdDoc '' + List of buckets to use for the response duration histogram. + ''; + }; + hosts = mkOption { + type = with types; listOf str; + description = lib.mdDoc '' + List of endpoints to probe. + ''; + }; + }; + serviceOpts = { + serviceConfig = { + AmbientCapabilities = [ "CAP_NET_RAW" ]; + CapabilityBoundingSet = [ "CAP_NET_RAW" ]; + ExecStart = '' + ${pkgs.prometheus-smokeping-prober}/bin/smokeping_prober \ + --web.listen-address ${cfg.listenAddress}:${toString cfg.port} \ + --web.telemetry-path ${cfg.telemetryPath} \ + --buckets ${cfg.buckets} \ + --ping.interval ${cfg.pingInterval} \ + --privileged \ + ${concatStringsSep " \\\n " cfg.extraFlags} \ + ${concatStringsSep " " cfg.hosts} + ''; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/snmp.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/snmp.nix new file mode 100644 index 000000000000..840ce493ee81 --- /dev/null +++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/snmp.nix @@ -0,0 +1,99 @@ +{ config, lib, pkgs, options }: + +with lib; + +let + cfg = config.services.prometheus.exporters.snmp; + + # This ensures that we can deal with string paths, path types and + # store-path strings with context. + coerceConfigFile = file: + if (builtins.isPath file) || (lib.isStorePath file) then + file + else + (lib.warn '' + ${logPrefix}: configuration file "${file}" is being copied to the nix-store. + If you would like to avoid that, please set enableConfigCheck to false. + '' /. + file); + + checkConfig = file: + pkgs.runCommandLocal "checked-snmp-exporter-config.yml" { + nativeBuildInputs = [ pkgs.buildPackages.prometheus-snmp-exporter ]; + } '' + ln -s ${coerceConfigFile file} $out + snmp_exporter --dry-run --config.file $out + ''; +in +{ + port = 9116; + extraOpts = { + configurationPath = mkOption { + type = types.nullOr types.path; + default = null; + description = lib.mdDoc '' + Path to a snmp exporter configuration file. Mutually exclusive with 'configuration' option. + ''; + example = literalExpression "./snmp.yml"; + }; + + configuration = mkOption { + type = types.nullOr types.attrs; + default = null; + description = lib.mdDoc '' + Snmp exporter configuration as nix attribute set. Mutually exclusive with 'configurationPath' option. + ''; + example = { + auths.public_v2 = { + community = "public"; + version = 2; + }; + }; + }; + + enableConfigCheck = mkOption { + type = types.bool; + default = true; + description = lib.mdDoc '' + Whether to run a correctness check for the configuration file. This depends + on the configuration file residing in the nix-store. Paths passed as string will + be copied to the store. + ''; + }; + + logFormat = mkOption { + type = types.enum ["logfmt" "json"]; + default = "logfmt"; + description = lib.mdDoc '' + Output format of log messages. + ''; + }; + + logLevel = mkOption { + type = types.enum ["debug" "info" "warn" "error"]; + default = "info"; + description = lib.mdDoc '' + Only log messages with the given severity or above. + ''; + }; + }; + serviceOpts = let + uncheckedConfigFile = if cfg.configurationPath != null + then cfg.configurationPath + else "${pkgs.writeText "snmp-exporter-conf.yml" (builtins.toJSON cfg.configuration)}"; + configFile = if cfg.enableConfigCheck then + checkConfig uncheckedConfigFile + else + uncheckedConfigFile; + in { + serviceConfig = { + ExecStart = '' + ${pkgs.prometheus-snmp-exporter}/bin/snmp_exporter \ + --config.file=${escapeShellArg configFile} \ + --log.format=${escapeShellArg cfg.logFormat} \ + --log.level=${cfg.logLevel} \ + --web.listen-address=${cfg.listenAddress}:${toString cfg.port} \ + ${concatStringsSep " \\\n " cfg.extraFlags} + ''; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/sql.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/sql.nix new file mode 100644 index 000000000000..678bc348679d --- /dev/null +++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/sql.nix @@ -0,0 +1,108 @@ +{ config, lib, pkgs, options }: +with lib; +let + cfg = config.services.prometheus.exporters.sql; + cfgOptions = { + options = with types; { + jobs = mkOption { + type = attrsOf (submodule jobOptions); + default = { }; + description = lib.mdDoc "An attrset of metrics scraping jobs to run."; + }; + }; + }; + jobOptions = { + options = with types; { + interval = mkOption { + type = str; + description = lib.mdDoc '' + How often to run this job, specified in + [Go duration](https://golang.org/pkg/time/#ParseDuration) format. + ''; + }; + connections = mkOption { + type = listOf str; + description = lib.mdDoc "A list of connection strings of the SQL servers to scrape metrics from"; + }; + startupSql = mkOption { + type = listOf str; + default = []; + description = lib.mdDoc "A list of SQL statements to execute once after making a connection."; + }; + queries = mkOption { + type = attrsOf (submodule queryOptions); + description = lib.mdDoc "SQL queries to run."; + }; + }; + }; + queryOptions = { + options = with types; { + help = mkOption { + type = nullOr str; + default = null; + description = lib.mdDoc "A human-readable description of this metric."; + }; + labels = mkOption { + type = listOf str; + default = [ ]; + description = lib.mdDoc "A set of columns that will be used as Prometheus labels."; + }; + query = mkOption { + type = str; + description = lib.mdDoc "The SQL query to run."; + }; + values = mkOption { + type = listOf str; + description = lib.mdDoc "A set of columns that will be used as values of this metric."; + }; + }; + }; + + configFile = + if cfg.configFile != null + then cfg.configFile + else + let + nameInline = mapAttrsToList (k: v: v // { name = k; }); + renameStartupSql = j: removeAttrs (j // { startup_sql = j.startupSql; }) [ "startupSql" ]; + configuration = { + jobs = map renameStartupSql + (nameInline (mapAttrs (k: v: (v // { queries = nameInline v.queries; })) cfg.configuration.jobs)); + }; + in + builtins.toFile "config.yaml" (builtins.toJSON configuration); +in +{ + extraOpts = { + configFile = mkOption { + type = with types; nullOr path; + default = null; + description = lib.mdDoc '' + Path to configuration file. + ''; + }; + configuration = mkOption { + type = with types; nullOr (submodule cfgOptions); + default = null; + description = lib.mdDoc '' + Exporter configuration as nix attribute set. Mutually exclusive with 'configFile' option. + ''; + }; + }; + + port = 9237; + serviceOpts = { + serviceConfig = { + ExecStart = '' + ${pkgs.prometheus-sql-exporter}/bin/sql_exporter \ + -web.listen-address ${cfg.listenAddress}:${toString cfg.port} \ + -config.file ${configFile} \ + ${concatStringsSep " \\\n " cfg.extraFlags} + ''; + RestrictAddressFamilies = [ + # Need AF_UNIX to collect data + "AF_UNIX" + ]; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/statsd.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/statsd.nix new file mode 100644 index 000000000000..d9d732d8c125 --- /dev/null +++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/statsd.nix @@ -0,0 +1,19 @@ +{ config, lib, pkgs, options }: + +with lib; + +let + cfg = config.services.prometheus.exporters.statsd; +in +{ + port = 9102; + serviceOpts = { + serviceConfig = { + ExecStart = '' + ${pkgs.prometheus-statsd-exporter}/bin/statsd_exporter \ + --web.listen-address ${cfg.listenAddress}:${toString cfg.port} \ + ${concatStringsSep " \\\n " cfg.extraFlags} + ''; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/surfboard.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/surfboard.nix new file mode 100644 index 000000000000..b1d6760b40b3 --- /dev/null +++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/surfboard.nix @@ -0,0 +1,31 @@ +{ config, lib, pkgs, options }: + +with lib; + +let + cfg = config.services.prometheus.exporters.surfboard; +in +{ + port = 9239; + extraOpts = { + modemAddress = mkOption { + type = types.str; + default = "192.168.100.1"; + description = lib.mdDoc '' + The hostname or IP of the cable modem. + ''; + }; + }; + serviceOpts = { + description = "Prometheus exporter for surfboard cable modem"; + unitConfig.Documentation = "https://github.com/ipstatic/surfboard_exporter"; + serviceConfig = { + ExecStart = '' + ${pkgs.prometheus-surfboard-exporter}/bin/surfboard_exporter \ + --web.listen-address ${cfg.listenAddress}:${toString cfg.port} \ + --modem-address ${cfg.modemAddress} \ + ${concatStringsSep " \\\n " cfg.extraFlags} + ''; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/systemd.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/systemd.nix new file mode 100644 index 000000000000..2edd1de83e1b --- /dev/null +++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/systemd.nix @@ -0,0 +1,22 @@ +{ config, pkgs, lib, ... }: + +with lib; + +let cfg = config.services.prometheus.exporters.systemd; + +in { + port = 9558; + + serviceOpts = { + serviceConfig = { + ExecStart = '' + ${pkgs.prometheus-systemd-exporter}/bin/systemd_exporter \ + --web.listen-address ${cfg.listenAddress}:${toString cfg.port} ${concatStringsSep " " cfg.extraFlags} + ''; + RestrictAddressFamilies = [ + # Need AF_UNIX to collect data + "AF_UNIX" + ]; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/tor.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/tor.nix new file mode 100644 index 000000000000..7a9167110a27 --- /dev/null +++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/tor.nix @@ -0,0 +1,44 @@ +{ config, lib, pkgs, options }: + +with lib; + +let + cfg = config.services.prometheus.exporters.tor; +in +{ + port = 9130; + extraOpts = { + torControlAddress = mkOption { + type = types.str; + default = "127.0.0.1"; + description = lib.mdDoc '' + Tor control IP address or hostname. + ''; + }; + + torControlPort = mkOption { + type = types.port; + default = 9051; + description = lib.mdDoc '' + Tor control port. + ''; + }; + }; + serviceOpts = { + serviceConfig = { + ExecStart = '' + ${pkgs.prometheus-tor-exporter}/bin/prometheus-tor-exporter \ + -b ${cfg.listenAddress} \ + -p ${toString cfg.port} \ + -a ${cfg.torControlAddress} \ + -c ${toString cfg.torControlPort} \ + ${concatStringsSep " \\\n " cfg.extraFlags} + ''; + }; + + # CPython requires a process to either have $HOME defined or run as a UID + # defined in /etc/passwd. The latter is false with DynamicUser, so define a + # dummy $HOME. https://bugs.python.org/issue10496 + environment = { HOME = "/var/empty"; }; + }; +} diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/unbound.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/unbound.nix new file mode 100644 index 000000000000..f2336429d42f --- /dev/null +++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/unbound.nix @@ -0,0 +1,95 @@ +{ config +, lib +, pkgs +, options +}: + +with lib; + +let + cfg = config.services.prometheus.exporters.unbound; +in +{ + imports = [ + (mkRemovedOptionModule [ "controlInterface" ] "This option was removed, use the `unbound.host` option instead.") + (mkRemovedOptionModule [ "fetchType" ] "This option was removed, use the `unbound.host` option instead.") + ({ options.warnings = options.warnings; options.assertions = options.assertions; }) + ]; + + port = 9167; + extraOpts = { + telemetryPath = mkOption { + type = types.str; + default = "/metrics"; + description = lib.mdDoc '' + Path under which to expose metrics. + ''; + }; + + unbound = { + ca = mkOption { + type = types.nullOr types.path; + default = "/var/lib/unbound/unbound_server.pem"; + example = null; + description = '' + Path to the Unbound server certificate authority + ''; + }; + + certificate = mkOption { + type = types.nullOr types.path; + default = "/var/lib/unbound/unbound_control.pem"; + example = null; + description = '' + Path to the Unbound control socket certificate + ''; + }; + + key = mkOption { + type = types.nullOr types.path; + default = "/var/lib/unbound/unbound_control.key"; + example = null; + description = '' + Path to the Unbound control socket key. + ''; + }; + + host = mkOption { + type = types.str; + default = "tcp://127.0.0.1:8953"; + example = "unix:///run/unbound/unbound.socket"; + description = lib.mdDoc '' + Path to the unbound control socket. Supports unix domain sockets, as well as the TCP interface. + ''; + }; + }; + }; + + serviceOpts = mkMerge ([{ + serviceConfig = { + User = "unbound"; # to access the unbound_control.key + ExecStart = '' + ${pkgs.prometheus-unbound-exporter}/bin/unbound_exporter \ + --unbound.host "${cfg.unbound.host}" \ + --web.listen-address ${cfg.listenAddress}:${toString cfg.port} \ + --web.telemetry-path ${cfg.telemetryPath} \ + ${optionalString (cfg.unbound.ca != null) "--unbound.ca ${cfg.unbound.ca}"} \ + ${optionalString (cfg.unbound.certificate != null) "--unbound.cert ${cfg.unbound.certificate}"} \ + ${optionalString (cfg.unbound.key != null) "--unbound.key ${cfg.unbound.key}"} \ + ${toString cfg.extraFlags} + ''; + RestrictAddressFamilies = [ + "AF_UNIX" + "AF_INET" + "AF_INET6" + ]; + } // optionalAttrs (!config.services.unbound.enable) { + DynamicUser = true; + }; + }] ++ [ + (mkIf config.services.unbound.enable { + after = [ "unbound.service" ]; + requires = [ "unbound.service" ]; + }) + ]); +} diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/unifi.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/unifi.nix new file mode 100644 index 000000000000..70f26d9783be --- /dev/null +++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/unifi.nix @@ -0,0 +1,66 @@ +{ config, lib, pkgs, options }: + +with lib; + +let + cfg = config.services.prometheus.exporters.unifi; +in +{ + port = 9130; + extraOpts = { + unifiAddress = mkOption { + type = types.str; + example = "https://10.0.0.1:8443"; + description = lib.mdDoc '' + URL of the UniFi Controller API. + ''; + }; + + unifiInsecure = mkOption { + type = types.bool; + default = false; + description = lib.mdDoc '' + If enabled skip the verification of the TLS certificate of the UniFi Controller API. + Use with caution. + ''; + }; + + unifiUsername = mkOption { + type = types.str; + example = "ReadOnlyUser"; + description = lib.mdDoc '' + username for authentication against UniFi Controller API. + ''; + }; + + unifiPassword = mkOption { + type = types.str; + description = lib.mdDoc '' + Password for authentication against UniFi Controller API. + ''; + }; + + unifiTimeout = mkOption { + type = types.str; + default = "5s"; + example = "2m"; + description = lib.mdDoc '' + Timeout including unit for UniFi Controller API requests. + ''; + }; + }; + serviceOpts = { + serviceConfig = { + ExecStart = '' + ${pkgs.prometheus-unifi-exporter}/bin/unifi_exporter \ + -telemetry.addr ${cfg.listenAddress}:${toString cfg.port} \ + -unifi.addr ${cfg.unifiAddress} \ + -unifi.username ${escapeShellArg cfg.unifiUsername} \ + -unifi.password ${escapeShellArg cfg.unifiPassword} \ + -unifi.timeout ${cfg.unifiTimeout} \ + ${optionalString cfg.unifiInsecure "-unifi.insecure" } \ + ${concatStringsSep " \\\n " cfg.extraFlags} + ''; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/unpoller.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/unpoller.nix new file mode 100644 index 000000000000..3b7f978528cd --- /dev/null +++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/unpoller.nix @@ -0,0 +1,37 @@ +{ config, lib, pkgs, options }: + +with lib; + +let + cfg = config.services.prometheus.exporters.unpoller; + + configFile = pkgs.writeText "prometheus-unpoller-exporter.json" (generators.toJSON {} { + poller = { inherit (cfg.log) debug quiet; }; + unifi = { inherit (cfg) controllers; }; + influxdb.disable = true; + datadog.disable = true; # workaround for https://github.com/unpoller/unpoller/issues/442 + prometheus = { + http_listen = "${cfg.listenAddress}:${toString cfg.port}"; + report_errors = cfg.log.prometheusErrors; + }; + inherit (cfg) loki; + }); + +in { + port = 9130; + + extraOpts = { + inherit (options.services.unpoller.unifi) controllers; + inherit (options.services.unpoller) loki; + log = { + debug = mkEnableOption (lib.mdDoc "debug logging including line numbers, high resolution timestamps, per-device logs"); + quiet = mkEnableOption (lib.mdDoc "startup and error logs only"); + prometheusErrors = mkEnableOption (lib.mdDoc "emitting errors to prometheus"); + }; + }; + + serviceOpts.serviceConfig = { + ExecStart = "${pkgs.unpoller}/bin/unpoller --config ${configFile}"; + DynamicUser = false; + }; +} diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/v2ray.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/v2ray.nix new file mode 100644 index 000000000000..a019157c664b --- /dev/null +++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/v2ray.nix @@ -0,0 +1,29 @@ +{ config, lib, pkgs, options }: + +with lib; + +let + cfg = config.services.prometheus.exporters.v2ray; +in +{ + port = 9299; + extraOpts = { + v2rayEndpoint = mkOption { + type = types.str; + default = "127.0.0.1:54321"; + description = lib.mdDoc '' + v2ray grpc api endpoint + ''; + }; + }; + serviceOpts = { + serviceConfig = { + ExecStart = '' + ${pkgs.prometheus-v2ray-exporter}/bin/v2ray-exporter \ + --v2ray-endpoint ${cfg.v2rayEndpoint} \ + --listen ${cfg.listenAddress}:${toString cfg.port} \ + ${concatStringsSep " \\\n " cfg.extraFlags} + ''; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/varnish.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/varnish.nix new file mode 100644 index 000000000000..a7e5b41dffc6 --- /dev/null +++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/varnish.nix @@ -0,0 +1,89 @@ +{ config, lib, pkgs, options }: + +with lib; + +let + cfg = config.services.prometheus.exporters.varnish; +in +{ + port = 9131; + extraOpts = { + noExit = mkOption { + type = types.bool; + default = false; + description = lib.mdDoc '' + Do not exit server on Varnish scrape errors. + ''; + }; + withGoMetrics = mkOption { + type = types.bool; + default = false; + description = lib.mdDoc '' + Export go runtime and http handler metrics. + ''; + }; + verbose = mkOption { + type = types.bool; + default = false; + description = lib.mdDoc '' + Enable verbose logging. + ''; + }; + raw = mkOption { + type = types.bool; + default = false; + description = lib.mdDoc '' + Enable raw stdout logging without timestamps. + ''; + }; + varnishStatPath = mkOption { + type = types.str; + default = "varnishstat"; + description = lib.mdDoc '' + Path to varnishstat. + ''; + }; + instance = mkOption { + type = types.nullOr types.str; + default = config.services.varnish.stateDir; + defaultText = lib.literalExpression "config.services.varnish.stateDir"; + description = lib.mdDoc '' + varnishstat -n value. + ''; + }; + healthPath = mkOption { + type = types.nullOr types.str; + default = null; + description = lib.mdDoc '' + Path under which to expose healthcheck. Disabled unless configured. + ''; + }; + telemetryPath = mkOption { + type = types.str; + default = "/metrics"; + description = lib.mdDoc '' + Path under which to expose metrics. + ''; + }; + }; + serviceOpts = { + path = [ config.services.varnish.package ]; + serviceConfig = { + RestartSec = mkDefault 1; + DynamicUser = false; + ExecStart = '' + ${pkgs.prometheus-varnish-exporter}/bin/prometheus_varnish_exporter \ + --web.listen-address ${cfg.listenAddress}:${toString cfg.port} \ + --web.telemetry-path ${cfg.telemetryPath} \ + --varnishstat-path ${escapeShellArg cfg.varnishStatPath} \ + ${concatStringsSep " \\\n " (cfg.extraFlags + ++ optional (cfg.healthPath != null) "--web.health-path ${cfg.healthPath}" + ++ optional (cfg.instance != null) "-n ${escapeShellArg cfg.instance}" + ++ optional cfg.noExit "--no-exit" + ++ optional cfg.withGoMetrics "--with-go-metrics" + ++ optional cfg.verbose "--verbose" + ++ optional cfg.raw "--raw")} + ''; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/wireguard.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/wireguard.nix new file mode 100644 index 000000000000..9b7590314936 --- /dev/null +++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/wireguard.nix @@ -0,0 +1,71 @@ +{ config, lib, pkgs, options }: + +with lib; + +let + cfg = config.services.prometheus.exporters.wireguard; +in { + port = 9586; + imports = [ + (mkRenamedOptionModule [ "addr" ] [ "listenAddress" ]) + ({ options.warnings = options.warnings; options.assertions = options.assertions; }) + ]; + extraOpts = { + verbose = mkEnableOption (lib.mdDoc "verbose logging mode for prometheus-wireguard-exporter"); + + wireguardConfig = mkOption { + type = with types; nullOr (either path str); + default = null; + + description = lib.mdDoc '' + Path to the Wireguard Config to + [add the peer's name to the stats of a peer](https://github.com/MindFlavor/prometheus_wireguard_exporter/tree/2.0.0#usage). + + Please note that `networking.wg-quick` is required for this feature + as `networking.wireguard` uses + {manpage}`wg(8)` + to set the peers up. + ''; + }; + + singleSubnetPerField = mkOption { + type = types.bool; + default = false; + description = lib.mdDoc '' + By default, all allowed IPs and subnets are comma-separated in the + `allowed_ips` field. With this option enabled, + a single IP and subnet will be listed in fields like `allowed_ip_0`, + `allowed_ip_1` and so on. + ''; + }; + + withRemoteIp = mkOption { + type = types.bool; + default = false; + description = lib.mdDoc '' + Whether or not the remote IP of a WireGuard peer should be exposed via prometheus. + ''; + }; + }; + serviceOpts = { + path = [ pkgs.wireguard-tools ]; + + serviceConfig = { + AmbientCapabilities = [ "CAP_NET_ADMIN" ]; + CapabilityBoundingSet = [ "CAP_NET_ADMIN" ]; + ExecStart = '' + ${pkgs.prometheus-wireguard-exporter}/bin/prometheus_wireguard_exporter \ + -p ${toString cfg.port} \ + -l ${cfg.listenAddress} \ + ${optionalString cfg.verbose "-v true"} \ + ${optionalString cfg.singleSubnetPerField "-s true"} \ + ${optionalString cfg.withRemoteIp "-r true"} \ + ${optionalString (cfg.wireguardConfig != null) "-n ${escapeShellArg cfg.wireguardConfig}"} + ''; + RestrictAddressFamilies = [ + # Need AF_NETLINK to collect data + "AF_NETLINK" + ]; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/zfs.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/zfs.nix new file mode 100644 index 000000000000..ff12a52d49a9 --- /dev/null +++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/zfs.nix @@ -0,0 +1,44 @@ +{ config, lib, pkgs, options }: + +with lib; + +let + cfg = config.services.prometheus.exporters.zfs; +in +{ + port = 9134; + + extraOpts = { + telemetryPath = mkOption { + type = types.str; + default = "/metrics"; + description = lib.mdDoc '' + Path under which to expose metrics. + ''; + }; + + pools = mkOption { + type = with types; nullOr (listOf str); + default = [ ]; + description = lib.mdDoc '' + Name of the pool(s) to collect, repeat for multiple pools (default: all pools). + ''; + }; + }; + + serviceOpts = { + # needs zpool + path = [ config.boot.zfs.package ]; + serviceConfig = { + ExecStart = '' + ${pkgs.prometheus-zfs-exporter}/bin/zfs_exporter \ + --web.listen-address ${cfg.listenAddress}:${toString cfg.port} \ + --web.telemetry-path ${cfg.telemetryPath} \ + ${concatMapStringsSep " " (x: "--pool=${x}") cfg.pools} \ + ${concatStringsSep " \\\n " cfg.extraFlags} + ''; + ProtectClock = false; + PrivateDevices = false; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/pushgateway.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/pushgateway.nix new file mode 100644 index 000000000000..e93924e4fba8 --- /dev/null +++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/pushgateway.nix @@ -0,0 +1,159 @@ +{ config, pkgs, lib, ... }: + +with lib; + +let + cfg = config.services.prometheus.pushgateway; + + cmdlineArgs = + opt "web.listen-address" cfg.web.listen-address + ++ opt "web.telemetry-path" cfg.web.telemetry-path + ++ opt "web.external-url" cfg.web.external-url + ++ opt "web.route-prefix" cfg.web.route-prefix + ++ optional cfg.persistMetrics ''--persistence.file="/var/lib/${cfg.stateDir}/metrics"'' + ++ opt "persistence.interval" cfg.persistence.interval + ++ opt "log.level" cfg.log.level + ++ opt "log.format" cfg.log.format + ++ cfg.extraFlags; + + opt = k : v : optional (v != null) ''--${k}="${v}"''; + +in { + options = { + services.prometheus.pushgateway = { + enable = mkEnableOption (lib.mdDoc "Prometheus Pushgateway"); + + package = mkPackageOption pkgs "prometheus-pushgateway" { }; + + web.listen-address = mkOption { + type = types.nullOr types.str; + default = null; + description = lib.mdDoc '' + Address to listen on for the web interface, API and telemetry. + + `null` will default to `:9091`. + ''; + }; + + web.telemetry-path = mkOption { + type = types.nullOr types.str; + default = null; + description = lib.mdDoc '' + Path under which to expose metrics. + + `null` will default to `/metrics`. + ''; + }; + + web.external-url = mkOption { + type = types.nullOr types.str; + default = null; + description = lib.mdDoc '' + The URL under which Pushgateway is externally reachable. + ''; + }; + + web.route-prefix = mkOption { + type = types.nullOr types.str; + default = null; + description = lib.mdDoc '' + Prefix for the internal routes of web endpoints. + + Defaults to the path of + {option}`services.prometheus.pushgateway.web.external-url`. + ''; + }; + + persistence.interval = mkOption { + type = types.nullOr types.str; + default = null; + example = "10m"; + description = lib.mdDoc '' + The minimum interval at which to write out the persistence file. + + `null` will default to `5m`. + ''; + }; + + log.level = mkOption { + type = types.nullOr (types.enum ["debug" "info" "warn" "error" "fatal"]); + default = null; + description = lib.mdDoc '' + Only log messages with the given severity or above. + + `null` will default to `info`. + ''; + }; + + log.format = mkOption { + type = types.nullOr types.str; + default = null; + example = "logger:syslog?appname=bob&local=7"; + description = lib.mdDoc '' + Set the log target and format. + + `null` will default to `logger:stderr`. + ''; + }; + + extraFlags = mkOption { + type = types.listOf types.str; + default = []; + description = lib.mdDoc '' + Extra commandline options when launching the Pushgateway. + ''; + }; + + persistMetrics = mkOption { + type = types.bool; + default = false; + description = lib.mdDoc '' + Whether to persist metrics to a file. + + When enabled metrics will be saved to a file called + `metrics` in the directory + `/var/lib/pushgateway`. The directory below + `/var/lib` can be set using + {option}`services.prometheus.pushgateway.stateDir`. + ''; + }; + + stateDir = mkOption { + type = types.str; + default = "pushgateway"; + description = lib.mdDoc '' + Directory below `/var/lib` to store metrics. + + This directory will be created automatically using systemd's + StateDirectory mechanism when + {option}`services.prometheus.pushgateway.persistMetrics` + is enabled. + ''; + }; + }; + }; + + config = mkIf cfg.enable { + assertions = [ + { + assertion = !hasPrefix "/" cfg.stateDir; + message = + "The option services.prometheus.pushgateway.stateDir" + + " shouldn't be an absolute directory." + + " It should be a directory relative to /var/lib."; + } + ]; + systemd.services.pushgateway = { + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + serviceConfig = { + Restart = "always"; + DynamicUser = true; + ExecStart = "${cfg.package}/bin/pushgateway" + + optionalString (length cmdlineArgs != 0) (" \\\n " + + concatStringsSep " \\\n " cmdlineArgs); + StateDirectory = if cfg.persistMetrics then cfg.stateDir else null; + }; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/sachet.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/sachet.nix new file mode 100644 index 000000000000..c908d599bd4e --- /dev/null +++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/sachet.nix @@ -0,0 +1,88 @@ +{ config, pkgs, lib, ... }: + +with lib; + +let + cfg = config.services.prometheus.sachet; + configFile = pkgs.writeText "sachet.yml" (builtins.toJSON cfg.configuration); +in +{ + options = { + services.prometheus.sachet = { + enable = mkEnableOption (lib.mdDoc "Sachet, an SMS alerting tool for the Prometheus Alertmanager"); + + configuration = mkOption { + type = types.nullOr types.attrs; + default = null; + example = literalExpression '' + { + providers = { + twilio = { + # environment variables gets expanded at runtime + account_sid = "$TWILIO_ACCOUNT"; + auth_token = "$TWILIO_TOKEN"; + }; + }; + templates = [ ./some-template.tmpl ]; + receivers = [{ + name = "pager"; + provider = "twilio"; + to = [ "+33123456789" ]; + text = "{{ template \"message\" . }}"; + }]; + } + ''; + description = lib.mdDoc '' + Sachet's configuration as a nix attribute set. + ''; + }; + + address = mkOption { + type = types.str; + default = "localhost"; + description = lib.mdDoc '' + The address Sachet will listen to. + ''; + }; + + port = mkOption { + type = types.port; + default = 9876; + description = lib.mdDoc '' + The port Sachet will listen to. + ''; + }; + + }; + }; + + config = mkIf cfg.enable { + assertions = singleton { + assertion = cfg.configuration != null; + message = "Cannot enable Sachet without a configuration."; + }; + + systemd.services.sachet = { + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" "network-online.target" ]; + script = '' + ${pkgs.envsubst}/bin/envsubst -i "${configFile}" > /tmp/sachet.yaml + exec ${pkgs.prometheus-sachet}/bin/sachet -config /tmp/sachet.yaml -listen-address ${cfg.address}:${builtins.toString cfg.port} + ''; + + serviceConfig = { + Restart = "always"; + + ProtectSystem = "strict"; + ProtectHome = true; + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectControlGroups = true; + + DynamicUser = true; + PrivateTmp = true; + WorkingDirectory = "/tmp/"; + }; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/xmpp-alerts.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/xmpp-alerts.nix new file mode 100644 index 000000000000..4545ca37d278 --- /dev/null +++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/xmpp-alerts.nix @@ -0,0 +1,55 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.prometheus.xmpp-alerts; + settingsFormat = pkgs.formats.yaml {}; + configFile = settingsFormat.generate "prometheus-xmpp-alerts.yml" cfg.settings; +in +{ + imports = [ + (mkRenamedOptionModule + [ "services" "prometheus" "xmpp-alerts" "configuration" ] + [ "services" "prometheus" "xmpp-alerts" "settings" ]) + ]; + + options.services.prometheus.xmpp-alerts = { + enable = mkEnableOption (lib.mdDoc "XMPP Web hook service for Alertmanager"); + + settings = mkOption { + type = settingsFormat.type; + default = {}; + + description = lib.mdDoc '' + Configuration for prometheus xmpp-alerts, see + <https://github.com/jelmer/prometheus-xmpp-alerts/blob/master/xmpp-alerts.yml.example> + for supported values. + ''; + }; + }; + + config = mkIf cfg.enable { + systemd.services.prometheus-xmpp-alerts = { + wantedBy = [ "multi-user.target" ]; + after = [ "network-online.target" ]; + wants = [ "network-online.target" ]; + serviceConfig = { + ExecStart = "${pkgs.prometheus-xmpp-alerts}/bin/prometheus-xmpp-alerts --config ${configFile}"; + Restart = "on-failure"; + DynamicUser = true; + PrivateTmp = true; + PrivateDevices = true; + ProtectHome = true; + ProtectSystem = "strict"; + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectControlGroups = true; + NoNewPrivileges = true; + SystemCallArchitectures = "native"; + RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ]; + SystemCallFilter = [ "@system-service" ]; + }; + }; + }; +} |