Реализована проверка версионности и требование soft\force обновлений приложения

This commit is contained in:
mi
2026-09-03 19:12:38 +03:00
parent 465a70d488
commit 44db38f6fe
37 changed files with 3057 additions and 53 deletions
@@ -0,0 +1,479 @@
# Runbook внедрения soft/force update мобильного приложения
## 1. Назначение и границы
Документ описывает выпуск нативных версий HAN Chat через Google Play, RuStore
и App Store и последующее включение `soft`/`force update` через публичную
backend-политику.
Механика не использует EAS OTA Update. Пользователь всегда направляется в
магазин, соответствующий каналу установленной сборки:
- `google_play`;
- `rustore`;
- `app_store`.
Канал встраивается в бинарный файл через
`EXPO_PUBLIC_DISTRIBUTION_STORE`. Поэтому Android-сборки Google Play и RuStore
нужно собирать отдельно.
Решение принимает мобильный клиент:
- `current_build < minimum_build` — обязательное обновление (`force`);
- `minimum_build <= current_build < latest_build` — мягкое обновление (`soft`);
- `current_build >= latest_build` — карточка не показывается.
`latest_version` используется только в интерфейсе. Сравнение выполняется по
целому Android `versionCode` или iOS `buildNumber`.
## 2. Ответственные и данные окна выпуска
Перед началом назначьте:
- ответственного за EAS Build;
- ответственного за Google Play Console;
- ответственного за RuStore Console;
- ответственного за App Store Connect, если выпускается iOS;
- оператора ВМ1, применяющего backend settings;
- владельца решения о переводе soft update в force update.
Создайте запись окна выпуска:
```text
Маркетинговая версия:
Git commit/tag мобильного приложения:
Git commit/tag backend:
Google Play EAS build ID:
Google Play versionCode:
RuStore EAS build ID:
RuStore versionCode:
App Store EAS build ID:
App Store buildNumber:
Дата полной доступности каждого релиза:
Минимальная поддерживаемая сборка каждого канала:
```
Не вычисляйте пороги по порядку запуска команд. Записывайте фактические значения
из завершённой EAS-сборки и подтверждайте их в консоли соответствующего магазина.
## 3. Важная особенность EAS remote version
В проекте используется:
```json
{
"cli": {
"appVersionSource": "remote"
}
}
```
Android remote `versionCode` привязан к application ID `ru.han.chat`. Профили
`google-play` и `rustore` используют один application ID и общий счётчик.
Если текущее remote-значение равно `6`, последовательная сборка обычно даст:
1. первая Android-сборка — `versionCode=7`;
2. вторая Android-сборка — `versionCode=8`.
Это допустимо. Политики Google Play и RuStore независимы, поэтому в backend
следует указать `latest_build=7` для одного канала и `latest_build=8` для
другого, если именно такие артефакты опубликованы.
Локальный `android.versionCode` в `app.config.ts` не является источником истины
при remote version source. Источник истины для rollout — опубликованный
артефакт магазина.
## 4. Предварительные проверки
### 4.1. Мобильное приложение
На локальной машине:
```powershell
cd C:\Users\MI\Documents\Assistent\HAN_chat_specification\VM4_Expo-mobile
npm run typecheck
npm test
npx eas-cli whoami
```
Проверьте:
- `version` в `app.config.ts` соответствует выпускаемой маркетинговой версии;
- профиль `google-play` содержит `EXPO_PUBLIC_DISTRIBUTION_STORE=google_play`;
- профиль `rustore` содержит `EXPO_PUBLIC_DISTRIBUTION_STORE=rustore`;
- профиль `app-store` содержит `EXPO_PUBLIC_DISTRIBUTION_STORE=app_store`;
- `preview` и `development` не включают store policy;
- production API указывает на `https://chat.han0107.ru`.
### 4.2. Backend
На локальной машине:
```powershell
cd C:\Users\MI\Documents\Assistent\HAN_chat_specification\VM1_app\codebase\backend\api-backend
python -m pytest
cd ..
python -m pytest tests
```
Проверьте, что backend-релиз содержит:
- строгий объект `mobile_update` в `/api/v1/public/app-config`;
- поддержку `ETag` и `If-None-Match`;
- TTL app-config 60 секунд;
- валидацию build numbers и store URL;
- актуальный `openapi.yaml`;
- nginx `proxy_cache_valid 200 60s` для app-config.
## 5. Безопасное внедрение backend до выпуска приложения
Сначала разверните backend-код и nginx, затем мобильные сборки. Старые клиенты
игнорируют новую секцию `mobile_update`.
До публикации новой версии политика должна быть безопасной:
- либо канал отключён;
- либо `latest_build` не превышает уже опубликованный build;
- `minimum_build` не должен внезапно исключать поддерживаемые версии.
Начальные `latest_build=2` при фактической установленной сборке `6` не показывают
карточку и поэтому безопасны как временное no-op состояние.
Развёртывание backend выполняйте по основному production runbook:
`deployment/RUNBOOK.production.ru.md`.
После обновления образов и конфигурации оператор ВМ1 выполняет штатный job:
```sh
/usr/local/sbin/han-vm1-compose --profile ops run --rm seed-settings
```
Команда идемпотентна и включает `validate_settings`. Невалидная комбинация
порогов или URL должна завершить job ошибкой.
Проверка публичного контракта с внешней машины:
```sh
curl -fsS https://chat.han0107.ru/api/v1/public/app-config
ETAG="$(curl -fsSI https://chat.han0107.ru/api/v1/public/app-config \
| awk -F': ' 'tolower($1)=="etag" {gsub("\r","",$2); print $2}')"
curl -sS -o /dev/null -w '%{http_code}\n' \
-H "If-None-Match: $ETAG" \
https://chat.han0107.ru/api/v1/public/app-config
```
Ожидается:
- первый запрос — `200`;
- в ответе присутствуют три store policy;
- повторный запрос с актуальным ETag — `304`;
- `Cache-Control` содержит `max-age=60`.
## 6. Получение текущего remote build number
На локальной машине:
```powershell
cd C:\Users\MI\Documents\Assistent\HAN_chat_specification\VM4_Expo-mobile
npx eas-cli build:version:get --platform android --profile google-play
npx eas-cli build:version:get --platform android --profile rustore
npx eas-cli build:version:get --platform ios --profile app-store
```
Для автоматической обработки:
```powershell
npx eas-cli build:version:get --platform android --profile google-play --json
```
Одинаковое значение для двух Android-профилей до сборки ожидаемо: они используют
общий application ID. Каждая последующая Android-сборка с `autoIncrement`
увеличивает общий счётчик.
### 6.1. Тестовый APK с каналом RuStore
Профиль `preview-rustore` создаёт внутренний APK, обращается к
`https://dev-chat.han0107.ru` и встраивает канал `rustore`:
```powershell
npx eas-cli build:version:get --platform android --profile preview-rustore
npx eas-cli build --profile preview-rustore --platform android
```
У профиля задано `autoIncrement: false`, поэтому тестовая сборка не расходует
следующий production `versionCode`. Пусть фактический build APK равен `B`. Для
проверки на dev-backend задайте:
```text
soft: latest_build = B + 1, minimum_build <= B
force: latest_build = B + 1, minimum_build = B + 1
none: latest_build <= B
```
Меняйте только RuStore policy dev-окружения и применяйте её через штатный
`seed-settings` этого окружения. Не используйте тестовые пороги на production.
После отказа от soft update запись сохраняется в SecureStore без TTL. Для
повторной проверки той же политики очистите данные приложения либо увеличьте
`latest_build`.
APK использует package `ru.han.chat` и может заменить установленную
store-сборку. Для теста предпочтительно отдельное устройство.
## 7. Создание store-сборок
Рекомендуется собирать и публиковать магазины по одному, сразу записывая
фактический build number:
```powershell
npx eas-cli build --profile google-play --platform android
npx eas-cli build --profile rustore --platform android
npx eas-cli build --profile app-store --platform ios
```
App Store-команду не выполняйте, пока не настроены Apple credentials, Apple App
ID и рабочий `store_url`.
После каждой сборки:
```powershell
npx eas-cli build:list --platform android --limit 5
npx eas-cli build:view <BUILD_ID>
```
Зафиксируйте:
- EAS build ID;
- commit;
- channel/profile;
- `version`;
- фактический `versionCode`/`buildNumber`;
- checksum скачанного артефакта, если он используется в процедуре публикации.
Не запускайте вторую Android-сборку, пока не записан номер первой.
## 8. Публикация и проверка магазинов
### 8.1. Google Play
1. Загрузите AAB в требуемый track.
2. Убедитесь, что Console показывает ожидаемый `versionCode`.
3. Проведите internal/closed testing.
4. Проверьте установку и переход по ссылке:
`https://play.google.com/store/apps/details?id=ru.han.chat`.
5. Зафиксируйте процент rollout и время полной доступности.
### 8.2. RuStore
1. Загрузите предназначенный для RuStore артефакт.
2. Убедитесь, что Console показывает фактический `versionCode`.
3. Проведите тестирование канала.
4. Проверьте страницу:
`https://www.rustore.ru/catalog/app/ru.han.chat`.
5. Зафиксируйте статус модерации и время доступности.
### 8.3. App Store
До включения политики:
1. получите Apple App ID;
2. опубликуйте и проверьте сборку в App Store Connect/TestFlight;
3. укажите канонический URL `https://apps.apple.com/.../id<APPLE_ID>`;
4. подтвердите фактический `buildNumber`;
5. только после этого установите `enabled: true`.
## 9. Включение soft update
Изменяйте
`deployment/app-settings.production-like.yaml` отдельно для каждого магазина.
Пример, если Google Play опубликовал build `7`, а RuStore — build `8`:
```yaml
mobile_update.google_play.enabled: {type: boolean, value: true, public: true}
mobile_update.google_play.latest_build: {type: integer, value: 7, public: true}
mobile_update.google_play.minimum_build: {type: integer, value: 1, public: true}
mobile_update.google_play.latest_version: {type: string, value: "1.0.1", public: true}
mobile_update.rustore.enabled: {type: boolean, value: true, public: true}
mobile_update.rustore.latest_build: {type: integer, value: 8, public: true}
mobile_update.rustore.minimum_build: {type: integer, value: 1, public: true}
mobile_update.rustore.latest_version: {type: string, value: "1.0.1", public: true}
```
Выбор `minimum_build` требует отдельного решения:
- оставить `1` — все более старые builds получают soft update;
- установить `6` — builds `15` немедленно получают force update, а build `6`
получает soft update;
- установить новый build (`7` или `8`) — все предыдущие builds получают force.
Для первого rollout рекомендуется сохранить прежний минимальный поддерживаемый
build и включить только soft update.
После review и merge настроек оператор ВМ1 выполняет:
```sh
/usr/local/sbin/han-vm1-compose --profile ops run --rm seed-settings
```
Подождите до 60 секунд и повторно запросите app-config. Если внешний nginx уже
имел закешированный ответ, допускайте до двух минут на проверку с разных
клиентов, но не продолжайте rollout при значении старше ожидаемого.
## 10. Приёмка soft update
Используйте реальное устройство со старой store-сборкой каждого канала.
Проверьте:
1. При cold start появляется «Доступно обновление».
2. Указаны правильные текущая и новая версии.
3. Кнопка открывает правильный магазин, а не другой Android-магазин.
4. «Позже», крестик и Android Back закрывают карточку.
5. После отказа карточка той же `latest_build` не появляется при следующем
запуске: отказ хранится в SecureStore без TTL.
6. После увеличения `latest_build` появляется новая карточка.
7. После установки нового build карточка исчезает.
8. При недоступном backend приложение не блокируется.
Если продукту требуется повторное напоминание через интервал, текущую механику
следует изменить отдельно: сейчас soft-dismiss действует до появления нового
`latest_build`, очистки данных или переустановки.
## 11. Перевод в force update
Force разрешено включать только когда обязательный build:
- прошёл модерацию;
- доступен в нужном production track;
- доступен всем пользователям, которых затронет `minimum_build`;
- устанавливается и запускается;
- корректно открывается по `store_url`;
- backend и store не находятся в инциденте.
Для Google Play build `7`:
```yaml
mobile_update.google_play.latest_build: {type: integer, value: 7, public: true}
mobile_update.google_play.minimum_build: {type: integer, value: 7, public: true}
```
Для RuStore build `8`:
```yaml
mobile_update.rustore.latest_build: {type: integer, value: 8, public: true}
mobile_update.rustore.minimum_build: {type: integer, value: 8, public: true}
```
Не повышайте minimum одного магазина только потому, что релиз доступен в другом.
После изменения снова примените `seed-settings`, проверьте app-config и
протестируйте старую сборку:
- force-карточка не имеет крестика и кнопки «Позже»;
- Android Back не закрывает её;
- кнопка открывает правильный магазин;
- после возврата без установки карточка остаётся;
- после установки поддерживаемого build блокировка исчезает.
## 12. App Store policy
Пока Apple App ID неизвестен, политика должна оставаться полностью выключенной:
```yaml
mobile_update.app_store.enabled: {type: boolean, value: false, public: true}
mobile_update.app_store.latest_build: {type: integer, value: null, public: true}
mobile_update.app_store.minimum_build: {type: integer, value: null, public: true}
mobile_update.app_store.latest_version: {type: string, value: "", public: true}
mobile_update.app_store.store_url: {type: string, value: "", public: true}
mobile_update.app_store.release_notes: {type: string, value: "", public: true}
```
Отключённая политика не должна содержать частично заполненные release-поля:
backend отклонит такую конфигурацию.
## 13. Откат
### 13.1. Немедленно снять force
Понизьте `minimum_build` до последнего подтверждённого поддерживаемого значения,
не меняя `latest_build`, затем примените seed.
Пример:
```yaml
mobile_update.google_play.latest_build: {type: integer, value: 7, public: true}
mobile_update.google_play.minimum_build: {type: integer, value: 1, public: true}
```
После успешного получения новой политики клиент снимет force-блокировку.
Кратковременная сетевая ошибка сохраняет уже показанный force до следующей
успешной проверки, поэтому дополнительно подтвердите доступность app-config.
### 13.2. Полностью отключить канал
Установите `enabled=false`, integer-поля в `null`, строковые release-поля в
пустую строку:
```yaml
mobile_update.google_play.enabled: {type: boolean, value: false, public: true}
mobile_update.google_play.latest_build: {type: integer, value: null, public: true}
mobile_update.google_play.minimum_build: {type: integer, value: null, public: true}
mobile_update.google_play.latest_version: {type: string, value: "", public: true}
mobile_update.google_play.store_url: {type: string, value: "", public: true}
mobile_update.google_play.release_notes: {type: string, value: "", public: true}
```
Не откатывайте уже использованный store build number и не публикуйте другой
артефакт с тем же `versionCode`/`buildNumber`.
### 13.3. Дефект новой версии
Если новая версия дефектна:
1. не направляйте на неё новых пользователей — отключите policy или верните
`latest_build` к безопасному опубликованному build;
2. остановите rollout в соответствующем магазине;
3. выпустите исправленную сборку с новым build number;
4. после публикации укажите новый `latest_build`;
5. только после приёмки принимайте решение о новом `minimum_build`.
## 14. Наблюдение после включения
В течение окна наблюдения контролируйте:
- `5xx` и latency `/api/v1/public/app-config`;
- долю `200/304`;
- ошибки rate limit публичного endpoint;
- доступность страниц магазинов;
- crash/error rate новой мобильной версии;
- обращения о циклической force-карточке;
- соответствие фактического store build политике каждого канала.
Stop conditions:
- URL ведёт не в тот магазин или не на HAN Chat;
- опубликованный build ниже `latest_build`;
- часть rollout-групп не может скачать minimum build;
- app-config отдаёт старую или частичную политику дольше двух минут;
- новая версия не запускается или не проходит авторизацию;
- force нельзя снять успешным изменением backend policy.
## 15. Контрольный чек-лист
- [ ] Backend с `mobile_update` развёрнут до мобильного rollout.
- [ ] ETag/304 и TTL 60 секунд проверены извне.
- [ ] Фактические build numbers записаны после каждой EAS-сборки.
- [ ] Build numbers подтверждены в консолях магазинов.
- [ ] Google Play и RuStore thresholds заполнены независимо.
- [ ] Soft update проверен на старой сборке каждого канала.
- [ ] Soft-dismiss и повторный показ для нового latest build проверены.
- [ ] Force включается только после полной доступности minimum build.
- [ ] App Store остаётся disabled до получения Apple App ID.
- [ ] Процедура отката проверена до включения force.
- [ ] Итоговые значения политики и время применения записаны в журнал выпуска.
@@ -376,6 +376,9 @@ stack unit. Обновление active/exited oneshot всегда требуе
```sh
curl -sS -o /dev/null -w '%{http_code}\n' http://<PUBLIC_HOST>/
curl -fsS https://<PUBLIC_HOST>/api/v1/public/app-config
ETAG="$(curl -fsSI https://<PUBLIC_HOST>/api/v1/public/app-config | awk -F': ' 'tolower($1)=="etag" {gsub("\r","",$2); print $2}')"
curl -sS -o /dev/null -w '%{http_code}\n' \
-H "If-None-Match: $ETAG" https://<PUBLIC_HOST>/api/v1/public/app-config
curl -fsS https://<PUBLIC_HOST>/auth/realms/han-chat/.well-known/openid-configuration
curl -sS -o /dev/null -w '%{http_code}\n' \
https://<PUBLIC_HOST>/internal/safety/v2/messages/check
@@ -383,7 +386,12 @@ openssl s_client -connect <PUBLIC_HOST>:443 -servername <PUBLIC_HOST> \
-verify_hostname <PUBLIC_HOST> -verify_return_error </dev/null
```
Ожидается `308`, public endpoints `200`, internal route `404`, valid chain.
Ожидается `308`, public endpoints `200`, повторный app-config `304`, internal
route `404`, valid chain. В app-config проверьте `mobile_update`: Google Play и
RuStore включены (`latest_build=2`, `minimum_build=1`, `latest_version=1.0.1`,
RuStore URL `https://www.rustore.ru/catalog/app/ru.han.chat`), App Store
отключён, его release-поля равны `null`; пустой `release_notes` у всех политик
также возвращается как `null`.
Проверьте guest/auth PKCE/OTP, SMS mode, Open Lines, idempotency, ownership,
rate limits, WS reconciliation, S3 quarantine/promote/deny и Safety v2
allow/deny/pending/timeout. Safety status `stub` не принимается.
@@ -427,6 +435,12 @@ staging, выполнять config test и HUP.
5xx/auth/Safety/PG/Redis/OOM/disk/OTEL queue/TLS. Отправьте только fake canary
token/PII markers и докажите их отсутствие в logs/traces.
KESL 12.4 устанавливается и принимается только по отдельному операторскому
runbook `deployment/kesl/RUNBOOK.KESL.ru.md`. Не совмещайте установку,
полную/контейнерную антивирусную проверку или изменение File Threat Protection
с deploy, миграциями, PG backup, TLS renewal и перезапуском Docker/стека.
Изменение политики KESL не является частью обычного application release.
Перед reboot проверьте admin SSH и provider console:
```sh
@@ -47,5 +47,23 @@ settings:
notification.expire_job.run_at: {type: string, value: "00:01", public: false}
notification.upload_draft.ttl_days: {type: integer, value: 7, public: false}
ux.session.idle_timeout_minutes: {type: integer, value: 30, public: true}
mobile_update.google_play.enabled: {type: boolean, value: true, public: true}
mobile_update.google_play.latest_build: {type: integer, value: 2, public: true}
mobile_update.google_play.minimum_build: {type: integer, value: 1, public: true}
mobile_update.google_play.latest_version: {type: string, value: "1.0.1", public: true}
mobile_update.google_play.store_url: {type: string, value: "https://play.google.com/store/apps/details?id=ru.han.chat", public: true}
mobile_update.google_play.release_notes: {type: string, value: "", public: true}
mobile_update.rustore.enabled: {type: boolean, value: true, public: true}
mobile_update.rustore.latest_build: {type: integer, value: 2, public: true}
mobile_update.rustore.minimum_build: {type: integer, value: 1, public: true}
mobile_update.rustore.latest_version: {type: string, value: "1.0.1", public: true}
mobile_update.rustore.store_url: {type: string, value: "https://www.rustore.ru/catalog/app/ru.han.chat", public: true}
mobile_update.rustore.release_notes: {type: string, value: "", public: true}
mobile_update.app_store.enabled: {type: boolean, value: false, public: true}
mobile_update.app_store.latest_build: {type: integer, value: null, public: true}
mobile_update.app_store.minimum_build: {type: integer, value: null, public: true}
mobile_update.app_store.latest_version: {type: string, value: "", public: true}
mobile_update.app_store.store_url: {type: string, value: "", public: true}
mobile_update.app_store.release_notes: {type: string, value: "", public: true}
security.cors.allowed_origins: {type: string_list, value: "https://chat.example.ru", public: false}
security.public_cache.max_age_seconds: {type: integer, value: 3600, public: false}
security.public_cache.max_age_seconds: {type: integer, value: 60, public: false}
@@ -0,0 +1,192 @@
# Карта доказательств АВЗ.1 и АВЗ.2 для ВМ1
Форма заполняется оператором после выполнения `RUNBOOK.KESL.ru.md`. Она не
должна содержать activation code, ключи, токены, DSN, environment, содержимое
secret-файлов, персональные данные или тестовый файл EICAR.
## 1. Идентификация изменения
- Change ID:
- Дата и окно:
- Оператор:
- Security approver:
- Service owner:
- Hostname ВМ1:
- Ubuntu version:
- Kernel version:
- KESL package/version:
- SHA-256 DEB:
- Источник пакета:
- HAN release SHA:
- Container image digests зафиксированы: да / нет
Коммерческая KESL 12.4 не должна быть обозначена как сертифицированная ФСТЭК
сборка. Решение о допустимости коммерческой версии и ссылка на модель угроз:
- Решение:
- Документ/раздел:
- Утвердил:
## 2. Входной baseline
- Все steady-state контейнеры healthy/running:
- Restart count:
- Public smoke:
- Negative port probes:
- CPU:
- Available RAM:
- Swap activity:
- Disk free:
- IO wait:
- API p95:
- Redis latency / blocked clients:
- OTEL queue:
- Открытые до установки проблемы:
Stop conditions и численные пороги утверждены:
- p95/Redis:
- available RAM/swap:
- IO wait:
- disk:
- health/restarts:
## 3. АВЗ.1 — реализация антивирусной защиты
Нормативная опора:
- Приказ ФСТЭК России № 21, приложение, АВЗ.1 — «Реализация
антивирусной защиты»;
- пункт 8.6 — обнаружение вредоносных программ/информации и реагирование.
Необходимые доказательства:
- [ ] `kesl` active.
- [ ] Лицензия действительна.
- [ ] File Threat Protection (ID 1) имеет состояние `Started`.
- [ ] InterceptorProtectionMode = `Block`.
- [ ] ActionOnThreat = `DisinfectDeleteIfNotPossible` либо иное утверждённое
блокирующее/лечащее действие.
- [ ] ScanArchived = `No` для real-time защиты.
- [ ] Исключения ограничены тремя утверждёнными hot-data mountpoint.
- [ ] Контролируемый EICAR заблокирован/обезврежен/помещён в карантин.
- [ ] Событие EICAR зарегистрировано в журнале KESL.
- [ ] После теста EICAR отсутствует вне карантина и тестовый каталог удалён.
- [ ] Public smoke и health после включения Block успешны.
- [ ] UFW и `HAN-CHAT-DOCKER` не изменены.
- [ ] За 24 часа нет новых restart/OOM/5xx и неприемлемой деградации.
Артефакты без секретов:
- `systemctl is-active kesl`:
- `kesl-control --app-info`:
- `kesl-control --get-task-state 1`:
- reviewed excerpt `kesl-control --get-settings 1`:
- EICAR event ID/time/action:
- smoke result/time:
- firewall comparison:
- 24h resource comparison:
Вывод по АВЗ.1: реализована / не реализована.
## 4. АВЗ.2 — обновление баз признаков вредоносных программ
Нормативная опора:
- Приказ ФСТЭК России № 21, приложение, АВЗ.2 — «Обновление базы данных
признаков вредоносных компьютерных программ (вирусов)».
Необходимые доказательства:
- [ ] Update (ID 6) завершилась успешно.
- [ ] Базы загружены.
- [ ] Дата выпуска баз актуальна на момент проверки.
- [ ] Расписание Update = `Hourly`.
- [ ] Утверждён alert/регламент на ошибку и устаревание баз.
- [ ] Назначен ответственный за ежедневный контроль.
- [ ] Проверено успешное автоматическое обновление после ручного запуска.
Артефакты без секретов:
- `kesl-control --app-info`:
- `kesl-control --get-task-state 6`:
- `kesl-control --get-schedule 6`:
- время последнего успешного автоматического Update:
- ссылка на alert/регламент:
- ответственный:
Вывод по АВЗ.2: реализована / не реализована.
## 5. Связанные меры
### РСБ.13, РСБ.7
- [ ] Определены события: detection, remediation/quarantine, component stop,
update failure, stale bases, license failure.
- [ ] Определён состав полей: time, host, component/task, threat, object,
action, result, severity.
- [ ] Определены срок и место хранения.
- [ ] Доступ к журналу ограничен; изменение/удаление контролируется.
- [ ] Экспорт в syslog/SIEM включён либо документирован локальный контроль.
Ссылка на регламент и настройки:
### АНЗ.2
- [ ] Контролируется версия и жизненный цикл самого KESL, а не только баз.
- [ ] Upgrade KESL проходит совместимость, pilot, smoke и rollback review.
- [ ] Обновление kernel/Docker вызывает повторную проверку совместимости.
Ссылка на регламент:
## 6. Исключения и компенсирующие проверки
Для каждого исключения укажите точный фактический mountpoint, владельца,
причину, риск, компенсирующую проверку и дату пересмотра.
### Redis data
- Mountpoint:
- Причина: AOF/RDB, latency-sensitive write path.
- Компенсация:
- Владелец:
- Review date:
### OTEL queue
- Mountpoint:
- Причина: persistent high-churn telemetry queue.
- Компенсация:
- Владелец:
- Review date:
### nginx cache
- Mountpoint:
- Причина: regenerable high-churn cache.
- Компенсация:
- Владелец:
- Review date:
Иных исключений нет / перечислить отдельно с утверждением Security:
## 7. Проверка отката
- [ ] Команда переключения `Block``Notify` проверена документально.
- [ ] Процедура остановки KESL доступна break-glass admin.
- [ ] Процедура `apt-get purge kesl` проверена по документации текущей версии.
- [ ] Откат не использует `docker compose down -v` и не удаляет volumes.
- [ ] После отката предусмотрены smoke, health и firewall checks.
- [ ] Reboot выполняется только отдельным согласованным окном при необходимости.
Результат rehearsal/desk check:
## 8. Итоговая приёмка
- АВЗ.1: принято / не принято.
- АВЗ.2: принято / не принято.
- Ограничения/остаточные риски:
- Следующий review:
- Operations, ФИО/подпись/дата:
- Security, ФИО/подпись/дата:
- Service owner, ФИО/подпись/дата:
@@ -0,0 +1,566 @@
# KESL 12.4 standalone на production ВМ1
Это операторский runbook для поэтапного внедрения Kaspersky Endpoint Security
для Linux 12.4 на Ubuntu 24.04 ВМ1. Команды выполняются только персональной
ролью `admin` через `sudo`; repository automation этот runbook не запускает.
Цель: реализовать АВЗ.1 (обнаружение и реагирование) и АВЗ.2 (обновление баз)
без деградации Docker-стека HAN Chat.
Не выполняйте установку одновременно с deploy, миграциями, backup, ротацией
секретов, TLS renewal, перезапуском Docker или host reboot.
Официальная документация:
- [программные требования](https://support.kaspersky.ru/kes-for-linux/12.4.0/197645);
- [краткое руководство по установке](https://support.kaspersky.ru/kes-for-linux/12.4.0/install/16099);
- [автоматическая первоначальная настройка](https://support.kaspersky.ru/kes-for-linux/12.4.0/197909);
- [параметры autoinstall.ini](https://support.kaspersky.ru/kes-for-linux/12.4.0/197593);
- [ограничение CPU и памяти](https://support.kaspersky.ru/kes-for-linux/12.4.0/264979);
- [настройка File Threat Protection](https://support.kaspersky.ru/kes-for-linux/12.4.0/248490);
- [проверка контейнеров](https://support.kaspersky.ru/kes-for-linux/12.4.0/197612);
- [удаление DEB-пакета](https://support.kaspersky.ru/kes-for-linux/12.4.0/197596).
## 0. Участники, входные данные и stop conditions
До окна работ зафиксируйте:
- change ID, время окна, оператора, approver Security и on-call;
- hostname/IP ВМ1, фактическую версию Ubuntu и ядра;
- точное имя, версию и SHA-256 полученного от Kaspersky DEB-пакета;
- источник пакета и лицензию/код активации;
- текущий release SHA и image digests;
- место хранения evidence вне immutable release.
Значения лицензии, activation code, proxy credentials и секреты HAN нельзя
помещать в репозиторий, shell history, журналы или evidence.
Немедленно остановитесь, если:
- ОС, архитектура или ядро отсутствуют в матрице KESL 12.4;
- уже установлен другой антивирус или неизвестная версия KESL;
- свободно менее 4 ГБ или нет рабочего swap;
- до установки есть unhealthy/restarting контейнеры, 5xx или дефицит ресурсов;
- не совпал SHA-256 пакета;
- после этапа выросли restart count, 5xx, Redis latency/blocked clients,
OTEL queue или host IO wait сверх согласованного порога;
- File Threat Protection изменил UFW/iptables или доступность портов;
- базы не загрузились либо лицензия недействительна.
Рекомендуемые пороги отката для пилота (утвердить до установки):
- новый unhealthy/restart любого steady-state сервиса;
- публичный smoke не проходит два запуска подряд;
- host available memory менее 2 ГБ или начинается устойчивый swap-in/swap-out;
- IO wait более 10% в течение 5 минут;
- p95 API/Redis latency выросла более чем на 20% от baseline в течение 10 минут;
- свободное место уменьшилось ниже 10 ГБ или ниже 15%.
### 0.1. Исключение только для constrained test VM
На тестовой ВМ допускается пилот с 4 ГБ RAM без увеличения памяти только по
явному решению владельца среды. Это не отменяет production-gate и не является
обоснованием для переноса той же конфигурации в боевую среду.
Обязательные ограничения такого пилота:
- provider snapshot/console и оператор доступны до начала;
- `ScanMemoryLimit=512`, `MaxMemory=1024MB`;
- `UseOnDemandCPULimit=Yes`, `OnDemandCPULimit=15`;
- не запускать full filesystem scan и проверку архивов;
- ODS и ContainerScan выполнять по одному объекту, не одновременно;
- сначала Update и health, затем один stateless image, затем File Threat
Protection в `Notify`;
- остановить KESL при available memory менее 512 МБ, устойчивом swap IO,
появлении host/container OOM, restart или провале smoke;
- до режима `Block` требуется отдельное подтверждение стабильности.
Перед production-внедрением повторить sizing и baseline на боевых ресурсах;
test-профиль 512/1024 МБ автоматически не переносить.
## 1. Read-only preflight и baseline
Команды разделены на небольшие блоки: сохраните вывод каждого блока в
change record, предварительно проверив отсутствие секретов. Не публикуйте
полный `docker inspect`, Compose config или environment.
### 1.1. Host и совместимость
```sh
date -Is
hostnamectl
uname -a
dpkg --print-architecture
findmnt -no TARGET,SOURCE,FSTYPE,OPTIONS / /var/lib/docker /tmp 2>/dev/null
free -h
swapon --show
df -hT / /var/lib/docker /tmp
df -ih / /var/lib/docker /tmp
systemctl is-active docker fail2ban ufw
dpkg-query -W -f='${Package}\t${Version}\t${Status}\n' \
kesl kesl-gui kav4fs 2>/dev/null || true
```
Проверка проходит только для `amd64`/поддерживаемой архитектуры, Ubuntu 24.04
LTS и поддерживаемого KESL ядра. Требования Kaspersky — минимум 2 ГБ RAM,
1 ГБ swap и 4 ГБ свободного диска, но этого недостаточно для данной ВМ:
Compose-лимиты суммарно около 11,6 ГБ. При RAM менее 16 ГБ установка требует
отдельного решения владельца сервиса о доступном запасе.
### 1.2. Docker и приложение
```sh
docker info --format \
'driver={{.Driver}} root={{.DockerRootDir}} containers={{.Containers}} running={{.ContainersRunning}}'
/usr/local/sbin/han-vm1-compose ps
docker ps --format \
'table {{.Names}}\t{{.Status}}\t{{.Image}}'
docker stats --no-stream --format \
'table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.BlockIO}}\t{{.PIDs}}'
```
Сохраните список и состояние именованных volumes без содержимого:
```sh
for volume in redis-data otel-queue nginx-cache; do
docker volume ls --format '{{.Name}}' |
while IFS= read -r name; do
case "$name" in
*"$volume"*)
docker volume inspect --format \
'{{.Name}}\t{{.Mountpoint}}' "$name"
;;
esac
done
done
```
В change record перенесите три фактических mountpoint. Не подставляйте
предполагаемый Compose prefix.
### 1.3. Health, edge и firewall
Под root на ВМ:
```sh
systemctl --no-pager status \
han-secrets@production.service han-stack@production.service
iptables -S HAN-CHAT-DOCKER
iptables -L HAN-CHAT-DOCKER -n -v
ufw status verbose
journalctl --since '-30 min' --no-pager \
-u han-stack@production.service -p warning
```
С trusted external host выполните smoke из
`deployment/RUNBOOK.production.ru.md`, раздел 10. Если запускается repository
`deployment/scripts/smoke.sh`, он должен использовать штатный secret launcher;
не печатайте resolved environment.
Снимите из штатного observability baseline:
- API request rate, 5xx и p50/p95/p99 latency;
- Redis latency, blocked clients и memory;
- container restart/OOM count;
- host CPU, available RAM, swap, disk IO/IO wait;
- OTEL exporter failures и queue depth.
Без доступных baseline и rollback approver к установке не переходить.
## 2. Проверка пакета и подготовка
GUI на сервер не устанавливается. Пакет передаётся в root-only staging,
например `/root/kesl-install`, и удаляется после приёмки.
```sh
install -d -m 0700 -o root -g root /root/kesl-install
install -m 0600 -o root -g root \
/tmp/<KESL_12_4_AMD64_DEB> /root/kesl-install/kesl.deb
sha256sum /root/kesl-install/kesl.deb
dpkg-deb -f /root/kesl-install/kesl.deb Package Version Architecture
```
Сравните SHA-256 с опубликованным/полученным по доверенному каналу значением.
Не продолжайте при package name не `kesl`, неверной архитектуре или версии не
12.4.x.
Перед установкой сохраните только безопасные snapshots:
```sh
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
```
## 3. Установка с отключённой защитой
Установка изменяет host и выполняется только в maintenance window.
```sh
apt-get install /root/kesl-install/kesl.deb
```
Создайте `/root/kesl-install/autoinstall.ini` с mode `0600`. Значения EULA,
Privacy Policy и KSN должны быть осознанно согласованы с Security/Legal, а не
скопированы механически:
```ini
KSVLA_MODE=No
ENDPOINT_AGENT_MODE=No
EULA_AGREED=<Yes_AFTER_APPROVAL>
PRIVACY_POLICY_AGREED=<Yes_AFTER_APPROVAL>
USE_KSN=<Yes_OR_No_AFTER_APPROVAL>
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
```
Для Ubuntu AppArmor значение `CONFIGURE_SELINUX=No` ожидаемо. `UseFanotify`
не требует сборки стороннего kernel module. Если выбран KSN, документируйте
передачу данных и правовое основание.
Первоначальная настройка:
```sh
chmod 0600 /root/kesl-install/autoinstall.ini
/opt/kaspersky/kesl/bin/kesl-setup.pl \
--autoinstall=/root/kesl-install/autoinstall.ini
test "$?" -eq 0
systemctl --no-pager status kesl
kesl-control --app-info
kesl-control --supported-tech-info
kesl-control --get-task-list
```
Если используется activation code, не передавайте его аргументом команды и не
храните в этом репозитории. Выполните активацию по официальной инструкции
Kaspersky с защищённым локальным вводом/файлом ключа.
## 4. Ресурсные ограничения до первого scan
В KESL 12.4 `ScanMemoryLimit` по умолчанию равен 8192 МБ, а `MaxMemory=auto`
может разрешить до 50% доступной RAM. Для ВМ1 эти defaults не принимаются без
измерений.
Выберите значения по фактическому baseline:
- constrained test VM, 4 ГБ RAM: `ScanMemoryLimit=512`,
`MaxMemory=1024MB`, `OnDemandCPULimit=15`; только по разделу 0.1;
- 16 ГБ RAM: начните с `ScanMemoryLimit=1024`, `MaxMemory=2048MB`;
- 24–32 ГБ RAM: начните с `ScanMemoryLimit=2048`, `MaxMemory=4096MB`;
- иной размер: согласуйте значения; `ScanMemoryLimit` должен быть ниже
`MaxMemory`, а после резервирования KESL у приложения должен оставаться
исходный запас.
Сначала сохраните исходные настройки:
```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
```
Установите CPU limit для ODS/ContainerScan:
```sh
kesl-control --set-app-settings \
UseOnDemandCPULimit=Yes OnDemandCPULimit=<15_FOR_TEST_OR_APPROVED_VALUE>
```
Для изменения `ScanMemoryLimit` и `MaxMemory` следуйте официальной процедуре:
остановите KESL, внесите значения в секцию `[General]` файла
`/var/opt/kaspersky/kesl/common/kesl.ini`, затем запустите KESL. Не заменяйте
файл целиком и не применяйте шаблон из репозитория как готовый конфиг.
После запуска проверьте:
```sh
systemctl is-active kesl
kesl-control --get-app-settings
kesl-control --app-info
```
## 5. Обновление баз — АВЗ.2
Запустите предустановленную задачу Update (ID 6) и дождитесь результата:
```sh
kesl-control --get-settings 6
kesl-control --get-schedule 6
kesl-control --start-task 6 -W
kesl-control --get-task-state 6
kesl-control --app-info
```
Проверьте действующую лицензию, `Базы приложения загружены: Да`, свежую дату
выпуска баз и успешное завершение Update. Затем задайте почасовой запуск:
```sh
kesl-control --set-schedule 6 RuleType=Hourly
kesl-control --get-schedule 6
```
Если установленная сборка требует интервал в `StartTime`, не угадывайте
синтаксис: экспортируйте schedule и измените его по документации именно этой
сборки. До успешного автообновления АВЗ.2 не принимается.
Сразу после обновления повторите разделы 1.2–1.3. При деградации выполните
rollback из раздела 11.
## 6. Исключения hot-data
Исключения создаются только после получения фактических mountpoint в разделе
1.2. Разрешены три области:
- `<REDIS_DATA_MOUNTPOINT>` — AOF/RDB;
- `<OTEL_QUEUE_MOUNTPOINT>` — persistent telemetry queue;
- `<NGINX_CACHE_MOUNTPOINT>` — regenerable cache.
До изменения экспортируйте параметры:
```sh
kesl-control --get-settings 1 \
--file /root/kesl-install/file-threat.before.ini
```
Добавьте обычные исключения File Threat Protection:
```sh
kesl-control --set-settings 1 \
--add-exclusion <REDIS_DATA_MOUNTPOINT>
kesl-control --set-settings 1 \
--add-exclusion <OTEL_QUEUE_MOUNTPOINT>
kesl-control --set-settings 1 \
--add-exclusion <NGINX_CACHE_MOUNTPOINT>
kesl-control --get-settings 1
```
Не исключать:
- весь `/var/lib/docker`, `/var/lib/docker/overlay2` или все volumes;
- `/opt/han-chat/releases` и `/opt/han-chat/current`;
- `/var/lib/han-deploy/incoming`;
- `/etc/han`, `/run/han-chat`, `/etc/letsencrypt`;
- `/tmp`, `/var/tmp`, `/root` или весь filesystem.
Обычное исключение из scan может не исключить файловый перехват. Не создавайте
bind mounts и не добавляйте `ExcludedMountPoint` в первой итерации. Это
допустимо только если измерена деградация и Security письменно принял
компенсацию плановой проверкой/container scan.
## 7. Пилот задач проверки
Убедитесь, что все ODS/ContainerScan schedules, кроме Update, пока ручные:
```sh
kesl-control --get-task-list
kesl-control --get-schedule 2
kesl-control --get-schedule 18
kesl-control --set-schedule 2 RuleType=Manual
kesl-control --set-schedule 18 RuleType=Manual
```
Идентификаторы подтвердите через `--get-task-list`; не применяйте команды,
если тип задачи не совпадает.
### 7.1. Ограниченная on-demand проверка host
Сначала проверьте небольшой immutable release, не корень filesystem:
```sh
kesl-control --scan-file /opt/han-chat/current/backend \
--action Inform
```
В первом пилоте действие `Inform` не изменяет release. Проверьте результат,
events, ресурсы и application health:
```sh
kesl-control -E --query -n 100 --reverse
kesl-control --get-statistic
/usr/local/sbin/han-vm1-compose ps
docker stats --no-stream
```
### 7.2. Проверка контейнеров
Перед scan снимите список running containers. Проверяйте по одному объекту,
начиная с stateless/oneshot image, не Redis и не Keycloak:
```sh
docker ps --format '{{.Names}}\t{{.Image}}'
kesl-control --get-settings 19
kesl-control --scan-container <STATELESS_CONTAINER_OR_IMAGE>
```
После успешного одиночного теста задачу `Container_Scan` (ID 18) можно
назначить еженедельно в согласованное время. Перед этим проверьте параметры:
по умолчанию `ContainerScanAction=StopContainerIfFailed`; production-контейнер
не должен останавливаться из-за технической ошибки сканирования. Итоговое
действие отдельно утверждает Security.
Container scan после deploy выполняется только после завершения smoke, а не
одновременно с pull/start/migrations.
## 8. Ступенчатое включение File Threat Protection — АВЗ.1
`DISABLE_PROTECTION=Yes` отключает компоненты после setup. До старта сохраните
параметры и убедитесь, что `ScanArchived=No`:
```sh
kesl-control --get-settings 1
kesl-control --get-task-state 1
```
Для пилота включите асинхронный режим перехватчика `Notify`, при котором
KESL журналирует обнаружения, но не выполняет блокирующее действие:
```sh
kesl-control --set-app-settings InterceptorProtectionMode=Notify
kesl-control --start-task 1
kesl-control --get-task-state 1
```
Пилот длится минимум 2–4 часа обычной нагрузки. Каждые 15 минут проверяйте
метрики раздела 1 и события KESL. Не считайте этот режим конечной реализацией
АВЗ.1: он не обеспечивает блокирование/лечение.
Если пилот стабилен, в том же maintenance window:
1. проверьте, что `ActionOnThreat=DisinfectDeleteIfNotPossible`;
2. установите `InterceptorProtectionMode=Block`;
3. перезапустите task 1, если этого требует текущая сборка;
4. повторите health, smoke, firewall и resource checks.
```sh
kesl-control --set-settings 1 \
ActionOnThreat=DisinfectDeleteIfNotPossible ScanArchived=No
kesl-control --set-app-settings InterceptorProtectionMode=Block
kesl-control --stop-task 1
kesl-control --start-task 1
kesl-control --get-task-state 1
kesl-control --app-info
```
В режиме `Block` доступ к файлу ожидает результат проверки. При появлении
latency вернитесь в `Notify` либо остановите task 1 и выполните rollback;
не расширяйте исключения вслепую.
## 9. Приёмочный тест и evidence
Тест EICAR выполняется только с письменным разрешением Security в отдельном
безопасном каталоге, не в release, volume, backup, secret или upload path.
Используйте официальную контрольную строку/файл с сайта EICAR/Kaspersky; этот
репозиторий намеренно не содержит тестовый образец.
До теста:
```sh
install -d -m 0700 -o root -g root /root/kesl-eicar-test
date -Is
kesl-control --app-info
kesl-control --get-task-state 1
```
Ожидается блокирование/лечение/карантин и событие KESL. Не прикладывайте сам
образец к evidence. Сохраните:
```sh
kesl-control --app-info
kesl-control --get-task-list
kesl-control --get-settings 1
kesl-control --get-schedule 6
kesl-control -E --query -n 100 --reverse
```
Очистите тестовый каталог после подтверждения реакции и заполните
`deployment/kesl/EVIDENCE.AVZ.ru.md`.
## 10. Финальные проверки
На ВМ:
```sh
systemctl is-active kesl docker \
han-secrets@production.service han-stack@production.service
kesl-control --app-info
kesl-control --get-task-state 1
kesl-control --get-task-state 6
/usr/local/sbin/han-vm1-compose ps
iptables -S HAN-CHAT-DOCKER
ufw status verbose
```
С внешнего trusted host повторите production smoke и negative port probes.
Сравните метрики минимум за 24 часа. Не выполняйте reboot только ради KESL.
Если пакет/ядро явно запросили reboot, проведите его отдельным окном по
reboot gate основного production-runbook.
После приёмки удалите package/autoinstall и временные snapshots с ВМ только
после переноса разрешённого evidence:
```sh
rm -rf /root/kesl-install /root/kesl-eicar-test
```
## 11. Rollback
### 11.1. До включения блокирующей защиты
```sh
kesl-control --stop-task 1 2>/dev/null || true
apt-get purge kesl
systemctl daemon-reload
systemctl restart han-chat-docker-firewall.service
/usr/local/sbin/han-vm1-compose ps
iptables -S HAN-CHAT-DOCKER
ufw status verbose
```
Повторите smoke и resource checks. Docker и application stack без причины не
перезапускайте.
### 11.2. При инциденте после включения Block
Сначала минимально обратимое действие:
```sh
kesl-control --set-app-settings InterceptorProtectionMode=Notify
```
Если управление KESL не отвечает:
```sh
systemctl stop kesl
```
Затем восстановите доступность и соберите события. Полный `apt-get purge kesl`
выполняйте только по решению change approver. Reboot — только если он требуется
для удаления/ядра и есть отдельное окно.
Нельзя выполнять `docker compose down -v`, удалять volumes, чистить Redis AOF,
пересоздавать VM или менять firewall ради обхода проблемы KESL.
## 12. Эксплуатационный режим
- Update (ID 6): каждый час; alert при ошибке или устаревании баз.
- File Threat Protection (ID 1): постоянно, `Block`,
`DisinfectDeleteIfNotPossible`, `ScanArchived=No`.
- ODS: еженедельно в низкую нагрузку после подтверждения resource budget.
- ContainerScan: еженедельно и после deploy, только после smoke.
- Ежедневно: статус лицензии, компонентов, дата баз и ошибки KESL.
- Ежемесячно: review исключений и фактической нагрузки.
- После upgrade KESL/kernel/Docker: повтор пилота, smoke и evidence delta.
Любое новое исключение должно иметь владельца, причину, срок пересмотра,
компенсирующую проверку и подтверждение Security.
@@ -0,0 +1,90 @@
# KESL 12.4 standalone policy decisions for HAN Chat VM1.
#
# REFERENCE ONLY: this is deliberately not a complete kesl-control import file.
# Export the settings from the installed build, review the diff, and apply
# individual values by the commands in RUNBOOK.KESL.ru.md. Importing a partial
# or version-mismatched file can reset settings that are not listed here.
#
# This file contains no license, activation code, proxy credentials, hostname,
# IP address, secret or environment value.
[deployment]
product_major_minor=12.4
mode=standard_standalone
gui=disabled
update_source=KLServers
interceptor=fanotify
network_features=disabled
ksn=<Yes_OR_No_AFTER_SECURITY_AND_LEGAL_APPROVAL>
[resource_budget]
# Choose from measured host capacity; see runbook section 4.
scan_memory_limit_mb=<1024_OR_APPROVED_VALUE>
max_memory=<2048MB_OR_APPROVED_VALUE>
use_on_demand_cpu_limit=Yes
on_demand_cpu_limit_percent=25
[constrained_test_vm_override]
# Explicitly approved only for the 4 GB non-production VM. Never copy this
# profile to production without new sizing and baseline.
scan_memory_limit_mb=512
max_memory=1024MB
use_on_demand_cpu_limit=Yes
on_demand_cpu_limit_percent=15
full_filesystem_scan=forbidden
scan_archived=No
parallel_ods_and_container_scan=forbidden
stop_available_memory_mb=512
[update_task_6]
rule_type=Hourly
required_result=completed_successfully
required_bases_loaded=Yes
stale_bases_alert=<APPROVED_THRESHOLD>
[file_threat_protection_task_1]
steady_state=Started
interceptor_protection_mode=Block
action_on_threat=DisinfectDeleteIfNotPossible
scan_archived=No
[file_threat_exclusions]
# Replace placeholders only with mountpoints returned by docker volume inspect.
# Do not guess the Compose project prefix.
item_0000=<REDIS_DATA_MOUNTPOINT>
item_0001=<OTEL_QUEUE_MOUNTPOINT>
item_0002=<NGINX_CACHE_MOUNTPOINT>
[forbidden_broad_exclusions]
item_0000=/var/lib/docker
item_0001=/var/lib/docker/overlay2
item_0002=/opt/han-chat
item_0003=/var/lib/han-deploy/incoming
item_0004=/etc/han
item_0005=/run/han-chat
item_0006=/tmp
item_0007=/
[on_demand_scan]
initial_scope=/opt/han-chat/current/backend
initial_action=Inform
steady_schedule=<APPROVED_WEEKLY_LOW_LOAD_WINDOW>
[container_scan_task_18]
initial_schedule=Manual
steady_schedule=<APPROVED_WEEKLY_LOW_LOAD_WINDOW>
post_deploy=after_smoke_only
# Review before enabling: the vendor default can stop a container when scan
# fails technically.
container_scan_action=<SECURITY_APPROVED_NON_DISRUPTIVE_VALUE>
[evidence]
application_info=required
license_valid=required
bases_loaded_and_fresh=required
file_threat_task_started=required
update_schedule_hourly=required
eicar_block_or_remediation_event=required
application_smoke_after_each_stage=required
firewall_unchanged=required
resource_comparison_24h=required