Skip to content

ClamAV support #407

Description

@chris-snyder-totara

Context

docker-dev has no ClamAV support today. There is no compose service, no client binary in any PHP image, and no antivirus_clamav wiring in php/includes/config-after.php. Developers who need to work on or test antivirus behaviour currently have to stand up clamd themselves.

The goal is a clamav container that developers opt into with tup clamav, and site config that wires itself up automatically whenever that container is running — matching how maildev and the binary paths are already handled in config-after.php.

Constraints

Two obvious approaches do not work:

  1. tcpsocket is a dead end. server/lib/antivirus/clamav/classes/scanner.php::is_configured() only returns true for commandline and unixsocket; the tcpsocket branch falls through to return false. \core\antivirus\manager::get_enabled() filters on is_configured(), so a TCP-configured plugin is silently never invoked.
  2. Path-passing methods need the file visible to clamd. The unixsocket method sends nSCAN <path>. The primary web upload path scans $_FILES[...]['tmp_name'], which lives in the PHP container's /tmp — not in the shared totara-data volume. Making that work would require sharing /tmp across all PHP containers.
    --fdpass solves it. Totara adds --fdpass automatically when basename($pathtoclam) === 'clamdscan'. Over a unix socket this passes the open file descriptor via SCM_RIGHTS, so clamd reads the file through the kernel regardless of mount namespace or permissions. This is the intended deployment shape and needs no path sharing. (--fdpass is unavailable over TCP, which is the other reason TCP is out.)
  3. There is no whitelist problem, at least. \core\command\executable::get_whitelist() adds get_config('antivirus_clamav', 'pathtoclam') with web execution allowed, so nothing extra is needed there. The template already sets $CFG->preventexecpath = false.
  4. clamav/clamav on Docker Hub is amd64-only (verified via docker manifest inspect on latest, latest_base, 1.4, 1.5). The PR template requires ARM64 compatibility, so we build our own multiarch image using the existing CI machinery.

Assumptions

Scan method: commandline + clamdscan --fdpass over a shared unix socket. Costs two PHP Dockerfile edits and an image rebuild, but avoids sharing /tmp and works for every scan path.

Image source: build our own multiarch ghcr.io/totara/docker-dev-clamav.

Config behaviour: auto-enable when the container is up, with clamfailureonupload => 'donothing' so a still-starting clamd never blocks a dev upload.

Scope

ClamAV support is limited to PHP 8.4 and 8.5. Only php/php84/Dockerfile and php/php85/Dockerfile gain the client, and only the four services php-8.4, php-8.4-debug, php-8.5, and php-8.5-debug in compose/php.yml gain the socket mount. compose/php-legacy.yml needs no change — it contains no 8.x services.

The file_exists() gate in config-after.php scopes itself for free: on 7.3–8.3 containers the socket volume is not mounted, so the socket never appears and the antivirus block stays inert. No version check is needed.

Metadata

Metadata

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions