This is the multi-page printable view of this section. Click here to print.

Return to the regular view of this page.

ClamAV controller lifecycle

    The fleet and XOA callers import one shared Ansible lifecycle playbook, passing their host patterns and connection variables while retaining separate inventory and authentication packaging. Native tasks and configuration templates prepare an unprivileged controller. The official pinned payload is extracted into a mode 0700 invocation directory; it is not installed as a host package. No custom controller helper or database parser is used.

    The playbooks require clamav_scan_report_directory and clamav_database_directory as existing operator-owned mode 0700 absolute paths under ignored task scratch. Paths use ASCII letters, numbers, underscores, periods, slashes, and hyphens. Preparation requires a working systemd user manager, native tar/flock/timeout tools, at least 4 GiB available RAM, 5 GiB free disk on both paths, and writable directories.

    One native freshclam invocation preserves the existing database cache and its cooldown state. An exclusive nonblocking flock prevents overlapping update commands. Use one controller scan workflow at a time against the cache. The relocated tools receive LD_LIBRARY_PATH=prefix/usr/local/lib and CVD_CERTS_DIR=prefix/usr/local/etc/certs; freshclam/clamd configuration and sigtool’s command also specify the certificate directory. The environment is needed by freshclam’s database-test subprocess as well. TLS, code-signature verification, and TestDatabases yes remain enabled.

    Native sigtool --info verifies daily/main/bytecode containers. Ansible filters retain their versions, signature counts, build times, and ages. Daily signatures may be at most 72 hours old by default (clamav_daily_max_age_seconds=259200). Main and bytecode ages are recorded without a daily-age limit because their release cadence differs; future timestamps beyond five minutes are rejected. A successful freshclam return code alone is not evidence of freshness.

    systemd-run --user starts foreground clamd as a uniquely named transient unit with RuntimeMaxSec (default 172800 seconds), TimeoutStopSec=15, KillMode=control-group, UMask=0077, and default Nice=19, IOSchedulingClass=idle, and MemoryMax=6G. clamav_controller_memory_gib selects the integer memory ceiling in GiB (default 6, range 1–64), mapped to systemd’s MemoryMax. The shared caller boolean clamav_scan_background defaults to true; false selects Nice=0 and IOSchedulingClass=best-effort. Supply booleans through JSON/YAML extra variables, for example --extra-vars '{"clamav_scan_background": false}'. Controller preparation supplies the default before the endpoint role is loaded. Select overrides only after checking controller headroom; the existing RAM/disk minimums remain. The unit owns the process group, its private working directory, and native log output. One native clamdscan --version polling task waits for a daemon response containing the engine version, verified daily version, and database timestamp; the client’s local-version fallback is rejected. One outer timeout bounds the whole loop, including a stalled client, with a 15-second forced-stop grace. The archive pin owns the engine version; the actual server version is retained in lifecycle evidence. Real clean-file and EICAR validation remain separate acceptance steps.

    The daemon binds a mode 0600 Unix socket inside a mode 0700 native RuntimeDirectory, under the operator’s XDG_RUNTIME_DIR. Systemd owns its creation and removal with the transient unit. This gives clients a short absolute path without process/cwd aliases. Reports, logs, extraction and the signature cache stay under ignored workspace scratch. The native runtime root must be operator-owned mode 0700. SSH controls use separate private runtime subdirectories, retained until authenticated recovery is finished.

    clamav_controller_threads selects native MaxThreads (default 2, integer range 1–32); MaxQueue is four times that value (default 8). Thread and memory inputs accept integers or decimal digit strings; invalid values fail before engine execution. Lifecycle receipts retain the selected resources and explicit scheduling settings in their controller object.

    The fixed engine policy uses no concurrent reload, a 100 MiB file limit, matching client/server stream limits of 100 MiB plus one byte, 400 MiB aggregate scan size, 10,000 archive members, recursion 17, and 120,000 ms scan time. Heuristic alerts, encrypted-content alerts, and limit alerts are enabled; heuristic precedence is disabled. ClamAV 1.5.4 has no ScanEncrypted option. Encrypted content and limit alerts are limitations or heuristics, not confirmed malware. Neither deletion nor quarantine is configured. The extra stream byte makes oversized inputs produce an explicit file-limit alert instead of accepting a truncated prefix or aborting directory traversal at the protocol limit.

    The first play prepares the controller; the selected-host play invokes the shared role and retains failures while continuing later serial batches; the final play attempts controller cleanup before reporting accumulated failures. Host-failure reporting still runs when cleanup fails. Every --limit must include localhost. Startup failure stops the named unit before failing the first play. If Ansible is killed or cleanup is otherwise skipped, RuntimeMaxSec is the fallback: shutdown is bounded, not immediate on parent-process exit. The private lifecycle receipt records the exact unit for systemctl --user stop UNIT recovery. No process-name search or unverified PID signal is used. The unit is transient; no permanent user service or host configuration is installed.

    Mode 0600 configuration, signature, freshclam, clamd, and lifecycle artifacts remain in the invocation directory. Signature metadata is owned by signatures.json; lifecycle evidence retains the actual server version without duplicating signature metadata. The initial receipt is incomplete before engine execution. Cleanup confirmation is not an endpoint scan verdict. Native role reports must be checked for detections, read errors, exclusions, and coverage limits. For a local Host Bot scan, the caller explicitly excludes the whole task scratch tree, including the database cache and controller extraction.

    Build //infra/security/ansible:clamav_bin and //infra/security/appliances:clamav_bin to check packaging. Their separate :clamav_syntax targets parse playbooks locally without AL authentication or engine/host execution. The parent owns real-engine, clean/EICAR, and fleet validation. The appliance scan defaults to XOA; XCP-ng is unscanned because of the native payload’s glibc requirement.