From 64cd84846973902c798e785432c3f5b77d3f0624 Mon Sep 17 00:00:00 2001 From: Sven Neuhaus Date: Tue, 4 Aug 2026 12:13:28 +0200 Subject: [PATCH 1/3] =?UTF-8?q?=F0=9F=93=9D(docs)=20document=20ICAP=20malw?= =?UTF-8?q?are=20detection=20backend?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a documentation page describing how to configure the ICAP malware detection backend from django-lasuite with an external ICAP server (c-icap/ClamAV), including the environment variables, the Helm wiring through backend.envVars and the scanner requirements. Link it from the environment variables reference and the documentation table of contents. --- documentation/README.md | 1 + documentation/env.md | 4 +- documentation/malware_detection.md | 85 ++++++++++++++++++++++++++++++ 3 files changed, 88 insertions(+), 2 deletions(-) create mode 100644 documentation/malware_detection.md diff --git a/documentation/README.md b/documentation/README.md index 574c817360..41bbe2c2ae 100644 --- a/documentation/README.md +++ b/documentation/README.md @@ -19,6 +19,7 @@ Use the section below to quickly find what you are looking for. - Configuration - [Environment variables](env.md) + - [Malware detection](malware_detection.md) - [Object storage](s3.md) - [Collaboration](collaboration.md) - [Format conversion](format_conversion.md) diff --git a/documentation/env.md b/documentation/env.md index 6da1e30e86..b204ac8c53 100644 --- a/documentation/env.md +++ b/documentation/env.md @@ -92,8 +92,8 @@ These are the environment variables you can set for the `impress-backend` contai | LOGIN_REDIRECT_URL | Login redirect url | | | LOGIN_REDIRECT_URL_FAILURE | Login redirect url on failure | | | LOGOUT_REDIRECT_URL | Logout redirect url | | -| MALWARE_DETECTION_BACKEND | The malware detection backend use from the django-lasuite package | lasuite.malware_detection.backends.dummy.DummyBackend | -| MALWARE_DETECTION_PARAMETERS | A dict containing all the parameters to initiate the malware detection backend | {"callback_path": "core.malware_detection.malware_detection_callback",} | +| MALWARE_DETECTION_BACKEND | The malware detection backend use from the django-lasuite package. See [malware detection documentation](malware_detection.md) to configure the ICAP backend | lasuite.malware_detection.backends.dummy.DummyBackend | +| MALWARE_DETECTION_PARAMETERS | A dict containing all the parameters to initiate the malware detection backend. See [malware detection documentation](malware_detection.md) | {"callback_path": "core.malware_detection.malware_detection_callback",} | | MEDIA_BASE_URL | | | | MEDIA_AUTH_ORIGINAL_URL_HEADER | Parameter containing the original request URL, as seen at the media auth endpoint, in CGI/WSGI form (HTTP_HEADER_NAME_ALL_CAPS_WITH_UNDERSCORES) | HTTP_X_ORIGINAL_URL | | NO_WEBSOCKET_CACHE_TIMEOUT | Cache used to store current editor session key when only users without websocket are editing a document | 120 | diff --git a/documentation/malware_detection.md b/documentation/malware_detection.md new file mode 100644 index 0000000000..9624c3b1cb --- /dev/null +++ b/documentation/malware_detection.md @@ -0,0 +1,85 @@ +# Malware detection + +Docs can scan every uploaded file with an antivirus engine before making it +available to other users. Scanning is delegated to +[django-lasuite](https://github.com/suitenumerique/django-lasuite) which +provides several backend implementations. By default the `DummyBackend` is +used: every file is immediately considered safe. + +To actually scan files, configure the `ICAPBackend` to use an antivirus engine +exposed through the +[Internet Content Adaptation Protocol](https://datatracker.ietf.org/doc/html/rfc3507) +(ICAP, RFC 3507), for example [c-icap](https://github.com/c-icap/c-icap) with +ClamAV or SquidClamAV. + +> [!NOTE] +> The `ICAPBackend` is available in `django-lasuite` 0.0.28+. Upgrade the +> `django-lasuite` dependency in `src/backend/pyproject.toml` before enabling +> it. + +## How it works + +The backend runs in the Celery workers. For every uploaded file, a task sends +the file to the ICAP service using REQMOD and reads the verdict from the +response: + +- `204` No Modification, or `200` without an `X-Infection-Found` header: the + file is reported `safe`. +- An `X-Infection-Found` header containing a `Threat=` part: the file is + reported `unsafe`. +- Any other ICAP status or a `500` server error: the file is reported + `unknown`. Transient connection, timeout and protocol errors are retried by + the task. + +The scan result is handled by the docs callback +(`core.malware_detection.malware_detection_callback`) which fails closed: only +a `safe` result makes the file `READY`, everything else removes the +attachment, deletes the file and logs a `docs.security` entry. + +## Configuration + +The backend is configured with two environment variables: + +```bash +MALWARE_DETECTION_BACKEND=lasuite.malware_detection.backends.icap.ICAPBackend +MALWARE_DETECTION_PARAMETERS={"server_address":"icap.example.com","server_port":1344,"service":"avscan","callback_path":"core.malware_detection.malware_detection_callback"} +``` + +`MALWARE_DETECTION_PARAMETERS` is a JSON dict, see the +[django-lasuite documentation](https://github.com/suitenumerique/django-lasuite/blob/main/documentation/how-to-use-malware-detection-backend.md) +for all the supported parameters (`server_port`, `service`, `timeout`, +`max_processing_files`, TLS options, ...). + +### Helm deployment + +In a Kubernetes deployment, set these variables with the `backend.envVars` +value of the chart or with a ConfigMap referenced from `backend.envFrom`: + +```yaml +backend: + envVars: + MALWARE_DETECTION_BACKEND: lasuite.malware_detection.backends.icap.ICAPBackend + MALWARE_DETECTION_PARAMETERS: '{"server_address":"icap.example.com","server_port":1344,"service":"avscan","callback_path":"core.malware_detection.malware_detection_callback"}' +``` + +Both the backend web deployment and the Celery worker deployment receive these +variables. + +## ICAP server requirements + +The ICAP server is external to Docs: you must provide and operate it yourself +(c-icap + ClamAV containers, an existing appliance, ...). + +- The server must be reachable from the Celery workers on the configured port + (`1344` by default, or the `server_port` parameter). Keep it on a private + network or enable TLS with the backend `tls` parameters. +- The `service` parameter must match a REQMOD service on the server. With + c-icap, the `avscan` service maps to the `virus_scan` module backed by + `clamd`. +- The `clamd` `StreamMaxLength` setting must be high enough for the largest + files you allow, otherwise large uploads are rejected by the scanner. +- Make sure the scanning engine is monitored: c-icap can report a file as + clean when `clamd` is down. The docs callback only protects against + non-`safe` verdicts, so a scan engine outage must be detected out of band + (alerting on `unknown` statuses, on the callback logs or on the engine + itself). From 2c531b7608c5a17347f98b953501690ad4acc981 Mon Sep 17 00:00:00 2001 From: Sven Neuhaus Date: Tue, 4 Aug 2026 12:24:10 +0200 Subject: [PATCH 2/3] =?UTF-8?q?=F0=9F=93=9D(docs)=20document=20ICAP=20dev?= =?UTF-8?q?=20server=20in=20kubernetes=20installation=20guide?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a section to the Kubernetes installation guide showing how to provision a development ICAP antivirus server (c-icap with ClamAV) from the dev dependencies chart, with the backend environment variables to enable malware detection, and link it from the malware detection documentation. --- documentation/examples/helm/cicap.values.yaml | 7 ++++++ documentation/installation/kubernetes.md | 25 +++++++++++++++++++ documentation/malware_detection.md | 4 ++- 3 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 documentation/examples/helm/cicap.values.yaml diff --git a/documentation/examples/helm/cicap.values.yaml b/documentation/examples/helm/cicap.values.yaml new file mode 100644 index 0000000000..b5c3d206a1 --- /dev/null +++ b/documentation/examples/helm/cicap.values.yaml @@ -0,0 +1,7 @@ +cicap: + enabled: true + name: cicap + #serviceNameOverride: cicap + image: opencloudeu/clamav-icap:latest + port: 1344 + resources: {} diff --git a/documentation/installation/kubernetes.md b/documentation/installation/kubernetes.md index 41410e76b7..3c3e0b505a 100644 --- a/documentation/installation/kubernetes.md +++ b/documentation/installation/kubernetes.md @@ -212,6 +212,31 @@ redis-dev-backend-redis-68c9f66786-4dgxj 1/1 Running 0 4m21s ``` +### Find ICAP server connection values + +Docs can scan every uploaded file with an antivirus engine exposed through the +Internet Content Adaptation Protocol (ICAP), see +[malware detection](../malware_detection.md). For development, an ICAP server +(c-icap with ClamAV) is provided by the dev dependencies chart, so you can +install a local testing environment as follow: + +``` +$ helm install --repo https://suitenumerique.github.io/helm-dev-backend -f documentation/examples/helm/cicap.values.yaml cicap dev-backend +$ kubectl get pods +NAME READY STATUS RESTARTS AGE +cicap-dev-backend-cicap-xxxxx-xxxxx 1/1 Running 0 10s +``` + +Wait for the pod to be ready (ClamAV loads its virus database on startup, this +can take a minute). From here the important information you will need are: + +```yaml +MALWARE_DETECTION_BACKEND: lasuite.malware_detection.backends.icap.ICAPBackend +MALWARE_DETECTION_PARAMETERS: '{"server_address":"cicap-dev-backend-cicap","server_port":1344,"service":"avscan","callback_path":"core.malware_detection.malware_detection_callback"}' +``` + +You can find these values in **documentation/examples/helm/cicap.values.yaml** + ## Deployment Now you are ready to deploy Docs without AI. AI requires more dependencies (OpenAI API). To deploy Docs you need to provide all previous information to the helm chart. diff --git a/documentation/malware_detection.md b/documentation/malware_detection.md index 9624c3b1cb..e0953fcaf2 100644 --- a/documentation/malware_detection.md +++ b/documentation/malware_detection.md @@ -68,7 +68,9 @@ variables. ## ICAP server requirements The ICAP server is external to Docs: you must provide and operate it yourself -(c-icap + ClamAV containers, an existing appliance, ...). +(c-icap + ClamAV containers, an existing appliance, ...). For a Kubernetes +development environment, the dev dependencies chart provides one, see the +[Kubernetes installation guide](installation/kubernetes.md). - The server must be reachable from the Celery workers on the configured port (`1344` by default, or the `server_port` parameter). Keep it on a private From a940234f86bb9810bd1a4853ff72938fb9bfb1d5 Mon Sep 17 00:00:00 2001 From: Sven Neuhaus Date: Tue, 4 Aug 2026 12:41:21 +0200 Subject: [PATCH 3/3] =?UTF-8?q?=F0=9F=93=9D(docs)=20update=20cicap=20dev?= =?UTF-8?q?=20server=20example=20to=20the=20openDesk=20image?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- documentation/examples/helm/cicap.values.yaml | 10 +++++++++- documentation/installation/kubernetes.md | 6 ++++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/documentation/examples/helm/cicap.values.yaml b/documentation/examples/helm/cicap.values.yaml index b5c3d206a1..9885020ab7 100644 --- a/documentation/examples/helm/cicap.values.yaml +++ b/documentation/examples/helm/cicap.values.yaml @@ -2,6 +2,14 @@ cicap: enabled: true name: cicap #serviceNameOverride: cicap - image: opencloudeu/clamav-icap:latest + registry: registry.opencode.de + repository: bmi/opendesk/components/platform-development/images/clamav-icap + tag: "0.6.4" + clamd: + image: clamav/clamav:stable port: 1344 + # MaxObjectSize limits the size of files scanned by the ICAP service + maxObjectSize: 100M + # StreamMaxLength limits the size of files scanned by clamd + streamMaxLength: 100M resources: {} diff --git a/documentation/installation/kubernetes.md b/documentation/installation/kubernetes.md index 3c3e0b505a..68a45018c3 100644 --- a/documentation/installation/kubernetes.md +++ b/documentation/installation/kubernetes.md @@ -227,8 +227,10 @@ NAME READY STATUS RESTARTS AGE cicap-dev-backend-cicap-xxxxx-xxxxx 1/1 Running 0 10s ``` -Wait for the pod to be ready (ClamAV loads its virus database on startup, this -can take a minute). From here the important information you will need are: +Wait for the pod to be ready. On the first start the clamd sidecar +downloads the ClamAV virus database, which takes a few minutes and requires +access to `database.clamav.net`. From here the important information you will +need are: ```yaml MALWARE_DETECTION_BACKEND: lasuite.malware_detection.backends.icap.ICAPBackend