1{ config, lib, pkgs, options }: 2 3with lib; 4 5let 6 cfg = config.services.prometheus.exporters.mail; 7 8 configFile = if cfg.configuration != null then configurationFile else (escapeShellArg cfg.configFile); 9 10 configurationFile = pkgs.writeText "prometheus-mail-exporter.conf" (builtins.toJSON ( 11 # removes the _module attribute, null values and converts attrNames to lowercase 12 mapAttrs' (name: value: 13 if name == "servers" 14 then nameValuePair (toLower name) 15 ((map (srv: (mapAttrs' (n: v: nameValuePair (toLower n) v) 16 (filterAttrs (n: v: !(n == "_module" || v == null)) srv) 17 ))) value) 18 else nameValuePair (toLower name) value 19 ) (filterAttrs (n: _: !(n == "_module")) cfg.configuration) 20 )); 21 22 serverOptions.options = { 23 name = mkOption { 24 type = types.str; 25 description = lib.mdDoc '' 26 Value for label 'configname' which will be added to all metrics. 27 ''; 28 }; 29 server = mkOption { 30 type = types.str; 31 description = lib.mdDoc '' 32 Hostname of the server that should be probed. 33 ''; 34 }; 35 port = mkOption { 36 type = types.port; 37 example = 587; 38 description = lib.mdDoc '' 39 Port to use for SMTP. 40 ''; 41 }; 42 from = mkOption { 43 type = types.str; 44 example = "exporteruser@domain.tld"; 45 description = lib.mdDoc '' 46 Content of 'From' Header for probing mails. 47 ''; 48 }; 49 to = mkOption { 50 type = types.str; 51 example = "exporteruser@domain.tld"; 52 description = lib.mdDoc '' 53 Content of 'To' Header for probing mails. 54 ''; 55 }; 56 detectionDir = mkOption { 57 type = types.path; 58 example = "/var/spool/mail/exporteruser/new"; 59 description = lib.mdDoc '' 60 Directory in which new mails for the exporter user are placed. 61 Note that this needs to exist when the exporter starts. 62 ''; 63 }; 64 login = mkOption { 65 type = types.nullOr types.str; 66 default = null; 67 example = "exporteruser@domain.tld"; 68 description = lib.mdDoc '' 69 Username to use for SMTP authentication. 70 ''; 71 }; 72 passphrase = mkOption { 73 type = types.nullOr types.str; 74 default = null; 75 description = lib.mdDoc '' 76 Password to use for SMTP authentication. 77 ''; 78 }; 79 }; 80 81 exporterOptions.options = { 82 monitoringInterval = mkOption { 83 type = types.str; 84 example = "10s"; 85 description = lib.mdDoc '' 86 Time interval between two probe attempts. 87 ''; 88 }; 89 mailCheckTimeout = mkOption { 90 type = types.str; 91 description = lib.mdDoc '' 92 Timeout until mails are considered "didn't make it". 93 ''; 94 }; 95 disableFileDeletion = mkOption { 96 type = types.bool; 97 default = false; 98 description = lib.mdDoc '' 99 Disables the exporter's function to delete probing mails. 100 ''; 101 }; 102 servers = mkOption { 103 type = types.listOf (types.submodule serverOptions); 104 default = []; 105 example = literalExpression '' 106 [ { 107 name = "testserver"; 108 server = "smtp.domain.tld"; 109 port = 587; 110 from = "exporteruser@domain.tld"; 111 to = "exporteruser@domain.tld"; 112 detectionDir = "/path/to/Maildir/new"; 113 } ] 114 ''; 115 description = lib.mdDoc '' 116 List of servers that should be probed. 117 118 *Note:* if your mailserver has {manpage}`rspamd(8)` configured, 119 it can happen that emails from this exporter are marked as spam. 120 121 It's possible to work around the issue with a config like this: 122 ``` 123 { 124 services.rspamd.locals."multimap.conf".text = ''' 125 ALLOWLIST_PROMETHEUS { 126 filter = "email:domain:tld"; 127 type = "from"; 128 map = "''${pkgs.writeText "allowmap" "domain.tld"}"; 129 score = -100.0; 130 } 131 '''; 132 } 133 ``` 134 ''; 135 }; 136 }; 137in 138{ 139 port = 9225; 140 extraOpts = { 141 environmentFile = mkOption { 142 type = types.nullOr types.str; 143 default = null; 144 description = lib.mdDoc '' 145 File containing env-vars to be substituted into the exporter's config. 146 ''; 147 }; 148 configFile = mkOption { 149 type = types.nullOr types.path; 150 default = null; 151 description = lib.mdDoc '' 152 Specify the mailexporter configuration file to use. 153 ''; 154 }; 155 configuration = mkOption { 156 type = types.nullOr (types.submodule exporterOptions); 157 default = null; 158 description = lib.mdDoc '' 159 Specify the mailexporter configuration file to use. 160 ''; 161 }; 162 telemetryPath = mkOption { 163 type = types.str; 164 default = "/metrics"; 165 description = lib.mdDoc '' 166 Path under which to expose metrics. 167 ''; 168 }; 169 }; 170 serviceOpts = { 171 serviceConfig = { 172 DynamicUser = false; 173 EnvironmentFile = mkIf (cfg.environmentFile != null) [ cfg.environmentFile ]; 174 RuntimeDirectory = "prometheus-mail-exporter"; 175 ExecStartPre = [ 176 "${pkgs.writeShellScript "subst-secrets-mail-exporter" '' 177 umask 0077 178 ${pkgs.envsubst}/bin/envsubst -i ${configFile} -o ''${RUNTIME_DIRECTORY}/mail-exporter.json 179 ''}" 180 ]; 181 ExecStart = '' 182 ${pkgs.prometheus-mail-exporter}/bin/mailexporter \ 183 --web.listen-address ${cfg.listenAddress}:${toString cfg.port} \ 184 --web.telemetry-path ${cfg.telemetryPath} \ 185 --config.file ''${RUNTIME_DIRECTORY}/mail-exporter.json \ 186 ${concatStringsSep " \\\n " cfg.extraFlags} 187 ''; 188 }; 189 }; 190}