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