#!/usr/bin/env bash # ════════════════════════════════════════════════════════════════ # install-collector.sh # Полная установка collector-сервера VPN Logs. # # Использование: # Вариант 1 (интерактивный, спрашивает значения): # curl -fsSL https://logs.killu.net/install-collector.sh | bash # # Вариант 2 (с готовым config.env): # wget https://logs.killu.net/config.env.example -O config.env # nano config.env # заполни свои значения # curl -fsSL https://logs.killu.net/install-collector.sh | bash -s -- --config=./config.env # # Требования: Debian 12+ / Ubuntu 22+, root, доступ к интернету. # ════════════════════════════════════════════════════════════════ set -euo pipefail export DEBIAN_FRONTEND=noninteractive C_GR="\033[32m"; C_YE="\033[33m"; C_RD="\033[31m"; C_BL="\033[34m"; C_OF="\033[0m" say() { echo -e "${C_BL}▸${C_OF} $*"; } ok() { echo -e "${C_GR}✓${C_OF} $*"; } warn() { echo -e "${C_YE}⚠${C_OF} $*"; } err() { echo -e "${C_RD}✗${C_OF} $*" >&2; } [ "$(id -u)" -eq 0 ] || { err "Запусти под root"; exit 1; } # ── 0. парсинг аргументов / config.env ─────────────────────── CONFIG_FILE="" for arg in "$@"; do case "$arg" in --config=*) CONFIG_FILE="${arg#*=}" ;; esac done if [ -n "$CONFIG_FILE" ] && [ -f "$CONFIG_FILE" ]; then say "Читаю конфиг из $CONFIG_FILE" # shellcheck disable=SC1090 . "$CONFIG_FILE" fi ask() { local var=$1 prompt=$2 default=${3:-} # ${var+x} возвращает 'x' если переменная SET (даже empty), иначе пусто. # Если из env / config переменная уже set (хоть и пустая) — НЕ спрашиваем. if [ -z "${!var+x}" ]; then if [ ! -r /dev/tty ]; then err "Нет TTY и значение '${var}' не задано. Используй --config=FILE или запусти не через pipe:" err " wget https://logs.killu.net/install-collector.sh && bash install-collector.sh" exit 1 fi if [ -n "$default" ]; then read -rp "$prompt [$default]: " val /dev/null 2>&1 || fuser /var/lib/apt/lists/lock >/dev/null 2>&1; do [ $n -eq 0 ] && warn "Жду освобождения apt-lock (unattended-upgrades?)..." sleep 5 n=$((n+1)) [ $n -ge 60 ] && { err "apt-lock не освободился за 5 минут"; exit 1; } done } wait_apt timedatectl set-timezone UTC || true # базовые тулзы для скачивания (на минимальном образе могут отсутствовать) apt-get update -qq apt-get -y -qq install ca-certificates curl wget gnupg wait_apt apt-get -y -qq upgrade || warn "apt upgrade прошёл с warning'ами, продолжаю" ok "система обновлена" # ── 2. зависимости ─────────────────────────────────────────── say "2/6 · установка пакетов" wait_apt apt-get -y -qq install \ zstd jq psmisc \ python3 python3-yaml python3-requests python3-boto3 \ btop htop mtr-tiny vim tmux git \ fail2ban ufw ok "пакеты установлены" # проверка что python-боto3 действительно работает python3 -c 'import boto3, yaml, requests' 2>&1 || { err "Python-зависимости не работают"; exit 1; } # ── 3. MinIO client (mc) + проверка S3 ─────────────────────── say "3/6 · MinIO client + проверка S3" if [ ! -x /usr/local/bin/mc ]; then curl -fLs https://dl.min.io/client/mc/release/linux-amd64/mc -o /usr/local/bin/mc chmod +x /usr/local/bin/mc fi /usr/local/bin/mc alias set tw "$S3_ENDPOINT" "$AWS_ACCESS_KEY_ID" "$AWS_SECRET_ACCESS_KEY" --api S3v4 >/dev/null if ! /usr/local/bin/mc ls "tw/$S3_BUCKET/" >/dev/null 2>&1; then err "S3 не отвечает! Проверь endpoint/bucket/keys." err " endpoint: $S3_ENDPOINT" err " bucket: $S3_BUCKET" exit 1 fi ok "mc установлен, S3 доступен" # проверка Telegram TG_RESP=$(curl -s --max-time 10 "https://api.telegram.org/bot${TG_BOT_TOKEN}/getMe" 2>&1) if ! echo "$TG_RESP" | grep -q '"ok":true'; then err "Telegram bot не отвечает (проверь TG_BOT_TOKEN):" echo "$TG_RESP" | head -2 exit 1 fi ok "Telegram bot работает" # ── 4. fail2ban + UFW ──────────────────────────────────────── say "4/6 · fail2ban + UFW (только SSH открыт)" F2B_IGNORE="127.0.0.1/8 ::1" [ -n "$WHITELIST_IP" ] && F2B_IGNORE="$F2B_IGNORE $WHITELIST_IP" cat >/etc/fail2ban/jail.local </dev/null ufw default deny incoming ufw default allow outgoing ufw allow 22/tcp ufw logging low ufw --force enable >/dev/null ok "защита настроена" # ── 5. конфиги ─────────────────────────────────────────────── say "5/6 · конфиги /etc/log-collector/" mkdir -p /etc/log-collector /var/log/log-collector /var/lib/log-collector cat >/etc/log-collector/secrets.env </etc/log-collector/inventory.yaml <<'EOF' # Список нод. Имя должно совпадать с LOG_NODE_NAME при установке uploader'а. nodes: # пример: # - { country: DE, name: de-1, ip: 91.92.34.5, role: vpn } # - { country: NL, name: nl-1, ip: 151.240.151.122, role: vpn } EOF fi ok "конфиги созданы" # ── 6. скрипт отчёта + systemd timer ───────────────────────── say "6/6 · log-report.py + systemd timer" curl -fsSL https://logs.killu.net/log-report.py -o /usr/local/bin/log-report.py chmod +x /usr/local/bin/log-report.py cat >/etc/systemd/system/log-report.service <<'EOF' [Unit] Description=Daily VPN logs report → Telegram After=network-online.target Wants=network-online.target [Service] Type=oneshot ExecStart=/usr/local/bin/log-report.py StandardOutput=journal StandardError=journal EOF cat >/etc/systemd/system/log-report.timer </dev/null && ok "тестовое сообщение отправлено" || warn "не удалось отправить (проверь TG_TOKEN / TG_CHAT_ID)" echo "" ok "════ collector готов ════" echo "" echo " • Заполни inventory: nano /etc/log-collector/inventory.yaml" echo " • Раскатай uploader на ноды через install-uploader.sh" echo " • Ручной тест: /usr/local/bin/log-report.py" echo " • Статус таймера: systemctl list-timers log-report.timer"