# Ставим KES на ВМ1 Копируем на диск: scp "C:\Users\MI\Documents\job\HAN_new_life\HANapp\docs\kes 12.4\049-16-d-01.iso" "C:\Users\MI\Documents\job\HAN_new_life\HANapp\docs\kes 12.4\049-16-d-01.sig" prodVM1Deploy:/var/lib/han-deploy/incoming/ apt install rhash ISO='/var/lib/han-deploy/incoming/049-16-d-01.iso' command -v rhash rhash --gost "$ISO" ГОСТ совпал: rhash --gost дал ту же сумму, что на сайте вендора. ISO подлинный. 6a94b16afad211e8b9be5ec86f5379184f2b3a9e5869843fe763e2796e5ac1d3 sudo apt remove -y rhash sudo apt purge -y rhash Дальше только копирование нужного DEB и снимки. GUI, i386 и RPM не трогайте. Установку ещё не запускайте. ```sh ISO='/var/lib/han-deploy/incoming/049-16-d-01.iso' install -d -m 0700 -o root -g root \ /root/kesl-install /mnt/kesl-iso mount -o ro,loop "$ISO" /mnt/kesl-iso find /mnt/kesl-iso -type f \ \( -iname 'kesl_*_amd64.deb' -o -iname '*.deb' \) | sort DEB='/mnt/kesl-iso/kesl/kesl_12.4.0-1225_amd64.deb' ``` ```sh dpkg-deb -f "$DEB" Package Version Architecture sha256sum "$DEB" install -m 0600 -o root -g root \ "$DEB" /root/kesl-install/kesl.deb umount /mnt/kesl-iso rmdir /mnt/kesl-iso ``` `apt-get install /root/kesl-install/kesl.deb` cp -a /etc/docker/daemon.json /root/kesl-install/docker-daemon.before.json iptables-save > /root/kesl-install/iptables.before ufw status verbose > /root/kesl-install/ufw.before ls -l /root/kesl-install Ожидается: Package: kesl, Version: 12.4.0-1225, Architecture: amd64. Дальше установка с выключенной защитой, без кода активации и без сканирования. Перед командами явно примите EULA и политику конфиденциальности Kaspersky. KSN для этого пилота выключаем (USE_KSN=No). ```sh install -m 0600 -o root -g root /dev/null /root/kesl-install/autoinstall.ini cat >/root/kesl-install/autoinstall.ini <<'EOF' KSVLA_MODE=No ENDPOINT_AGENT_MODE=No EULA_AGREED=Yes PRIVACY_POLICY_AGREED=Yes USE_KSN=No GROUP_CLEAN=Yes LOCALE=ru_RU.UTF-8 INSTALL_LICENSE=None UPDATER_SOURCE=KLServers UPDATE_EXECUTE=No KERNEL_SRCS_INSTALL=No USE_GUI=No CONFIGURE_SELINUX=No DISABLE_PROTECTION=Yes INTERCEPTOR_MODE=UseFanotify ENABLE_TRACES_ON_FIRST_STARTUP=No EOF chmod 0600 /root/kesl-install/autoinstall.ini ``` ```sh /opt/kaspersky/kesl/bin/kesl-setup.pl \ --autoinstall=/root/kesl-install/autoinstall.ini echo "kesl-setup exit=$?" ``` ```sh systemctl --no-pager --no-legend status kesl || true kesl-control --app-info kesl-control --get-task-list free -h /usr/local/sbin/han-vm1-compose ps docker stats --no-stream --format \ 'table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.PIDs}}' ``` Ожидается: служба kesl запущена, File Threat Protection не в Started, контейнеры healthy, available RAM не уходит ниже ~512 МБ. kesl-control --get-app-settings \ --file /root/kesl-install/app-settings.before.ini install -m 0600 -o root -g root \ /var/opt/kaspersky/kesl/common/kesl.ini \ /root/kesl-install/kesl.ini.before systemctl stop kesl python3 - <<'PY' from pathlib import Path path = Path("/var/opt/kaspersky/kesl/common/kesl.ini") text = path.read_text(encoding="utf-8") if "[General]" not in text: raise SystemExit("no [General] section in kesl.ini") wanted = {"ScanMemoryLimit": "1024", "MaxMemory": "2048MB"} lines = text.splitlines() in_general = False seen = set() out = [] for line in lines: stripped = line.strip() if stripped.startswith("[") and stripped.endswith("]"): in_general = stripped == "[General]" if in_general and "=" in stripped and not stripped.startswith("#"): key = stripped.split("=", 1)[0].strip() if key in wanted: out.append(f"{key}={wanted[key]}") seen.add(key) continue out.append(line) if seen != set(wanted): rebuilt = [] inserted = False in_general = False for line in out: stripped = line.strip() rebuilt.append(line) if stripped.startswith("[") and stripped.endswith("]"): if in_general and not inserted: for key, value in wanted.items(): if key not in seen: rebuilt.insert(-1, f"{key}={value}") inserted = True in_general = stripped == "[General]" if in_general and not inserted: for key, value in wanted.items(): if key not in seen: rebuilt.append(f"{key}={value}") out = rebuilt path.write_text("\n".join(out) + "\n", encoding="utf-8") print("updated ScanMemoryLimit/MaxMemory") PY grep -E '^(ScanMemoryLimit|MaxMemory)=' /var/opt/kaspersky/kesl/common/kesl.ini systemctl start kesl systemctl is-active kesl kesl-control --set-app-settings UseOnDemandCPULimit=Yes OnDemandCPULimit=15 free -h swapon --show set +o history unset HISTFILE read -rsp 'KESL activation code: ' KESL_CODE; echo kesl-control --add-active-key "$KESL_CODE" unset KESL_CODE set -o history kesl-control -L --query kesl-control --app-info kesl-control --get-task-state 1 /usr/local/sbin/han-vm1-compose ps --format "table {{.Service}}\t{{.Status}}\t{{.Ports}}" free -h kesl-control --start-task 6 -W kesl-control --get-task-state 6 kesl-control --app-info free -h /usr/local/sbin/han-vm1-compose ps --format "table {{.Service}}\t{{.Status}}\t{{.Ports}}" Ожидается ровно одна строка MaxMemory=1024MB, Update завершилась, Application databases loaded: Yes, лицензия не Inconsistent update. Если available RAM < 512 МБ или контейнер unhealthy — остановите Update (kesl-control --stop-task 6) и пришлите вывод. GUID ключа в следующий лог лучше не включать. Базы загружены, ключ действителен, File Threat Protection доступна и остановлена, сервисы HAN healthy. АВЗ.2 по сути закрыта после почасового Update. Вендор просит перезапуск KESL после обновления модуля. Container Monitoring этой лицензией не покрыт — контейнерный мониторинг пропускаем, компенсируем host File Threat Protection и точечным scan-file. Сеть/firewall KESL не включаем. Сначала restart, расписание Update и исключения hot-data. systemctl restart kesl systemctl is-active kesl kesl-control --get-task-state 1 kesl-control --get-task-state 6 kesl-control --app-info --json START="$(LC_ALL=C date +'%Y/%b/%d %H:%M:%S;1')" kesl-control --set-schedule 6 RuleType=Hourly \ "StartTime=${START}" RunMissedStartRules=No RandomInterval=0 kesl-control --get-schedule 6 kesl-control --get-settings 1 \ --file /root/kesl-install/file-threat.before.ini kesl-control --set-settings 1 \ --add-exclusion /var/lib/docker/volumes/han-chat_redis-data/_data kesl-control --set-settings 1 \ --add-exclusion /var/lib/docker/volumes/han-chat_otel-queue/_data kesl-control --set-settings 1 \ --add-exclusion /var/lib/docker/volumes/han-chat_nginx-cache/_data kesl-control --set-settings 1 ScanArchived=No kesl-control --get-settings 1 free -h /usr/local/sbin/han-vm1-compose ps --format "table {{.Service}}\t{{.Status}}\t{{.Ports}}" Ожидается: Post-update actions: No action required, базы загружены, FTP Stopped, RuleType=Hourly, в настройках задачи 1 три исключения и ScanArchived=No. Пришлите вывод. Исключения приняты, сервисы healthy. Почасовой Update не применился: 12.4 требует StartTime с интервалом, иначе остаётся Manual. Сначала расписание, затем узкий scan-file без изменения файлов. File Threat Protection пока не запускаем. kesl-control --get-schedule 6 kesl-control --scan-file /opt/han-chat/current/backend --action Inform kesl-control --get-task-state 1 free -h /usr/local/sbin/han-vm1-compose ps --format "table {{.Service}}\t{{.Status}}\t{{.Ports}}" Ожидается: RuleType=Hourly и StartTime=...;1, scan-file без ошибок, FTP Stopped, контейнеры healthy. Если scan-file сильно поднимет swap или появятся unhealthy — остановите его и пришлите вывод. Расписание Update почасовое, узкий scan-file прошёл чисто (421 объект, 0 угроз), swap не вырос, сервисы healthy. Дальше File Threat Protection в режиме Notify: обнаружения пишутся в журнал, файлы не блокируются. Сеть/firewall KESL и Behavior Detection не включаем. Container scan этой лицензией недоступен — пропускаем. kesl-control --set-app-settings InterceptorProtectionMode=Notify kesl-control --start-task 1 kesl-control --get-task-state 1 kesl-control --app-info --json free -h docker stats --no-stream --format \ 'table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.PIDs}}' /usr/local/sbin/han-vm1-compose ps --format "table {{.Service}}\t{{.Status}}\t{{.Ports}}" iptables -S HAN-CHAT-DOCKER File Threat Protection уже Running, сервисы healthy, firewall не тронут. InterceptorProtectionMode на fanotify в 12.4 не существует — штатный режим и есть блокировка на время проверки. Для АВЗ.1 это достаточный путь; откат: kesl-control --stop-task 1. Дальше контролируемый EICAR. Образец в чат после теста не копируйте. ```sh install -d -m 0700 -o root -g root /root/kesl-eicar-test printf '%s' 'X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*' \ > /root/kesl-eicar-test/eicar.com ls -l /root/kesl-eicar-test kesl-control --scan-file /root/kesl-eicar-test --action DisinfectDeleteIfNotPossible kesl-control -E --query -n 30 --reverse ls -la /root/kesl-eicar-test rm -rf /root/kesl-eicar-test kesl-control --get-task-state 1 kesl-control --get-schedule 6 free -h /usr/local/sbin/han-vm1-compose ps --format "table {{.Service}}\t{{.Status}}\t{{.Ports}}" ``` # Ставим KES на ВМ2 Готовим новый образ Message Safety MESSAGE_SAFETY_IMAGE=cr.selcloud.ru/han-images/han-message-safety@sha256:225808bd111be03112ab6693b5a10e3d97ae8cdbe5370a5d0aa5a8e2278a3044 Копируем проект ```powershell $Release = "1.0.1" tar --exclude=services/.env ` --exclude='services/**/__pycache__' ` --exclude='services/**/.pytest_cache' ` --exclude='services/**/.ruff_cache' ` -czf "vm2-services-$Release.tar.gz" -C C:\Users\MI\Documents\Assistent\HAN_chat_specification\VM2_services\codebase services Get-FileHash "vm2-services-$Release.tar.gz" -Algorithm SHA256 scp "vm2-services-$Release.tar.gz" prodVM2Deploy:/var/lib/han-deploy/incoming/ scp "vm2-services-$Release.tar.gz" devVM2Deploy:/var/lib/han-deploy/incoming/ ``` Проверить размер диска RELEASE='1.0.1' EXPECTED_SHA256='1030DE6A50BA945FAEC5A45E1D42C47A01808E737AA1955871F3D2ED089F8879' ARCHIVE="/var/lib/han-deploy/incoming/vm2-services-${RELEASE}.tar.gz" printf '%s %s\n' "$EXPECTED_SHA256" "$ARCHIVE" | sha256sum --check - tar -tvzf "$ARCHIVE" if tar -tzf "$ARCHIVE" | grep -Eq '(^/|(^|/)\.\.(/|$)|^services/\.env$)'; then echo 'ОШИБКА: архив содержит небезопасный путь или .env' >&2 exit 1 fi if tar -tzf "$ARCHIVE" | grep -Ev '^services(/|$)' | grep -q .; then echo 'ОШИБКА: архив содержит файлы вне каталога services' >&2 exit 1 fi if tar -tvzf "$ARCHIVE" | awk '$1 ~ /^[lh]/ { found=1 } END { exit !found }'; then echo 'ОШИБКА: архив содержит symlink или hardlink' >&2 exit 1 fi install -d -m 0755 -o root -g root /opt/han-chat/services STAGING="$(mktemp -d /opt/han-chat/.vm2-release.XXXXXX)" tar --extract --gzip --file "$ARCHIVE" \ --directory "$STAGING" --no-same-owner --no-same-permissions test -f "$STAGING/services/docker-compose.yml" rsync -a --delete --exclude=.env \ --chown=root:root --chmod=D755,F644 \ "$STAGING/services/" /opt/han-chat/services/ rm -rf -- "$STAGING" chmod 0755 /opt/han-chat/services/deployment/preflight.sh cd /opt/han-chat/services/ sudo apt update sudo apt install -y dos2unix find . -type f \( \ -name '*.sh' -o -name '*.py' -o -name '*.service' -o -name '*.sudoers' -o -name 'han-compose' -o -name 'han-secrets' -o -name 'han-message-safety-mode' -o -name '*.yml' -o -name '*.conf*' -o -name 'preflight.sh' -o -name '*.yaml' -o -name '*.md' -o -name '*.toml' -o -name 'Dockerfile' -o -name '*.acl*' \ \) -exec dos2unix {} + find -type f -exec file {} \; | grep -i 'CRLF' sudo apt remove -y dos2unix sudo apt purge -y dos2unix Копируем образ на ВМ: scp "C:\Users\MI\Documents\job\HAN_new_life\HANapp\docs\kes 12.4\049-16-d-01.iso" "C:\Users\MI\Documents\job\HAN_new_life\HANapp\docs\kes 12.4\049-16-d-01.sig" prodVM2Deploy:/var/lib/han-deploy/incoming/ Проверка файла: apt install -y rhash ISO='/var/lib/han-deploy/incoming/049-16-d-01.iso' command -v rhash rhash --gost "$ISO" Должно быть 6a94b16afad211e8b9be5ec86f5379184f2b3a9e5869843fe763e2796e5ac1d3 sudo apt remove -y rhash sudo apt purge -y rhash Если совпадёт, извлеките DEB: ```sh install -d -m 0700 -o root -g root \ /root/kesl-install /mnt/kesl-iso mount -o ro,loop "$ISO" /mnt/kesl-iso find /mnt/kesl-iso -type f \ \( -iname 'kesl_*_amd64.deb' -o -iname '*.deb' \) | sort ``` Затем: ```sh DEB='/mnt/kesl-iso/kesl/kesl_12.4.0-1225_amd64.deb' dpkg-deb -f "$DEB" Package Version Architecture sha256sum "$DEB" install -m 0600 -o root -g root \ "$DEB" /root/kesl-install/kesl.deb umount /mnt/kesl-iso rmdir /mnt/kesl-iso ``` Ожидаемые свойства: kesl, версия 12.4.x, архитектура amd64. После этого можно выполнять: apt-get install /root/kesl-install/kesl.deb Далее ```sh install -m 0600 -o root -g root /dev/null /root/kesl-install/autoinstall.ini cat > /root/kesl-install/autoinstall.ini <<'EOF' KSVLA_MODE=No ENDPOINT_AGENT_MODE=No EULA_AGREED=Yes PRIVACY_POLICY_AGREED=Yes USE_KSN=No GROUP_CLEAN=Yes LOCALE=ru_RU.UTF-8 INSTALL_LICENSE=None UPDATER_SOURCE=KLServers UPDATE_EXECUTE=No KERNEL_SRCS_INSTALL=No USE_GUI=No CONFIGURE_SELINUX=No DISABLE_PROTECTION=Yes INTERCEPTOR_MODE=UseFanotify ENABLE_TRACES_ON_FIRST_STARTUP=No EOF chmod 0600 /root/kesl-install/autoinstall.ini stat -c '%U:%G:%a %n' /root/kesl-install/autoinstall.ini /opt/kaspersky/kesl/bin/kesl-setup.pl \ --autoinstall=/root/kesl-install/autoinstall.ini echo "kesl-setup exit=$?" systemctl --no-pager status kesl kesl-control --app-info --json kesl-control --get-task-list ``` ```sh set +o history unset HISTFILE read -rsp 'KESL activation code: ' KESL_CODE; echo kesl-control --add-active-key "$KESL_CODE" unset KESL_CODE set -o history kesl-control -L --query ``` Не стартуйте Update (task 6) и File Threat Protection (task 1), пока не срежете дефолты памяти. На 8 ГБ `ScanMemoryLimit=8192` и `MaxMemory=auto` (до 50% RAM, минимум 2 ГБ) слишком много, тем более пока жив ClamAV. Ключ из `-L --query` в evidence/репозиторий не копируйте — достаточно статуса `Active subscription`. Дальше — секция 3 runbook. Кандидат для ВМ2: `ScanMemoryLimit=1024`, `MaxMemory=2048MB`. CPU: `OnDemandCPULimit=15`, как на пилоте ВМ1, пока ClamAV не снят. **1. Снимок текущих настроек** ```sh kesl-control --get-app-settings \ --file /root/kesl-install/app-settings.before.ini install -m 0600 -o root -g root \ /var/opt/kaspersky/kesl/common/kesl.ini \ /root/kesl-install/kesl.ini.before awk '/^\[General\]/,/^\[/' /var/opt/kaspersky/kesl/common/kesl.ini | head -n 40 ``` **2. Лимит CPU (KESL ещё работает)** ```sh kesl-control --set-app-settings \ UseOnDemandCPULimit=Yes OnDemandCPULimit=15 ``` **3. Память — только при остановленном KESL, файл целиком не подменять** Сначала посмотрите `[General]` из шага 1. Если там уже есть `ScanMemoryLimit` и `MaxMemory`: ```sh systemctl stop kesl systemctl is-active kesl grep -nE '^(ScanMemoryLimit|MaxMemory)=' /var/opt/kaspersky/kesl/common/kesl.ini sed -i \ -e 's/^ScanMemoryLimit=8192$/ScanMemoryLimit=1024/' \ -e 's/^MaxMemory=auto$/MaxMemory=2048MB/' \ /var/opt/kaspersky/kesl/common/kesl.ini grep -nE '^(ScanMemoryLimit|MaxMemory)=' /var/opt/kaspersky/kesl/common/kesl.ini systemctl start kesl systemctl is-active kesl kesl-control --get-app-settings | grep -iE 'UseOnDemandCPULimit|OnDemandCPULimit' ``` **Ожидаемо после рестарта** - `kesl.service` = `active` - в settings: `ScanMemoryLimit=1024`, `MaxMemory=2048MB`, `UseOnDemandCPULimit=Yes`, `OnDemandCPULimit=15` - task 1 и 6 по-прежнему `Stopped` - базы всё ещё не загружены После этого — загрузка баз: `kesl-control --start-task 6 -W` ```sh kesl-control --get-task-state 6 kesl-control --app-info --json START="$(LC_ALL=C date +'%Y/%b/%d %H:%M:%S;1')" kesl-control --set-schedule 6 RuleType=Hourly \ "StartTime=${START}" RunMissedStartRules=No RandomInterval=0 kesl-control --get-schedule 6 ``` Обязательный следующий шаг — рестарт KESL. В --app-info есть: Post-update actions: The application module has been updated. Restart the application. Task 1 до рестарта не запускайте. ```sh systemctl restart kesl systemctl is-active kesl kesl-control --app-info --json grep -nE '^(ScanMemoryLimit|MaxMemory)=' /var/opt/kaspersky/kesl/common/kesl.ini kesl-control --get-app-settings | grep -iE 'UseOnDemandCPULimit|OnDemandCPULimit' kesl-control --get-schedule 6 kesl-control --get-task-state 1 kesl-control --get-task-state 6 ``` ```sh kesl-control --get-settings 1 \ --file /root/kesl-install/file-threat.before.ini kesl-control --set-settings 1 \ --add-exclusion /var/lib/docker/volumes/han-processing_redis-safety-data/_data kesl-control --set-settings 1 \ --add-exclusion /var/lib/docker/volumes/han-processing_otel-queue/_data kesl-control --start-task 1 kesl-control --set-settings 1 \ ActionOnThreat=DisinfectDeleteIfNotPossible ScanArchived=No kesl-control --get-task-state 1 kesl-control --get-settings 1 ``` Сразу снимите coexistence, пока OAS прогревается: ```sh date -Is free -h swapon --show vmstat 1 5 systemctl is-active kesl docker han-processing.service kesl-control --app-info --json | grep -E 'File Threat|databases loaded|license' /usr/local/sbin/han-vm2-compose ps docker stats --no-stream ``` Установка брокера ```sh set -Eeuo pipefail BASE=/opt/han-chat/services/deployment/kesl for file in \ kesl_scan_broker.py \ han-kesl-scan-broker.service \ han-kesl-scan-broker.socket \ han-kesl-scan-broker.tmpfiles.conf do test -f "$BASE/$file" test ! -L "$BASE/$file" done if getent group han-message-safety >/dev/null; then test "$(getent group han-message-safety | cut -d: -f3)" = 10001 else ! getent group 10001 >/dev/null groupadd --system --gid 10001 han-message-safety fi install -d -m 0755 -o root -g root /usr/local/libexec install -m 0750 -o root -g root \ "$BASE/kesl_scan_broker.py" \ /usr/local/libexec/han-kesl-scan-broker install -m 0644 -o root -g root \ "$BASE/han-kesl-scan-broker.service" \ /etc/systemd/system/han-kesl-scan-broker.service install -m 0644 -o root -g root \ "$BASE/han-kesl-scan-broker.socket" \ /etc/systemd/system/han-kesl-scan-broker.socket install -m 0644 -o root -g root \ "$BASE/han-kesl-scan-broker.tmpfiles.conf" \ /etc/tmpfiles.d/han-kesl-scan-broker.conf systemd-tmpfiles --create \ /etc/tmpfiles.d/han-kesl-scan-broker.conf systemctl daemon-reload systemd-analyze verify \ /etc/systemd/system/han-kesl-scan-broker.service \ /etc/systemd/system/han-kesl-scan-broker.socket ``` Если systemd-analyze verify завершился без ошибок, запуск: ```sh systemctl enable --now han-kesl-scan-broker.socket systemctl is-active kesl han-kesl-scan-broker.socket stat -c '%U:%G:%a %n' /run/han-kesl /run/han-kesl/scan.sock /usr/local/libexec/han-kesl-scan-broker --probe ``` Ожидаемые права: /run/han-kesl root:han-message-safety:750 /run/han-kesl/scan.sock root:han-message-safety:660 Создаём папку: ```sh install -d -m 0700 -o root -g han-message-safety \ /var/lib/han-kesl-scan \ /var/lib/han-kesl-scan/staging ``` Проверка и запуск socket ``` systemd-analyze verify \ /etc/systemd/system/han-kesl-scan-broker.service \ /etc/systemd/system/han-kesl-scan-broker.socket systemctl enable --now han-kesl-scan-broker.socket systemctl is-active kesl han-kesl-scan-broker.socket stat -c '%U:%G:%a %n' /run/han-kesl /run/han-kesl/scan.sock /usr/local/libexec/han-kesl-scan-broker --probe journalctl --since '-10 min' --no-pager -u han-kesl-scan-broker.service ``` Обновить .env editor /opt/han-chat/services/.env MESSAGE_SAFETY_IMAGE=cr.selcloud.ru/han-images/han-message-safety@sha256:225808bd111be03112ab6693b5a10e3d97ae8cdbe5370a5d0aa5a8e2278a3044 Удалить CLAMAV_IMAGE MESSAGE_SAFETY_CLAMAV_HOST MESSAGE_SAFETY_CLAMAV_PORT Добавить MESSAGE_SAFETY_ANTIVIRUS_SOCKET=/run/han-kesl/scan.sock Активировать config schema 2 NEXT_VERSION='2' /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 '' /usr/local/sbin/han-vm2-compose --profile ops run --rm \ --entrypoint message-safety-config message-safety-migrate \ activate --version "$NEXT_VERSION" --approved-by '' Вернуть затертые IP editor /opt/han-chat/services/nginx/allowlists/bitrix-webhook-allowlist.conf allow 195.208.184.200; allow 89.208.230.2; allow 195.208.184.34; allow 89.208.228.219; deny all; editor /opt/han-chat/services/nginx/allowlists/private-caller-allowlist.conf allow 10.0.0.11; deny all; Переключить только Message Safety ```sh cd /opt/han-chat/services/ deployment/preflight.sh /usr/local/sbin/han-vm2-compose config --quiet /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 /usr/local/sbin/han-vm2-compose ps --format "table {{.Service}}\t{{.Status}}\t{{.Ports}}" ``` HUP NGINX ```sh /usr/local/sbin/han-vm2-compose exec -T nginx nginx -t -c /etc/nginx/nginx.conf NGINX_ID=$(/usr/local/sbin/han-vm2-compose ps --status running --quiet nginx) docker kill --signal HUP "$NGINX_ID" /usr/local/sbin/han-vm2-compose exec -T nginx getent hosts message-safety-api /usr/local/sbin/han-vm2-compose exec -T message-safety-api \ python -c 'import json,urllib.request; print(json.dumps(json.load(urllib.request.urlopen("http://127.0.0.1:8080/health/ready")), indent=2))' ``` Тестирование: Секция 7 — не один скрипт, а **пять независимых gates**. EICAR только после письменного Security; образец не класть в git, chat и evidence — брать с [eicar.org](https://www.eicar.org). Пункты 4–5 и cutover **нельзя** закрыть текущим образом Message Safety (он ещё на ClamAV): сначала 1–3 на хосте. Все команды на `devhanservice` — под **root**. `han-processing.service` не restart/stop. --- ### 0. Перед тестами ```sh kesl-control --get-task-state 1 kesl-control --app-info --json | grep -E 'File Threat|databases loaded|license' systemctl is-active kesl han-kesl-scan-broker.socket test -S /run/han-kesl/scan.sock stat -c '%U:%G:%a %n' /run/han-kesl/scan.sock /usr/local/libexec/han-kesl-scan-broker --probe ``` Нужно: task 1 `Started`, базы `Yes`, socket `root:han-message-safety:660`, probe `"status":"ready"`. Если sock нет — `systemctl restart han-kesl-scan-broker.socket`, не `.service`. --- ### 1. Host OAS (АВЗ.1) Каталог **не** в docker volume, `/tmp`, release, staging: ```sh install -d -m 0700 -o root -g root /root/kesl-eicar-test date -Is kesl-control --get-task-state 1 ``` Положите официальный `eicar.com` в `/root/kesl-eicar-test/eicar.com`: curl -fsSL -o /root/kesl-eicar-test/eicar.com \ 'https://secure.eicar.org/eicar.com' chmod 0600 /root/kesl-eicar-test/eicar.com ```sh cat /root/kesl-eicar-test/eicar.com >/dev/null ls -l /root/kesl-eicar-test kesl-control -E --query -n 20 --reverse ``` Ожидаемо: объект удалён/в Backup, в журнале `EICAR-Test-File` / `ThreatDetected`. Если OAS на запись молчит (как на пилоте ВМ1) — явный ODS: ```sh kesl-control --scan-file /root/kesl-eicar-test/eicar.com kesl-control -E --query -n 20 --reverse ``` Уборка (не весь Backup): ```sh kesl-control -B --query --reverse -n 20 kesl-control -B --mass-remove --query "DetectName == 'EICAR-Test-File'" rm -rf /root/kesl-eicar-test ``` --- ### 2. Broker: clean / EICAR / KESL down install -d -m 0700 -o root -g root /root/kesl-install cat > /root/kesl-install/broker-client.py <<'PY' #!/usr/bin/env python3 import asyncio, json, struct, sys SOCK = "/run/han-kesl/scan.sock" async def call(op: str, body: bytes = b"") -> dict: r, w = await asyncio.open_unix_connection(SOCK) header = {"op": op} if op == "scan": header["size"] = len(body) raw = json.dumps(header, separators=(",", ":")).encode() w.write(struct.pack(">I", len(raw)) + raw + body) await w.drain() n = struct.unpack(">I", await r.readexactly(4))[0] resp = json.loads(await r.readexactly(n)) w.close() await w.wait_closed() return resp async def main() -> None: op = sys.argv[1] body = sys.stdin.buffer.read() if op == "scan" else b"" print(json.dumps(await call(op, body), ensure_ascii=True, sort_keys=True)) if __name__ == "__main__": if sys.argv[1:] not in (["status"], ["scan"]): raise SystemExit("usage: broker-client.py status|scan") asyncio.run(main()) PY chmod 0700 /root/kesl-install/broker-client.py Проверка позитивного решения python3 /root/kesl-install/broker-client.py status printf 'HAN kesl broker clean canary\n' | python3 /root/kesl-install/broker-client.py scan Проверка негативного решения curl -fsSL 'https://secure.eicar.org/eicar.com' | python3 /root/kesl-install/broker-client.py scan Проверка недоступного сканнера systemctl stop kesl systemctl is-active kesl || true python3 /root/kesl-install/broker-client.py status printf 'error canary\n' | python3 /root/kesl-install/broker-client.py scan Оба запроса должны вернуть scanner_unavailable. Затем восстановить: systemctl start kesl systemctl is-active kesl kesl-control --start-task 1 kesl-control --get-task-state 1 systemctl restart han-kesl-scan-broker.socket /usr/local/libexec/han-kesl-scan-broker --probe --- ### 3. 5 слотов и ~2 файла/с На 4 ГБ DEV это нагрузка: смотрите `free -h` / `vmstat`. Откат OAS: `kesl-control --stop-task 1`. ```sh python3 - <<'PY' import asyncio, json, struct, time, statistics SOCK = "/run/han-kesl/scan.sock" BODY = b"HAN kesl throughput canary\n" async def scan(): r, w = await asyncio.open_unix_connection(SOCK) h = json.dumps({"op": "scan", "size": len(BODY)}, separators=(",", ":")).encode() t0 = time.perf_counter() w.write(struct.pack(">I", len(h)) + h + BODY) await w.drain() n = struct.unpack(">I", await r.readexactly(4))[0] resp = json.loads(await r.readexactly(n)) dt = time.perf_counter() - t0 w.close(); await w.wait_closed() return dt, resp async def main(): burst = await asyncio.gather(*[scan() for _ in range(5)]) print("burst5", [(round(dt, 3), r.get("verdict") or r.get("error")) for dt, r in burst]) times = [] for _ in range(20): dt, r = await scan() assert r.get("verdict") == "clean", r times.append(dt) await asyncio.sleep(0.5) # ~2 file/s times.sort() p95 = times[int(0.95 * (len(times) - 1))] print(f"n={len(times)} p50={times[len(times)//2]:.3f}s p95={p95:.3f}s max={times[-1]:.3f}s") asyncio.run(main()) PY ``` Gate: все `clean`, p95 ≤ 60 с. Queue age p95 ≤ 5 с — из метрик Safety (после пункта 4), не из этого скрипта. --- ### 4. Message Safety 200 / 403 / 503 Сейчас контейнеры ещё на ClamAV. Пункт 4 — **после** recreate `message-safety-api`/`worker` на образ с bind `/run/han-kesl` (digest из окна 1.0.1). Clamd пока можно не снимать. Проверка, что API видит KESL: ```sh /usr/local/sbin/han-vm2-compose exec -T message-safety-api \ python -c 'import json,urllib.request; print(json.dumps(json.load(urllib.request.urlopen("http://127.0.0.1:8080/health/ready")), indent=2))' ``` `antivirus=ok`, `files=ready` (не mock). - **clean `200`** — text canary с ВМ1, как в `RUNBOOK.ru.md` (`/internal/safety/v2/messages/check`, `content_kind=text`). - **malware `403` + sticky** — file через **реальный** versioned object в quarantine S3 (выдуманный key нельзя; EICAR в bucket только если Security разрешил). Повтор того же `message_id`+body — тот же `403`. - **scanner error `503`** — `systemctl stop kesl`, file-check, ожидание `503` / files unavailable; затем start kesl + restart **socket** + `--start-task 1`. Пока worker ходит в clamd, это **не** gate KESL. --- ### 5. Smoke, firewall, 24 ч ```sh iptables -S HAN-CHAT-VM2 ufw status verbose /usr/local/sbin/han-vm2-compose ps free -h swapon --show kesl-control --get-task-state 6 kesl-control --get-schedule 6 ``` С ВМ1: `openssl s_client` + `curl` `/internal/safety/status` из `RUNBOOK.ru.md`. Сравните `HAN-CHAT-VM2` с baseline. 24 ч: без OOM/restart/5xx, hourly Update сам зелёный. curl --fail --silent --show-error \ --cacert /etc/han/ca/vm2-internal-ca.crt \ https://prodhanservice.ihan.ru:8443/internal/safety/status https://processing.internal:8443/internal/safety/status --- ### Cutover (только после 1–5) Новая **монотонная** config version (не реактивировать старую): ```sh cd /opt/han-chat/services/ NEXT_VERSION='3' /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 '' /usr/local/sbin/han-vm2-compose --profile ops run --rm \ --entrypoint message-safety-config message-safety-migrate \ activate --version "$NEXT_VERSION" --approved-by '' /usr/local/sbin/han-vm2-compose up -d --no-deps --force-recreate \ message-safety-api message-safety-worker ``` В `.env` / compose не должно остаться `clamd`/`freshclam`. Затем: ```sh /usr/local/sbin/han-vm2-compose up -d --remove-orphans /usr/local/sbin/han-vm2-compose ps docker ps -a --format '{{.Names}}' | grep -E 'clamd|freshclam' || echo 'no clamd/freshclam' docker volume ls | grep -i clam || true ``` /usr/local/sbin/han-vm2-compose exec -T nginx nginx -t -c /etc/nginx/nginx.conf NGINX_ID=$(/usr/local/sbin/han-vm2-compose ps --status running --quiet nginx) docker kill --signal HUP "$NGINX_ID" /usr/local/sbin/han-vm2-compose exec -T nginx getent hosts message-safety-api /usr/local/sbin/han-vm2-compose exec -T message-safety-api \ python -c 'import json,urllib.request; print(json.dumps(json.load(urllib.request.urlopen("http://127.0.0.1:8080/health/ready")), indent=2))' Старый signature-CDN egress закрывается отдельно в firewall/SG — не через `compose down -v`. На этом DEV-хосте (3.8 ГиБ, swap 391 МиБ) пункт 3 и file-EICAR через Safety лучше не совмещать с чем-то ещё. Если нужно, могу выписать только gate 1+2 компактным блоком под копирование в `260907_1.md`. # Ставим KESL на ВМ1 Копируем на диск: scp -i C:\Users\MI\.ssh\prodHan "C:\Users\MI\Documents\job\HAN_new_life\HANapp\docs\kes 12.4\049-16-d-01.iso" "C:\Users\MI\Documents\job\HAN_new_life\HANapp\docs\kes 12.4\049-16-d-01.sig" root@185.31.164.97:/tmp/ apt install rhash ISO='/tmp/049-16-d-01.iso' command -v rhash rhash --gost "$ISO" ГОСТ совпал: rhash --gost дал ту же сумму, что на сайте вендора. ISO подлинный. 6a94b16afad211e8b9be5ec86f5379184f2b3a9e5869843fe763e2796e5ac1d3 sudo apt remove -y rhash sudo apt purge -y rhash Подготовка ```sh free -h swapon --show df -hT / /var/lib/docker /opt /tmp df -ih / /var/lib/docker /opt /tmp systemctl is-active docker ufw ``` ### 1.2. SigNoz и volumes ```sh cd /opt/signoz docker info --format \ 'driver={{.Driver}} root={{.DockerRootDir}} containers={{.Containers}} running={{.ContainersRunning}}' docker compose -f pours/deployment/compose.yaml ps -a docker stats --no-stream --format \ 'table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.BlockIO}}\t{{.PIDs}}' curl -fsS --max-time 5 http://127.0.0.1:8080/api/v1/health ss -lntp '( sport = :8080 or sport = :4317 or sport = :4318 )' ``` Снять фактические mountpoint hot-data. Prefix Compose не угадывать: ```sh for c in \ signoz-telemetrystore-clickhouse-0-0 \ signoz-telemetrykeeper-clickhousekeeper-0 \ signoz-metastore-postgres-0 \ signoz-signoz-0 do echo "=== $c ===" docker inspect --format \ '{{range .Mounts}}{{.Type}} {{.Name}} {{.Source}} -> {{.Destination}}{{println}}{{end}}' \ "$c" done ``` В change record перенести только host-пути данных ClickHouse, ClickHouse Keeper, PostgreSQL metastore и, если есть отдельный volume приложения SigNoz. Overlay2, `/opt/signoz` и `/tmp` в исключения не входят. ### 1.3. Firewall и OTLP ```sh ufw status verbose iptables-save | head ss -lntp '( sport = :8080 or sport = :4317 or sport = :4318 )' ``` С ВМ1 (не ICMP): ```sh timeout 3 bash -c 'exec 3<>/dev/tcp/10.0.0.13/4317' \ && echo 'OTLP gRPC reachable' ``` Без живого OTLP и healthy стека к установке не переходить. ## 2. Дистрибутив и установка ISO='/tmp/049-16-d-01.iso' ```sh install -d -m 0700 -o root -g root \ /root/kesl-install /mnt/kesl-iso mount -o ro,loop "$ISO" /mnt/kesl-iso find /mnt/kesl-iso -type f \ \( -iname 'kesl_*_amd64.deb' -o -iname '*.deb' \) | sort ``` ```sh DEB='/mnt/kesl-iso/kesl/kesl_12.4.0-1225_amd64.deb' dpkg-deb -f "$DEB" Package Version Architecture sha256sum "$DEB" install -m 0600 -o root -g root \ "$DEB" /root/kesl-install/kesl.deb umount /mnt/kesl-iso rmdir /mnt/kesl-iso ``` ```sh apt-get remove -y rhash apt-get purge -y rhash apt-get autoremove -y ``` Снимки до установки: ```sh test -f /etc/docker/daemon.json && \ cp -a /etc/docker/daemon.json /root/kesl-install/docker-daemon.before.json iptables-save > /root/kesl-install/iptables.before ufw status verbose > /root/kesl-install/ufw.before ss -lntp '( sport = :8080 or sport = :4317 or sport = :4318 )' \ > /root/kesl-install/listeners.before ls -l /root/kesl-install ``` ```sh apt-get install /root/kesl-install/kesl.deb ``` ### 2.3. Первоначальная настройка без защиты Значения EULA, Privacy Policy и KSN — после согласования, не механически. Для этого окна, как на ВМ1/ВМ2: `USE_KSN=No`. ```sh install -m 0600 -o root -g root /dev/null /root/kesl-install/autoinstall.ini cat > /root/kesl-install/autoinstall.ini <<'EOF' KSVLA_MODE=No ENDPOINT_AGENT_MODE=No EULA_AGREED=Yes PRIVACY_POLICY_AGREED=Yes USE_KSN=No GROUP_CLEAN=Yes LOCALE=ru_RU.UTF-8 INSTALL_LICENSE=None UPDATER_SOURCE=KLServers UPDATE_EXECUTE=No KERNEL_SRCS_INSTALL=No USE_GUI=No CONFIGURE_SELINUX=No DISABLE_PROTECTION=Yes INTERCEPTOR_MODE=UseFanotify ENABLE_TRACES_ON_FIRST_STARTUP=No EOF chmod 0600 /root/kesl-install/autoinstall.ini stat -c '%U:%G:%a %n' /root/kesl-install/autoinstall.ini ``` ```sh /opt/kaspersky/kesl/bin/kesl-setup.pl \ --autoinstall=/root/kesl-install/autoinstall.ini echo "kesl-setup exit=$?" ``` При `INSTALL_LICENSE=None` setup может вернуть 71. Не запускать повторно, если `kesl.service` active и File Threat Protection Stopped. ```sh systemctl --no-pager --no-legend status kesl || true kesl-control --app-info kesl-control --get-task-list free -h cd /opt/signoz docker compose -f pours/deployment/compose.yaml ps curl -fsS --max-time 5 http://127.0.0.1:8080/api/v1/health ``` Ожидается: `kesl` запущен, задача 1 не `Started`, контейнеры healthy. ## 3. Resource budget до Update и FTP ### 3.1. Снимок ```sh kesl-control --get-app-settings \ --file /root/kesl-install/app-settings.before.ini install -m 0600 -o root -g root \ /var/opt/kaspersky/kesl/common/kesl.ini \ /root/kesl-install/kesl.ini.before awk '/^\[General\]/,/^\[/' /var/opt/kaspersky/kesl/common/kesl.ini | head -n 40 ``` ### 3.2. Лимит CPU (KESL ещё работает) ```sh kesl-control --set-app-settings \ UseOnDemandCPULimit=Yes OnDemandCPULimit=15 ``` ### 3.3. Память — только при остановленном KESL Файл целиком не подменять. Сначала смотрите `[General]` из шага 3.1. ```sh systemctl stop kesl systemctl is-active kesl || true grep -nE '^(ScanMemoryLimit|MaxMemory)=' \ /var/opt/kaspersky/kesl/common/kesl.ini sed -i \ -e 's/^ScanMemoryLimit=8192$/ScanMemoryLimit=1024/' \ -e 's/^MaxMemory=auto$/MaxMemory=2048MB/' \ /var/opt/kaspersky/kesl/common/kesl.ini grep -nE '^(ScanMemoryLimit|MaxMemory)=' \ /var/opt/kaspersky/kesl/common/kesl.ini systemctl start kesl systemctl is-active kesl kesl-control --get-app-settings | grep -iE \ 'UseOnDemandCPULimit|OnDemandCPULimit|ScanMemoryLimit|MaxMemory' free -h cd /opt/signoz docker compose -f pours/deployment/compose.yaml ps ``` На constrained 4 ГиБ замените значения в `sed` на `512` и `1024MB`. Ожидаемо: `kesl.service` active; task 1 и 6 `Stopped`; базы ещё не загружены; ClickHouse/SigNoz healthy. ## 4. Активация Код не помещать в `autoinstall.ini`, репозиторий, evidence, чат и history. Нужен открытый egress из раздела 0.1. ```sh set +o history unset HISTFILE read -rsp 'KESL activation code: ' KESL_CODE; echo kesl-control --add-active-key "$KESL_CODE" unset KESL_CODE set -o history kesl-control -L --query kesl-control --app-info ``` В evidence — только статус вроде `Active subscription` и срок, не GUID ключа. Если лицензия `Inconsistent update` или недействительна — stop. ## 5. АВЗ.2 — базы ID подтвердить через `--get-task-list` (Update = 6). ```sh kesl-control --get-settings 6 kesl-control --start-task 6 -W kesl-control --get-task-state 6 kesl-control --app-info --json free -h cd /opt/signoz docker compose -f pours/deployment/compose.yaml ps ``` ```sh START="$(LC_ALL=C date +'%Y/%b/%d %H:%M:%S;1')" kesl-control --set-schedule 6 RuleType=Hourly \ "StartTime=${START}" RunMissedStartRules=No RandomInterval=0 kesl-control --get-schedule 6 ``` ```sh systemctl restart kesl systemctl is-active kesl kesl-control --app-info --json grep -nE '^(ScanMemoryLimit|MaxMemory)=' \ /var/opt/kaspersky/kesl/common/kesl.ini kesl-control --get-app-settings | grep -iE \ 'UseOnDemandCPULimit|OnDemandCPULimit' kesl-control --get-schedule 6 kesl-control --get-task-state 1 kesl-control --get-task-state 6 ``` ## 6. Исключения hot-data Только фактические mountpoint из раздела 1.2. Кандидаты: данные ClickHouse, ClickHouse Keeper, PostgreSQL metastore. Не исключать `/var/lib/docker`, `overlay2`, `/opt/signoz`, `/var/lib/han-deploy/incoming`, `/tmp`, `/root`. Снять mountpoint: ```sh cd /opt/signoz docker compose -f pours/deployment/compose.yaml ps --format '{{.Name}}' for c in \ signoz-telemetrystore-clickhouse-0-0 \ signoz-telemetrykeeper-clickhousekeeper-0 \ signoz-metastore-postgres-0 do echo "=== $c ===" docker inspect --format \ '{{range .Mounts}}{{if eq .Type "volume"}}{{.Source}} dest={{.Destination}} vol={{.Name}}{{println}}{{end}}{{end}}' \ "$c" done ``` ```sh kesl-control --get-settings 1 \ --file /root/kesl-install/file-threat.before.ini kesl-control --set-settings 1 \ --add-exclusion /var/lib/docker/volumes/signoz-telemetrystore-0-0-data/_data kesl-control --set-settings 1 \ --add-exclusion /var/lib/docker/volumes/signoz-telemetrykeeper-0-data/_data kesl-control --set-settings 1 \ --add-exclusion /var/lib/docker/volumes/signoz-metastore-postgres-0-data/_data kesl-control --set-settings 1 ScanArchived=No kesl-control --get-settings 1 ``` Каждое исключение: владелец, причина (write-hot telemetry/metadata), компенсация (FTP вне volume + периодический `scan-file` host-дерева `/opt/signoz/scripts`), срок пересмотра, подтверждение Security. ## 7. Узкий on-demand scan-file ODS schedules, кроме Update, оставить Manual. Не сканировать volumes ClickHouse/PostgreSQL и не запускать Container Scan. ```sh kesl-control --get-task-list kesl-control --get-schedule 2 kesl-control --set-schedule 2 RuleType=Manual kesl-control --scan-file /opt/signoz/scripts --action Inform kesl-control --get-task-state 1 free -h cd /opt/signoz docker compose -f pours/deployment/compose.yaml ps ``` Ожидается: scan-file без ошибок, FTP всё ещё Stopped, стек healthy. Если вырос swap или unhealthy — остановить scan и не включать task 1. ## 8. Host protection — АВЗ.1 На Ubuntu 24.04 с fanotify параметр `InterceptorProtectionMode` **не поддерживается**. Штатный режим — блокировка на время проверки. Откат: `kesl-control --stop-task 1`. ```sh kesl-control --start-task 1 kesl-control --set-settings 1 \ ActionOnThreat=DisinfectDeleteIfNotPossible ScanArchived=No kesl-control --get-task-state 1 kesl-control --get-settings 1 kesl-control --app-info --json free -h docker stats --no-stream --format \ 'table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.PIDs}}' cd /opt/signoz docker compose -f pours/deployment/compose.yaml ps ufw status verbose ss -lntp '( sport = :8080 or sport = :4317 or sport = :4318 )' ``` Пилот: каждые 15 минут — health, OTLP, RAM/swap, события KESL, firewall. При деградации: `kesl-control --stop-task 1`. Не расширять исключения вслепую. ## 10. Приёмка стека и сужение egress На ВМ3: ```sh systemctl is-active kesl docker kesl-control --app-info kesl-control --get-task-state 1 kesl-control --get-task-state 6 kesl-control --get-schedule 6 cd /opt/signoz sudo PRIVATE_IP=10.0.0.13 ./scripts/30-verify-signoz.sh docker compose -f pours/deployment/compose.yaml ps ufw status verbose diff -u /root/kesl-install/ufw.before <(ufw status verbose) || true ss -lntp '( sport = :8080 or sport = :4317 or sport = :4318 )' free -h swapon --show df -hT / /opt /var/lib/docker ``` С ВМ1 повторить TCP `4317`. Слушатели `8080` только на `127.0.0.1`, `4317/4318` только на `192.168.0.5`. Сравнить UFW с baseline: новых правил KESL быть не должно. 24 часа: без OOM/restart/unhealthy, hourly Update сам зелёный, диск ClickHouse не упёрся в stop-порог. ```sh rm -rf /root/kesl-eicar-test # после копирования evidence: # rm -rf /root/kesl-install # rm -f /var/lib/han-deploy/incoming/049-16-d-01.iso \ # /var/lib/han-deploy/incoming/049-16-d-01.sig ``` ## 13. Эксплуатационный режим - Update (ID 6): каждый час; нужен постоянный узкий egress к KLServers. - File Threat Protection (ID 1): постоянно, `DisinfectDeleteIfNotPossible`, `ScanArchived=No`. - ODS: по согласованию в низкую нагрузку; не полный диск и не volumes БД. - ContainerScan: не использовать при текущей лицензии. - Ежедневно: лицензия, task 1 `Started`, дата баз, ошибки KESL, health SigNoz, свободное место `/opt`. - После upgrade KESL/kernel/Docker: повтор пилота и evidence delta. Обновление образов SigNoz (`20-deploy-signoz.sh`) — отдельное break-glass окно; его egress к Docker Hub/GitHub не заменяет allow-list KESL.