1 { config, lib, pkgs, ... }:
5 stateDir = "/var/lib/clamav";
6 clamavGroup = clamavUser;
7 cfg = config.services.clamav;
9 toKeyValue = generators.toKeyValue {
10 mkKeyValue = generators.mkKeyValueDefault { } " ";
11 listsAsDuplicateKeys = true;
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}
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.")
29 package = mkPackageOption pkgs "clamav" { };
31 enable = mkEnableOption "ClamAV clamd daemon";
34 type = with types; attrsOf (oneOf [ bool int str (listOf str) ]);
37 ClamAV configuration. Refer to <https://linux.die.net/man/5/clamd.conf>,
38 for details on supported values.
43 enable = mkEnableOption "ClamAV freshclam updater";
45 frequency = mkOption {
49 Number of database checks per day.
57 How often freshclam is invoked. See systemd.time(7) for more
58 information about the format.
63 type = with types; attrsOf (oneOf [ bool int str (listOf str) ]);
66 freshclam configuration. Refer to <https://linux.die.net/man/5/freshclam.conf>,
67 for details on supported values.
72 enable = mkEnableOption "ClamAV fangfrisch updater";
78 How often freshclam is invoked. See systemd.time(7) for more
79 information about the format.
84 type = lib.types.submodule {
85 freeformType = with types; attrsOf (attrsOf (oneOf [ str int bool ]));
91 customer_id = "your customer_id";
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.
103 enable = mkEnableOption "ClamAV scanner";
105 interval = mkOption {
107 default = "*-*-* 04:00:00";
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.
115 scanDirectories = mkOption {
116 type = with types; listOf str;
117 default = [ "/home" "/var/lib" "/tmp" "/etc" "/var/tmp" ];
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.
127 config = mkIf (cfg.updater.enable || cfg.daemon.enable) {
128 environment.systemPackages = [ cfg.package ];
130 users.users.${clamavUser} = {
131 uid = config.ids.uids.clamav;
133 description = "ClamAV daemon user";
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";
148 services.clamav.updater.settings = {
149 DatabaseDirectory = stateDir;
151 Checks = cfg.updater.frequency;
152 DatabaseMirror = [ "database.clamav.net" ];
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";
164 environment.etc."clamav/freshclam.conf".source = freshclamConfigFile;
165 environment.etc."clamav/clamd.conf".source = clamdConfigFile;
167 systemd.slices.system-clamav = {
168 description = "ClamAV slice";
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 ];
179 ExecStart = "${cfg.package}/bin/clamd";
180 ExecReload = "${pkgs.coreutils}/bin/kill -USR2 $MAINPID";
183 StateDirectory = "clamav";
184 RuntimeDirectory = "clamav";
186 PrivateDevices = "yes";
187 PrivateNetwork = "yes";
188 Slice = "system-clamav.slice";
192 systemd.timers.clamav-freshclam = mkIf cfg.updater.enable {
193 description = "Timer for ClamAV virus database updater (freshclam)";
194 wantedBy = [ "timers.target" ];
196 OnCalendar = cfg.updater.interval;
197 Unit = "clamav-freshclam.service";
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" ];
209 ExecStart = "${cfg.package}/bin/freshclam";
210 SuccessExitStatus = "1"; # if databases are up to date
211 StateDirectory = "clamav";
215 PrivateDevices = "yes";
216 Slice = "system-clamav.slice";
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
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
233 StateDirectory = "clamav";
237 PrivateDevices = "yes";
238 Slice = "system-clamav.slice";
242 systemd.timers.clamav-fangfrisch = mkIf cfg.fangfrisch.enable {
243 description = "Timer for ClamAV virus database updater (fangfrisch)";
244 wantedBy = [ "timers.target" ];
246 OnCalendar = cfg.fangfrisch.interval;
247 Unit = "clamav-fangfrisch.service";
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" ];
259 ExecStart = "${pkgs.fangfrisch}/bin/fangfrisch --conf ${fangfrischConfigFile} refresh";
260 StateDirectory = "clamav";
264 PrivateDevices = "yes";
265 Slice = "system-clamav.slice";
269 systemd.timers.clamdscan = mkIf cfg.scanner.enable {
270 description = "Timer for ClamAV virus scanner";
271 wantedBy = [ "timers.target" ];
273 OnCalendar = cfg.scanner.interval;
274 Unit = "clamdscan.service";
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" ];
285 ExecStart = "${cfg.package}/bin/clamdscan --multiscan --fdpass --infected --allmatch ${lib.concatStringsSep " " cfg.scanner.scanDirectories}";
286 Slice = "system-clamav.slice";