116 lines
4.2 KiB
Bash
116 lines
4.2 KiB
Bash
|
|
#!/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 ]
|