Внедрение KESL на ВМ2 + замена CLAMAV на KESL
This commit is contained in:
@@ -1,10 +1,10 @@
|
||||
Логирование операций
|
||||
|
||||
1. env:
|
||||
|
||||
editor /etc/han/vm1.env
|
||||
изменение Release_Version
|
||||
+ две переменные:
|
||||
HOST_NAME=devhanapp \ prodhanapp.ihan.ru.
|
||||
HOST_NAME=prodhanapp.ihan.ru \devhanapp \
|
||||
TELEMETRYGEN_IMAGE=ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen@sha256:9461e2c7213219467e5f775ea8652d15c990afb4d1ca94e62344f2025a7587d1
|
||||
|
||||
2. Обновить образы:
|
||||
@@ -25,7 +25,7 @@ tar --exclude=backend/.env `
|
||||
-czf "vm1-backend-$Release.tar.gz" `
|
||||
-C C:/Users/MI/Documents/Assistent/HAN_chat_specification/VM1_app/codebase backend
|
||||
Get-FileHash "vm1-backend-$Release.tar.gz" -Algorithm SHA256
|
||||
scp "vm1-backend-$Release.tar.gz" devVM1Deploy:/var/lib/han-deploy/incoming/
|
||||
scp "vm1-backend-$Release.tar.gz" prodVM1Deploy:/var/lib/han-deploy/incoming/
|
||||
```
|
||||
|
||||
RELEASE='1.0.1'
|
||||
@@ -91,14 +91,14 @@ sudo apt purge -y dos2unix
|
||||
|
||||
|
||||
scp `
|
||||
C:/Users/MI/.ssh/devDeploy.pub `
|
||||
C:/Users/MI/.ssh/devAdmin.pub `
|
||||
devVM1Deploy:/var/lib/han-deploy/incoming/
|
||||
C:/Users/MI/.ssh/prodDeploy.pub `
|
||||
C:/Users/MI/.ssh/prodAdmin.pub `
|
||||
prodVM1Deploy:/var/lib/han-deploy/incoming/
|
||||
|
||||
install -m 0600 -o root -g root /var/lib/han-deploy/incoming/devDeploy.pub /root/bootstrap/deploy.pub
|
||||
install -m 0600 -o root -g root /var/lib/han-deploy/incoming/devAdmin.pub /root/bootstrap/admin.pub
|
||||
install -m 0600 -o root -g root /var/lib/han-deploy/incoming/prodDeploy.pub /root/bootstrap/deploy.pub
|
||||
install -m 0600 -o root -g root /var/lib/han-deploy/incoming/prodAdmin.pub /root/bootstrap/admin.pub
|
||||
|
||||
rm -f /var/lib/han-deploy/incoming/devDeploy.pub /var/lib/han-deploy/incoming/devAdmin.pub
|
||||
rm -f /var/lib/han-deploy/incoming/prodDeploy.pub /var/lib/han-deploy/incoming/prodAdmin.pub
|
||||
|
||||
cd /opt/han-chat/current/backend
|
||||
|
||||
@@ -109,6 +109,12 @@ ADMIN_AUTHORIZED_KEY_FILE=/root/bootstrap/admin.pub \
|
||||
HARDEN_SSH=true SKIP_APT_UPGRADE=true \
|
||||
/opt/han-chat/current/backend/deployment/scripts/setup-vm.sh
|
||||
|
||||
export OTEL_REMOTE_ENDPOINT="$(awk -F= '$1=="OTEL_REMOTE_ENDPOINT"{print substr($0,index($0,"=")+1)}' /etc/han/vm1.env)"
|
||||
export OTEL_REMOTE_TLS_INSECURE="$(awk -F= '$1=="OTEL_REMOTE_TLS_INSECURE"{print substr($0,index($0,"=")+1)}' /etc/han/vm1.env)"
|
||||
export HOST_NAME="$(awk -F= '$1=="HOST_NAME"{print substr($0,index($0,"=")+1)}' /etc/han/vm1.env)"
|
||||
export APP_ENV="$(awk -F= '$1=="APP_ENV"{print substr($0,index($0,"=")+1)}' /etc/han/vm1.env)"
|
||||
export RELEASE_VERSION="$(awk -F= '$1=="RELEASE_VERSION"{print substr($0,index($0,"=")+1)}' /etc/han/vm1.env)"
|
||||
printf '%s\n' "$OTEL_REMOTE_ENDPOINT"
|
||||
/usr/local/bin/otelcol-contrib validate \
|
||||
--config=/opt/han-chat/current/backend/deployment/observability/otel-host-collector.yaml
|
||||
|
||||
@@ -116,12 +122,16 @@ chmod 0755 /opt/han-chat/current/backend/scripts/validate-env
|
||||
/opt/han-chat/current/backend/deployment/preflight.sh
|
||||
|
||||
systemctl restart han-stack@production.service
|
||||
если ошибка systemctl status han-stack@production.service
|
||||
если ошибка
|
||||
journalctl -u han-stack@production.service --no-pager -n 80 --output=cat
|
||||
### !!! (образы могут устареть в будущем)
|
||||
docker pull cr.selcloud.ru/han-images/nginx@sha256:2cd7d32967b400c15bb6aaa18e97e99d429b2b6f2bfd54b965fc7d42a91a228e
|
||||
docker pull cr.selcloud.ru/han-images/sms-service@sha256:404685c45b7391a78f95c014ce8fdff64a84ccd7ae5b280e7a65b524c109d898
|
||||
|
||||
systemctl start han-host-otel-collector@production.service
|
||||
rm -f /root/bootstrap/deploy.pub /root/bootstrap/admin.pub
|
||||
|
||||
Canary test: CONFIG_FILE=/etc/han/vm1.env ./deployment/scripts/verify-observability.sh
|
||||
Canary test:
|
||||
chmod 0755 /opt/han-chat/current/backend/deployment/scripts/verify-observability.sh
|
||||
CONFIG_FILE=/etc/han/vm1.env ./deployment/scripts/verify-observability.sh
|
||||
|
||||
rm -f /root/bootstrap/deploy.pub /root/bootstrap/admin.pub
|
||||
@@ -0,0 +1,647 @@
|
||||
# Ставим KES на ВМ1
|
||||
|
||||
|
||||
|
||||
|
||||
# Ставим KES на ВМ2
|
||||
|
||||
Готовим новый образ Message Safety
|
||||
MESSAGE_SAFETY_IMAGE=cr.selcloud.ru/han-images/han-message-safety@sha256:225808bd111be03112ab6693b5a10e3d97ae8cdbe5370a5d0aa5a8e2278a3044
|
||||
|
||||
Копируем проект
|
||||
```powershell
|
||||
$Release = "1.0.1"
|
||||
tar --exclude=services/.env `
|
||||
--exclude='services/**/__pycache__' `
|
||||
--exclude='services/**/.pytest_cache' `
|
||||
--exclude='services/**/.ruff_cache' `
|
||||
-czf "vm2-services-$Release.tar.gz" -C C:\Users\MI\Documents\Assistent\HAN_chat_specification\VM2_services\codebase services
|
||||
Get-FileHash "vm2-services-$Release.tar.gz" -Algorithm SHA256
|
||||
scp "vm2-services-$Release.tar.gz" prodVM2Deploy:/var/lib/han-deploy/incoming/
|
||||
|
||||
|
||||
scp "vm2-services-$Release.tar.gz" devVM2Deploy:/var/lib/han-deploy/incoming/
|
||||
|
||||
```
|
||||
|
||||
Проверить размер диска
|
||||
|
||||
RELEASE='1.0.1'
|
||||
EXPECTED_SHA256='1030DE6A50BA945FAEC5A45E1D42C47A01808E737AA1955871F3D2ED089F8879'
|
||||
ARCHIVE="/var/lib/han-deploy/incoming/vm2-services-${RELEASE}.tar.gz"
|
||||
printf '%s %s\n' "$EXPECTED_SHA256" "$ARCHIVE" | sha256sum --check -
|
||||
tar -tvzf "$ARCHIVE"
|
||||
if tar -tzf "$ARCHIVE" | grep -Eq '(^/|(^|/)\.\.(/|$)|^services/\.env$)'; then
|
||||
echo 'ОШИБКА: архив содержит небезопасный путь или .env' >&2
|
||||
exit 1
|
||||
fi
|
||||
if tar -tzf "$ARCHIVE" | grep -Ev '^services(/|$)' | grep -q .; then
|
||||
echo 'ОШИБКА: архив содержит файлы вне каталога services' >&2
|
||||
exit 1
|
||||
fi
|
||||
if tar -tvzf "$ARCHIVE" | awk '$1 ~ /^[lh]/ { found=1 } END { exit !found }'; then
|
||||
echo 'ОШИБКА: архив содержит symlink или hardlink' >&2
|
||||
exit 1
|
||||
fi
|
||||
install -d -m 0755 -o root -g root /opt/han-chat/services
|
||||
STAGING="$(mktemp -d /opt/han-chat/.vm2-release.XXXXXX)"
|
||||
tar --extract --gzip --file "$ARCHIVE" \
|
||||
--directory "$STAGING" --no-same-owner --no-same-permissions
|
||||
test -f "$STAGING/services/docker-compose.yml"
|
||||
rsync -a --delete --exclude=.env \
|
||||
--chown=root:root --chmod=D755,F644 \
|
||||
"$STAGING/services/" /opt/han-chat/services/
|
||||
rm -rf -- "$STAGING"
|
||||
chmod 0755 /opt/han-chat/services/deployment/preflight.sh
|
||||
|
||||
|
||||
cd /opt/han-chat/services/
|
||||
sudo apt update
|
||||
sudo apt install -y dos2unix
|
||||
find . -type f \( \
|
||||
-name '*.sh' -o -name '*.py' -o -name '*.service' -o -name '*.sudoers' -o -name 'han-compose' -o -name 'han-secrets' -o -name 'han-message-safety-mode' -o -name '*.yml' -o -name '*.conf*' -o -name 'preflight.sh' -o -name '*.yaml' -o -name '*.md' -o -name '*.toml' -o -name 'Dockerfile' -o -name '*.acl*' \
|
||||
\) -exec dos2unix {} +
|
||||
find -type f -exec file {} \; | grep -i 'CRLF'
|
||||
|
||||
sudo apt remove -y dos2unix
|
||||
sudo apt purge -y dos2unix
|
||||
|
||||
Копируем образ на ВМ:
|
||||
scp "C:\Users\MI\Documents\job\HAN_new_life\HANapp\docs\kes 12.4\049-16-d-01.iso" "C:\Users\MI\Documents\job\HAN_new_life\HANapp\docs\kes 12.4\049-16-d-01.sig" prodVM2Deploy:/var/lib/han-deploy/incoming/
|
||||
|
||||
Проверка файла:
|
||||
apt install -y rhash
|
||||
ISO='/var/lib/han-deploy/incoming/049-16-d-01.iso'
|
||||
command -v rhash
|
||||
rhash --gost "$ISO"
|
||||
|
||||
Должно быть 6a94b16afad211e8b9be5ec86f5379184f2b3a9e5869843fe763e2796e5ac1d3
|
||||
|
||||
sudo apt remove -y rhash
|
||||
sudo apt purge -y rhash
|
||||
|
||||
Если совпадёт, извлеките DEB:
|
||||
```sh
|
||||
install -d -m 0700 -o root -g root \
|
||||
/root/kesl-install /mnt/kesl-iso
|
||||
|
||||
mount -o ro,loop "$ISO" /mnt/kesl-iso
|
||||
|
||||
find /mnt/kesl-iso -type f \
|
||||
\( -iname 'kesl_*_amd64.deb' -o -iname '*.deb' \) |
|
||||
sort
|
||||
```
|
||||
|
||||
Затем:
|
||||
```sh
|
||||
DEB='/mnt/kesl-iso/kesl/kesl_12.4.0-1225_amd64.deb'
|
||||
|
||||
dpkg-deb -f "$DEB" Package Version Architecture
|
||||
sha256sum "$DEB"
|
||||
|
||||
install -m 0600 -o root -g root \
|
||||
"$DEB" /root/kesl-install/kesl.deb
|
||||
|
||||
umount /mnt/kesl-iso
|
||||
rmdir /mnt/kesl-iso
|
||||
```
|
||||
|
||||
Ожидаемые свойства: kesl, версия 12.4.x, архитектура amd64.
|
||||
После этого можно выполнять:
|
||||
apt-get install /root/kesl-install/kesl.deb
|
||||
|
||||
|
||||
Далее
|
||||
```sh
|
||||
install -m 0600 -o root -g root /dev/null /root/kesl-install/autoinstall.ini
|
||||
|
||||
cat > /root/kesl-install/autoinstall.ini <<'EOF'
|
||||
KSVLA_MODE=No
|
||||
ENDPOINT_AGENT_MODE=No
|
||||
EULA_AGREED=Yes
|
||||
PRIVACY_POLICY_AGREED=Yes
|
||||
USE_KSN=No
|
||||
GROUP_CLEAN=Yes
|
||||
LOCALE=ru_RU.UTF-8
|
||||
INSTALL_LICENSE=None
|
||||
UPDATER_SOURCE=KLServers
|
||||
UPDATE_EXECUTE=No
|
||||
KERNEL_SRCS_INSTALL=No
|
||||
USE_GUI=No
|
||||
CONFIGURE_SELINUX=No
|
||||
DISABLE_PROTECTION=Yes
|
||||
INTERCEPTOR_MODE=UseFanotify
|
||||
ENABLE_TRACES_ON_FIRST_STARTUP=No
|
||||
EOF
|
||||
|
||||
chmod 0600 /root/kesl-install/autoinstall.ini
|
||||
stat -c '%U:%G:%a %n' /root/kesl-install/autoinstall.ini
|
||||
|
||||
/opt/kaspersky/kesl/bin/kesl-setup.pl \
|
||||
--autoinstall=/root/kesl-install/autoinstall.ini
|
||||
echo "kesl-setup exit=$?"
|
||||
|
||||
systemctl --no-pager status kesl
|
||||
kesl-control --app-info --json
|
||||
kesl-control --get-task-list
|
||||
```
|
||||
|
||||
|
||||
```sh
|
||||
set +o history
|
||||
unset HISTFILE
|
||||
read -rsp 'KESL activation code: ' KESL_CODE; echo
|
||||
kesl-control --add-active-key "$KESL_CODE"
|
||||
unset KESL_CODE
|
||||
set -o history
|
||||
kesl-control -L --query
|
||||
```
|
||||
|
||||
Не стартуйте Update (task 6) и File Threat Protection (task 1), пока не срежете дефолты памяти. На 8 ГБ `ScanMemoryLimit=8192` и `MaxMemory=auto` (до 50% RAM, минимум 2 ГБ) слишком много, тем более пока жив ClamAV.
|
||||
|
||||
Ключ из `-L --query` в evidence/репозиторий не копируйте — достаточно статуса `Active subscription`.
|
||||
|
||||
Дальше — секция 3 runbook. Кандидат для ВМ2: `ScanMemoryLimit=1024`, `MaxMemory=2048MB`. CPU: `OnDemandCPULimit=15`, как на пилоте ВМ1, пока ClamAV не снят.
|
||||
|
||||
**1. Снимок текущих настроек**
|
||||
|
||||
```sh
|
||||
kesl-control --get-app-settings \
|
||||
--file /root/kesl-install/app-settings.before.ini
|
||||
install -m 0600 -o root -g root \
|
||||
/var/opt/kaspersky/kesl/common/kesl.ini \
|
||||
/root/kesl-install/kesl.ini.before
|
||||
awk '/^\[General\]/,/^\[/' /var/opt/kaspersky/kesl/common/kesl.ini | head -n 40
|
||||
```
|
||||
|
||||
**2. Лимит CPU (KESL ещё работает)**
|
||||
|
||||
```sh
|
||||
kesl-control --set-app-settings \
|
||||
UseOnDemandCPULimit=Yes OnDemandCPULimit=15
|
||||
```
|
||||
|
||||
**3. Память — только при остановленном KESL, файл целиком не подменять**
|
||||
|
||||
Сначала посмотрите `[General]` из шага 1. Если там уже есть `ScanMemoryLimit` и `MaxMemory`:
|
||||
|
||||
```sh
|
||||
systemctl stop kesl
|
||||
systemctl is-active kesl
|
||||
|
||||
grep -nE '^(ScanMemoryLimit|MaxMemory)=' /var/opt/kaspersky/kesl/common/kesl.ini
|
||||
|
||||
sed -i \
|
||||
-e 's/^ScanMemoryLimit=8192$/ScanMemoryLimit=1024/' \
|
||||
-e 's/^MaxMemory=auto$/MaxMemory=2048MB/' \
|
||||
/var/opt/kaspersky/kesl/common/kesl.ini
|
||||
|
||||
grep -nE '^(ScanMemoryLimit|MaxMemory)=' /var/opt/kaspersky/kesl/common/kesl.ini
|
||||
|
||||
systemctl start kesl
|
||||
systemctl is-active kesl
|
||||
kesl-control --get-app-settings | grep -iE 'UseOnDemandCPULimit|OnDemandCPULimit'
|
||||
```
|
||||
|
||||
**Ожидаемо после рестарта**
|
||||
|
||||
- `kesl.service` = `active`
|
||||
- в settings: `ScanMemoryLimit=1024`, `MaxMemory=2048MB`, `UseOnDemandCPULimit=Yes`, `OnDemandCPULimit=15`
|
||||
- task 1 и 6 по-прежнему `Stopped`
|
||||
- базы всё ещё не загружены
|
||||
|
||||
После этого — загрузка баз:
|
||||
`kesl-control --start-task 6 -W`
|
||||
|
||||
```sh
|
||||
kesl-control --get-task-state 6
|
||||
kesl-control --app-info --json
|
||||
START="$(LC_ALL=C date +'%Y/%b/%d %H:%M:%S;1')"
|
||||
kesl-control --set-schedule 6 RuleType=Hourly \
|
||||
"StartTime=${START}" RunMissedStartRules=No RandomInterval=0
|
||||
kesl-control --get-schedule 6
|
||||
```
|
||||
|
||||
Обязательный следующий шаг — рестарт KESL. В --app-info есть:
|
||||
|
||||
Post-update actions: The application module has been updated. Restart the application.
|
||||
|
||||
Task 1 до рестарта не запускайте.
|
||||
```sh
|
||||
systemctl restart kesl
|
||||
systemctl is-active kesl
|
||||
kesl-control --app-info --json
|
||||
grep -nE '^(ScanMemoryLimit|MaxMemory)=' /var/opt/kaspersky/kesl/common/kesl.ini
|
||||
kesl-control --get-app-settings | grep -iE 'UseOnDemandCPULimit|OnDemandCPULimit'
|
||||
kesl-control --get-schedule 6
|
||||
kesl-control --get-task-state 1
|
||||
kesl-control --get-task-state 6
|
||||
```
|
||||
|
||||
```sh
|
||||
kesl-control --get-settings 1 \
|
||||
--file /root/kesl-install/file-threat.before.ini
|
||||
kesl-control --set-settings 1 \
|
||||
--add-exclusion /var/lib/docker/volumes/han-processing_redis-safety-data/_data
|
||||
kesl-control --set-settings 1 \
|
||||
--add-exclusion /var/lib/docker/volumes/han-processing_otel-queue/_data
|
||||
kesl-control --start-task 1
|
||||
kesl-control --set-settings 1 \
|
||||
ActionOnThreat=DisinfectDeleteIfNotPossible ScanArchived=No
|
||||
kesl-control --get-task-state 1
|
||||
kesl-control --get-settings 1
|
||||
```
|
||||
|
||||
Сразу снимите coexistence, пока OAS прогревается:
|
||||
```sh
|
||||
date -Is
|
||||
free -h
|
||||
swapon --show
|
||||
vmstat 1 5
|
||||
systemctl is-active kesl docker han-processing.service
|
||||
kesl-control --app-info --json | grep -E 'File Threat|databases loaded|license'
|
||||
/usr/local/sbin/han-vm2-compose ps
|
||||
docker stats --no-stream
|
||||
```
|
||||
|
||||
Установка брокера
|
||||
```sh
|
||||
set -Eeuo pipefail
|
||||
|
||||
BASE=/opt/han-chat/services/deployment/kesl
|
||||
|
||||
for file in \
|
||||
kesl_scan_broker.py \
|
||||
han-kesl-scan-broker.service \
|
||||
han-kesl-scan-broker.socket \
|
||||
han-kesl-scan-broker.tmpfiles.conf
|
||||
do
|
||||
test -f "$BASE/$file"
|
||||
test ! -L "$BASE/$file"
|
||||
done
|
||||
|
||||
if getent group han-message-safety >/dev/null; then
|
||||
test "$(getent group han-message-safety | cut -d: -f3)" = 10001
|
||||
else
|
||||
! getent group 10001 >/dev/null
|
||||
groupadd --system --gid 10001 han-message-safety
|
||||
fi
|
||||
|
||||
install -d -m 0755 -o root -g root /usr/local/libexec
|
||||
|
||||
install -m 0750 -o root -g root \
|
||||
"$BASE/kesl_scan_broker.py" \
|
||||
/usr/local/libexec/han-kesl-scan-broker
|
||||
|
||||
install -m 0644 -o root -g root \
|
||||
"$BASE/han-kesl-scan-broker.service" \
|
||||
/etc/systemd/system/han-kesl-scan-broker.service
|
||||
|
||||
install -m 0644 -o root -g root \
|
||||
"$BASE/han-kesl-scan-broker.socket" \
|
||||
/etc/systemd/system/han-kesl-scan-broker.socket
|
||||
|
||||
install -m 0644 -o root -g root \
|
||||
"$BASE/han-kesl-scan-broker.tmpfiles.conf" \
|
||||
/etc/tmpfiles.d/han-kesl-scan-broker.conf
|
||||
|
||||
systemd-tmpfiles --create \
|
||||
/etc/tmpfiles.d/han-kesl-scan-broker.conf
|
||||
|
||||
systemctl daemon-reload
|
||||
|
||||
systemd-analyze verify \
|
||||
/etc/systemd/system/han-kesl-scan-broker.service \
|
||||
/etc/systemd/system/han-kesl-scan-broker.socket
|
||||
```
|
||||
|
||||
Если systemd-analyze verify завершился без ошибок, запуск:
|
||||
```sh
|
||||
systemctl enable --now han-kesl-scan-broker.socket
|
||||
|
||||
systemctl is-active kesl han-kesl-scan-broker.socket
|
||||
stat -c '%U:%G:%a %n' /run/han-kesl /run/han-kesl/scan.sock
|
||||
/usr/local/libexec/han-kesl-scan-broker --probe
|
||||
```
|
||||
|
||||
Ожидаемые права:
|
||||
/run/han-kesl root:han-message-safety:750
|
||||
/run/han-kesl/scan.sock root:han-message-safety:660
|
||||
|
||||
Создаём папку:
|
||||
|
||||
```sh
|
||||
install -d -m 0700 -o root -g han-message-safety \
|
||||
/var/lib/han-kesl-scan \
|
||||
/var/lib/han-kesl-scan/staging
|
||||
```
|
||||
|
||||
Проверка и запуск socket
|
||||
```
|
||||
systemd-analyze verify \
|
||||
/etc/systemd/system/han-kesl-scan-broker.service \
|
||||
/etc/systemd/system/han-kesl-scan-broker.socket
|
||||
systemctl enable --now han-kesl-scan-broker.socket
|
||||
systemctl is-active kesl han-kesl-scan-broker.socket
|
||||
stat -c '%U:%G:%a %n' /run/han-kesl /run/han-kesl/scan.sock
|
||||
/usr/local/libexec/han-kesl-scan-broker --probe
|
||||
journalctl --since '-10 min' --no-pager -u han-kesl-scan-broker.service
|
||||
```
|
||||
|
||||
Обновить .env
|
||||
editor /opt/han-chat/services/.env
|
||||
MESSAGE_SAFETY_IMAGE=cr.selcloud.ru/han-images/han-message-safety@sha256:225808bd111be03112ab6693b5a10e3d97ae8cdbe5370a5d0aa5a8e2278a3044
|
||||
Удалить
|
||||
CLAMAV_IMAGE
|
||||
MESSAGE_SAFETY_CLAMAV_HOST
|
||||
MESSAGE_SAFETY_CLAMAV_PORT
|
||||
Добавить
|
||||
MESSAGE_SAFETY_ANTIVIRUS_SOCKET=/run/han-kesl/scan.sock
|
||||
|
||||
Активировать config schema 2
|
||||
NEXT_VERSION='2'
|
||||
|
||||
/usr/local/sbin/han-vm2-compose --profile ops run --rm \
|
||||
--entrypoint message-safety-config message-safety-migrate \
|
||||
validate /app/app/artifacts/seed-config.yaml
|
||||
|
||||
/usr/local/sbin/han-vm2-compose --profile ops run --rm \
|
||||
--entrypoint message-safety-config message-safety-migrate \
|
||||
create /app/app/artifacts/seed-config.yaml \
|
||||
--version "$NEXT_VERSION" --actor '<OPERATOR>'
|
||||
|
||||
/usr/local/sbin/han-vm2-compose --profile ops run --rm \
|
||||
--entrypoint message-safety-config message-safety-migrate \
|
||||
activate --version "$NEXT_VERSION" --approved-by '<APPROVER>'
|
||||
|
||||
Вернуть затертые IP
|
||||
editor /opt/han-chat/services/nginx/allowlists/bitrix-webhook-allowlist.conf
|
||||
allow 195.208.184.200;
|
||||
allow 89.208.230.2;
|
||||
allow 195.208.184.34;
|
||||
allow 89.208.228.219;
|
||||
deny all;
|
||||
|
||||
editor /opt/han-chat/services/nginx/allowlists/private-caller-allowlist.conf
|
||||
allow 10.0.0.11;
|
||||
deny all;
|
||||
|
||||
Переключить только Message Safety
|
||||
cd /opt/han-chat/services/
|
||||
|
||||
deployment/preflight.sh
|
||||
/usr/local/sbin/han-vm2-compose config --quiet
|
||||
|
||||
/usr/local/sbin/han-vm2-compose up -d --no-deps --force-recreate \
|
||||
message-safety-api message-safety-worker
|
||||
|
||||
/usr/local/sbin/han-vm2-compose ps
|
||||
/usr/local/sbin/han-vm2-compose ps --format "table {{.Service}}\t{{.Status}}\t{{.Ports}}"
|
||||
|
||||
Тестирование:
|
||||
|
||||
Секция 7 — не один скрипт, а **пять независимых gates**. EICAR только после письменного Security; образец не класть в git, chat и evidence — брать с [eicar.org](https://www.eicar.org). Пункты 4–5 и cutover **нельзя** закрыть текущим образом Message Safety (он ещё на ClamAV): сначала 1–3 на хосте.
|
||||
|
||||
Все команды на `devhanservice` — под **root**. `han-processing.service` не restart/stop.
|
||||
|
||||
---
|
||||
|
||||
### 0. Перед тестами
|
||||
|
||||
```sh
|
||||
kesl-control --get-task-state 1
|
||||
kesl-control --app-info --json | grep -E 'File Threat|databases loaded|license'
|
||||
systemctl is-active kesl han-kesl-scan-broker.socket
|
||||
test -S /run/han-kesl/scan.sock
|
||||
stat -c '%U:%G:%a %n' /run/han-kesl/scan.sock
|
||||
/usr/local/libexec/han-kesl-scan-broker --probe
|
||||
```
|
||||
|
||||
Нужно: task 1 `Started`, базы `Yes`, socket `root:han-message-safety:660`, probe `"status":"ready"`. Если sock нет — `systemctl restart han-kesl-scan-broker.socket`, не `.service`.
|
||||
|
||||
---
|
||||
|
||||
### 1. Host OAS (АВЗ.1)
|
||||
|
||||
Каталог **не** в docker volume, `/tmp`, release, staging:
|
||||
|
||||
```sh
|
||||
install -d -m 0700 -o root -g root /root/kesl-eicar-test
|
||||
date -Is
|
||||
kesl-control --get-task-state 1
|
||||
```
|
||||
|
||||
Положите официальный `eicar.com` в `/root/kesl-eicar-test/eicar.com`:
|
||||
curl -fsSL -o /root/kesl-eicar-test/eicar.com \
|
||||
'https://secure.eicar.org/eicar.com'
|
||||
chmod 0600 /root/kesl-eicar-test/eicar.com
|
||||
|
||||
```sh
|
||||
cat /root/kesl-eicar-test/eicar.com >/dev/null
|
||||
ls -l /root/kesl-eicar-test
|
||||
kesl-control -E --query -n 20 --reverse
|
||||
```
|
||||
|
||||
Ожидаемо: объект удалён/в Backup, в журнале `EICAR-Test-File` / `ThreatDetected`. Если OAS на запись молчит (как на пилоте ВМ1) — явный ODS:
|
||||
|
||||
```sh
|
||||
kesl-control --scan-file /root/kesl-eicar-test/eicar.com
|
||||
kesl-control -E --query -n 20 --reverse
|
||||
```
|
||||
|
||||
Уборка (не весь Backup):
|
||||
|
||||
```sh
|
||||
kesl-control -B --query --reverse -n 20
|
||||
kesl-control -B --mass-remove --query "DetectName == 'EICAR-Test-File'"
|
||||
rm -rf /root/kesl-eicar-test
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2. Broker: clean / EICAR / KESL down
|
||||
|
||||
install -d -m 0700 -o root -g root /root/kesl-install
|
||||
cat > /root/kesl-install/broker-client.py <<'PY'
|
||||
#!/usr/bin/env python3
|
||||
import asyncio, json, struct, sys
|
||||
|
||||
SOCK = "/run/han-kesl/scan.sock"
|
||||
|
||||
async def call(op: str, body: bytes = b"") -> dict:
|
||||
r, w = await asyncio.open_unix_connection(SOCK)
|
||||
header = {"op": op}
|
||||
if op == "scan":
|
||||
header["size"] = len(body)
|
||||
raw = json.dumps(header, separators=(",", ":")).encode()
|
||||
w.write(struct.pack(">I", len(raw)) + raw + body)
|
||||
await w.drain()
|
||||
n = struct.unpack(">I", await r.readexactly(4))[0]
|
||||
resp = json.loads(await r.readexactly(n))
|
||||
w.close()
|
||||
await w.wait_closed()
|
||||
return resp
|
||||
|
||||
async def main() -> None:
|
||||
op = sys.argv[1]
|
||||
body = sys.stdin.buffer.read() if op == "scan" else b""
|
||||
print(json.dumps(await call(op, body), ensure_ascii=True, sort_keys=True))
|
||||
|
||||
if __name__ == "__main__":
|
||||
if sys.argv[1:] not in (["status"], ["scan"]):
|
||||
raise SystemExit("usage: broker-client.py status|scan")
|
||||
asyncio.run(main())
|
||||
PY
|
||||
chmod 0700 /root/kesl-install/broker-client.py
|
||||
|
||||
Проверка позитивного решения
|
||||
python3 /root/kesl-install/broker-client.py status
|
||||
printf 'HAN kesl broker clean canary\n' | python3 /root/kesl-install/broker-client.py scan
|
||||
|
||||
Проверка негативного решения
|
||||
curl -fsSL 'https://secure.eicar.org/eicar.com' |
|
||||
python3 /root/kesl-install/broker-client.py scan
|
||||
|
||||
Проверка недоступного сканнера
|
||||
systemctl stop kesl
|
||||
systemctl is-active kesl || true
|
||||
python3 /root/kesl-install/broker-client.py status
|
||||
printf 'error canary\n' |
|
||||
python3 /root/kesl-install/broker-client.py scan
|
||||
|
||||
Оба запроса должны вернуть scanner_unavailable.
|
||||
Затем восстановить:
|
||||
systemctl start kesl
|
||||
systemctl is-active kesl
|
||||
kesl-control --start-task 1
|
||||
kesl-control --get-task-state 1
|
||||
systemctl restart han-kesl-scan-broker.socket
|
||||
/usr/local/libexec/han-kesl-scan-broker --probe
|
||||
|
||||
---
|
||||
|
||||
### 3. 5 слотов и ~2 файла/с
|
||||
|
||||
На 4 ГБ DEV это нагрузка: смотрите `free -h` / `vmstat`. Откат OAS: `kesl-control --stop-task 1`.
|
||||
|
||||
```sh
|
||||
python3 - <<'PY'
|
||||
import asyncio, json, struct, time, statistics
|
||||
SOCK = "/run/han-kesl/scan.sock"
|
||||
BODY = b"HAN kesl throughput canary\n"
|
||||
|
||||
async def scan():
|
||||
r, w = await asyncio.open_unix_connection(SOCK)
|
||||
h = json.dumps({"op": "scan", "size": len(BODY)}, separators=(",", ":")).encode()
|
||||
t0 = time.perf_counter()
|
||||
w.write(struct.pack(">I", len(h)) + h + BODY)
|
||||
await w.drain()
|
||||
n = struct.unpack(">I", await r.readexactly(4))[0]
|
||||
resp = json.loads(await r.readexactly(n))
|
||||
dt = time.perf_counter() - t0
|
||||
w.close(); await w.wait_closed()
|
||||
return dt, resp
|
||||
|
||||
async def main():
|
||||
burst = await asyncio.gather(*[scan() for _ in range(5)])
|
||||
print("burst5", [(round(dt, 3), r.get("verdict") or r.get("error")) for dt, r in burst])
|
||||
times = []
|
||||
for _ in range(20):
|
||||
dt, r = await scan()
|
||||
assert r.get("verdict") == "clean", r
|
||||
times.append(dt)
|
||||
await asyncio.sleep(0.5) # ~2 file/s
|
||||
times.sort()
|
||||
p95 = times[int(0.95 * (len(times) - 1))]
|
||||
print(f"n={len(times)} p50={times[len(times)//2]:.3f}s p95={p95:.3f}s max={times[-1]:.3f}s")
|
||||
|
||||
asyncio.run(main())
|
||||
PY
|
||||
```
|
||||
|
||||
Gate: все `clean`, p95 ≤ 60 с. Queue age p95 ≤ 5 с — из метрик Safety (после пункта 4), не из этого скрипта.
|
||||
|
||||
---
|
||||
|
||||
### 4. Message Safety 200 / 403 / 503
|
||||
|
||||
Сейчас контейнеры ещё на ClamAV. Пункт 4 — **после** recreate `message-safety-api`/`worker` на образ с bind `/run/han-kesl` (digest из окна 1.0.1). Clamd пока можно не снимать.
|
||||
|
||||
Проверка, что API видит KESL:
|
||||
|
||||
```sh
|
||||
/usr/local/sbin/han-vm2-compose exec -T message-safety-api \
|
||||
python -c 'import json,urllib.request; print(json.dumps(json.load(urllib.request.urlopen("http://127.0.0.1:8080/health/ready")), indent=2))'
|
||||
```
|
||||
|
||||
`antivirus=ok`, `files=ready` (не mock).
|
||||
|
||||
- **clean `200`** — text canary с ВМ1, как в `RUNBOOK.ru.md` (`/internal/safety/v2/messages/check`, `content_kind=text`).
|
||||
- **malware `403` + sticky** — file через **реальный** versioned object в quarantine S3 (выдуманный key нельзя; EICAR в bucket только если Security разрешил). Повтор того же `message_id`+body — тот же `403`.
|
||||
- **scanner error `503`** — `systemctl stop kesl`, file-check, ожидание `503` / files unavailable; затем start kesl + restart **socket** + `--start-task 1`.
|
||||
|
||||
Пока worker ходит в clamd, это **не** gate KESL.
|
||||
|
||||
---
|
||||
|
||||
### 5. Smoke, firewall, 24 ч
|
||||
|
||||
```sh
|
||||
iptables -S HAN-CHAT-VM2
|
||||
ufw status verbose
|
||||
/usr/local/sbin/han-vm2-compose ps
|
||||
free -h
|
||||
swapon --show
|
||||
kesl-control --get-task-state 6
|
||||
kesl-control --get-schedule 6
|
||||
```
|
||||
|
||||
С ВМ1: `openssl s_client` + `curl` `/internal/safety/status` из `RUNBOOK.ru.md`. Сравните `HAN-CHAT-VM2` с baseline. 24 ч: без OOM/restart/5xx, hourly Update сам зелёный.
|
||||
|
||||
curl --fail --silent --show-error \
|
||||
--cacert /etc/han/ca/vm2-internal-ca.crt \
|
||||
https://processing.internal:8443/internal/safety/status
|
||||
|
||||
https://prodhanservice.ihan.ru:8443/internal/safety/status
|
||||
---
|
||||
|
||||
### Cutover (только после 1–5)
|
||||
|
||||
Новая **монотонная** config version (не реактивировать старую):
|
||||
|
||||
```sh
|
||||
cd /opt/han-chat/services/
|
||||
NEXT_VERSION='3'
|
||||
/usr/local/sbin/han-vm2-compose --profile ops run --rm \
|
||||
--entrypoint message-safety-config message-safety-migrate \
|
||||
create /app/app/artifacts/seed-config.yaml \
|
||||
--version "$NEXT_VERSION" --actor '<OPERATOR>'
|
||||
/usr/local/sbin/han-vm2-compose --profile ops run --rm \
|
||||
--entrypoint message-safety-config message-safety-migrate \
|
||||
activate --version "$NEXT_VERSION" --approved-by '<APPROVER>'
|
||||
/usr/local/sbin/han-vm2-compose up -d --no-deps --force-recreate \
|
||||
message-safety-api message-safety-worker
|
||||
```
|
||||
|
||||
В `.env` / compose не должно остаться `clamd`/`freshclam`. Затем:
|
||||
|
||||
```sh
|
||||
/usr/local/sbin/han-vm2-compose up -d --remove-orphans
|
||||
/usr/local/sbin/han-vm2-compose ps
|
||||
docker ps -a --format '{{.Names}}' | grep -E 'clamd|freshclam' || echo 'no clamd/freshclam'
|
||||
docker volume ls | grep -i clam || true
|
||||
```
|
||||
|
||||
/usr/local/sbin/han-vm2-compose exec -T nginx nginx -t -c /etc/nginx/nginx.conf
|
||||
NGINX_ID=$(/usr/local/sbin/han-vm2-compose ps --status running --quiet nginx)
|
||||
docker kill --signal HUP "$NGINX_ID"
|
||||
|
||||
/usr/local/sbin/han-vm2-compose exec -T nginx getent hosts message-safety-api
|
||||
/usr/local/sbin/han-vm2-compose exec -T message-safety-api \
|
||||
python -c 'import json,urllib.request; print(json.dumps(json.load(urllib.request.urlopen("http://127.0.0.1:8080/health/ready")), indent=2))'
|
||||
|
||||
|
||||
|
||||
Старый signature-CDN egress закрывается отдельно в firewall/SG — не через `compose down -v`.
|
||||
|
||||
На этом DEV-хосте (3.8 ГиБ, swap 391 МиБ) пункт 3 и file-EICAR через Safety лучше не совмещать с чем-то ещё. Если нужно, могу выписать только gate 1+2 компактным блоком под копирование в `260907_1.md`.
|
||||
Reference in New Issue
Block a user