@@ -231,12 +231,366 @@ Files marked as scanned will not be scanned for the next four weeks.
231231Configuring ICAP on Nextcloud
232232-----------------------------
233233
234- Nextcloud offers the integration of antivirus protection based on the ICAP protocol.
235- The settings are outlined here.
236- Additional documentation is work in progress.
234+ Instead of talking to ClamAV directly, the Antivirus app for Files can hand
235+ files to an external scanning service using ICAP, the Internet Content
236+ Adaptation Protocol (`RFC 3507 <https://www.rfc-editor.org/rfc/rfc3507 >`_).
237+ In this mode Nextcloud does not run a scanner itself: it opens a TCP
238+ connection to an ICAP server, streams the file to it and acts on the verdict
239+ that comes back.
240+
241+ Use ICAP mode when:
242+
243+ * you already operate an enterprise scanning appliance or scanning service
244+ that speaks ICAP, possibly shared with your mail gateway or proxy. The app
245+ contains specific handling for Kaspersky and McAfee products, and the
246+ administration settings ship presets for ClamAV / c-icap, Kaspersky and
247+ FortiSandbox;
248+ * you want to run ClamAV behind ``c-icap `` on a separate host, so that
249+ scanning load and signature updates are kept away from the Nextcloud
250+ servers;
251+ * your scanner should also enforce a file type or file extension policy, which
252+ ICAP mode understands in addition to malware verdicts (see
253+ `How ICAP responses are interpreted `_).
254+
255+ To enable it, select ``ICAP `` as the mode in the Antivirus Configuration panel
256+ on your Admin page, or set it with ``occ ``::
257+
258+ sudo -E -u www-data php occ config:app:set files_antivirus av_mode --value="icap"
237259
238260.. figure :: ../images/antivirus-icap.png
239261
262+ Settings
263+ ~~~~~~~~
264+
265+ The host and port are shared with the other network modes; all other settings
266+ below are specific to ICAP mode.
267+
268+ .. list-table ::
269+ :header-rows: 1
270+ :widths: 30 20 50
271+
272+ * - Option
273+ - Default
274+ - Description
275+ * - ``av_mode ``
276+ - ``executable ``
277+ - Set to ``icap `` to enable ICAP mode.
278+ * - ``av_host ``
279+ - (empty)
280+ - Hostname or IP address of the ICAP server. Scanning fails if this is
281+ not set.
282+ * - ``av_port ``
283+ - ``3310 ``
284+ - Port of the ICAP server. Note that this default is the ClamAV daemon
285+ port, not the ICAP default port of 1344, so it almost always has to be
286+ changed.
287+ * - ``av_icap_request_service ``
288+ - ``avscan ``
289+ - The ICAP service to call, that is the path part of the request URI
290+ ``icap://<av_host>/<service> ``. ``c-icap `` with ClamAV uses ``avscan ``;
291+ other products use different names, for example ``req `` for Kaspersky
292+ and ``respmod `` for FortiSandbox.
293+ * - ``av_icap_mode ``
294+ - ``reqmod ``
295+ - Either ``reqmod `` or ``respmod ``. See
296+ `Choosing between REQMOD and RESPMOD `_.
297+ * - ``av_icap_response_header ``
298+ - ``X-Infection-Found ``
299+ - The ICAP response header from which the threat name is read. This is
300+ vendor specific; see `The virus response header `_.
301+ * - ``av_icap_tls ``
302+ - ``false ``
303+ - Whether to wrap the ICAP connection in TLS. See
304+ `Transport security `_.
305+ * - ``av_icap_chunk_size ``
306+ - ``1048576 ``
307+ - Number of bytes buffered in memory before they are written to the ICAP
308+ server as one chunk of the chunked request body.
309+ * - ``av_icap_connect_timeout ``
310+ - ``5 ``
311+ - Timeout in seconds for establishing the TCP or TLS connection to the
312+ ICAP server. The read timeout on an established connection is fixed at
313+ 600 seconds.
314+
315+ All of these can be set with ``occ ``, for example::
316+
317+ sudo -E -u www-data php occ config:app:set files_antivirus av_host --value="icap.example.com"
318+ sudo -E -u www-data php occ config:app:set files_antivirus av_port --value="1344"
319+ sudo -E -u www-data php occ config:app:set files_antivirus av_icap_request_service --value="avscan"
320+ sudo -E -u www-data php occ config:app:set files_antivirus av_icap_mode --value="reqmod"
321+ sudo -E -u www-data php occ config:app:set files_antivirus av_icap_response_header --value="X-Infection-Found"
322+ sudo -E -u www-data php occ config:app:set files_antivirus av_icap_tls --value="true"
323+ sudo -E -u www-data php occ config:app:set files_antivirus av_icap_chunk_size --value="1048576"
324+ sudo -E -u www-data php occ config:app:set files_antivirus av_icap_connect_timeout --value="5"
325+
326+ The Antivirus Configuration panel exposes the mode, host, port, service,
327+ virus response header and TLS switch, plus a preset selector that fills in
328+ service, header and mode for ClamAV / c-icap, Kaspersky and FortiSandbox.
329+ The chunk size and the connection timeout can only be set with ``occ ``.
330+
331+ The settings shared with the other modes also apply in ICAP mode: the
332+ streaming limits ``av_stream_max_length ``, ``av_max_file_size `` and
333+ ``av_scan_first_bytes `` (see `What is sent to the scanning service `_), and the
334+ handling options ``av_block_unscannable ``, ``av_block_unreachable `` and
335+ ``av_infected_action `` (see `How ICAP responses are interpreted `_).
336+
337+ Choosing between REQMOD and RESPMOD
338+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
339+
340+ ICAP servers usually offer both request modification (REQMOD) and response
341+ modification (RESPMOD) services, and expect the client to use the one the
342+ service was configured for. The app builds a synthetic HTTP message around the
343+ file in both cases:
344+
345+ * ``reqmod `` sends an ICAP ``REQMOD `` request whose encapsulated HTTP request
346+ is ``PUT <path> HTTP/1.0 `` with a ``Host: nextcloud `` header, followed by
347+ the file contents as the request body.
348+
349+ * ``respmod `` sends an ICAP ``RESPMOD `` request whose encapsulated HTTP
350+ request is ``GET <path> HTTP/1.0 `` with a ``Host: nextcloud `` header, plus a
351+ synthetic HTTP response consisting of ``HTTP/1.0 200 OK `` and a
352+ ``Content-Length `` header, followed by the file contents as the response
353+ body.
354+
355+ Both variants send ``Allow: 204 ``, so that the scanner may answer with
356+ ``204 No Content `` for a file it does not want to modify, and an
357+ ``X-Client-IP `` header (see `What is sent to the scanning service `_). In both
358+ cases the body is transferred with chunked encoding, so the scanner receives
359+ the file as a stream.
360+
361+ The practical difference is the ``Content-Length `` header: RESPMOD tells the
362+ scanner the size of the object before the body arrives, REQMOD does not.
363+ Scanners that need to decide up front whether they will accept an object, or
364+ that queue large objects differently, therefore work better with RESPMOD.
365+ Pick the mode your ICAP service expects; if the vendor documents both, prefer
366+ RESPMOD for large files.
367+
368+ .. note :: In RESPMOD the announced ``Content-Length`` is the real file size
369+ only when the size is known at the time the scan starts, which is the case
370+ for background scans and for uploads that provide a length. Where the size
371+ is not known, a dummy length of 1 byte is announced instead. This is
372+ accepted by the scanners tested, but it is a reason why a scanner that
373+ strictly validates ``Content-Length `` against the body may reject requests.
374+
375+ The virus response header
376+ ~~~~~~~~~~~~~~~~~~~~~~~~~
377+
378+ When the ICAP server reports an infection it names the threat in a response
379+ header, and the name of that header differs per vendor. ``av_icap_response_header ``
380+ must match exactly what your scanner sends: ``c-icap `` with ClamAV uses
381+ ``X-Infection-Found ``, Kaspersky uses ``X-Virus-ID ``, and other products use
382+ other names. The value of that header becomes the threat name that is logged
383+ and shown to the user.
384+
385+ .. warning :: A wrong ``av_icap_response_header`` fails silently and in the
386+ unsafe direction. If the configured header is not the one the scanner
387+ sends, no threat name is ever found, the response is treated as clean, and
388+ infected files are accepted without any error being logged. Always verify
389+ the setting against a real detection instead of assuming that an
390+ error-free upload means scanning works.
391+
392+ Two vendor behaviours are recognised without the header and therefore keep
393+ working if it is misconfigured: Kaspersky in product editions before 2020 and
394+ McAfee report an infection as an encapsulated HTTP status of
395+ ``403 Forbidden `` or ``403 VirusFound ``, which the app also treats as
396+ infected.
397+
398+ Verifying the configuration
399+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~
400+
401+ Use the built-in test command, which scans harmless content and two variants
402+ of the EICAR test file through the configured backend::
403+
404+ sudo -E -u www-data php occ files_antivirus:test
405+
406+ The command fails if the clean content is reported as infected, or if either
407+ EICAR sample is reported as clean, which is exactly the failure mode of a
408+ wrong virus response header. It reports files that could not be scanned or
409+ were not scanned separately, so a run that reports pending or unscannable
410+ results is not a confirmation that detection works.
411+
412+ ICAP mode also supports debug output, which prints the generated ICAP request
413+ headers and the raw ICAP response. This is the quickest way to see which
414+ header a scanner actually returns::
415+
416+ sudo -E -u www-data php occ files_antivirus:test --debug
417+
418+ Saving the settings in the Antivirus Configuration panel also runs a scan of
419+ dummy content and reports the result, so a saved configuration that cannot
420+ reach the ICAP server is reported immediately.
421+
422+ How ICAP responses are interpreted
423+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
424+
425+ ICAP mode distinguishes more outcomes than clean and infected:
426+
427+ .. list-table ::
428+ :header-rows: 1
429+ :widths: 30 20 50
430+
431+ * - ICAP response
432+ - Result
433+ - Handling
434+ * - ``200 `` or ``204 ``, no virus response header and no encapsulated
435+ ``403 ``
436+ - Clean
437+ - The file is accepted.
438+ * - ``200 `` with the configured virus response header, or an encapsulated
439+ HTTP status containing ``403 Forbidden `` or ``403 VirusFound ``
440+ - Infected
441+ - The upload is rejected and the file is deleted, see
442+ `Where scanning happens `_.
443+ * - ``500 `` with ``X-Error-Code: file_type_blocked `` or
444+ ``file_extension_blocked ``
445+ - Infected
446+ - Treated exactly like a malware detection, so an ICAP scanner can also
447+ enforce a file type policy for Nextcloud uploads.
448+ * - ``500 `` with ``X-Error-Code: decode_error ``,
449+ ``max_archive_layers_exceeded `` or ``password_protected ``
450+ - Unscannable
451+ - Accepted or rejected according to ``av_block_unscannable ``.
452+ * - ``202 ``
453+ - Not checked
454+ - Accepted or rejected according to ``av_block_unreachable ``.
455+ * - Any other response, including ``500 `` with an unknown or absent
456+ ``X-Error-Code ``
457+ - Error
458+ - The scan fails with an error and the upload does not complete.
459+
460+ Two settings decide what happens to a file the scanner did not clear:
461+
462+ ``av_block_unscannable ``
463+ Default ``false ``. Files the scanner reported as unscannable, such as
464+ password-protected archives or archives nested more deeply than the scanner
465+ will unpack, are **accepted ** with the default setting. Set it to ``true ``
466+ to reject them instead::
467+
468+ sudo -E -u www-data php occ config:app:set files_antivirus av_block_unscannable --value="true"
469+
470+ ``av_block_unreachable ``
471+ Default ``true ``. With the default setting, a file is rejected when the ICAP
472+ server cannot be reached at all or answers ``202 ``, and the user is told
473+ that the upload cannot be completed. Set it to ``false `` to accept uploads
474+ while the scanner is unavailable; such files are not marked as scanned, so
475+ the background scanner picks them up later.
476+
477+ .. warning :: These two defaults mean that, out of the box, a file that could
478+ not be checked because it is unscannable is treated as safe and stored,
479+ while a file that could not be checked because the scanner was unreachable
480+ is refused. Decide deliberately which behaviour you want before relying on
481+ ICAP scanning as a control.
482+
483+ Transport security
484+ ~~~~~~~~~~~~~~~~~~
485+
486+ By default, ``av_icap_tls `` is ``false `` and the ICAP connection is a plain
487+ TCP connection. With TLS enabled::
488+
489+ sudo -E -u www-data php occ config:app:set files_antivirus av_icap_tls --value="true"
490+
491+ the connection is established as ``tls://<av_host>:<av_port> `` with peer and
492+ peer name verification enabled, validated against the CA bundle managed by
493+ Nextcloud. A scanner presenting a self-signed or private-CA certificate
494+ therefore needs that certificate imported into Nextcloud, in the same way as
495+ for external storage.
496+
497+ .. warning :: ICAP has no transport security of its own. With
498+ ``av_icap_tls `` disabled, the complete contents of every scanned file, the
499+ file path and the uploader's IP address travel to the ICAP server in
500+ plaintext. Only leave TLS off when the connection stays on a network you
501+ fully control, and never for a remote or cloud-hosted scanning service.
502+
503+ What is sent to the scanning service
504+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
505+
506+ For a data protection assessment, this is what leaves the Nextcloud server on
507+ every ICAP scan:
508+
509+ * **The file contents. ** The full bytes of the file, streamed to the scanner
510+ in chunks of ``av_icap_chunk_size ``, subject to the limits below.
511+
512+ * **The file path **, in the URI of the encapsulated HTTP request and
513+ URL-encoded segment by segment. This is the internal absolute path of the
514+ file, so for a file in an account's own storage it has the form
515+ ``/<account>/files/<path> ``: the account's user ID is part of it. For
516+ chunked WebDAV uploads, which are written to a temporary
517+ ``.ocTransferId<number>.part `` file, the app recovers the real destination
518+ path from the WebDAV request URI so that the scanner sees the intended file
519+ name and extension rather than the ``.part `` name.
520+
521+ * **The uploader's IP address **, in the ``X-Client-IP `` header, whenever the
522+ scan is triggered by a request that has a remote address. Background scans
523+ have none, so the header is empty there.
524+
525+ * **Fixed protocol headers ** that carry no information about the instance: a
526+ constant ``User-Agent ``, the ICAP ``Host `` header, which is the configured
527+ ``av_host `` and thus the scanner's own name, and a literal
528+ ``Host: nextcloud `` in the encapsulated HTTP request.
529+
530+ No account name, display name, email address, group membership, share
531+ information, comment or tag is sent. Nothing is transmitted back into
532+ Nextcloud from the scanner except the ICAP status code and headers, and the
533+ threat name from the virus response header is stored and logged with the scan
534+ result.
535+
536+ Three settings bound how much of a file is sent, and all three change what
537+ the verdict is actually based on:
538+
539+ ``av_scan_first_bytes ``
540+ Default ``-1 ``, meaning the whole file. With a positive value, only about
541+ the first that many bytes of a file are sent, so the scanner returns a
542+ verdict on a prefix of the file and malware later in the file is not seen.
543+
544+ ``av_stream_max_length ``
545+ Default ``26214400 `` (25 MiB). A file larger than this is not sent over a
546+ single ICAP connection: the request is finished and evaluated, and a new
547+ ICAP request is opened for the following bytes. Each segment is scanned on
548+ its own, and a detection in any segment marks the file as infected.
549+ Detections that depend on seeing a whole object, such as a large archive,
550+ can be missed when the object spans a segment boundary, so raise this limit
551+ rather than lowering it if your scanner and the PHP ``memory_limit `` allow
552+ for it.
553+
554+ ``av_max_file_size ``
555+ Default ``-1 ``, meaning no limit. With a positive value, files larger than
556+ this are excluded from background scanning entirely, and chunked WebDAV
557+ uploads larger than this are not scanned at all. They are stored unscanned;
558+ they are not partially scanned.
559+
560+ .. warning :: If your ICAP server is operated by a third party or hosted
561+ outside your own infrastructure, then enabling ICAP mode means you are
562+ transferring file contents, file paths including account user IDs, and
563+ client IP addresses to that third party for every upload and every
564+ background scan. Cover this in your processing records and contracts
565+ before enabling it.
566+
567+ Where scanning happens
568+ ~~~~~~~~~~~~~~~~~~~~~~
569+
570+ ICAP scanning is not a separate code path; it is the same pipeline the ClamAV
571+ modes use, so the following applies to all modes:
572+
573+ * Uploads are scanned while they are being written. The app installs a storage
574+ wrapper that observes every write, so the data is streamed to the scanner as
575+ it arrives rather than after the file is complete.
576+
577+ * When the verdict is "infected", the newly written file is deleted and the
578+ upload is refused with the message *Virus <name> is detected in the file.
579+ Upload cannot be completed. * The trash bin is paused while the file is
580+ deleted, so the infected file does not end up in the user's trash. An entry
581+ is written to the log and to the user's activity stream.
582+
583+ * Files that already existed, files that were accepted while the scanner was
584+ unavailable, and files whose scan is older than ``av_rescan_days `` (default
585+ ``28 ``) are handled by the background scanner, which scans in batches on the
586+ cron schedule. For infected files found by the background scanner, the
587+ action is controlled by ``av_infected_action ``, which defaults to
588+ ``only_log ``. See `Manage the background scanner `_.
589+
590+ * Some content is never sent to the scanner: end-to-end encrypted files and
591+ their metadata, and anything under a directory listed in
592+ ``av_blocklisted_directories ``.
593+
240594
241595Disabling background scan task
242596------------------------------
0 commit comments