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