vuls: init at 0.27.0
[NixPkgs.git] / nixos / modules / services / security / clamav.nix
blob2b0929599723a4d360946ed7c62fd336fb39bfff
1 { config, lib, pkgs, ... }:
2 with lib;
3 let
4   clamavUser = "clamav";
5   stateDir = "/var/lib/clamav";
6   clamavGroup = clamavUser;
7   cfg = config.services.clamav;
9   toKeyValue = generators.toKeyValue {
10     mkKeyValue = generators.mkKeyValueDefault { } " ";
11     listsAsDuplicateKeys = true;
12   };
14   clamdConfigFile = pkgs.writeText "clamd.conf" (toKeyValue cfg.daemon.settings);
15   freshclamConfigFile = pkgs.writeText "freshclam.conf" (toKeyValue cfg.updater.settings);
16   fangfrischConfigFile = pkgs.writeText "fangfrisch.conf" ''
17     ${lib.generators.toINI {} cfg.fangfrisch.settings}
18   '';
21   imports = [
22     (mkRemovedOptionModule [ "services" "clamav" "updater" "config" ] "Use services.clamav.updater.settings instead.")
23     (mkRemovedOptionModule [ "services" "clamav" "updater" "extraConfig" ] "Use services.clamav.updater.settings instead.")
24     (mkRemovedOptionModule [ "services" "clamav" "daemon" "extraConfig" ] "Use services.clamav.daemon.settings instead.")
25   ];
27   options = {
28     services.clamav = {
29       package = mkPackageOption pkgs "clamav" { };
30       daemon = {
31         enable = mkEnableOption "ClamAV clamd daemon";
33         settings = mkOption {
34           type = with types; attrsOf (oneOf [ bool int str (listOf str) ]);
35           default = { };
36           description = ''
37             ClamAV configuration. Refer to <https://linux.die.net/man/5/clamd.conf>,
38             for details on supported values.
39           '';
40         };
41       };
42       updater = {
43         enable = mkEnableOption "ClamAV freshclam updater";
45         frequency = mkOption {
46           type = types.int;
47           default = 12;
48           description = ''
49             Number of database checks per day.
50           '';
51         };
53         interval = mkOption {
54           type = types.str;
55           default = "hourly";
56           description = ''
57             How often freshclam is invoked. See systemd.time(7) for more
58             information about the format.
59           '';
60         };
62         settings = mkOption {
63           type = with types; attrsOf (oneOf [ bool int str (listOf str) ]);
64           default = { };
65           description = ''
66             freshclam configuration. Refer to <https://linux.die.net/man/5/freshclam.conf>,
67             for details on supported values.
68           '';
69         };
70       };
71       fangfrisch = {
72         enable = mkEnableOption "ClamAV fangfrisch updater";
74         interval = mkOption {
75           type = types.str;
76           default = "hourly";
77           description = ''
78             How often freshclam is invoked. See systemd.time(7) for more
79             information about the format.
80           '';
81         };
83         settings = mkOption {
84           type = lib.types.submodule {
85             freeformType = with types; attrsOf (attrsOf (oneOf [ str int bool ]));
86           };
87           default = { };
88           example = {
89             securiteinfo = {
90               enabled = "yes";
91               customer_id = "your customer_id";
92             };
93           };
94           description = ''
95             fangfrisch configuration. Refer to <https://rseichter.github.io/fangfrisch/#_configuration>,
96             for details on supported values.
97             Note that by default urlhaus and sanesecurity are enabled.
98           '';
99         };
100       };
102       scanner = {
103         enable = mkEnableOption "ClamAV scanner";
105         interval = mkOption {
106           type = types.str;
107           default = "*-*-* 04:00:00";
108           description = ''
109             How often clamdscan is invoked. See systemd.time(7) for more
110             information about the format.
111             By default this runs using 10 cores at most, be sure to run it at a time of low traffic.
112           '';
113         };
115         scanDirectories = mkOption {
116           type = with types; listOf str;
117           default = [ "/home" "/var/lib" "/tmp" "/etc" "/var/tmp" ];
118           description = ''
119             List of directories to scan.
120             The default includes everything I could think of that is valid for nixos. Feel free to contribute a PR to add to the default if you see something missing.
121           '';
122         };
123       };
124     };
125   };
127   config = mkIf (cfg.updater.enable || cfg.daemon.enable) {
128     environment.systemPackages = [ cfg.package ];
130     users.users.${clamavUser} = {
131       uid = config.ids.uids.clamav;
132       group = clamavGroup;
133       description = "ClamAV daemon user";
134       home = stateDir;
135     };
137     users.groups.${clamavGroup} =
138       { gid = config.ids.gids.clamav; };
140     services.clamav.daemon.settings = {
141       DatabaseDirectory = stateDir;
142       LocalSocket = "/run/clamav/clamd.ctl";
143       PidFile = "/run/clamav/clamd.pid";
144       User = "clamav";
145       Foreground = true;
146     };
148     services.clamav.updater.settings = {
149       DatabaseDirectory = stateDir;
150       Foreground = true;
151       Checks = cfg.updater.frequency;
152       DatabaseMirror = [ "database.clamav.net" ];
153     };
155     services.clamav.fangfrisch.settings = {
156       DEFAULT.db_url = mkDefault "sqlite:////var/lib/clamav/fangfrisch_db.sqlite";
157       DEFAULT.local_directory = mkDefault stateDir;
158       DEFAULT.log_level = mkDefault "INFO";
159       urlhaus.enabled = mkDefault "yes";
160       urlhaus.max_size = mkDefault "2MB";
161       sanesecurity.enabled = mkDefault "yes";
162     };
164     environment.etc."clamav/freshclam.conf".source = freshclamConfigFile;
165     environment.etc."clamav/clamd.conf".source = clamdConfigFile;
167     systemd.slices.system-clamav = {
168       description = "ClamAV Antivirus Slice";
169     };
171     systemd.services.clamav-daemon = mkIf cfg.daemon.enable {
172       description = "ClamAV daemon (clamd)";
173       after = optionals cfg.updater.enable [ "clamav-freshclam.service" ];
174       wants = optionals cfg.updater.enable [ "clamav-freshclam.service" ];
175       wantedBy = [ "multi-user.target" ];
176       restartTriggers = [ clamdConfigFile ];
178       serviceConfig = {
179         ExecStart = "${cfg.package}/bin/clamd";
180         ExecReload = "${pkgs.coreutils}/bin/kill -USR2 $MAINPID";
181         User = clamavUser;
182         Group = clamavGroup;
183         StateDirectory = "clamav";
184         RuntimeDirectory = "clamav";
185         PrivateTmp = "yes";
186         PrivateDevices = "yes";
187         PrivateNetwork = "yes";
188         Slice = "system-clamav.slice";
189       };
190     };
192     systemd.timers.clamav-freshclam = mkIf cfg.updater.enable {
193       description = "Timer for ClamAV virus database updater (freshclam)";
194       wantedBy = [ "timers.target" ];
195       timerConfig = {
196         OnCalendar = cfg.updater.interval;
197         Unit = "clamav-freshclam.service";
198       };
199     };
201     systemd.services.clamav-freshclam = mkIf cfg.updater.enable {
202       description = "ClamAV virus database updater (freshclam)";
203       restartTriggers = [ freshclamConfigFile ];
204       requires = [ "network-online.target" ];
205       after = [ "network-online.target" ];
207       serviceConfig = {
208         Type = "oneshot";
209         ExecStart = "${cfg.package}/bin/freshclam";
210         SuccessExitStatus = "1"; # if databases are up to date
211         StateDirectory = "clamav";
212         User = clamavUser;
213         Group = clamavGroup;
214         PrivateTmp = "yes";
215         PrivateDevices = "yes";
216         Slice = "system-clamav.slice";
217       };
218     };
220     systemd.services.clamav-fangfrisch-init = mkIf cfg.fangfrisch.enable {
221       wantedBy = [ "multi-user.target" ];
222       # if the sqlite file can be found assume the database has already been initialised
223       script = ''
224         db_url="${cfg.fangfrisch.settings.DEFAULT.db_url}"
225         db_path="''${db_url#sqlite:///}"
227         if [ ! -f "$db_path" ]; then
228           ${pkgs.fangfrisch}/bin/fangfrisch --conf ${fangfrischConfigFile} initdb
229         fi
230       '';
231       serviceConfig = {
232         Type = "oneshot";
233         StateDirectory = "clamav";
234         User = clamavUser;
235         Group = clamavGroup;
236         PrivateTmp = "yes";
237         PrivateDevices = "yes";
238         Slice = "system-clamav.slice";
239       };
240     };
242     systemd.timers.clamav-fangfrisch = mkIf cfg.fangfrisch.enable {
243       description = "Timer for ClamAV virus database updater (fangfrisch)";
244       wantedBy = [ "timers.target" ];
245       timerConfig = {
246         OnCalendar = cfg.fangfrisch.interval;
247         Unit = "clamav-fangfrisch.service";
248       };
249     };
251     systemd.services.clamav-fangfrisch = mkIf cfg.fangfrisch.enable {
252       description = "ClamAV virus database updater (fangfrisch)";
253       restartTriggers = [ fangfrischConfigFile ];
254       requires = [ "network-online.target" ];
255       after = [ "network-online.target" "clamav-fangfrisch-init.service" ];
257       serviceConfig = {
258         Type = "oneshot";
259         ExecStart = "${pkgs.fangfrisch}/bin/fangfrisch --conf ${fangfrischConfigFile} refresh";
260         StateDirectory = "clamav";
261         User = clamavUser;
262         Group = clamavGroup;
263         PrivateTmp = "yes";
264         PrivateDevices = "yes";
265         Slice = "system-clamav.slice";
266       };
267     };
269     systemd.timers.clamdscan = mkIf cfg.scanner.enable {
270       description = "Timer for ClamAV virus scanner";
271       wantedBy = [ "timers.target" ];
272       timerConfig = {
273         OnCalendar = cfg.scanner.interval;
274         Unit = "clamdscan.service";
275       };
276     };
278     systemd.services.clamdscan = mkIf cfg.scanner.enable {
279       description = "ClamAV virus scanner";
280       after = optionals cfg.updater.enable [ "clamav-freshclam.service" ];
281       wants = optionals cfg.updater.enable [ "clamav-freshclam.service" ];
283       serviceConfig = {
284         Type = "oneshot";
285         ExecStart = "${cfg.package}/bin/clamdscan --multiscan --fdpass --infected --allmatch ${lib.concatStringsSep " " cfg.scanner.scanDirectories}";
286         Slice = "system-clamav.slice";
287       };
288     };
289   };