Theme
Building a free ICAP antivirus scanner with ClamAV and c-icap
Content scanning needs a scanner that speaks ICAP. ClamAV is a free antivirus engine; c-icap is a free ICAP server that can load it. Together they make a capable scanner on one small Linux machine, for a Syncplify Server! on Linux or on Windows alike.
There is one catch, and it is the reason for this page. The two are packaged for filtering web traffic, where letting a large download through is an acceptable default. As installed, they pass most of what a file transfer server handles without looking at it, and answer exactly as they would for a clean file. Every setting below was tested, and the list of what we found is at the end.
Everything here was tested on Debian 12, with its own packages: c-icap 0.5.10, its antivirus service 0.5.5, and ClamAV 1.4.6. Ubuntu 24.04 packages the same versions of c-icap and of the service.
What the machine needs
| Resource | How much | Why |
|---|---|---|
| Memory | 4 GiB or more | The signatures take about 1 GiB once loaded, and twice that for half a minute whenever new ones are loaded. Files being scanned come on top. |
| Processor | One core for every file you want scanned at the same moment | Eight files at once took barely longer than one alone on a machine with enough cores. |
| Disk | The largest file you scan, times the number scanned at once, with room to spare | Every file is written to a temporary folder while it is scanned, and archives are unpacked there. |
| Network | Reachable from every node of Syncplify Server!, and from nothing else | ICAP has no authentication. |
Run the commands as root.
Step 1: install the packages
bash
apt-get update
apt-get install c-icap libc-icap-mod-virus-scan clamav-freshclamStep 2: get the signatures
The package clamav-freshclam downloads the ClamAV signatures and then checks for new ones every hour. Make sure its service runs:
bash
systemctl enable --now clamav-freshclamWait until the three signature databases, main, daily and bytecode, are there:
bash
ls /var/lib/clamavtext
bytecode.cvd daily.cvd freshclam.dat main.cvdTwo messages you may meet, both harmless:
Failed to lock the log file /var/log/clamav/freshclam.log, when you runfreshclamby hand: the service is already running and does the work itself.NotifyClamd: Can't find or parse configuration file /etc/clamav/clamd.conf, in the log of the service: it refers to a ClamAV daemon that this setup does not use, because c-icap loads the engine itself.
Signatures first, c-icap second
A c-icap that starts without signatures still starts, and then answers clean for everything, the antivirus test file included. Syncplify Server! notices, because it tests a scanner before using it: such a scanner shows as Failed its self test and is not used. Do not start c-icap before this step has finished.
Step 3: configure c-icap
Add three lines at the end of /etc/c-icap/c-icap.conf. The first two load the antivirus service and the ClamAV engine; the third reads a file of your own, so that the packaged files stay as they are:
text
Include virus_scan.conf
Include clamav_mod.conf
Include local.confCreate /etc/c-icap/local.conf with this content:
text
# Settings of this installation, kept apart from the packaged files.
# One address that scans every file, whatever its type and its size.
ServiceAlias scanall virus_scan?allow204=on&sizelimit=off&mode=simple&force=on
# The limits of the engine itself, raised to the most ClamAV can scan.
clamav_mod.MaxScanSize 2047M
clamav_mod.MaxFileSizeInArchive 2047M
clamav_mod.MaxRecLevel 17
# Reject archives that cannot be scanned because they are password protected.
clamav_mod.ArchiveBlockEncrypted on
# Where files are written while they are scanned.
TmpDir /var/tmp
# Two worker processes of 16 threads each: 32 files at a time.
StartServers 2
MaxServers 2
ThreadsPerChild 16
MinSpareThreads 4
MaxSpareThreads 32What each part does, and what the packaged settings did in its place in our tests:
| Setting | What it changes | With the packaged settings |
|---|---|---|
ServiceAlias scanall ... | Defines the service name scanall, which is what you will put in the address. sizelimit=off scans files of any size. force=on scans every file type. The other two arguments are the ones the packaged alias uses. | The service name virus_scan passed every file above 5 MiB unscanned. The packaged alias avscan lifts that limit, but scans only the file types on a list (virus_scan.ScanFileTypes): a PDF, a PNG picture and an MP4 video carrying a test signature were all passed as clean. |
clamav_mod.MaxScanSize, clamav_mod.MaxFileSizeInArchive | Raise the engine's own size limits to just under 2 GiB. Give them a value: zero, which the packaged file describes as "no limit", leaves ClamAV's limit of 100 MB in place. | Every file above 100 MB was passed unscanned, including one that carried a test signature in its very first bytes. |
clamav_mod.MaxRecLevel | How deep the engine goes into archives inside archives. | With the packaged value of 5, the test file was found inside four nested ZIP archives and missed inside five. With 17 it was found inside nine and missed inside seventeen. |
clamav_mod.ArchiveBlockEncrypted | Reports a password protected archive as Heuristics.Encrypted.Zip, so it is rejected. Leave the line out if your users legitimately exchange such archives, and accept that nobody can scan them. | A password protected archive was passed as clean. |
TmpDir | Where c-icap writes each file while it is scanned. Point it at a disk with enough room. | /tmp, which on many systems is small or held in memory. |
StartServers to MaxSpareThreads | A fixed number of worker processes and threads, so that you know how many files are scanned at once (32 here) and what the machine must carry. | Three processes that grow to ten, for up to 100 files at once. |
Step 4: start c-icap
bash
systemctl enable --now c-icapGive it half a minute to load the signatures. Then ask the service what it offers:
bash
c-icap-client -i 127.0.0.1 -p 1344 -s scanallThe answer must include these two lines:
text
ICAP/1.0 200 OK
Service: C-ICAP/0.5.10 server - Antivirus serviceAn answer of 404 Service not found means the alias was not read: check the three Include lines and the name of your file.
Step 5: load new signatures when they arrive
c-icap loads the signatures when it starts and does not look at them again. Without this step your scanner would keep working with the signatures of the day it was started.
c-icap takes commands through a pipe, /run/c-icap/c-icap.ctl. Let the account that downloads the signatures write to it:
bash
usermod -aG c-icap clamavAdd this line at the end of /etc/clamav/freshclam.conf. It runs after every update that brought something new:
text
OnUpdateExecute /usr/bin/timeout 10 /bin/sh -c "printf reconfigure > /run/c-icap/c-icap.ctl"Restart the updater so that it picks up both changes:
bash
systemctl restart clamav-freshclamThe command reconfigure makes c-icap read its configuration and the signatures again and replace its worker processes. In our tests it kept answering throughout, a scan that was under way finished normally, and the definitions tag the scanner announces changed with the signatures, which is what makes Syncplify Server! test the scanner again.
Do not use the command clamav:dbreload
c-icap's ClamAV module has a command of its own for this, clamav:dbreload, and it is the one you will find recommended elsewhere. In our tests the engine it loads forgets the limits of step 3 and falls back to 100 MB: after it ran, a 110 MiB archive holding the antivirus test file was passed as clean, where it had been rejected a minute before. It also does not change the definitions tag, and it multiplies the memory in use, because every worker process then holds its own copy of the signatures.
Step 6: restrict who may connect
Anyone who can reach the ICAP port can use the scanner. Allow the addresses of your Syncplify Server! nodes, and nothing else, in the firewall of the machine or of the network. That is the control to rely on.
For ICAP in clear text, c-icap can enforce the same list itself, as a second layer. Add to local.conf, with your nodes' addresses (the first entry keeps the check of step 4 working), and restart c-icap:
text
acl fileservers src 127.0.0.1/255.255.255.255 192.0.2.10/255.255.255.255 192.0.2.11/255.255.255.255
acl all src 0.0.0.0/0.0.0.0
client_access allow fileservers
client_access deny allThe line that defines all is not optional. Without it the last line refers to a list that does not exist, and in our test c-icap then let everyone in.
Do not combine this list with TLS
In c-icap 0.5.10 the list does not see the address of a client that connects over TLS. In our tests it refused every TLS connection after a start, and then let them through once a clear text connection had been made. If you use TLS, leave these four lines out and rely on the firewall.
Step 7 (optional): ICAP over TLS
Without TLS, every file travels from the server to the scanner in clear text. That is acceptable on a network segment that carries nothing else; anywhere else, switch TLS on.
Have your certificate authority issue a certificate for the name your nodes will use for the scanner, here
scanner.example.com. Put the certificate and its key on the scanner, for example in/etc/c-icap/tls/, and make the key readable by thec-icapaccount only.Add to
local.conf, and restart c-icap:textTlsPort 11344 cert=/etc/c-icap/tls/scanner.pem key=/etc/c-icap/tls/scanner.keyIn the firewall, open port 11344 to your nodes and close port 1344 to everything but the scanner itself.
In Syncplify Server!, use the address
icaps://scanner.example.com:11344/scanall, and paste the certificate of your certificate authority into Certificate authority (PEM) unless the nodes already trust it.
Step 8: add it to Syncplify Server!
Follow Add the scanner and test it with these values:
| Setting | Value | Why |
|---|---|---|
| Address | icap://scanner.example.com:1344/scanall | The service name of step 3. |
| Method | RESPMOD | |
| Connections | 32 divided by the number of nodes, and by the number of virtual sites that use this scanner | The scanner of step 3 serves 32 connections, and an idle connection occupies one as much as a busy one: with 40 connections open, the next request was not answered. c-icap announces no limit of its own, so the server would otherwise open up to 32 from every node, for every site. |
| Largest file to scan (MiB) | 2000 | ClamAV cannot scan a file of 2 GiB or more, and passes it as clean. With this setting such a file is never sent, and the server accepts and records it, or refuses it, as you choose under A larger file. |
| Verdict wait (seconds) | 120, or more if your users send large archives | See the figures below. |
Then make the five uploads of Prove that it scans what you think it scans.
The repeated self test and the setting Scan again after new definitions both rely on the definitions tag the scanner announces. This scanner changes its tag when step 5 runs, so both work only when step 5 is in place.
What we measured
On one desktop class machine, with the scanner alone on it and the settings of this page:
| File | Time to a verdict |
|---|---|
| 1 GiB of very regular data | 4 seconds |
| 64 MiB of data that does not compress | 2 seconds |
| 1 GiB of the same | 30 seconds |
| 2000 MiB of the same | 1 minute |
| A 150 MiB ZIP archive of two files | 10 seconds |
| A 78 MiB ZIP archive of a source code tree, 17,000 files | 1 to 2 minutes |
| A 2 MiB ZIP archive of 9,000 small text files | 40 seconds to 1 minute |
| Eight files of 256 MiB at the same time | 8 seconds each, against 7 for one alone |
Memory: 1 GiB at rest, 2.2 GiB for half a minute while new signatures were loaded.
The number of files in an archive counts for more than its size. An archive that takes longer than the Verdict wait of the virtual site is treated as a file no scanner could judge.
The limits that remain
No setting removes these. Know them, and decide what you want to happen at each.
- Files of 2 GiB and more are not scanned. ClamAV cannot, and c-icap answers clean. Our file of exactly 2000 MiB was scanned; one just over 2 GiB was passed in six seconds, although it carried a test signature in its first bytes. This is what Largest file to scan is for.
- Archives nested deeper than the engine goes are not opened. Nothing is reported.
- Password protected archives cannot be scanned, by any engine. Step 3 rejects them; the alternative is to let them through unscanned.
- Time. A large archive of many small files can take minutes.
Keeping it healthy
- Logs.
/var/log/c-icap/server.logrecords every detection (VIRUS DETECTED, with the name of the file) and every client the access list refused (Request from ... denied)./var/log/c-icap/access.loghas one line per request.journalctl -u clamav-freshclamshows the signature updates. - Are the signatures current?
ls -l /var/lib/clamavshows when each database last changed. The daily one should never be more than a few days old. - Temporary files. A c-icap that is killed while it scans leaves the file it was scanning in its temporary folder, under a name that starts with
CI_TMP_. Files of that name older than a day can be deleted. - Package updates. When an update of the
c-icappackage asks whether to replacec-icap.conf, keep your version: it carries your threeIncludelines. - After any change, to c-icap or to ClamAV, repeat the five test uploads. They take two minutes, and they are the only thing that tells you the scanner still scans.
Everything we found, in one list
For the record, and for anyone comparing notes. With c-icap 0.5.10, its antivirus service 0.5.5 and ClamAV 1.4.6 as packaged by Debian 12, each of these was answered as a clean file:
- Through the service name
virus_scan: any file above 5 MiB. - Through the alias
avscan: any file whose type is not on the list of types to scan, which leaves out PDF documents, PNG pictures and MP4 videos among others. - Any file above 100 MB.
- Any file above 100 MB again after the command
clamav:dbreload, whatever limits were configured. - Any file of 2 GiB or more, whatever limits are configured.
- An archive nested deeper than the configured level.
- A password protected archive.
- Everything, when c-icap was started before the signatures were downloaded.
And one that refuses instead of passing: the access list of c-icap refused TLS clients it should have admitted.
The settings on this page close 1, 2, 3, 4, 6 and 7. Syncplify Server! catches 8 by itself, and Largest file to scan deals with 5.