Files
glchat/deploy/security-check.sh
grendervill 4bd1770cb5 feat(deploy): установщик, docker-скелет, шаблоны Caddy/LiveKit и CLI
- deploy/install.sh: идемпотентная установка, режимы встроенного и внешнего
  прокси (§10.13), --dry-run/--skip-tls/--reconfigure, auto-детект профиля (§10.10)
- deploy/compose.yaml.tpl: app, livekit, caddy с лимитами, healthcheck,
  ротацией логов, cap_drop и no-new-privileges (§4.3)
- deploy/caddy: Caddyfile.tpl (встроенный) и external-proxy.conf.tpl (наш стенд)
- deploy/livekit: конфиг с TURN, dynacast и лимитами комнат (§10.6)
- deploy/backup.sh + restore.sh: sqlite .backup, tar, age-шифрование (§10.8)
- deploy/update.sh: бэкап → pull/сборка → up → миграции → smoke (§9.3)
- deploy/smoke.sh: проверки §10.9 (healthz/readyz/meta, порты, UFW, fail2ban, бэкап)
- deploy/security-check.sh: аудит SSH, UFW, fail2ban, прав .env и ключей
- deploy/ufw-docker.sh, nftables и fail2ban-конфиги (sshd, glchat-auth, recidive)
- deploy/glchat-cli.sh: команды §10.7, systemd-юнит glchat.service
- scripts/test-install.sh: прогон установщика в чистом Ubuntu 24.04 (systemd-контейнер)
- Dockerfile: multi-stage (node → golang CGO → debian-slim), non-root, healthcheck
- Makefile: lint/test/build/dev/generate/security/smoke и фиксация версий инструментов
2026-09-19 18:21:02 +03:00

116 lines
4.2 KiB
Bash
Executable File

#!/usr/bin/env bash
# glchat: аудит безопасности инстанса (AGENT.md 9.2, 9.6, 9.7).
set -uo pipefail
GLCHAT_ETC_DIR="${GLCHAT_ETC_DIR:-/opt/glchat}"
ENV_FILE="${GLCHAT_ETC_DIR}/.env"
ISSUES=0
say_ok() { printf ' \033[32m✓\033[0m %s\n' "$*"; }
say_warn() {
printf ' \033[33m!\033[0m %s\n' "$*"
ISSUES=$((ISSUES + 1))
}
say_info() { printf ' · %s\n' "$*"; }
printf '\n=== glchat security-check ===\n'
printf '\n[SSH]\n'
if sshd -T 2>/dev/null | grep -q '^passwordauthentication no'; then
say_ok "вход по паролю запрещён"
else
say_warn "PasswordAuthentication включён (AGENT.md 9.2 требует ключи)"
fi
if sshd -T 2>/dev/null | grep -qE '^permitrootlogin (no|prohibit-password)'; then
say_ok "root-логин ограничен"
else
say_warn "PermitRootLogin разрешён полностью"
fi
max_tries="$(sshd -T 2>/dev/null | awk '/^maxauthtries/ {print $2}')"
if [ -n "$max_tries" ] && [ "$max_tries" -le 3 ]; then
say_ok "MaxAuthTries=${max_tries}"
else
say_warn "MaxAuthTries=${max_tries:-unknown} (рекомендуется ≤ 3)"
fi
printf '\n[UFW]\n'
if command -v ufw >/dev/null 2>&1 && ufw status 2>/dev/null | grep -q 'Status: active'; then
say_ok "UFW активен"
ufw status numbered 2>/dev/null | sed -n 's/^\[[ 0-9]*\]\s*/ · /p' | head -20
else
say_warn "UFW не активен"
fi
printf '\n[DOCKER-USER]\n'
if command -v iptables >/dev/null 2>&1 && iptables -nL DOCKER-USER >/dev/null 2>&1; then
if iptables -nL DOCKER-USER | grep -q glchat-block-internal; then
say_ok "внутренние порты приложения закрыты снаружи"
else
say_warn "нет правил glchat в DOCKER-USER"
fi
fi
printf '\n[fail2ban]\n'
if command -v fail2ban-client >/dev/null 2>&1; then
jails="$(fail2ban-client status 2>/dev/null | sed -n 's/.*Jail list:\s*//p')"
for jail in sshd glchat-auth recidive; do
if printf '%s' "$jails" | grep -q "$jail"; then
banned="$(fail2ban-client status "$jail" 2>/dev/null | sed -n 's/.*Currently banned:\s*//p')"
say_ok "jail ${jail} (забанено: ${banned:-0})"
else
say_warn "jail ${jail} не активен"
fi
done
else
say_warn "fail2ban не установлен"
fi
printf '\n[Слушающие порты]\n'
if command -v ss >/dev/null 2>&1; then
ss -tulnH | awk '{print $1, $5}' | sort -u | while read -r proto addr; do
case "$addr" in
127.0.0.1:* | [::1]:*) say_info "${proto} ${addr} (локальный)" ;;
0.0.0.0:* | [::]:* | *:*) say_info "${proto} ${addr}" ;;
esac
done
fi
printf '\n[Секреты и права]\n'
if [ -f "$ENV_FILE" ]; then
perms="$(stat -c '%a' "$ENV_FILE")"
if [ "$perms" = "600" ]; then
say_ok ".env: права 600"
else
say_warn ".env: права ${perms} (нужно 600)"
fi
for key in SESSION_PEPPER MASTER_KEY LIVEKIT_API_SECRET TOTP_ENCRYPTION_KEY WEBHOOK_SECRET; do
value="$(grep -E "^${key}=" "$ENV_FILE" | cut -d= -f2- || true)"
if [ -z "$value" ]; then
say_warn "${key} не задан"
elif [ "${#value}" -lt 32 ]; then
say_warn "${key} короче 32 символов"
else
say_ok "${key} задан (${#value} символов)"
fi
done
else
say_warn "не найден ${ENV_FILE}"
fi
age_key="${GLCHAT_ETC_DIR}/data/backups/age-key.txt"
if [ -f "$age_key" ]; then
say_warn "приватный age-ключ всё ещё на сервере (${age_key}): сохраните его вне сервера и удалите"
fi
printf '\n[Обновления пакетов]\n'
if command -v apt-get >/dev/null 2>&1; then
upgradable="$(apt-get -s upgrade 2>/dev/null | grep -c '^Inst' || true)"
say_info "пакетов с доступными обновлениями: ${upgradable}"
fi
if [ -f /var/run/reboot-required ]; then
say_warn "требуется перезагрузка сервера (обновления ядра)"
fi
printf '\n=== Замечаний: %d ===\n\n' "$ISSUES"
[ "$ISSUES" -eq 0 ]