Реализована проверка версионности и требование soft\force обновлений приложения
This commit is contained in:
@@ -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 `1–5` немедленно получают 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. Связанные меры
|
||||
|
||||
### РСБ.1–3, РСБ.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
|
||||
Reference in New Issue
Block a user