Реализация на отдельных двух машинах с протестированным взаимодействием по проверке сообщений
This commit is contained in:
@@ -0,0 +1,960 @@
|
||||
# Usefull commands
|
||||
|
||||
## Скопировать файл по технологии через архив
|
||||
cd C:\Users\MI\Documents\Assistent\HAN_chat_specification
|
||||
|
||||
$Hotfix = "bug-v15"
|
||||
tar -czf "vm2-$Hotfix.tar.gz" -C .\codebase `
|
||||
services/deployment/scripts/setup-vm.sh
|
||||
Get-FileHash "vm2-$Hotfix.tar.gz" -Algorithm SHA256
|
||||
scp -i C:\Users\MI\.ssh\han_vm2_deploy `
|
||||
"vm2-$Hotfix.tar.gz" `
|
||||
deploy@135.106.179.209:/var/lib/han-deploy/incoming/
|
||||
|
||||
|
||||
services/docker-compose.yml
|
||||
HAN_chat_specification\codebase\services\docker-compose.yml
|
||||
HAN_chat_specification\codebase\services\deployment\scripts\setup-vm.sh
|
||||
|
||||
HOTFIX='bug-v15'
|
||||
EXPECTED_SHA256='BF85F75F45F63BFB8310C6E0B835276DE7EBF316180120F6383590C19E44FEE4'
|
||||
ARCHIVE="/var/lib/han-deploy/incoming/vm2-${HOTFIX}.tar.gz"
|
||||
|
||||
printf '%s %s\n' "$EXPECTED_SHA256" "$ARCHIVE" | sha256sum --check -
|
||||
tar -tzf "$ARCHIVE"
|
||||
|
||||
STAGING="$(mktemp -d /opt/han-chat/.nginx-hotfix.XXXXXX)"
|
||||
tar -xzf "$ARCHIVE" -C "$STAGING" --no-same-owner --no-same-permissions
|
||||
|
||||
install -m 0644 -o root -g root "$STAGING/services/deployment/scripts/setup-vm.sh" /opt/han-chat/services/deployment/scripts/setup-vm.sh
|
||||
|
||||
sed -i 's/\r$//' ./deployment/scripts/setup-vm.sh
|
||||
|
||||
find -type f -exec file {} \; | grep -i 'CRLF'
|
||||
|
||||
## Перезапустить контейнер
|
||||
/usr/local/sbin/han-vm2-compose up -d --force-recreate freshclam
|
||||
|
||||
# Создание докер-образов
|
||||
|
||||
docker login cr.selcloud.ru
|
||||
|
||||
cd /mnt/c/Users/MI/Documents/Assistent/.../codebase/services/bitrix-sync
|
||||
docker build -t han-bitrix-sync:1.0.3 .
|
||||
REGISTRY=cr.selcloud.ru/han-images
|
||||
docker tag han-bitrix-sync:1.0.3 $REGISTRY/han-bitrix-sync:1.0.3
|
||||
docker push $REGISTRY/han-bitrix-sync:1.0.3
|
||||
docker image inspect $REGISTRY/han-bitrix-sync:1.0.3 --format '{{index .RepoDigests 0}}'
|
||||
|
||||
cd /mnt/c/Users/MI/Documents/Assistent/.../codebase/services/message-safety
|
||||
docker build -t han-message-safety:1.0.2 .
|
||||
REGISTRY=cr.selcloud.ru/han-images # ваш registry
|
||||
docker tag han-message-safety:1.0.2 $REGISTRY/han-message-safety:1.0.2
|
||||
docker push $REGISTRY/han-message-safety:1.0.2
|
||||
docker image inspect $REGISTRY/han-message-safety:1.0.2 --format '{{index .RepoDigests 0}}'
|
||||
|
||||
|
||||
|
||||
## Чтобы собрать sha256 с остальных образов, их надо закачать
|
||||
docker pull nginxinc/nginx-unprivileged:1.27
|
||||
docker pull redis:7.4
|
||||
docker pull clamav/clamav:1.4
|
||||
docker pull otel/opentelemetry-collector-contrib:0.117.0
|
||||
|
||||
docker image inspect nginxinc/nginx-unprivileged:1.27 --format '{{index .RepoDigests 0}}'
|
||||
docker image inspect redis:7.4 --format '{{index .RepoDigests 0}}'
|
||||
docker image inspect clamav/clamav:1.4.6 --format '{{index .RepoDigests 0}}'
|
||||
docker image inspect otel/opentelemetry-collector-contrib:0.117.0 --format '{{index .RepoDigests 0}}'
|
||||
|
||||
# Генерация ключей для новых пользователей и первичная настройка ВМ2
|
||||
|
||||
ssh-keygen -t ed25519 -a 100 -f C:\Users\MI\.ssh\han_vm2_deploy -C "han-vm2-deploy"
|
||||
ssh-keygen -t ed25519 -a 100 -f C:\Users\MI\.ssh\han_vm2_admin -C "han-vm2-break-glass-admin"
|
||||
|
||||
|
||||
scp -i C:\Users\MI\.ssh\hansel C:\Users\MI\Documents\Assistent\HAN_chat_specification\codebase\services\deployment\scripts\setup-vm.sh root@135.106.179.209:/root/setup-vm2.sh
|
||||
scp -i C:\Users\MI\.ssh\hansel C:\Users\MI\.ssh\han_vm2_deploy.pub C:\Users\MI\.ssh\han_vm2_admin.pub root@135.106.179.209:/root/
|
||||
|
||||
|
||||
На VM2 в текущей root-сессии задайте реальные CIDR. `/0` скрипт отклоняет:
|
||||
```sh
|
||||
ssh -i C:\Users\MI\.ssh\hansel root@135.106.179.209
|
||||
sed -i 's/\r$//' /root/setup-vm2.sh
|
||||
|
||||
install -d -m 0700 -o root -g root /root/bootstrap
|
||||
install -m 0600 -o root -g root /root/han_vm2_deploy.pub /root/bootstrap/deploy.pub
|
||||
install -m 0600 -o root -g root /root/han_vm2_admin.pub /root/bootstrap/admin.pub
|
||||
chmod 0700 /root/setup-vm2.sh
|
||||
DEPLOY_AUTHORIZED_KEY_FILE=/root/bootstrap/deploy.pub \
|
||||
ADMIN_AUTHORIZED_KEY_FILE=/root/bootstrap/admin.pub \
|
||||
VM1_PRIVATE_CIDRS='192.168.0.0/24' \
|
||||
/root/setup-vm2.sh
|
||||
```
|
||||
|
||||
1 В текущей root-сессии задайте `admin` отдельный сложный sudo-пароль. Он не
|
||||
разрешает password SSH: пароль нужен только после входа по admin key:
|
||||
```sh
|
||||
passwd admin
|
||||
```
|
||||
2 Не закрывая root-сессию, проверьте отдельные SSH-ключи deploy и admin.
|
||||
ssh -i C:\Users\MI\.ssh\han_vm2_deploy deploy@135.106.179.209
|
||||
ssh -i C:\Users\MI\.ssh\han_vm2_admin admin@135.106.179.209
|
||||
В сессии admin проверьте sudo -v и sudo -i, затем завершите root shell:
|
||||
sudo -v # проверить, что пароль admin принимается
|
||||
sudo -i # открыть root shell (приглашение обычно root@...)
|
||||
id # убедиться, что uid=0
|
||||
exit # ← вот это «завершите root shell» — вернуться к admin@
|
||||
|
||||
|
||||
# Только после успешной проверки `deploy`, `admin` и `sudo` повторите на VM2
|
||||
под `root`:
|
||||
|
||||
```sh
|
||||
DEPLOY_AUTHORIZED_KEY_FILE=/root/bootstrap/deploy.pub \
|
||||
ADMIN_AUTHORIZED_KEY_FILE=/root/bootstrap/admin.pub \
|
||||
VM1_PRIVATE_CIDRS='192.168.0.0/24' \
|
||||
HARDEN_SSH=true \
|
||||
SKIP_APT_UPGRADE=true \
|
||||
/root/setup-vm2.sh
|
||||
```
|
||||
Это добавит `PermitRootLogin no` и `AllowUsers deploy admin`.
|
||||
Публичные bootstrap-копии после проверки можно удалить под `admin`:
|
||||
|
||||
```sh
|
||||
sudo rm -f /root/han_vm2_deploy.pub /root/han_vm2_admin.pub
|
||||
```
|
||||
|
||||
# Проверка наличия сетевого интерфейса для ВМ и его создание
|
||||
ls -la /etc/netplan
|
||||
netplan get
|
||||
ip route
|
||||
|
||||
|
||||
cat >/etc/netplan/60-private-network.yaml <<'EOF'
|
||||
network:
|
||||
version: 2
|
||||
ethernets:
|
||||
eth1:
|
||||
dhcp4: false
|
||||
addresses:
|
||||
- 192.168.0.4/24
|
||||
optional: true
|
||||
EOF
|
||||
|
||||
chmod 0600 /etc/netplan/60-private-network.yaml
|
||||
netplan generate
|
||||
netplan try
|
||||
|
||||
Подтвердите конфигурацию в течение тайм-аута. Затем проверьте:
|
||||
ip -br -4 address show eth1
|
||||
ip route
|
||||
ping -c 3 192.168.0.210
|
||||
|
||||
# nginx правим разрешенные адреса
|
||||
nginx/allowlists/
|
||||
|
||||
# Копируем проект
|
||||
(Текущая команда копирует весь каталог ради простоты формирования единого архива, но технической необходимости в исходниках приложений нет)
|
||||
|
||||
На локальном компьютере из каталога `HAN_chat_specification`:
|
||||
|
||||
```powershell
|
||||
$Release = "1.0.3"
|
||||
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\codebase services
|
||||
Get-FileHash "vm2-services-$Release.tar.gz" -Algorithm SHA256
|
||||
scp -i C:\Users\MI\.ssh\han_vm2_deploy "vm2-services-$Release.tar.gz" `
|
||||
deploy@135.106.179.209:/var/lib/han-deploy/incoming/
|
||||
```
|
||||
|
||||
Под `deploy` на VM2 вычислите checksum. Значение должно совпасть с локальным:
|
||||
|
||||
```sh
|
||||
RELEASE='1.0.2'
|
||||
cd /var/lib/han-deploy/incoming
|
||||
sha256sum "vm2-services-${RELEASE}.tar.gz"
|
||||
tar -tzf "vm2-services-${RELEASE}.tar.gz"
|
||||
```
|
||||
На этом действия `deploy` с файлами заканчиваются. Не распаковывайте релиз
|
||||
через `sudo` и не копируйте его в production от имени `deploy`.
|
||||
|
||||
# Активация и установка файлов под `root`
|
||||
|
||||
ssh -i C:\Users\MI\.ssh\han_vm2_admin admin@135.106.179.209
|
||||
sudo -i
|
||||
id
|
||||
Должно быть uid=0(root).
|
||||
|
||||
Под `root` ещё раз сверьте ожидаемый SHA-256 и список архива. Не продолжайте,
|
||||
если архив содержит абсолютные пути, `..`, symlink/hardlink или лишний проект:
|
||||
|
||||
```sh
|
||||
RELEASE='1.0.3'
|
||||
EXPECTED_SHA256='0FC8F2B85EC39EE41BD6D3E3789963A4D985B3E2E9638E50BEAF978EA6A46271'
|
||||
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 /opt/han-chat/services/deployment/scripts/setup-vm.sh /root/setup-vm2.sh
|
||||
chmod 0700 /root/setup-vm2.sh
|
||||
|
||||
Была проблема с каретками, полечилась так:
|
||||
(sed -i 's/\r$//' \
|
||||
/opt/han-chat/services/deployment/deploy-message-safety-mode.sudoers
|
||||
|
||||
install -m 0440 -o root -g root \
|
||||
/opt/han-chat/services/deployment/deploy-message-safety-mode.sudoers \
|
||||
/etc/sudoers.d/deploy-message-safety-mode
|
||||
|
||||
visudo -cf /etc/sudoers.d/deploy-message-safety-mode)
|
||||
|
||||
|
||||
|
||||
Повторите setup под `root`: теперь он установит helpers и units из активного
|
||||
релиза. Приложение всё ещё не запускается:
|
||||
|
||||
```sh
|
||||
DEPLOY_AUTHORIZED_KEY_FILE=/root/bootstrap/deploy.pub \
|
||||
ADMIN_AUTHORIZED_KEY_FILE=/root/bootstrap/admin.pub \
|
||||
VM1_PRIVATE_CIDRS='192.168.0.0/24' \
|
||||
HARDEN_SSH=true \
|
||||
SKIP_APT_UPGRADE=true \
|
||||
/root/setup-vm2.sh
|
||||
```
|
||||
|
||||
# Несекретная конфигурация и Selectel под `root`
|
||||
|
||||
`APP_ENV` определяет имя loader config. При значении из `.env.example`
|
||||
`APP_ENV=production-like` файл обязан называться
|
||||
`/etc/han/secrets/vm2-production-like.selectel.json`:
|
||||
|
||||
```sh
|
||||
cd /opt/han-chat/services
|
||||
install -m 0600 -o root -g root .env.example .env
|
||||
editor .env
|
||||
|
||||
# я скопировал и вставил значения из локального
|
||||
sed -i 's/\r$//' .env
|
||||
|
||||
|
||||
install -m 0600 -o root -g root \
|
||||
deployment/secrets/config.example.json \
|
||||
/etc/han/secrets/vm2-production-like.selectel.json
|
||||
editor /etc/han/secrets/vm2-production-like.selectel.json
|
||||
```
|
||||
sudo sed -i 's/\r$//' /etc/han/secrets/vm2-production-like.selectel.json
|
||||
ls /etc/han/secrets/vm2-production-like.selectel.json
|
||||
|
||||
# Для заполнения секретов нужно создать сертификаты для ВМ1-ВМ2.
|
||||
|
||||
### 1. Создать CA (один раз)
|
||||
|
||||
На безопасной машине (не обязательно VM2):
|
||||
|
||||
```bash
|
||||
mkdir -p ~/han-internal-ca && cd ~/han-internal-ca
|
||||
|
||||
openssl genrsa -out ca.key 4096
|
||||
openssl req -x509 -new -nodes -key ca.key -sha256 -days 3650 \
|
||||
-subj "/CN=HAN Internal CA" \
|
||||
-out ca.crt
|
||||
```
|
||||
`ca.key` храните только у себя; на ВМ не кладите.
|
||||
|
||||
### 2. Выпустить серверный сертификат VM2
|
||||
|
||||
```bash
|
||||
openssl genrsa -out vm2-processing.key 2048
|
||||
|
||||
cat > vm2-processing.ext <<'EOF'
|
||||
authorityKeyIdentifier=keyid,issuer
|
||||
basicConstraints=CA:FALSE
|
||||
keyUsage = digitalSignature, keyEncipherment
|
||||
extendedKeyUsage = serverAuth
|
||||
subjectAltName = @alt_names
|
||||
|
||||
[alt_names]
|
||||
DNS.1 = processing.internal
|
||||
EOF
|
||||
|
||||
openssl req -new -key vm2-processing.key \
|
||||
-subj "/CN=processing.internal" \
|
||||
-out vm2-processing.csr
|
||||
|
||||
openssl x509 -req -in vm2-processing.csr \
|
||||
-CA ca.crt -CAkey ca.key -CAcreateserial \
|
||||
-out vm2-processing.crt -days 825 -sha256 \
|
||||
-extfile vm2-processing.ext
|
||||
```
|
||||
|
||||
Проверка:
|
||||
|
||||
```bash
|
||||
openssl x509 -in vm2-processing.crt -noout -text | grep -A2 'Subject Alternative Name'
|
||||
```
|
||||
|
||||
### 3. Разложить секреты
|
||||
|
||||
**VM2** (в Selectel Secrets / файлы loader’а):
|
||||
|
||||
- `VM2_INTERNAL_TLS_CERTIFICATE` ← содержимое `vm2-processing.crt` (можно fullchain: cert + ca.crt)
|
||||
- `VM2_INTERNAL_TLS_PRIVATE_KEY` ← `vm2-processing.key`
|
||||
|
||||
**VM1**:
|
||||
scp -i ~/.ssh/hansel ./han-internal-ca/ca.crt root@135.106.164.58:/tmp/ca.crt
|
||||
|
||||
На VM1 от root:
|
||||
install -d -o root -g root -m 0755 /opt/han-chat/backend/secrets/tls
|
||||
install -o root -g root -m 0644 /tmp/ca.crt \
|
||||
/opt/han-chat/backend/secrets/tls/processing-internal-ca.crt
|
||||
rm -f /tmp/ca.crt
|
||||
|
||||
ls /opt/han-chat/backend/secrets/tls/processing-internal-ca.crt
|
||||
|
||||
nano /opt/han-chat/backend/.env
|
||||
|
||||
MESSAGE_SAFETY_URL=https://processing.internal:8443
|
||||
MESSAGE_SAFETY_CA_HOST_PATH=/opt/han-chat/backend/secrets/tls/processing-internal-ca.crt
|
||||
|
||||
# PRIVATE NETWORK
|
||||
|
||||
В private DNS / hosts на VM1:
|
||||
echo '192.168.0.4 processing.internal' >> /etc/hosts
|
||||
|
||||
Зашифруйте пароль Selectel service user через systemd credentials, не помещая
|
||||
его в аргументы или history:
|
||||
|
||||
```sh
|
||||
read -rsp 'Selectel VM2 service-user password: ' SELECTEL_PASSWORD; echo
|
||||
printf '%s' "$SELECTEL_PASSWORD" | systemd-creds encrypt \
|
||||
--name=selectel-service-user-password - \
|
||||
/etc/han/credentials/vm2.selectel-password.cred
|
||||
unset SELECTEL_PASSWORD
|
||||
chown root:root /etc/han/credentials/vm2.selectel-password.cred
|
||||
chmod 0600 /etc/han/credentials/vm2.selectel-password.cred
|
||||
```
|
||||
|
||||
Активные nginx allow-list файлы редактирует только `root`; последней строкой
|
||||
обязательно остаётся `deny all;`. До cutover Bitrix public allow-list должен
|
||||
оставаться закрытым:
|
||||
|
||||
```sh
|
||||
editor /opt/han-chat/services/nginx/allowlists/private-caller-allowlist.conf
|
||||
install -m 0644 -o root -g root \
|
||||
/opt/han-chat/services/nginx/allowlists/bitrix-webhook-allowlist.conf.template \
|
||||
/opt/han-chat/services/nginx/allowlists/bitrix-webhook-allowlist.conf
|
||||
chown root:root /opt/han-chat/services/nginx/allowlists/*.conf
|
||||
chmod 0644 /opt/han-chat/services/nginx/allowlists/*.conf
|
||||
```
|
||||
|
||||
Нужно заполнить **два активных nginx allow-list** на VM2 (не UFW из `setup-vm.sh`). Шаблоны лежат в `nginx/allowlists/`.
|
||||
|
||||
### 1. `private-caller-allowlist.conf` — обязательно для работы Safety
|
||||
|
||||
Кто может ходить на **private API `:8443`** (VM1 → Message Safety / sync status).
|
||||
|
||||
Пример:
|
||||
|
||||
```nginx
|
||||
# allow приватный IP VM1
|
||||
allow 192.168.0.10/32;
|
||||
# опционально — ваш VPN/ops в private сети
|
||||
# allow 192.168.0.50/32;
|
||||
deny all;
|
||||
```
|
||||
|
||||
Берёте приватный IP VM1 (тот же смысл, что `VM1_PRIVATE_CIDRS`). Последняя строка всегда `deny all;`.
|
||||
|
||||
### 2. `bitrix-webhook-allowlist.conf` — только перед включением Bitrix
|
||||
|
||||
Кто может бить в **публичные webhook** (`/bitrix/sync/webhook/...`).
|
||||
|
||||
До cutover ранбук говорит: **оставить закрытым** (`deny all;` без `allow`) — это нормально, пока `BITRIX_SYNC_ENABLED=false`.
|
||||
|
||||
Перед включением sync — IP/CIDR исходящих адресов Битрикс24 (или прокси), например:
|
||||
|
||||
```nginx
|
||||
allow 185.x.x.x/32;
|
||||
deny all;
|
||||
```
|
||||
|
||||
### Где править
|
||||
|
||||
На VM2 после раскладки релиза:
|
||||
|
||||
```text
|
||||
/opt/han-chat/services/nginx/allowlists/private-caller-allowlist.conf
|
||||
/opt/han-chat/services/nginx/allowlists/bitrix-webhook-allowlist.conf
|
||||
```
|
||||
|
||||
Шаблоны-подсказки: `*.conf.template`. Активные `.conf` в git намеренно с одним `deny all;` — заполняете на сервере (или копируете из template и раскомментируете `allow`).
|
||||
|
||||
Это **отдельный слой от UFW**: UFW режет порт на хосте, nginx allow-list — кто дойдёт до location после TLS.
|
||||
|
||||
|
||||
# Сертификат PostgreSQL и первоначальный выпуск public TLS
|
||||
|
||||
### CA управляемой PostgreSQL
|
||||
|
||||
Скачайте CA-сертификат кластера из панели провайдера и передайте его на VM2 во
|
||||
временный путь. Под `root` установите сертификат вне каталога релиза:
|
||||
|
||||
scp -i C:\Users\MI\.ssh\han_vm2_deploy -r C:\Users\MI\Documents\job\HAN_new_life\HANapp\Production\sertificates\CA.pem deploy@135.106.179.209:/tmp/ca.pem
|
||||
|
||||
```sh
|
||||
install -d -m 0755 -o root -g root /etc/han/ca
|
||||
install -m 0644 -o root -g root \
|
||||
/tmp/ca.pem \
|
||||
/etc/han/ca/managed-postgresql-ca.pem
|
||||
openssl x509 -in /etc/han/ca/managed-postgresql-ca.pem \
|
||||
-noout -subject -issuer -dates
|
||||
rm -f /tmp/ca.pem
|
||||
```
|
||||
|
||||
# Первоначальный выпуск Let's Encrypt
|
||||
|
||||
`PROCESSING_PUBLIC_HOST` должен быть DNS-именем, A-запись которого уже указывает
|
||||
на публичный IP VM2. Сертификат на IP-адрес этим порядком не выпускается. Порт
|
||||
`80` должен быть разрешён в cloud firewall/UFW и пока не занят nginx.
|
||||
|
||||
Под `root` задайте значения только для текущей shell-сессии и подготовьте
|
||||
постоянный webroot:
|
||||
|
||||
```sh
|
||||
PUBLIC_HOST=service4chat.han0107.ru
|
||||
ACME_EMAIL=ap@han.ru
|
||||
install -d -m 0755 -o root -g root /var/lib/han-chat/acme
|
||||
getent ahostsv4 "$PUBLIC_HOST"
|
||||
ss -lntp | grep -E ':80[[:space:]]' && {
|
||||
echo 'Порт 80 уже занят; остановите listener перед standalone-проверкой' >&2
|
||||
exit 1
|
||||
} || true
|
||||
```
|
||||
|
||||
Сначала проверьте ACME через staging CA. Этот сертификат nginx не использует:
|
||||
|
||||
```sh
|
||||
certbot certonly --standalone --preferred-challenges http \
|
||||
--staging \
|
||||
-d "$PUBLIC_HOST" \
|
||||
--cert-name "${PUBLIC_HOST}-staging" \
|
||||
--email "$ACME_EMAIL" \
|
||||
--agree-tos --no-eff-email --non-interactive
|
||||
certbot delete --cert-name "${PUBLIC_HOST}-staging" --non-interactive
|
||||
```
|
||||
|
||||
После успешного staging-теста выпустите production-сертификат:
|
||||
|
||||
```sh
|
||||
certbot certonly --standalone --preferred-challenges http \
|
||||
-d "$PUBLIC_HOST" \
|
||||
--cert-name "$PUBLIC_HOST" \
|
||||
--email "$ACME_EMAIL" \
|
||||
--agree-tos --no-eff-email --non-interactive
|
||||
certbot certificates
|
||||
test -s "/etc/letsencrypt/live/${PUBLIC_HOST}/fullchain.pem"
|
||||
test -s "/etc/letsencrypt/live/${PUBLIC_HOST}/privkey.pem"
|
||||
```
|
||||
Nginx получает `/etc/letsencrypt` с host read-only и после этого может пройти
|
||||
Gate 4 и первый запуск. Не копируйте private key в каталог релиза.
|
||||
|
||||
|
||||
# Preflight, миграции и первый запуск под `root`
|
||||
|
||||
Сначала синхронизируйте секреты. Затем выполните статический preflight:
|
||||
|
||||
```sh
|
||||
systemctl start han-secrets-vm2.service
|
||||
/opt/han-chat/services/deployment/preflight.sh
|
||||
/usr/local/sbin/han-vm2-compose config --quiet
|
||||
```
|
||||
|
||||
для перезапуска секретов
|
||||
systemctl restart han-secrets-vm2.service
|
||||
systemctl show han-secrets-vm2.service -p ActiveState -p SubState -p Result -p ExecMainStartTimestamp -p ExecMainStatus
|
||||
|
||||
Перед первым `bitrix-sync-migrate` владелец `han_app` или администратор БД
|
||||
выдаёт Bitrix migration-role временный read-only доступ к legacy mapping:
|
||||
|
||||
```sql
|
||||
GRANT USAGE ON SCHEMA han_app TO bitrix_sync_user;
|
||||
GRANT SELECT ON TABLE han_app.entity_external_mapping
|
||||
TO bitrix_sync_user;
|
||||
|
||||
|
||||
До runtime выполните миграции отдельными DB roles и активируйте начальный
|
||||
Message Safety config:
|
||||
|
||||
```sh
|
||||
/usr/local/sbin/han-vm2-compose --profile ops run --rm message-safety-migrate
|
||||
/usr/local/sbin/han-vm2-compose --profile ops run --rm bitrix-sync-migrate
|
||||
|
||||
/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 1 --actor '<OPERATOR>'
|
||||
/usr/local/sbin/han-vm2-compose --profile ops run --rm \
|
||||
--entrypoint message-safety-config message-safety-migrate \
|
||||
activate --version 1 --approved-by '<APPROVER>'
|
||||
```
|
||||
|
||||
Потом права отзываем
|
||||
REVOKE SELECT ON TABLE han_app.entity_external_mapping
|
||||
FROM bitrix_sync_user;
|
||||
REVOKE USAGE ON SCHEMA han_app FROM bitrix_sync_user;
|
||||
|
||||
Первый запуск и enable выполняет `root` только после прохождения gates:
|
||||
|
||||
|
||||
Установка/редактирование unit, Compose, `.env`, secret mapping, credential,
|
||||
TLS, allow-list и запуск migration jobs остаются операциями `root`.
|
||||
|
||||
# GATES
|
||||
|
||||
### Gate 1 — секреты материализованы
|
||||
|
||||
Под `root` на VM2:
|
||||
|
||||
```sh
|
||||
systemctl restart han-secrets-vm2.service
|
||||
systemctl is-active han-secrets-vm2.service
|
||||
journalctl --no-pager -u han-secrets-vm2.service
|
||||
test -s /run/han-chat/secrets/manifest
|
||||
cut -d= -f1 /run/han-chat/secrets/manifest | sort
|
||||
```
|
||||
Ожидается `active`; журнал не содержит значений секретов; последняя команда
|
||||
показывает только имена всех ключей из mapping. Не выполняйте `cat` файлов
|
||||
секретов и не вставляйте реальные значения в terminal history.
|
||||
|
||||
### Gate 2 — статический preflight и Compose
|
||||
Под `root` на VM2:
|
||||
|
||||
```sh
|
||||
cd /opt/han-chat/services
|
||||
deployment/preflight.sh
|
||||
/usr/local/sbin/han-vm2-compose config --quiet
|
||||
/usr/local/sbin/han-vm2-compose config --services
|
||||
/usr/local/sbin/han-vm2-compose config --images
|
||||
```
|
||||
Все команды должны завершиться с кодом `0`. В списке services нет PostgreSQL,
|
||||
а все production images содержат `@sha256:`. Вывод полного resolved Compose в
|
||||
файл не сохраняйте.
|
||||
|
||||
### Gate 3 — миграции и активный Message Safety config
|
||||
|
||||
Команды миграций из предыдущего раздела выполняются под `root`. После них:
|
||||
|
||||
```sh
|
||||
/usr/local/sbin/han-vm2-compose --profile ops run --rm \
|
||||
message-safety-migrate current
|
||||
/usr/local/sbin/han-vm2-compose --profile ops run --rm \
|
||||
bitrix-sync-migrate current
|
||||
```
|
||||
Ожидается по одной head revision каждого сервиса. `create --version 1`
|
||||
выполняется только при первом развёртывании. Для следующего конфига используйте
|
||||
новый монотонный номер и отдельные значения `--actor`/`--approved-by`; повторно
|
||||
активировать старую версию нельзя. Alembic downgrade запрещён.
|
||||
|
||||
### Gate 4 — конфигурация nginx до запуска
|
||||
|
||||
После выпуска public TLS в `/etc/letsencrypt` и материализации internal TLS
|
||||
secrets. В Selectel значения `VM2_INTERNAL_TLS_CERTIFICATE` и
|
||||
`VM2_INTERNAL_TLS_PRIVATE_KEY` сохраняются как исходный PEM с настоящими
|
||||
переводами строк, не как повторный base64 и не как строка с литералами `\n`.
|
||||
После изменения remote secret перезапустите `han-secrets-vm2.service`; preflight
|
||||
проверит формат PEM и соответствие certificate/key без вывода их содержимого:
|
||||
|
||||
```sh
|
||||
/opt/han-chat/services/deployment/preflight.sh
|
||||
/usr/local/sbin/han-vm2-compose run --rm --no-deps \
|
||||
-e MESSAGE_SAFETY_UPSTREAM_HOST=127.0.0.1 \
|
||||
-e BITRIX_SYNC_UPSTREAM_HOST=127.0.0.1 \
|
||||
nginx \
|
||||
nginx -t -c /etc/nginx/nginx.conf
|
||||
```
|
||||
|
||||
Базовый `nginx.conf` подключает обязательный
|
||||
`/etc/nginx/conf.d/10-vm2.conf`, поэтому команда завершится ошибкой, если
|
||||
entrypoint не создал конфигурацию из шаблона. Временные значения upstream
|
||||
нужны только для проверки до первого запуска backend-контейнеров; production
|
||||
Compose подставляет DNS-имена сервисов. Ожидается `syntax is ok` и `test is
|
||||
successful`; ошибок `conf.d is not writable` и предупреждения о превышении
|
||||
open-file limit быть не должно. Ошибка отсутствующего сертификата является
|
||||
блокером, а не основанием временно убрать TLS.
|
||||
|
||||
|
||||
### Gate 5 — упорядоченный первый запуск
|
||||
|
||||
Под `root` на VM2:
|
||||
|
||||
```sh
|
||||
/usr/local/sbin/han-vm2-compose up -d redis-safety otel-collector
|
||||
/usr/local/sbin/han-vm2-compose up -d freshclam clamd
|
||||
/usr/local/sbin/han-vm2-compose up -d \
|
||||
message-safety-api message-safety-worker
|
||||
/usr/local/sbin/han-vm2-compose up -d \
|
||||
bitrix-sync bitrix-sync-worker bitrix-sync-reconciliation
|
||||
/usr/local/sbin/han-vm2-compose up -d nginx
|
||||
/usr/local/sbin/han-vm2-compose ps
|
||||
```
|
||||
|
||||
Дождитесь `healthy` у сервисов с healthcheck. Не продолжайте при
|
||||
`Restarting`, `unhealthy`, OOM или неожиданном `Exited`. После успешного
|
||||
первого запуска передайте дальнейший lifecycle systemd:
|
||||
|
||||
```sh
|
||||
systemctl enable han-secrets-vm2.service han-processing.service
|
||||
systemctl start han-processing.service
|
||||
systemctl --no-pager status han-processing.service
|
||||
```
|
||||
|
||||
### Gate 6 — host ports и сертификаты
|
||||
|
||||
Под `root` на VM2:
|
||||
|
||||
```sh
|
||||
ss -lntp | grep -E ':(80|443|8443|6379|8080|4317|4318)[[:space:]]'
|
||||
/usr/local/sbin/han-vm2-compose ps --format json | jq .
|
||||
```
|
||||
|
||||
Ожидаются host listeners только nginx: public `80`, `443` и private-bound
|
||||
`8443` на `PROCESSING_PRIVATE_BIND_ADDRESS`. `6379`, container `8080` и OTLP
|
||||
`4317/4318` на host отсутствуют.
|
||||
|
||||
С доверенной рабочей станции проверьте public chain:
|
||||
|
||||
```sh
|
||||
openssl s_client -connect service4chat.han0107.ru:443 \
|
||||
-servername service4chat.han0107.ru \
|
||||
-verify_hostname service4chat.han0107.ru -verify_return_error </dev/null
|
||||
```
|
||||
|
||||
С VM1 или ops host, имеющего private route, проверьте internal chain и SAN:
|
||||
|
||||
```sh
|
||||
openssl s_client -connect 192.168.0.4:8443 \
|
||||
-servername processing.internal \
|
||||
-verify_hostname processing.internal \
|
||||
-CAfile /opt/han-chat/backend/secrets/tls/processing-internal-ca.crt \
|
||||
-verify_return_error </dev/null
|
||||
```
|
||||
|
||||
Обе команды должны завершить certificate verification без ошибки.
|
||||
|
||||
Переключите renewal с первоначального `standalone` на webroot, который nginx
|
||||
обслуживает по `/.well-known/acme-challenge/`. `certbot reconfigure` сам
|
||||
проверит новый способ через staging CA:
|
||||
|
||||
```sh
|
||||
PUBLIC_HOST='service4chat.han0107.ru'
|
||||
certbot reconfigure \
|
||||
--cert-name "$PUBLIC_HOST" \
|
||||
--authenticator webroot \
|
||||
--webroot-path /var/lib/han-chat/acme
|
||||
```
|
||||
|
||||
Повторный setup после активации релиза устанавливает deploy-hook
|
||||
`/etc/letsencrypt/renewal-hooks/deploy/han-processing-nginx`: после успешного
|
||||
обновления он атомарно размещает certificate/key с группой `han-nginx-tls` в
|
||||
`/var/lib/han-chat/public-tls`, проверяет конфигурацию nginx и отправляет
|
||||
контейнеру `HUP`.
|
||||
Проверьте полный цикл и включите штатное расписание Certbot:
|
||||
|
||||
```sh
|
||||
test -x /etc/letsencrypt/renewal-hooks/deploy/han-processing-nginx
|
||||
certbot renew --dry-run --run-deploy-hooks
|
||||
systemctl enable --now certbot.timer
|
||||
systemctl --no-pager status certbot.timer
|
||||
systemctl list-timers certbot.timer
|
||||
```
|
||||
|
||||
`certbot.timer` проверяет необходимость продления дважды в сутки; сертификат
|
||||
перевыпускается только при приближении срока. Ошибка dry-run или deploy-hook —
|
||||
блокер. Итог `Congratulations, all simulated renewals succeeded` означает
|
||||
успех. Со старым hook сообщение `Hook 'deploy-hook' ran with error output`
|
||||
могло быть ложным: Compose писал успешный `HUP` как `Killing/Killed`, а
|
||||
успешный `nginx -t` — `syntax is ok` в stderr. Исправленный hook выводит
|
||||
config-test только при ошибке и отправляет HUP без progress-вывода. Порт `80`
|
||||
после этого остаётся доступен для HTTP-01 renewal.
|
||||
|
||||
|
||||
### Gate 7 — public routing
|
||||
|
||||
С внешней тестовой машины:
|
||||
|
||||
```sh
|
||||
curl -sS -o /dev/null -w '%{http_code}\n' \
|
||||
http://service4chat.han0107.ru/not-a-route
|
||||
curl -sS -o /dev/null -w '%{http_code}\n' \
|
||||
https://service4chat.han0107.ru/not-a-route
|
||||
curl -sS -o /dev/null -w '%{http_code}\n' \
|
||||
http://service4chat.han0107.ru/bitrix/sync/webhook/contact
|
||||
curl -sS -o /dev/null -w '%{http_code}\n' \
|
||||
https://service4chat.han0107.ru/internal/safety/status
|
||||
curl -sS -o /dev/null -w '%{http_code}\n' -X GET \
|
||||
https://service4chat.han0107.ru/bitrix/sync/webhook/contact
|
||||
```
|
||||
|
||||
Ожидаемые коды по порядку: `308`, `404`, `426`, `404`, `405`. Для HTTPS
|
||||
используйте только валидный public certificate, без `-k`.
|
||||
|
||||
POST к webhook с адреса вне Bitrix allow-list должен получить `403`; если
|
||||
cloud firewall настроен на drop, допустим timeout. Затем повторите с
|
||||
разрешённого source IP и заведомо неверным receiver token: upstream должен
|
||||
ответить `403`, не `2xx`.
|
||||
|
||||
|
||||
### Gate 8 — private API только с VM1/ops
|
||||
|
||||
Следующие команды выполняются **на VM1** или approved ops host, не на VM2.
|
||||
Используйте private DNS/SAN и внутреннюю CA.
|
||||
|
||||
Safety status:
|
||||
|
||||
```sh
|
||||
curl --fail --silent --show-error \
|
||||
--cacert /opt/han-chat/backend/secrets/tls/processing-internal-ca.crt \
|
||||
https://processing.internal:8443/internal/safety/status
|
||||
```
|
||||
|
||||
Benign text check через тот же private listener:
|
||||
|
||||
```sh
|
||||
SAFETY_TOKEN="$(cat /run/han-chat/secrets/MESSAGE_SAFETY_SERVICE_TOKEN)"
|
||||
MESSAGE_ID="$(uuidgen)"
|
||||
curl --silent --show-error --write-out '\nHTTP %{http_code}\n' --config - <<EOF
|
||||
url = "https://processing.internal:8443/internal/safety/v2/messages/check"
|
||||
cacert = "/opt/han-chat/backend/secrets/tls/processing-internal-ca.crt"
|
||||
request = "POST"
|
||||
header = "X-Service-Token: ${SAFETY_TOKEN}"
|
||||
header = "Content-Type: application/json"
|
||||
data = "{\"message_id\":\"${MESSAGE_ID}\",\"content_kind\":\"text\",\"text\":\"VM2 safety canary\",\"attachment\":null}"
|
||||
EOF
|
||||
unset SAFETY_TOKEN MESSAGE_ID
|
||||
```
|
||||
|
||||
В standard mode ожидается `HTTP 200`, `verdict=allow` и непустые
|
||||
`config_version`/`rules_version`. Проверку `202 → Location → task GET`
|
||||
выполняйте отдельным file smoke только с реальным versioned quarantine object:
|
||||
выдуманные S3 key/version/ETag не являются валидным тестом.
|
||||
|
||||
Для Bitrix status прочитайте token из уже защищённого secret file VM1 в
|
||||
переменную и передайте curl config через stdin, чтобы значение не попало в
|
||||
argv/history:
|
||||
|
||||
```sh
|
||||
BITRIX_TOKEN="$(cat /run/han-chat/secrets/BITRIX_SYNC_SERVICE_TOKEN)"
|
||||
curl --silent --show-error --output /tmp/vm2-sync-status.json \
|
||||
--write-out '%{http_code}\n' --config - <<EOF
|
||||
url = "https://processing.internal:8443/internal/sync/v1/status"
|
||||
cacert = "/opt/han-chat/backend/secrets/tls/processing-internal-ca.crt"
|
||||
header = "Authorization: Bearer ${BITRIX_TOKEN}"
|
||||
EOF
|
||||
unset BITRIX_TOKEN
|
||||
cat /tmp/vm2-sync-status.json
|
||||
rm -f /tmp/vm2-sync-status.json
|
||||
```
|
||||
|
||||
При `BITRIX_SYNC_ENABLED=false` ожидается закрытая/неготовая синхронизация, а
|
||||
не ложный успешный full-mode status. С машины вне `VM1_PRIVATE_CIDRS` и
|
||||
необязательных приватных/VPN-сетей `OPS_CIDRS` подключение к `8443` должно
|
||||
завершиться timeout/reject.
|
||||
|
||||
### Gate 9 — canary на отсутствие секретов в логах и traces
|
||||
|
||||
Создайте **фейковый**, не production token marker и отправьте его с
|
||||
разрешённого тестового source IP:
|
||||
|
||||
CANARY можно ставить любой
|
||||
|
||||
```sh
|
||||
CANARY="HAN_VM2_REDACTION_260813"
|
||||
curl -sS -o /dev/null \
|
||||
-H "Authorization: Bearer ${CANARY}" \
|
||||
-H 'Content-Type: application/x-www-form-urlencoded' \
|
||||
--data-urlencode "auth[application_token]=${CANARY}" \
|
||||
"https://service4chat.han0107.ru/bitrix/sync/webhook/contact?token=${CANARY}"
|
||||
```
|
||||
|
||||
На VM2 под `root`:
|
||||
|
||||
```sh
|
||||
CANARY='HAN_VM2_REDACTION_260813'
|
||||
if /usr/local/sbin/han-vm2-compose logs --no-color \
|
||||
nginx bitrix-sync message-safety-api otel-collector |
|
||||
grep -F -- "$CANARY"; then
|
||||
echo 'FAIL: canary попал в логи' >&2
|
||||
exit 1
|
||||
fi
|
||||
unset CANARY
|
||||
```
|
||||
|
||||
В SigNoz выполните поиск этого же marker по logs и span attributes за окно
|
||||
теста: результат должен быть пустым. Отдельными фейковыми markers повторите
|
||||
проверку для DSN-подобной строки, S3 key и object key. Реальные secrets для
|
||||
такой проверки не используйте.
|
||||
|
||||
Не открывайте webhook-трафик, пока `bitrix-sync` отключён. Отключённый или
|
||||
упавший receiver должен возвращать retryable `503`/закрытую маршрутизацию,
|
||||
никогда успешный `2xx ignored`.
|
||||
|
||||
## Политика отказов
|
||||
|
||||
- Отказ зависимости Safety — fail-closed: VM1 не должна отправлять/продвигать
|
||||
контент.
|
||||
- Устаревшие/недоступные сигнатуры ClamAV отключают только файловую
|
||||
capability; ошибка сканирования никогда не превращается в allow.
|
||||
- Потеря Redis может убрать ускорение, но PostgreSQL остаётся источником
|
||||
истины.
|
||||
- Сбой OTEL ставит в очередь в пределах ограниченного тома и не должен
|
||||
менять вердикты.
|
||||
- Rollback не понижает схемы, не удаляет durable tasks/mappings и не
|
||||
запускает `docker compose down -v`.
|
||||
|
||||
## Аварийный MOCK
|
||||
|
||||
Разрешены только эти пять sudo-команд:
|
||||
|
||||
```text
|
||||
han-message-safety-mode standard
|
||||
han-message-safety-mode mock --text-free true --file-free true
|
||||
han-message-safety-mode mock --text-free true --file-free false
|
||||
han-message-safety-mode mock --text-free false --file-free true
|
||||
han-message-safety-mode mock --text-free false --file-free false
|
||||
```
|
||||
|
||||
Хелпер атомарно пишет только
|
||||
`/etc/han-chat/message-safety-mode.env`, пересоздаёт только Safety API,
|
||||
проверяет health и при сбое восстанавливает предыдущий режим. У MOCK нет
|
||||
таймаута: держите high-severity alert активным до явного `standard`, затем
|
||||
проверьте нормальные text/link/file capabilities и EICAR-canary.
|
||||
|
||||
## Известные исключения по образам
|
||||
|
||||
Образы ClamAV могут потребовать корректировок UID/path после валидации
|
||||
точного digest. Не ослабляйте `read_only`, capabilities или mounts глобально:
|
||||
задокументируйте минимальные writable пути для сигнатур/runtime и
|
||||
компенсируйте сетевыми и ресурсными лимитами. Egress к signature-CDN
|
||||
получает только `freshclam`; `clamd` — нет.
|
||||
|
||||
|
||||
Проверка отправки в Сигноз
|
||||
|
||||
docker network ls | grep observability
|
||||
|
||||
docker run --rm --network han-processing_observability \
|
||||
ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen:latest \
|
||||
traces --otlp-endpoint otel-collector:4317 --otlp-insecure \
|
||||
--service han-processing-otlp-smoke --traces 100 --rate 20
|
||||
|
||||
Проверка
|
||||
/usr/local/sbin/han-vm2-compose logs --since=5m otel-collector |
|
||||
grep -Ei 'error|refused|queue is full|Unauthenticated|tls' || echo 'нет ошибок экспорта'
|
||||
|
||||
после успешной проверки нужно удалить
|
||||
docker rmi ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen:latest
|
||||
|
||||
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
|
||||
|
||||
# Проверка финальная
|
||||
|
||||
1. Проверить автозапуск:
|
||||
|
||||
```sh
|
||||
systemctl is-enabled \
|
||||
docker.service \
|
||||
han-chat-vm2-docker-firewall.service \
|
||||
han-secrets-vm2.service \
|
||||
han-processing.service \
|
||||
certbot.timer
|
||||
|
||||
(все enabled)
|
||||
|
||||
systemctl is-active \
|
||||
docker.service \
|
||||
han-chat-vm2-docker-firewall.service \
|
||||
han-processing.service \
|
||||
certbot.timer
|
||||
|
||||
(все active)
|
||||
|
||||
/usr/local/sbin/han-vm2-compose ps --format "table {{.Service}}\t{{.Status}}\t{{.Ports}}"
|
||||
```
|
||||
|
||||
2. Провести reboot-gate. Только после проверки отдельного входа `admin` и доступа к консоли Selectel:
|
||||
|
||||
```sh
|
||||
systemctl reboot
|
||||
```
|
||||
|
||||
После переподключения повторить команды выше и кратко Gate 6–8: HTTPS, firewall, private Safety API.
|
||||
|
||||
3. Зафиксировать итог релиза:
|
||||
|
||||
```sh
|
||||
/usr/local/sbin/han-vm2-compose config --images
|
||||
/usr/local/sbin/han-vm2-compose ps
|
||||
systemctl list-timers certbot.timer
|
||||
journalctl --no-pager -u han-processing.service -u han-secrets-vm2.service
|
||||
```
|
||||
|
||||
Сохранить версии образов, дату приёмки и результаты gates без значений секретов.
|
||||
|
||||
4. Настроить эксплуатационный мониторинг:
|
||||
|
||||
- container unhealthy/restart/OOM;
|
||||
- срок TLS;
|
||||
- возраст ClamAV signatures;
|
||||
- OTEL queue/export errors;
|
||||
- disk/RAM;
|
||||
- активный MOCK mode;
|
||||
- недоступность private Safety API.
|
||||
|
||||
5. Далее — отдельный controlled cutover Message Safety на VM1: private URL, internal CA, service token, API integration, rollback rehearsal и функциональные проверки.
|
||||
|
||||
6. `bitrix-sync` пока оставить:
|
||||
|
||||
```dotenv
|
||||
BITRIX_SYNC_ENABLED=false
|
||||
BITRIX_SYNC_MODE=disabled
|
||||
```
|
||||
|
||||
Public allow-list — только `deny all;`. Включать Bitrix можно лишь после выполнения gates `module-07`: поля портала, webhooks, migrations, grants, backfill/watermark и rollback rehearsal.
|
||||
|
||||
Таким образом, ближайший шаг сейчас — reboot-gate и фиксация приёмки VM2. Затем переход к интеграции VM1, а не немедленное включение Bitrix.
|
||||
Reference in New Issue
Block a user