mirror of
https://github.com/dartdavros/chatballs.git
synced 2026-10-05 09:14:58 +03:00
Том chatballs-secrets целиком монтировался во все сервисы: публичный backend-app читал пароли платформенной роли и роли миграций, которая обходит RLS. Теперь томов три — общий, платформенной роли и роли миграций вместе с паролем владельца кластера — и каждый процесс монтирует только свои. Генератор переносит файлы существующих установок: копия, побайтная сверка, только потом удаление из общего тома; повторный запуск ничего не трогает, пароли не меняются. doctor на работающем стеке проверяет, что backend-app паролей не видит. Воркер получает алиас platform флагом CHATBALLS_DB_PLATFORM_ALIAS. README перечисляет три тома в бэкапе. nginx (dev-шлюз и production-фронтенд) резолвил имя сервиса один раз при старте: после пересоздания контейнеров старый адрес backend-app достался backend-platform, и туда уходил WebSocket приложения. Upstream задан переменной с resolver на DNS Docker. ADR-CHATBALLS-0048 §3, §5. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
138 lines
4.6 KiB
Bash
138 lines
4.6 KiB
Bash
#!/usr/bin/env bash
|
|
# doctor.sh — read-only pre-flight checks before deployment.
|
|
|
|
cmd_doctor() {
|
|
local failures=0 inst rel
|
|
inst="$(instance_dir)"
|
|
rel="$(release_dir)"
|
|
|
|
_doctor_report() {
|
|
local ok="$1"
|
|
shift
|
|
local msg="$*"
|
|
if [[ "$ok" == "1" ]]; then
|
|
[[ "$CHATBALLS_JSON" == "1" ]] || log_ok "$msg"
|
|
else
|
|
[[ "$CHATBALLS_JSON" == "1" ]] || log_err "$msg"
|
|
failures=$((failures + 1))
|
|
fi
|
|
}
|
|
|
|
require_cmd docker
|
|
_doctor_report 1 "docker found"
|
|
require_cmd flock
|
|
_doctor_report 1 "flock found"
|
|
|
|
if command -v sha256sum >/dev/null 2>&1; then
|
|
_doctor_report 1 "sha256sum found"
|
|
else
|
|
_doctor_report 0 "sha256sum missing"
|
|
fi
|
|
|
|
if docker info >/dev/null 2>&1; then
|
|
_doctor_report 1 "docker daemon reachable"
|
|
else
|
|
_doctor_report 0 "docker daemon not reachable"
|
|
fi
|
|
|
|
if docker compose version >/dev/null 2>&1; then
|
|
_doctor_report 1 "docker compose plugin available"
|
|
else
|
|
_doctor_report 0 "docker compose plugin missing"
|
|
fi
|
|
|
|
local arch
|
|
arch="$(uname -m 2>/dev/null || echo unknown)"
|
|
if [[ "$arch" == "x86_64" ]]; then
|
|
_doctor_report 1 "host arch x86_64"
|
|
else
|
|
_doctor_report 0 "unsupported host arch: $arch (target: x86_64)"
|
|
fi
|
|
|
|
if [[ -d "$inst" ]] && [[ -w "$inst" ]]; then
|
|
_doctor_report 1 "instance dir writable: $inst"
|
|
else
|
|
_doctor_report 0 "instance dir not writable: $inst"
|
|
fi
|
|
|
|
if [[ -f "$(compose_file)" ]]; then
|
|
_doctor_report 1 "compose.yaml present in release: $rel"
|
|
else
|
|
_doctor_report 0 "compose.yaml missing in release: $rel"
|
|
fi
|
|
|
|
if [[ -f "$(release_env_file)" ]]; then
|
|
_doctor_report 1 "release.env present"
|
|
else
|
|
_doctor_report 0 "release.env missing: $(release_env_file)"
|
|
fi
|
|
|
|
if verify_release_checksums; then
|
|
_doctor_report 1 "release checksums valid"
|
|
else
|
|
_doctor_report 0 "release checksums invalid"
|
|
fi
|
|
|
|
if validate_release_image_refs; then
|
|
_doctor_report 1 "release image references are immutable"
|
|
else
|
|
_doctor_report 0 "release image references are invalid"
|
|
fi
|
|
|
|
# Ни доменов, ни ACME-почты, ни ключа подписи, ни паролей БД здесь не
|
|
# проверяем: первое задаёт владелец в «Настройках», второе генерирует в том
|
|
# с секретами первый старт стека. Снаружи, с хоста, этого не видно, и
|
|
# прежние проверки просто ругались на исправную установку — они читали
|
|
# instance .env, которого у продукта нет.
|
|
|
|
if profile_enabled calls; then
|
|
local missing=0 k
|
|
for k in CHATBALLS_CALL_TURN_REALM CHATBALLS_TURN_EXTERNAL_IP CHATBALLS_TURN_LISTENING_IP; do
|
|
if [[ -z "${!k:-}" ]]; then
|
|
_doctor_report 0 "$k required for calls profile"
|
|
missing=1
|
|
fi
|
|
done
|
|
if [[ "$missing" == "0" ]] && validate_calls_network_boundary; then
|
|
_doctor_report 1 "calls profile network boundary valid"
|
|
else
|
|
_doctor_report 0 "calls profile network boundary invalid"
|
|
fi
|
|
fi
|
|
|
|
if { [[ -d "$inst/backups" ]] && [[ -w "$inst/backups" ]]; } || [[ -w "$inst" ]]; then
|
|
_doctor_report 1 "backup dir available"
|
|
else
|
|
_doctor_report 0 "backup dir not creatable: $inst/backups"
|
|
fi
|
|
|
|
if compose_config_validate >/dev/null 2>&1; then
|
|
_doctor_report 1 "compose config valid"
|
|
else
|
|
_doctor_report 0 "compose config invalid"
|
|
fi
|
|
|
|
# Разделение томов с секретами: у публичного backend-app не должно быть
|
|
# паролей ролей platform и migration даже в файловой системе. Проверяется
|
|
# только на работающем стеке; до первого запуска проверять нечего.
|
|
if run_compose ps --status running --services 2>/dev/null | grep -qx backend-app; then
|
|
if run_compose exec -T backend-app sh -c \
|
|
'for f in platform/postgres_platform_password schema/postgres_migration_password schema/postgres_password postgres_platform_password postgres_migration_password postgres_password; do test ! -s "/run/chatballs/secrets/$f" || exit 1; done' \
|
|
>/dev/null 2>&1; then
|
|
_doctor_report 1 "backend-app sees no platform/migration database passwords"
|
|
else
|
|
_doctor_report 0 "backend-app can read platform or migration database passwords"
|
|
fi
|
|
fi
|
|
|
|
if [[ "$failures" != "0" ]]; then
|
|
die "doctor: $failures check(s) failed" 1
|
|
fi
|
|
|
|
if [[ "$CHATBALLS_JSON" == "1" ]]; then
|
|
printf '{"status":"ok","checks":"passed"}\n'
|
|
else
|
|
log_ok "doctor: all checks passed"
|
|
fi
|
|
}
|