Skip to content

Commit 58ae857

Browse files
author
Chris Snyder
committed
feat: Add ClamAV container with automatic config wiring for PHP 8.4 and 8.5
1 parent 2f7af4c commit 58ae857

9 files changed

Lines changed: 103 additions & 0 deletions

File tree

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ Although this project started as a development environment for Totara Learn it c
2323
* A [PHPUnit](https://phpunit.de/) and [Behat](http://behat.org/en/latest/) setup to run tests (including [Selenium](https://www.seleniumhq.org/))
2424
* A [MailDev](https://github.com/maildev/maildev?tab=readme-ov-file#maildev) instance to view sent emails
2525
* [Redis](https://redis.io/) for caching and/or session handling
26+
* [ClamAV](https://www.clamav.net/) for antivirus scanning, wired up automatically on PHP 8.4 and 8.5
2627
* [XHProf](https://github.com/tideways/php-xhprof-extension) for profiling
2728
* [XDebug](https://xdebug.org/) installed, ready for debugging with your favorite IDE
2829
* [Excimer](https://www.mediawiki.org/wiki/Excimer) installed, for discovering performance problems

bin/tdocker

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ source "$project_path/tools/check_for_update.sh"
1111
files=(
1212
"docker-compose.yml"
1313
"compose/apache.yml"
14+
"compose/clamav.yml"
1415
"compose/mariadb.yml"
1516
"compose/mssql.yml"
1617
"compose/mysql.yml"

clamav/clamd.conf

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# docker-dev ClamAV daemon configuration, mounted over the stock image's /etc/clamav/clamd.conf.
2+
#
3+
# Only the socket settings differ from the image default. Everything else - logging, the freshclam
4+
# daemon, the clamav user, and creating /run/clamav - is already handled by the stock entrypoint.
5+
# Do not set LogFile: ClamAV rejects /dev/stdout with "Symbolic link loop", and the image already
6+
# logs to stdout.
7+
8+
# Unix socket, shared with the PHP 8.4/8.5 containers via the clamav-socket volume. The stock
9+
# default is /tmp/clamd.sock, which we cannot share without mounting over the whole of /tmp.
10+
# Those containers run clamdscan with --fdpass, which passes the open file descriptor over this
11+
# socket, so clamd never needs to see the scanned file's path or be able to read it itself.
12+
# Mode 0666 so that www-data in the PHP containers can connect.
13+
LocalSocket /run/clamav/clamd.sock
14+
LocalSocketMode 0666
15+
16+
# Exposed for ad-hoc debugging only. Totara cannot use TCP: antivirus_clamav's
17+
# scanner::is_configured() returns false for the tcpsocket running method, so a TCP-configured
18+
# plugin is silently never invoked.
19+
TCPSocket 3310
20+
TCPAddr 0.0.0.0
21+
22+
# Must stay above upload_max_filesize in php/config/php.ini.
23+
StreamMaxLength 100M

compose/clamav.yml

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
services:
2+
3+
clamav:
4+
# The default (Alpine) tags are amd64 only - the -debian variants are multiarch.
5+
image: clamav/clamav:stable-debian13-slim
6+
container_name: totara_clamav
7+
restart: ${RESTART_POLICY:-no}
8+
environment:
9+
TZ: ${TIME_ZONE}
10+
volumes:
11+
# Only the socket settings differ from the image default.
12+
- ./clamav/clamd.conf:/etc/clamav/clamd.conf
13+
# Shared with the PHP 8.4/8.5 containers so clamdscan can reach clamd.
14+
- clamav-socket:/run/clamav
15+
# Persists signature updates between runs.
16+
- clamav-data:/var/lib/clamav
17+
networks:
18+
- totara
19+
# No healthcheck here: the image ships HEALTHCHECK --start-period=6m CMD clamdcheck.sh, which
20+
# reads clamd.conf and so already tests the unix socket the PHP containers use.
21+
22+
volumes:
23+
clamav-socket:
24+
clamav-data:

compose/php.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,8 @@ services:
339339
- bash-history:/root/.bash_history
340340
- zsh-history:/root/.zsh_history
341341
- ./shell:/root/custom_shell
342+
# Lets clamdscan reach clamd in the clamav container (see compose/clamav.yml).
343+
- clamav-socket:/run/clamav
342344
depends_on:
343345
- php-8.4-debug
344346
networks:
@@ -368,6 +370,8 @@ services:
368370
- bash-history:/root/.bash_history
369371
- zsh-history:/root/.zsh_history
370372
- ./shell:/root/custom_shell
373+
# Lets clamdscan reach clamd in the clamav container (see compose/clamav.yml).
374+
- clamav-socket:/run/clamav
371375
networks:
372376
- totara
373377

@@ -391,6 +395,8 @@ services:
391395
- bash-history:/root/.bash_history
392396
- zsh-history:/root/.zsh_history
393397
- ./shell:/root/custom_shell
398+
# Lets clamdscan reach clamd in the clamav container (see compose/clamav.yml).
399+
- clamav-socket:/run/clamav
394400
depends_on:
395401
- php-8.5-debug
396402
networks:
@@ -420,6 +426,8 @@ services:
420426
- bash-history:/root/.bash_history
421427
- zsh-history:/root/.zsh_history
422428
- ./shell:/root/custom_shell
429+
# Lets clamdscan reach clamd in the clamav container (see compose/clamav.yml).
430+
- clamav-socket:/run/clamav
423431
networks:
424432
- totara
425433

@@ -430,6 +438,7 @@ services:
430438
# - Add a build entry in compose/build.yml
431439
# - Add a mutagen sync entry in compose/sync.yml
432440
# - Add relevant network aliases in compose/nginx.yml and compose/apache.yml
441+
# - Add the clamav-socket volume mount, if the version is 8.4 or newer
433442
# - Add a CI build entry in .github/workflows/build-php.yml
434443
# - Add a image matrix entry in .github/workflows/scan-images.yml
435444
# - Update the max PHP version in .github/workflows/validate-config.yml
@@ -440,3 +449,4 @@ volumes:
440449
coverage-report:
441450
bash-history:
442451
zsh-history:
452+
clamav-socket:

php/config/clamd.conf

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# docker-dev ClamAV client configuration.
2+
#
3+
# This is installed as /etc/clamav/clamd.conf because that is the path clamdscan reads by
4+
# default, and Totara invokes clamdscan without a --config-file argument. There is no clamd
5+
# running in this container - it runs in the clamav container and exposes the socket below
6+
# through the shared clamav-socket volume.
7+
#
8+
# Totara adds --fdpass whenever the configured binary is named clamdscan, which passes the open
9+
# file descriptor over this socket, so clamd does not need to see the scanned file's path.
10+
LocalSocket /run/clamav/clamd.sock

php/includes/config-after.php

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,36 @@
295295
$CFG->smtphosts = 'maildev:1025';
296296

297297

298+
// ClamAV antivirus, only active while the clamav container is running.
299+
// clamdscan is invoked with --fdpass (Totara adds that automatically whenever the binary is named
300+
// clamdscan), which hands the open file descriptor to clamd, so the clamav container does not need
301+
// to see the scanned file itself. Note the tcpsocket running method cannot be used here - the
302+
// plugin's is_configured() returns false for it, so it would silently never scan anything.
303+
// Note the stock clamav image does not forward SIGTERM to clamd, so a stopped container leaves its
304+
// socket file behind. Check that something is actually listening rather than just that the file
305+
// exists - connecting to a stale unix socket fails immediately, so this stays cheap.
306+
$DOCKER_DEV->clamav_socket = '/run/clamav/clamd.sock';
307+
$DOCKER_DEV->clamav_running = false;
308+
if (file_exists($DOCKER_DEV->clamav_socket)) {
309+
$clamav_probe = @stream_socket_client('unix://' . $DOCKER_DEV->clamav_socket, $errno, $errstr, 1);
310+
if ($clamav_probe !== false) {
311+
fclose($clamav_probe);
312+
$DOCKER_DEV->clamav_running = true;
313+
}
314+
unset($clamav_probe, $errno, $errstr);
315+
}
316+
if ($DOCKER_DEV->clamav_running) {
317+
$CFG->antiviruses = 'clamav';
318+
$CFG->forced_plugin_settings['antivirus_clamav'] = [
319+
'runningmethod' => 'commandline',
320+
'pathtoclam' => '/usr/bin/clamdscan',
321+
// Never fail a dev upload just because clamd is still loading its signatures.
322+
'clamfailureonupload' => 'donothing',
323+
'tries' => 1,
324+
];
325+
}
326+
327+
298328
// Paths to binaries
299329
$CFG->py3path = '/usr/bin/python3';
300330
$CFG->pathtogs = '/usr/bin/gs';

php/php84/Dockerfile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y \
2929
ghostscript \
3030
graphviz \
3131
aspell \
32+
clamdscan \
3233
libldap2-dev \
3334
libltdl-dev \
3435
libc-client-dev \
@@ -143,6 +144,7 @@ RUN ln -fs /usr/share/zoneinfo/${TIME_ZONE} /etc/localtime \
143144

144145
COPY config/php.ini /usr/local/etc/php/
145146
COPY config/fpm.conf /usr/local/etc/php-fpm.d/zz-totara.conf
147+
COPY config/clamd.conf /etc/clamav/clamd.conf
146148

147149
# Source each .sh file found in the /shell/ folder, always source the default_aliases.sh file first
148150
RUN echo 'if [[ -e "/root/custom_shell/default-aliases.sh" ]]; then source "/root/custom_shell/default-aliases.sh"; fi' >> ~/.bashrc && \

php/php85/Dockerfile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y \
2929
ghostscript \
3030
graphviz \
3131
aspell \
32+
clamdscan \
3233
libldap2-dev \
3334
libltdl-dev \
3435
libc-client-dev \
@@ -148,6 +149,7 @@ RUN ln -fs /usr/share/zoneinfo/${TIME_ZONE} /etc/localtime \
148149

149150
COPY config/php.ini /usr/local/etc/php/
150151
COPY config/fpm.conf /usr/local/etc/php-fpm.d/zz-totara.conf
152+
COPY config/clamd.conf /etc/clamav/clamd.conf
151153

152154
# Source each .sh file found in the /shell/ folder, always source the default_aliases.sh file first
153155
RUN echo 'if [[ -e "/root/custom_shell/default-aliases.sh" ]]; then source "/root/custom_shell/default-aliases.sh"; fi' >> ~/.bashrc && \

0 commit comments

Comments
 (0)