at 24.11-pre 9.3 kB view raw
1{ config, lib, pkgs, ... }: 2with lib; 3let 4 clamavUser = "clamav"; 5 stateDir = "/var/lib/clamav"; 6 clamavGroup = clamavUser; 7 cfg = config.services.clamav; 8 pkg = pkgs.clamav; 9 10 toKeyValue = generators.toKeyValue { 11 mkKeyValue = generators.mkKeyValueDefault { } " "; 12 listsAsDuplicateKeys = true; 13 }; 14 15 clamdConfigFile = pkgs.writeText "clamd.conf" (toKeyValue cfg.daemon.settings); 16 freshclamConfigFile = pkgs.writeText "freshclam.conf" (toKeyValue cfg.updater.settings); 17 fangfrischConfigFile = pkgs.writeText "fangfrisch.conf" '' 18 ${lib.generators.toINI {} cfg.fangfrisch.settings} 19 ''; 20in 21{ 22 imports = [ 23 (mkRemovedOptionModule [ "services" "clamav" "updater" "config" ] "Use services.clamav.updater.settings instead.") 24 (mkRemovedOptionModule [ "services" "clamav" "updater" "extraConfig" ] "Use services.clamav.updater.settings instead.") 25 (mkRemovedOptionModule [ "services" "clamav" "daemon" "extraConfig" ] "Use services.clamav.daemon.settings instead.") 26 ]; 27 28 options = { 29 services.clamav = { 30 daemon = { 31 enable = mkEnableOption "ClamAV clamd daemon"; 32 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"; 44 45 frequency = mkOption { 46 type = types.int; 47 default = 12; 48 description = '' 49 Number of database checks per day. 50 ''; 51 }; 52 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 }; 61 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"; 73 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 }; 82 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 }; 101 102 scanner = { 103 enable = mkEnableOption "ClamAV scanner"; 104 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 }; 114 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 }; 126 127 config = mkIf (cfg.updater.enable || cfg.daemon.enable) { 128 environment.systemPackages = [ pkg ]; 129 130 users.users.${clamavUser} = { 131 uid = config.ids.uids.clamav; 132 group = clamavGroup; 133 description = "ClamAV daemon user"; 134 home = stateDir; 135 }; 136 137 users.groups.${clamavGroup} = 138 { gid = config.ids.gids.clamav; }; 139 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 }; 147 148 services.clamav.updater.settings = { 149 DatabaseDirectory = stateDir; 150 Foreground = true; 151 Checks = cfg.updater.frequency; 152 DatabaseMirror = [ "database.clamav.net" ]; 153 }; 154 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 }; 163 164 environment.etc."clamav/freshclam.conf".source = freshclamConfigFile; 165 environment.etc."clamav/clamd.conf".source = clamdConfigFile; 166 167 systemd.services.clamav-daemon = mkIf cfg.daemon.enable { 168 description = "ClamAV daemon (clamd)"; 169 after = optionals cfg.updater.enable [ "clamav-freshclam.service" ]; 170 wants = optionals cfg.updater.enable [ "clamav-freshclam.service" ]; 171 wantedBy = [ "multi-user.target" ]; 172 restartTriggers = [ clamdConfigFile ]; 173 174 serviceConfig = { 175 ExecStart = "${pkg}/bin/clamd"; 176 ExecReload = "${pkgs.coreutils}/bin/kill -USR2 $MAINPID"; 177 User = clamavUser; 178 Group = clamavGroup; 179 StateDirectory = "clamav"; 180 RuntimeDirectory = "clamav"; 181 PrivateTmp = "yes"; 182 PrivateDevices = "yes"; 183 PrivateNetwork = "yes"; 184 }; 185 }; 186 187 systemd.timers.clamav-freshclam = mkIf cfg.updater.enable { 188 description = "Timer for ClamAV virus database updater (freshclam)"; 189 wantedBy = [ "timers.target" ]; 190 timerConfig = { 191 OnCalendar = cfg.updater.interval; 192 Unit = "clamav-freshclam.service"; 193 }; 194 }; 195 196 systemd.services.clamav-freshclam = mkIf cfg.updater.enable { 197 description = "ClamAV virus database updater (freshclam)"; 198 restartTriggers = [ freshclamConfigFile ]; 199 requires = [ "network-online.target" ]; 200 after = [ "network-online.target" ]; 201 202 serviceConfig = { 203 Type = "oneshot"; 204 ExecStart = "${pkg}/bin/freshclam"; 205 SuccessExitStatus = "1"; # if databases are up to date 206 StateDirectory = "clamav"; 207 User = clamavUser; 208 Group = clamavGroup; 209 PrivateTmp = "yes"; 210 PrivateDevices = "yes"; 211 }; 212 }; 213 214 systemd.services.clamav-fangfrisch-init = mkIf cfg.fangfrisch.enable { 215 wantedBy = [ "multi-user.target" ]; 216 # if the sqlite file can be found assume the database has already been initialised 217 script = '' 218 db_url="${cfg.fangfrisch.settings.DEFAULT.db_url}" 219 db_path="''${db_url#sqlite:///}" 220 221 if [ ! -f "$db_path" ]; then 222 ${pkgs.fangfrisch}/bin/fangfrisch --conf ${fangfrischConfigFile} initdb 223 fi 224 ''; 225 serviceConfig = { 226 Type = "oneshot"; 227 StateDirectory = "clamav"; 228 User = clamavUser; 229 Group = clamavGroup; 230 PrivateTmp = "yes"; 231 PrivateDevices = "yes"; 232 }; 233 }; 234 235 systemd.timers.clamav-fangfrisch = mkIf cfg.fangfrisch.enable { 236 description = "Timer for ClamAV virus database updater (fangfrisch)"; 237 wantedBy = [ "timers.target" ]; 238 timerConfig = { 239 OnCalendar = cfg.fangfrisch.interval; 240 Unit = "clamav-fangfrisch.service"; 241 }; 242 }; 243 244 systemd.services.clamav-fangfrisch = mkIf cfg.fangfrisch.enable { 245 description = "ClamAV virus database updater (fangfrisch)"; 246 restartTriggers = [ fangfrischConfigFile ]; 247 requires = [ "network-online.target" ]; 248 after = [ "network-online.target" "clamav-fangfrisch-init.service" ]; 249 250 serviceConfig = { 251 Type = "oneshot"; 252 ExecStart = "${pkgs.fangfrisch}/bin/fangfrisch --conf ${fangfrischConfigFile} refresh"; 253 StateDirectory = "clamav"; 254 User = clamavUser; 255 Group = clamavGroup; 256 PrivateTmp = "yes"; 257 PrivateDevices = "yes"; 258 }; 259 }; 260 261 systemd.timers.clamdscan = mkIf cfg.scanner.enable { 262 description = "Timer for ClamAV virus scanner"; 263 wantedBy = [ "timers.target" ]; 264 timerConfig = { 265 OnCalendar = cfg.scanner.interval; 266 Unit = "clamdscan.service"; 267 }; 268 }; 269 270 systemd.services.clamdscan = mkIf cfg.scanner.enable { 271 description = "ClamAV virus scanner"; 272 after = optionals cfg.updater.enable [ "clamav-freshclam.service" ]; 273 wants = optionals cfg.updater.enable [ "clamav-freshclam.service" ]; 274 275 serviceConfig = { 276 Type = "oneshot"; 277 ExecStart = "${pkg}/bin/clamdscan --multiscan --fdpass --infected --allmatch ${lib.concatStringsSep " " cfg.scanner.scanDirectories}"; 278 }; 279 }; 280 }; 281}