1{ config, pkgs, lib, ... }:
2
3with lib;
4
5let
6 cfg = config.services.prometheus.exporters;
7
8 # each attribute in `exporterOpts` is expected to have specified:
9 # - port (types.int): port on which the exporter listens
10 # - serviceOpts (types.attrs): config that is merged with the
11 # default definition of the exporter's
12 # systemd service
13 # - extraOpts (types.attrs): extra configuration options to
14 # configure the exporter with, which
15 # are appended to the default options
16 #
17 # Note that `extraOpts` is optional, but a script for the exporter's
18 # systemd service must be provided by specifying either
19 # `serviceOpts.script` or `serviceOpts.serviceConfig.ExecStart`
20 exporterOpts = {
21 blackbox = import ./exporters/blackbox.nix { inherit config lib pkgs; };
22 collectd = import ./exporters/collectd.nix { inherit config lib pkgs; };
23 dnsmasq = import ./exporters/dnsmasq.nix { inherit config lib pkgs; };
24 dovecot = import ./exporters/dovecot.nix { inherit config lib pkgs; };
25 fritzbox = import ./exporters/fritzbox.nix { inherit config lib pkgs; };
26 json = import ./exporters/json.nix { inherit config lib pkgs; };
27 minio = import ./exporters/minio.nix { inherit config lib pkgs; };
28 nginx = import ./exporters/nginx.nix { inherit config lib pkgs; };
29 node = import ./exporters/node.nix { inherit config lib pkgs; };
30 postfix = import ./exporters/postfix.nix { inherit config lib pkgs; };
31 snmp = import ./exporters/snmp.nix { inherit config lib pkgs; };
32 surfboard = import ./exporters/surfboard.nix { inherit config lib pkgs; };
33 unifi = import ./exporters/unifi.nix { inherit config lib pkgs; };
34 varnish = import ./exporters/varnish.nix { inherit config lib pkgs; };
35 };
36
37 mkExporterOpts = ({ name, port }: {
38 enable = mkEnableOption "the prometheus ${name} exporter";
39 port = mkOption {
40 type = types.int;
41 default = port;
42 description = ''
43 Port to listen on.
44 '';
45 };
46 listenAddress = mkOption {
47 type = types.str;
48 default = "0.0.0.0";
49 description = ''
50 Address to listen on.
51 '';
52 };
53 extraFlags = mkOption {
54 type = types.listOf types.str;
55 default = [];
56 description = ''
57 Extra commandline options to pass to the ${name} exporter.
58 '';
59 };
60 openFirewall = mkOption {
61 type = types.bool;
62 default = false;
63 description = ''
64 Open port in firewall for incoming connections.
65 '';
66 };
67 firewallFilter = mkOption {
68 type = types.str;
69 default = "-p tcp -m tcp --dport ${toString port}";
70 example = literalExample ''
71 "-i eth0 -p tcp -m tcp --dport ${toString port}"
72 '';
73 description = ''
74 Specify a filter for iptables to use when
75 <option>services.prometheus.exporters.${name}.openFirewall</option>
76 is true. It is used as `ip46tables -I nixos-fw <option>firewallFilter</option> -j nixos-fw-accept`.
77 '';
78 };
79 user = mkOption {
80 type = types.str;
81 default = "nobody";
82 description = ''
83 User name under which the ${name} exporter shall be run.
84 Has no effect when <option>systemd.services.prometheus-${name}-exporter.serviceConfig.DynamicUser</option> is true.
85 '';
86 };
87 group = mkOption {
88 type = types.str;
89 default = "nobody";
90 description = ''
91 Group under which the ${name} exporter shall be run.
92 Has no effect when <option>systemd.services.prometheus-${name}-exporter.serviceConfig.DynamicUser</option> is true.
93 '';
94 };
95 });
96
97 mkSubModule = { name, port, extraOpts, ... }: {
98 ${name} = mkOption {
99 type = types.submodule {
100 options = (mkExporterOpts {
101 inherit name port;
102 } // extraOpts);
103 };
104 internal = true;
105 default = {};
106 };
107 };
108
109 mkSubModules = (foldl' (a: b: a//b) {}
110 (mapAttrsToList (name: opts: mkSubModule {
111 inherit name;
112 inherit (opts) port serviceOpts;
113 extraOpts = opts.extraOpts or {};
114 }) exporterOpts)
115 );
116
117 mkExporterConf = { name, conf, serviceOpts }:
118 mkIf conf.enable {
119 networking.firewall.extraCommands = mkIf conf.openFirewall (concatStrings [
120 "ip46tables -I nixos-fw ${conf.firewallFilter} "
121 "-m comment --comment ${name}-exporter -j nixos-fw-accept"
122 ]);
123 systemd.services."prometheus-${name}-exporter" = mkMerge ([{
124 wantedBy = [ "multi-user.target" ];
125 after = [ "network.target" ];
126 serviceConfig = {
127 Restart = mkDefault "always";
128 PrivateTmp = mkDefault true;
129 WorkingDirectory = mkDefault /tmp;
130 } // mkIf (!(serviceOpts.serviceConfig.DynamicUser or false)) {
131 User = conf.user;
132 Group = conf.group;
133 };
134 } serviceOpts ]);
135 };
136in
137{
138 options.services.prometheus.exporters = mkOption {
139 type = types.submodule {
140 options = (mkSubModules);
141 };
142 description = "Prometheus exporter configuration";
143 default = {};
144 example = literalExample ''
145 {
146 node = {
147 enable = true;
148 enabledCollectors = [ "systemd" ];
149 };
150 varnish.enable = true;
151 }
152 '';
153 };
154
155 config = mkMerge ([{
156 assertions = [{
157 assertion = (cfg.snmp.configurationPath == null) != (cfg.snmp.configuration == null);
158 message = ''
159 Please ensure you have either `services.prometheus.exporters.snmp.configuration'
160 or `services.prometheus.exporters.snmp.configurationPath' set!
161 '';
162 }];
163 }] ++ [(mkIf config.services.minio.enable {
164 services.prometheus.exporters.minio.minioAddress = mkDefault "http://localhost:9000";
165 services.prometheus.exporters.minio.minioAccessKey = mkDefault config.services.minio.accessKey;
166 services.prometheus.exporters.minio.minioAccessSecret = mkDefault config.services.minio.secretKey;
167 })] ++ (mapAttrsToList (name: conf:
168 mkExporterConf {
169 inherit name;
170 inherit (conf) serviceOpts;
171 conf = cfg.${name};
172 }) exporterOpts)
173 );
174
175 meta.doc = ./exporters.xml;
176}