Поменял настройки актуальности clamav

This commit is contained in:
mi
2026-08-14 10:39:47 +03:00
parent 99605b1c77
commit 9eb8b2bc6e
11 changed files with 196 additions and 16 deletions
@@ -255,6 +255,9 @@ security-профили:
- работоспособность updater подтверждается состоянием `Up`, отсутствием - работоспособность updater подтверждается состоянием `Up`, отсутствием
restart loop и отдельным контролем возраста/signature version, а не restart loop и отдельным контролем возраста/signature version, а не
искусственным container healthcheck. искусственным container healthcheck.
- Архитектурная верхняя граница допустимого возраста signatures — `720` часов
(30 дней); активный seed-порог `max_signature_age_hours``240` часов
(10 дней), то есть строже предельного значения.
Смена digest ClamAV требует повторной проверки entrypoint, UID/GID, writable Смена digest ClamAV требует повторной проверки entrypoint, UID/GID, writable
paths, `clamd` health и фактического обновления signatures. Нельзя менять paths, `clamd` health и фактического обновления signatures. Нельзя менять
+1 -1
View File
@@ -199,7 +199,7 @@ worker.lease_seconds=90
## Service-owned настройки `message-safety` ## Service-owned настройки `message-safety`
Runtime policy хранится в версионированной `message_safety.config_versions`, а не в `.env` и не в `han_app.app_settings`. Сюда входят task lease/deadline/attempts, internal rate/pending limits, retention/cache TTL, URL/DNS pipeline limits, ClamAV policy timeout/signature age и enabled file MIME/size policy. Полный schema/seed/activation contract — module-05 §10.1 и §15. Runtime policy хранится в версионированной `message_safety.config_versions`, а не в `.env` и не в `han_app.app_settings`. Сюда входят task lease/deadline/attempts, internal rate/pending limits, retention/cache TTL, URL/DNS pipeline limits, ClamAV policy timeout/signature age и enabled file MIME/size policy. Для ClamAV schema допускает возраст сигнатур не более `720` часов (30 дней), seed `max_signature_age_hours` равен `240` часам (10 дней). Полный schema/seed/activation contract — module-05 §10.1 и §15.
`han_app.app_settings:chat.attachments.*` остаётся бизнес-настройкой api-backend. Message Safety не получает cross-schema read к `han_app`; файл допускается только при пересечении business allow-list, active safety policy и immutable detector manifest. Active policy может сузить manifest, но не добавить parser и не увеличить hard limit. `han_app.app_settings:chat.attachments.*` остаётся бизнес-настройкой api-backend. Message Safety не получает cross-schema read к `han_app`; файл допускается только при пересечении business allow-list, active safety policy и immutable detector manifest. Active policy может сузить manifest, но не добавить parser и не увеличить hard limit.
@@ -479,7 +479,9 @@ certificate/key проверяются preflight.
не превращала успешный one-shot exit в download loop/rate limit; не превращала успешный one-shot exit в download loop/rate limit;
- унаследованный healthcheck, проверяющий отсутствующий в updater-контейнере - унаследованный healthcheck, проверяющий отсутствующий в updater-контейнере
daemon, отключается; updater контролируется по `Up`, restart count, логам и daemon, отключается; updater контролируется по `Up`, restart count, логам и
возрасту сигнатур. возрасту сигнатур;
- security policy разрешает настроить порог возраста не выше `720` часов
(30 дней); production-like seed использует более строгие `240` часов.
Ошибки `read-only file system` устраняются точечным writable mount. Запрещено Ошибки `read-only file system` устраняются точечным writable mount. Запрещено
лечить их глобальным `read_only: false`, root, `privileged` или broad лечить их глобальным `read_only: false`, root, `privileged` или broad
+3 -4
View File
@@ -75,16 +75,15 @@ Host signoz-private
IdentityFile C:\Users\MI\.ssh\hansel-private IdentityFile C:\Users\MI\.ssh\hansel-private
ProxyJump han-jump ProxyJump han-jump
``` ```
Затем ssh signoz-ui -N
(устаревшее:
На рабочем компьютере откройте туннель до SigNoz через HAN_CHAT: На рабочем компьютере откройте туннель до SigNoz через HAN_CHAT:
```powershell ```powershell
ssh -L 8080:127.0.0.1:8080 signoz-private -N ssh -L 8080:127.0.0.1:8080 signoz-private -N
``` ```
Пока есть доступ снаружи, можно проще: )
ssh -i C:\Users\MI\.ssh\hansel-private -L 8080:127.0.0.1:8080 root@135.106.166.7 -N
Маршрут SSH: Windows → публичный адрес HAN_CHAT → `192.168.0.5:22`. Маршрут SSH: Windows → публичный адрес HAN_CHAT → `192.168.0.5:22`.
Назначение `127.0.0.1:8080` в `-L` открывает конечная VM SigNoz, а не Назначение `127.0.0.1:8080` в `-L` открывает конечная VM SigNoz, а не
HAN_CHAT. HAN_CHAT.
@@ -504,6 +504,36 @@ deployment/preflight.sh
новый монотонный номер и отдельные значения `--actor`/`--approved-by`; повторно новый монотонный номер и отдельные значения `--actor`/`--approved-by`; повторно
активировать старую версию нельзя. Alembic downgrade запрещён. активировать старую версию нельзя. Alembic downgrade запрещён.
При обновлении ClamAV policy образ Message Safety должен содержать согласованные
seed и schema: seed `max_signature_age_hours=240`, schema maximum `720`
(30 дней). После обновления immutable image digest создайте новую config
version; существующую active version не редактируйте и не активируйте повторно:
```sh
NEXT_VERSION='<СЛЕДУЮЩИЙ_МОНОТОННЫЙ_НОМЕР>'
/usr/local/sbin/han-vm2-compose --profile ops pull \
message-safety-migrate
/usr/local/sbin/han-vm2-compose --profile ops run --rm \
--entrypoint message-safety-config message-safety-migrate \
validate /app/app/artifacts/seed-config.yaml
/usr/local/sbin/han-vm2-compose --profile ops run --rm \
--entrypoint message-safety-config message-safety-migrate \
create /app/app/artifacts/seed-config.yaml \
--version "$NEXT_VERSION" --actor '<OPERATOR>'
/usr/local/sbin/han-vm2-compose --profile ops run --rm \
--entrypoint message-safety-config message-safety-migrate \
activate --version "$NEXT_VERSION" --approved-by '<APPROVER>'
/usr/local/sbin/han-vm2-compose up -d --no-deps --force-recreate \
message-safety-api message-safety-worker
/usr/local/sbin/han-vm2-compose ps \
message-safety-api message-safety-worker clamd freshclam
unset NEXT_VERSION
```
Старый image, schema которого ограничивает поле значением `168`, нельзя
оставлять после активации значения `240`: сначала обновите
`MESSAGE_SAFETY_IMAGE` на новый digest и проверьте `config --quiet`.
### Gate 4 — конфигурация nginx до запуска ### Gate 4 — конфигурация nginx до запуска
После выпуска public TLS в `/etc/letsencrypt` и материализации internal TLS После выпуска public TLS в `/etc/letsencrypt` и материализации internal TLS
@@ -819,3 +849,69 @@ han-message-safety-mode mock --text-free false --file-free false
задокументируйте минимальные writable пути для сигнатур/runtime и задокументируйте минимальные writable пути для сигнатур/runtime и
компенсируйте сетевыми и ресурсными лимитами. Egress к signature-CDN компенсируйте сетевыми и ресурсными лимитами. Egress к signature-CDN
получает только `freshclam`; `clamd` — нет. получает только `freshclam`; `clamd` — нет.
# Gate 9 завершает техническую приёмку VM2, но не означает production cutover сервисов.
Дальнейший порядок:
1. Проверить автозапуск:
```sh
systemctl is-enabled \
docker.service \
han-chat-vm2-docker-firewall.service \
han-secrets-vm2.service \
han-processing.service \
certbot.timer
systemctl is-active \
docker.service \
han-chat-vm2-docker-firewall.service \
han-processing.service \
certbot.timer
/usr/local/sbin/han-vm2-compose ps
```
2. Провести reboot-gate. Только после проверки отдельного входа `admin` и доступа к консоли Selectel:
```sh
systemctl reboot
```
После переподключения повторить команды выше и кратко Gate 68: HTTPS, firewall, private Safety API.
3. Зафиксировать итог релиза:
```sh
/usr/local/sbin/han-vm2-compose config --images
/usr/local/sbin/han-vm2-compose ps
systemctl list-timers certbot.timer
journalctl --no-pager -u han-processing.service -u han-secrets-vm2.service
```
Сохранить версии образов, дату приёмки и результаты gates без значений секретов.
4. Настроить эксплуатационный мониторинг:
- container unhealthy/restart/OOM;
- срок TLS;
- возраст ClamAV signatures;
- OTEL queue/export errors;
- disk/RAM;
- активный MOCK mode;
- недоступность private Safety API.
5. Далее — отдельный controlled cutover Message Safety на VM1: private URL, internal CA, service token, API integration, rollback rehearsal и функциональные проверки.
6. `bitrix-sync` пока оставить:
```dotenv
BITRIX_SYNC_ENABLED=false
BITRIX_SYNC_MODE=disabled
```
Public allow-list — только `deny all;`. Включать Bitrix можно лишь после выполнения gates `module-07`: поля портала, webhooks, migrations, grants, backfill/watermark и rollback rehearsal.
Таким образом, ближайший шаг сейчас — reboot-gate и фиксация приёмки VM2. Затем переход к интеграции VM1, а не немедленное включение Bitrix.
@@ -50,7 +50,7 @@
}, },
"clamav": { "clamav": {
"type": "object", "additionalProperties": false, "required": ["scan_timeout_sec", "max_signature_age_hours"], "type": "object", "additionalProperties": false, "required": ["scan_timeout_sec", "max_signature_age_hours"],
"properties": {"scan_timeout_sec": {"type": "integer", "minimum": 1, "maximum": 120}, "max_signature_age_hours": {"type": "integer", "minimum": 1, "maximum": 168}} "properties": {"scan_timeout_sec": {"type": "integer", "minimum": 1, "maximum": 120}, "max_signature_age_hours": {"type": "integer", "minimum": 1, "maximum": 720}}
}, },
"file_policy": { "file_policy": {
"type": "object", "additionalProperties": false, "required": ["enabled_mime_types", "max_size_bytes"], "type": "object", "additionalProperties": false, "required": ["enabled_mime_types", "max_size_bytes"],
@@ -23,7 +23,7 @@ link:
pipeline_timeout_sec: 2 pipeline_timeout_sec: 2
clamav: clamav:
scan_timeout_sec: 45 scan_timeout_sec: 45
max_signature_age_hours: 24 max_signature_age_hours: 240
file_policy: file_policy:
enabled_mime_types: enabled_mime_types:
[image/jpeg, image/png, image/webp, image/heic, image/heif, application/pdf] [image/jpeg, image/png, image/webp, image/heic, image/heif, application/pdf]
@@ -7,6 +7,7 @@ from pathlib import Path
import pytest import pytest
import yaml import yaml
from jsonschema import ValidationError
from app.config import validate_config from app.config import validate_config
from app.db import Base from app.db import Base
@@ -34,6 +35,16 @@ def test_config_cross_field_and_manifest_subset(artifacts: Path) -> None:
validate_config(bad, artifacts) validate_config(bad, artifacts)
def test_clamav_signature_age_policy_bounds(artifacts: Path) -> None:
document = seed(artifacts)
assert document["clamav"]["max_signature_age_hours"] == 240
document["clamav"]["max_signature_age_hours"] = 720
validate_config(document, artifacts)
document["clamav"]["max_signature_age_hours"] = 721
with pytest.raises(ValidationError):
validate_config(document, artifacts)
def test_normative_tables_are_in_service_schema() -> None: def test_normative_tables_are_in_service_schema() -> None:
expected = { expected = {
"safety_requests", "safety_requests",
+4 -2
View File
@@ -405,6 +405,8 @@ MVP использует отдельный `clamd` sidecar/service в private D
- порт не публикуется наружу; - порт не публикуется наружу;
- сигнатуры обновляет `freshclam`; - сигнатуры обновляет `freshclam`;
- readiness требует daemon PING и допустимый возраст signatures; - readiness требует daemon PING и допустимый возраст signatures;
- `max_signature_age_hours` допускается в диапазоне `1..720` часов
(не более 30 дней); seed policy использует `240` часов (10 дней);
- limits согласованы с максимальным размером файла; - limits согласованы с максимальным размером файла;
- контейнер non-root, read-only root filesystem где возможно, отдельный writable volume только для signatures/runtime; - контейнер non-root, read-only root filesystem где возможно, отдельный writable volume только для signatures/runtime;
- worker не передаёт в clamd object key, имя пользователя или иные PII. - worker не передаёт в clamd object key, имя пользователя или иные PII.
@@ -820,7 +822,7 @@ link:
pipeline_timeout_sec: 2 pipeline_timeout_sec: 2
clamav: clamav:
scan_timeout_sec: 45 scan_timeout_sec: 45
max_signature_age_hours: 24 max_signature_age_hours: 240
file_policy: file_policy:
enabled_mime_types: enabled_mime_types:
- image/jpeg - image/jpeg
@@ -832,7 +834,7 @@ file_policy:
max_size_bytes: 5242880 max_size_bytes: 5242880
``` ```
JSON Schema задаёт типы/ranges и cross-field constraints: `heartbeat_sec < lease_sec < execution_deadline_sec`, scan/pipeline timeout не больше execution deadline, TTL/retention положительны. `enabled_mime_types` — непустое уникальное подмножество detector manifest; `max_size_bytes` и последующие format overrides не превышают hard limits manifest. Referenced rules/detector artifacts обязаны быть доступны и пройти hash/signature verification до activation. JSON Schema задаёт типы/ranges и cross-field constraints: `heartbeat_sec < lease_sec < execution_deadline_sec`, scan/pipeline timeout не больше execution deadline, TTL/retention положительны, `max_signature_age_hours` не превышает `720` часов (30 дней). `enabled_mime_types` — непустое уникальное подмножество detector manifest; `max_size_bytes` и последующие format overrides не превышают hard limits manifest. Referenced rules/detector artifacts обязаны быть доступны и пройти hash/signature verification до activation.
### 15.2. Env и secrets Message Safety на ВМ2 ### 15.2. Env и secrets Message Safety на ВМ2
+1 -1
View File
@@ -1318,7 +1318,7 @@ Root устанавливает helper и `/etc/sudoers.d/deploy-message-safety-
Перед включением operator фиксирует incident/change ID и выбранные text/file policies; после команды проверяет `processing_mode=mock`, forced canaries, отсутствие `202`, metric/active alert и audit actor. Ранее принятые standard tasks сохраняют mode и завершаются без переклассификации; только новые requests используют MOCK. Автоматического timeout нет: mode действует без ограничения по времени до явного `standard`. Поэтому перед закрытием incident обязателен возврат в standard, проверка normal capabilities и text/link/EICAR canary. File, разрешённый в MOCK, маркируется `scan_status=bypassed`, а не `clean`. Перед включением operator фиксирует incident/change ID и выбранные text/file policies; после команды проверяет `processing_mode=mock`, forced canaries, отсутствие `202`, metric/active alert и audit actor. Ранее принятые standard tasks сохраняют mode и завершаются без переклассификации; только новые requests используют MOCK. Автоматического timeout нет: mode действует без ограничения по времени до явного `standard`. Поэтому перед закрытием incident обязателен возврат в standard, проверка normal capabilities и text/link/EICAR canary. File, разрешённый в MOCK, маркируется `scan_status=bypassed`, а не `clean`.
`freshclam` имеет controlled egress только к утверждённому signature CDN. Max signature age 24 ч; stale/failed update выключает только `files` и поднимает alert. Новая база проходит integrity/load/EICAR canary и atomic activate/reload; при regression возвращается последняя валидная база. `freshclam` имеет controlled egress только к утверждённому signature CDN. Seed `max_signature_age_hours` равен `240` ч (10 дней), а schema запрещает значения выше `720` ч (30 дней); stale/failed update выключает только `files` и поднимает alert. Новая база проходит integrity/load/EICAR canary и atomic activate/reload; при regression возвращается последняя валидная база.
Initial ВМ2: 4 vCPU/8 GiB/80 GiB, resource/PID limits и backpressure. Workers масштабируются первыми по queue depth, `clamd` — scan lanes. ВМ3/scale-out инициируются при sustained CPU/RAM >70%, queue age >30 с, провале module-05 performance gates, contention `bitrix-sync` или независимом release cadence. Initial ВМ2: 4 vCPU/8 GiB/80 GiB, resource/PID limits и backpressure. Workers масштабируются первыми по queue depth, `clamd` — scan lanes. ВМ3/scale-out инициируются при sustained CPU/RAM >70%, queue age >30 с, провале module-05 performance gates, contention `bitrix-sync` или независимом release cadence.
+71 -4
View File
@@ -47,11 +47,11 @@ docker push $REGISTRY/han-bitrix-sync:1.0.3
docker image inspect $REGISTRY/han-bitrix-sync:1.0.3 --format '{{index .RepoDigests 0}}' docker image inspect $REGISTRY/han-bitrix-sync:1.0.3 --format '{{index .RepoDigests 0}}'
cd /mnt/c/Users/MI/Documents/Assistent/HAN_chat_specification/codebase/services/message-safety cd /mnt/c/Users/MI/Documents/Assistent/HAN_chat_specification/codebase/services/message-safety
docker build -t han-message-safety:1.0.1 . docker build -t han-message-safety:1.0.2 .
REGISTRY=cr.selcloud.ru/han-images # ваш registry REGISTRY=cr.selcloud.ru/han-images # ваш registry
docker tag han-message-safety:1.0.1 $REGISTRY/han-message-safety:1.0.1 docker tag han-message-safety:1.0.2 $REGISTRY/han-message-safety:1.0.2
docker push $REGISTRY/han-message-safety:1.0.1 docker push $REGISTRY/han-message-safety:1.0.2
docker image inspect $REGISTRY/han-message-safety:1.0.1 --format '{{index .RepoDigests 0}}' docker image inspect $REGISTRY/han-message-safety:1.0.2 --format '{{index .RepoDigests 0}}'
@@ -891,3 +891,70 @@ docker run --rm --network han-processing_observability \
docker rmi ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen:latest docker rmi ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen:latest
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}" docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
# Проверка финальная
1. Проверить автозапуск:
```sh
systemctl is-enabled \
docker.service \
han-chat-vm2-docker-firewall.service \
han-secrets-vm2.service \
han-processing.service \
certbot.timer
(все enabled)
systemctl is-active \
docker.service \
han-chat-vm2-docker-firewall.service \
han-processing.service \
certbot.timer
(все active)
/usr/local/sbin/han-vm2-compose ps --format "table {{.Service}}\t{{.Status}}\t{{.Ports}}"
```
2. Провести reboot-gate. Только после проверки отдельного входа `admin` и доступа к консоли Selectel:
```sh
systemctl reboot
```
После переподключения повторить команды выше и кратко Gate 68: HTTPS, firewall, private Safety API.
3. Зафиксировать итог релиза:
```sh
/usr/local/sbin/han-vm2-compose config --images
/usr/local/sbin/han-vm2-compose ps
systemctl list-timers certbot.timer
journalctl --no-pager -u han-processing.service -u han-secrets-vm2.service
```
Сохранить версии образов, дату приёмки и результаты gates без значений секретов.
4. Настроить эксплуатационный мониторинг:
- container unhealthy/restart/OOM;
- срок TLS;
- возраст ClamAV signatures;
- OTEL queue/export errors;
- disk/RAM;
- активный MOCK mode;
- недоступность private Safety API.
5. Далее — отдельный controlled cutover Message Safety на VM1: private URL, internal CA, service token, API integration, rollback rehearsal и функциональные проверки.
6. `bitrix-sync` пока оставить:
```dotenv
BITRIX_SYNC_ENABLED=false
BITRIX_SYNC_MODE=disabled
```
Public allow-list — только `deny all;`. Включать Bitrix можно лишь после выполнения gates `module-07`: поля портала, webhooks, migrations, grants, backfill/watermark и rollback rehearsal.
Таким образом, ближайший шаг сейчас — reboot-gate и фиксация приёмки VM2. Затем переход к интеграции VM1, а не немедленное включение Bitrix.