1{ config, lib, pkgs, ... }: 2 3with lib; 4 5let 6 cfg = config.services.logcheck; 7 8 defaultRules = pkgs.runCommand "logcheck-default-rules" {} '' 9 cp -prd ${pkgs.logcheck}/etc/logcheck $out 10 chmod u+w $out 11 rm $out/logcheck.* 12 ''; 13 14 rulesDir = pkgs.symlinkJoin "logcheck-rules-dir" ([ defaultRules ] ++ cfg.extraRulesDirs); 15 16 configFile = pkgs.writeText "logcheck.conf" cfg.config; 17 18 logFiles = pkgs.writeText "logcheck.logfiles" cfg.files; 19 20 flags = "-r ${rulesDir} -c ${configFile} -L ${logFiles} -${levelFlag} -m ${cfg.mailTo}"; 21 22 levelFlag = getAttrFromPath [cfg.level] 23 { "paranoid" = "p"; 24 "server" = "s"; 25 "workstation" = "w"; 26 }; 27 28 cronJob = '' 29 @reboot logcheck env PATH=/var/setuid-wrappers:$PATH nice -n10 ${pkgs.logcheck}/sbin/logcheck -R ${flags} 30 2 ${cfg.timeOfDay} * * * logcheck env PATH=/var/setuid-wrappers:$PATH nice -n10 ${pkgs.logcheck}/sbin/logcheck ${flags} 31 ''; 32 33 writeIgnoreRule = name: {level, regex, ...}: 34 pkgs.writeTextFile 35 { inherit name; 36 destination = "/ignore.d.${level}/${name}"; 37 text = '' 38 ^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ ${regex} 39 ''; 40 }; 41 42 writeIgnoreCronRule = name: {level, user, regex, cmdline, ...}: 43 let escapeRegex = escape (stringToCharacters "\\[]{}()^$?*+|."); 44 cmdline_ = builtins.unsafeDiscardStringContext cmdline; 45 re = if regex != "" then regex else if cmdline_ == "" then ".*" else escapeRegex cmdline_; 46 in writeIgnoreRule "cron-${name}" { 47 inherit level; 48 regex = '' 49 (/usr/bin/)?cron\[[0-9]+\]: \(${user}\) CMD \(${re}\)$ 50 ''; 51 }; 52 53 levelOption = mkOption { 54 default = "server"; 55 type = types.str; 56 description = '' 57 Set the logcheck level. Either "workstation", "server", or "paranoid". 58 ''; 59 }; 60 61 ignoreOptions = { 62 level = levelOption; 63 64 regex = mkOption { 65 default = ""; 66 type = types.str; 67 description = '' 68 Regex specifying which log lines to ignore. 69 ''; 70 }; 71 }; 72 73 ignoreCronOptions = { 74 user = mkOption { 75 default = "root"; 76 type = types.str; 77 description = '' 78 User that runs the cronjob. 79 ''; 80 }; 81 82 cmdline = mkOption { 83 default = ""; 84 type = types.str; 85 description = '' 86 Command line for the cron job. Will be turned into a regex for the logcheck ignore rule. 87 ''; 88 }; 89 90 timeArgs = mkOption { 91 default = null; 92 type = types.nullOr (types.str); 93 example = "02 06 * * *"; 94 description = '' 95 "min hr dom mon dow" crontab time args, to auto-create a cronjob too. 96 Leave at null to not do this and just add a logcheck ignore rule. 97 ''; 98 }; 99 }; 100 101in 102{ 103 options = { 104 services.logcheck = { 105 enable = mkOption { 106 default = false; 107 type = types.bool; 108 description = '' 109 Enable the logcheck cron job. 110 ''; 111 }; 112 113 user = mkOption { 114 default = "logcheck"; 115 type = types.str; 116 description = '' 117 Username for the logcheck user. 118 ''; 119 }; 120 121 timeOfDay = mkOption { 122 default = "*"; 123 example = "6"; 124 type = types.str; 125 description = '' 126 Time of day to run logcheck. A logcheck will be scheduled at xx:02 each day. 127 Leave default (*) to run every hour. Of course when nothing special was logged, 128 logcheck will be silent. 129 ''; 130 }; 131 132 mailTo = mkOption { 133 default = "root"; 134 example = "you@domain.com"; 135 type = types.str; 136 description = '' 137 Email address to send reports to. 138 ''; 139 }; 140 141 level = mkOption { 142 default = "server"; 143 type = types.str; 144 description = '' 145 Set the logcheck level. Either "workstation", "server", or "paranoid". 146 ''; 147 }; 148 149 config = mkOption { 150 default = "FQDN=1"; 151 type = types.string; 152 description = '' 153 Config options that you would like in logcheck.conf. 154 ''; 155 }; 156 157 files = mkOption { 158 default = [ "/var/log/messages" ]; 159 type = types.listOf types.path; 160 example = [ "/var/log/messages" "/var/log/mail" ]; 161 description = '' 162 Which log files to check. 163 ''; 164 }; 165 166 extraRulesDirs = mkOption { 167 default = []; 168 example = "/etc/logcheck"; 169 type = types.listOf types.path; 170 description = '' 171 Directories with extra rules. 172 ''; 173 }; 174 175 ignore = mkOption { 176 default = {}; 177 description = '' 178 This option defines extra ignore rules. 179 ''; 180 type = types.loaOf types.optionSet; 181 options = [ ignoreOptions ]; 182 }; 183 184 ignoreCron = mkOption { 185 default = {}; 186 description = '' 187 This option defines extra ignore rules for cronjobs. 188 ''; 189 type = types.loaOf types.optionSet; 190 options = [ ignoreOptions ignoreCronOptions ]; 191 }; 192 193 extraGroups = mkOption { 194 default = []; 195 type = types.listOf types.str; 196 example = [ "postdrop" "mongodb" ]; 197 description = '' 198 Extra groups for the logcheck user, for example to be able to use sendmail, 199 or to access certain log files. 200 ''; 201 }; 202 203 }; 204 }; 205 206 config = mkIf cfg.enable { 207 services.logcheck.extraRulesDirs = 208 mapAttrsToList writeIgnoreRule cfg.ignore 209 ++ mapAttrsToList writeIgnoreCronRule cfg.ignoreCron; 210 211 users.extraUsers = optionalAttrs (cfg.user == "logcheck") (singleton 212 { name = "logcheck"; 213 uid = config.ids.uids.logcheck; 214 shell = "/bin/sh"; 215 description = "Logcheck user account"; 216 extraGroups = cfg.extraGroups; 217 }); 218 219 system.activationScripts.logcheck = '' 220 mkdir -m 700 -p /var/{lib,lock}/logcheck 221 chown ${cfg.user} /var/{lib,lock}/logcheck 222 ''; 223 224 services.cron.systemCronJobs = 225 let withTime = name: {timeArgs, ...}: ! (builtins.isNull timeArgs); 226 mkCron = name: {user, cmdline, timeArgs, ...}: '' 227 ${timeArgs} ${user} ${cmdline} 228 ''; 229 in mapAttrsToList mkCron (filterAttrs withTime cfg.ignoreCron) 230 ++ [ cronJob ]; 231 }; 232}