From 8874f6ff664d6232d45bfd7f6907cf58b6d5fdd1 Mon Sep 17 00:00:00 2001 From: Hermes Date: Thu, 18 Jun 2026 21:53:33 -0400 Subject: [PATCH] feat: add gortium.clamav NixOS module MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - New module at modules/nixos/services/clamav.nix - Options: enable (CLI-only), enableDaemon (full services), onAccessScanning (clamonacc), scanPaths, dailyScanTime - All scans are logging-only — no auto-quarantine or deletion - uConsole: CLI tools only (enableDaemon=false) - lazyworkhorse: full setup with on-access scanning, daily 3 AM scan Also: remove neovim from uConsole (fails cross-compile, emacs available) --- flake.nix | 4 +- hosts/lazyworkhorse/configuration.nix | 16 +- hosts/uconsole-cm5/configuration.nix | 8 +- modules/nixos/services/clamav.nix | 240 ++++++++++++++++++++++++++ result | 1 - 5 files changed, 259 insertions(+), 10 deletions(-) create mode 100644 modules/nixos/services/clamav.nix delete mode 120000 result diff --git a/flake.nix b/flake.nix index e04ba59..0403137 100644 --- a/flake.nix +++ b/flake.nix @@ -194,6 +194,7 @@ ./hosts/uconsole-cm5/hardware-configuration.nix ./modules/nixos/services/remote-builder.nix ./modules/nixos/services/wireguard-client.nix + ./modules/nixos/services/clamav.nix ./modules/nixos/security/ai-worker-restricted.nix ./users/gortium/gortium.nix ./users/ai-worker/ai-worker.nix @@ -219,6 +220,7 @@ ./modules/nixos/filesystem/poup-16t-disk.nix ./modules/nixos/services/ollama_init_custom_models.nix ./modules/nixos/services/open_code_server.nix + ./modules/nixos/services/clamav.nix ./modules/nixos/security/ai-worker-restricted.nix ./users/gortium/gortium.nix ./users/ai-worker/ai-worker.nix @@ -237,7 +239,7 @@ ./hosts/cyt-pi/configuration.nix ./hosts/cyt-pi/hardware-configuration.nix ./modules/nixos/services/remote-builder.nix - ./modules/nixos/services/wireguard-client.nix + ./modules/nixos/services/wireguard-client.nix ./users/gortium/gortium.nix ]; }; diff --git a/hosts/lazyworkhorse/configuration.nix b/hosts/lazyworkhorse/configuration.nix index a5e61e2..db30fd3 100644 --- a/hosts/lazyworkhorse/configuration.nix +++ b/hosts/lazyworkhorse/configuration.nix @@ -166,9 +166,9 @@ settings = { PasswordAuthentication = false; KbdInteractiveAuthentication = false; - # Additional hardening settings below in SERVER HARDENING section - }; - hostKeys = [ + # ============================================================ + # ClamAV antivirus — daemon, hourly updates, daily scan, on-access + # ============================================================ { path = "/etc/ssh/ssh_host_ed25519_key"; type = "ed25519"; @@ -337,6 +337,16 @@ # networking.firewall.enable = false; # ============================================================================= + # ============================================================ + # ClamAV antivirus — daemon, hourly updates, daily scan, on-access + # ============================================================ + gortium.clamav = { + enable = true; + enableDaemon = true; + onAccessScanning = true; + dailyScanTime = "03:00"; + }; + # SERVER HARDENING - Firewall, Fail2ban, SSH, Kernel # ============================================================================= diff --git a/hosts/uconsole-cm5/configuration.nix b/hosts/uconsole-cm5/configuration.nix index 7fcd883..a6dc39e 100644 --- a/hosts/uconsole-cm5/configuration.nix +++ b/hosts/uconsole-cm5/configuration.nix @@ -83,7 +83,6 @@ fd htop tmux - neovim # ===== HAM Radio ===== wsjtx @@ -202,10 +201,9 @@ # ============================================================ # ClamAV antivirus — daily automatic scans # ============================================================ - services.clamav = { - daemon.enable = true; - updater.enable = true; - scanner.enable = true; + gortium.clamav = { + enable = true; + enableDaemon = false; }; } diff --git a/modules/nixos/services/clamav.nix b/modules/nixos/services/clamav.nix new file mode 100644 index 0000000..a93b162 --- /dev/null +++ b/modules/nixos/services/clamav.nix @@ -0,0 +1,240 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.gortium.clamav; + clamavPkg = pkgs.clamav; + + clamdConfig = pkgs.writeText "clamd.conf" '' + LogFile /var/log/clamav/clamd.log + LogTime yes + LogVerbose yes + LogSyslog yes + LocalSocket /run/clamav/clamd.sock + TCPSocket 3310 + TCPAddr 127.0.0.1 + User clamav + AllowSupplementaryGroups yes + ${cfg.clamdExtraConfig} + ''; + + freshclamConfig = pkgs.writeText "freshclam.conf" '' + DatabaseDirectory /var/lib/clamav + LogFile /var/log/clamav/freshclam.log + LogTime yes + LogVerbose yes + LogSyslog yes + User clamav + AllowSupplementaryGroups yes + ${cfg.freshclamExtraConfig} + ''; + + # Daily scan — logging only, no auto-quarantine/delete + scanScript = pkgs.writeShellScript "clamav-daily-scan" '' + set -e + PATHS="${concatStringsSep " " cfg.scanPaths}" + if [ -z "$PATHS" ]; then + echo "No paths configured for daily scan" + exit 0 + fi + echo "=== ClamAV daily scan started: $(date) ===" + ${clamavPkg}/bin/clamdscan --fdpass --log=/var/log/clamav/daily-scan.log --no-summary $PATHS + echo "=== ClamAV daily scan finished: $(date) ===" + ''; +in +{ + ##### Options ##### + options.gortium.clamav = { + enable = mkEnableOption "ClamAV antivirus — installs clamav CLI tools"; + + enableDaemon = mkOption { + type = types.bool; + default = true; + description = '' + Run clamd daemon + freshclam updater + daily scheduled scan. + Set to false on machines where you only want the CLI tools + (clamscan, clamdscan) for manual on-demand scanning. + ''; + }; + + onAccessScanning = mkOption { + type = types.bool; + default = false; + description = '' + Enable on-access scanning via clamonacc (fanotify-based). + Resource-heavy; server use only. Requires enableDaemon = true. + ''; + }; + + scanPaths = mkOption { + type = types.listOf types.str; + default = [ + "/home" + "/nix/store" + "/var/lib" + "/etc" + "/tmp" + "/var/tmp" + ]; + description = "Paths for the daily scheduled scan."; + }; + + dailyScanTime = mkOption { + type = types.str; + default = "daily"; + description = '' + When to run the daily scan. systemd calendar expression + or shortcuts like "daily", "weekly", "04:00". + ''; + }; + + clamdExtraConfig = mkOption { + type = types.lines; + default = ""; + description = "Extra lines appended to clamd.conf"; + }; + + freshclamExtraConfig = mkOption { + type = types.lines; + default = ""; + description = "Extra lines appended to freshclam.conf"; + }; + }; + + ##### Implementation ##### + config = mkIf cfg.enable { + # 1. Package — always installed when enable = true + environment.systemPackages = [ clamavPkg ]; + + # Everything below uses mkIf cfg.enableDaemon — conditionalized per attribute + + # 2. Users/groups (only if daemon runs) + users.users.clamav = mkIf cfg.enableDaemon { + isSystemUser = true; + group = "clamav"; + home = "/var/lib/clamav"; + createHome = true; + description = "ClamAV daemon user"; + }; + users.groups.clamav = mkIf cfg.enableDaemon {}; + + # 3. Directories (only if daemon runs) + systemd.tmpfiles.rules = mkIf cfg.enableDaemon [ + "d /var/lib/clamav 0750 clamav clamav -" + "d /var/log/clamav 0750 clamav clamav -" + "d /run/clamav 0755 clamav clamav -" + ]; + + # 4. ClamAV daemon (clamd) + systemd.services.clamav-daemon = mkIf cfg.enableDaemon { + description = "ClamAV Anti-Virus Daemon"; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + path = [ clamavPkg ]; + + preStart = '' + mkdir -p /var/lib/clamav /var/log/clamav /run/clamav + chown clamav:clamav /var/lib/clamav /var/log/clamav /run/clamav + ''; + + serviceConfig = { + Type = "simple"; + ExecStart = "${clamavPkg}/bin/clamd --config-file=${clamdConfig}"; + ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; + Restart = "on-failure"; + RestartSec = "10s"; + User = "clamav"; + Group = "clamav"; + RuntimeDirectory = "clamav"; + RuntimeDirectoryMode = "0755"; + StateDirectory = "clamav"; + StateDirectoryMode = "0750"; + LogsDirectory = "clamav"; + LogsDirectoryMode = "0750"; + PrivateTmp = true; + ProtectSystem = "strict"; + ProtectHome = true; + ReadWritePaths = [ + "/var/lib/clamav" + "/var/log/clamav" + "/run/clamav" + ]; + NoNewPrivileges = true; + }; + }; + + # 5. freshclam (database updater) — hourly via timer + systemd.services.clamav-freshclam = mkIf cfg.enableDaemon { + description = "ClamAV Virus Database Updater"; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + path = [ clamavPkg pkgs.curl ]; + + serviceConfig = { + Type = "oneshot"; + ExecStart = "${clamavPkg}/bin/freshclam --config-file=${freshclamConfig} --daemon-notify=${clamdConfig}"; + User = "clamav"; + Group = "clamav"; + PrivateTmp = true; + ProtectSystem = "full"; + NoNewPrivileges = true; + }; + }; + systemd.timers.clamav-freshclam = mkIf cfg.enableDaemon { + description = "ClamAV database update timer"; + wantedBy = [ "timers.target" ]; + timerConfig = { + OnCalendar = "hourly"; + Persistent = true; + RandomizedDelaySec = "1800"; + }; + }; + + # 6. Daily scan — logging only, no auto-quarantine + systemd.services.clamav-daily-scan = mkIf cfg.enableDaemon { + description = "ClamAV Daily Scheduled Scan"; + after = [ "clamav-daemon.service" ]; + requires = [ "clamav-daemon.service" ]; + path = [ clamavPkg ]; + + serviceConfig = { + Type = "oneshot"; + ExecStart = "${scanScript}"; + User = "clamav"; + Group = "clamav"; + PrivateTmp = true; + ProtectSystem = "strict"; + ReadWritePaths = [ "/var/log/clamav" ]; + }; + }; + systemd.timers.clamav-daily-scan = mkIf cfg.enableDaemon { + description = "ClamAV daily scan timer"; + wantedBy = [ "timers.target" ]; + timerConfig = { + OnCalendar = cfg.dailyScanTime; + Persistent = true; + }; + }; + + # 7. On-access scanning (clamonacc) — needs enableDaemon + systemd.services.clamav-onaccess = mkIf (cfg.enableDaemon && cfg.onAccessScanning) { + description = "ClamAV On-Access Scanner (clamonacc)"; + after = [ "clamav-daemon.service" ]; + requires = [ "clamav-daemon.service" ]; + wantedBy = [ "multi-user.target" ]; + path = [ clamavPkg ]; + + serviceConfig = { + Type = "simple"; + ExecStart = "${clamavPkg}/bin/clamonacc --config-file=${clamdConfig} --fdpass --log=/var/log/clamav/clamonacc.log"; + Restart = "on-failure"; + RestartSec = "10s"; + User = "root"; # clamonacc needs root for fanotify + Group = "root"; + PrivateTmp = true; + NoNewPrivileges = true; + }; + }; + }; +} diff --git a/result b/result deleted file mode 120000 index c445871..0000000 --- a/result +++ /dev/null @@ -1 +0,0 @@ -/nix/store/z86r4awsbrc5q9qhwwi757wxixcqgn31-nixos-system-uConsole-25.11.20260608.e820eb4 \ No newline at end of file