1. Вы находитесь в сообществе Rubukkit. Мы - администраторы серверов Minecraft, разрабатываем собственные плагины и переводим на различные языки плагины наших коллег из других стран.
    Скрыть объявление
Скрыть объявление
В преддверии глобального обновления, мы проводим исследования, которые помогут нам сделать опыт пользования форумом ещё удобнее. Помогите нам, примите участие!

Туториал Работа с VPS и безопасность сервера от А до Я

Тема в разделе "Руководства, инструкции, утилиты", создана пользователем iForgotPassword, 4 сен 2026.

?

Полезная статья?

  1. Да

    1 голосов
    100,0%
  2. Нет

    0 голосов
    0,0%
  1. Автор темы
    iForgotPassword

    iForgotPassword Активный участник Пользователь

    Баллы:
    66
    Имя в Minecraft:
    iForgotPassword
    Привет всем! У меня уже есть две статьи - Создание сервера с нуля от А до Я и Настройка сервера и оптимизация от А до Я. Обе про сам Minecraft. А эта - про машину, на которой он живет.

    Тема большая и почти всегда идет мимо новичков. Сервер собрали, плагины поставили, а дальше арендовали VPS, зашли по паролю от root и запустили все как есть. Работает же. Работает, пока однажды не перестает.

    Разберем по шагам - от письма с IP до того момента, когда Вы понимаете, что сервер могли взломать, и знаете, что с этим делать.

    p.s Я исхожу из того, что сервер Вы уже поднимали хотя бы локально и знаете, что такое server.properties и папка plugins. Linux при этом можете видеть впервые - это нормально, с него и начнем.

    Статья не влезает в один пост, поэтому разбита на части. Части 2 и далее лежат в комментариях этой темы.

    Часть 1 (этот пост) - Выбор машины, клиенты и первые шаги
    Часть 2 - Ключи вместо паролей (тык)
    Часть 3 - Файрвол и топология портов (тык)
    Часть 4 - Java, запуск и systemd (тык)
    Часть 5 - Docker и когда он оправдан (тык)
    Часть 6 - База данных и доступ к ней (тык)
    Часть 7 - Мониторинг и алерты (тык)
    Часть 8 - Бэкапы (тык)
    Часть 9 - Восстановление и хранилище провайдера (тык)
    Часть 10 - Защита самого Minecraft (тык)
    Часть 11 - Аудит: сервер могли взломать (тык)
    Часть 12 - Если подтвердилось, и частые вопросы (тык)
    Часть 13 - Инструменты и мелочи для удобства (тык)

    Берем VPS, а не хостинг

    Панельный хостинг - это когда Вам дают веб-морду, куда можно залить плагины и нажать "старт". Дешево, просто и заканчивается ровно там, где начинается эта статья. Ни своего порта, ни своих процессов, ни файрвола, ни бэкапов по расписанию. Про безопасность там говорить нечего, потому что управляете не Вы.

    Нам нужен VPS или VDS - виртуальная машина, где Вы root и делаете что хотите.

    Только KVM, не OpenVZ. При OpenVZ ядро общее с соседями: ресурсы плавают, параметры ядра не поменять, и половина того, что мы будем делать дальше, просто не сработает.

    Частота ядра важнее их количества. Основной тик Minecraft однопоточный, поэтому 4 быстрых ядра почти всегда лучше 8 медленных.

    NVMe, не HDD. Загрузка и сохранение чанков - это дисковые операции, и на них все упирается быстрее, чем в процессор.

    Память. На 5-20 игроков берите 4-8 ГБ. Серверу нельзя отдавать всю память машины, системе тоже надо чем-то дышать.

    Облачные тарифы не берите. "Облачный VPS" рассчитан на другую нагрузку: доля процессора плавает вместе с соседями, диск часто сетевой, а не локальный. Minecraft этого не прощает - получите рваный TPS без единой ошибки в логах и будете месяц искать несуществующий кривой плагин.

    Локация. Смотрите на пинг до Ваших игроков, а не на цену. Разница между Москвой и Франкфуртом для игрока из Новосибирска ощутимая.

    Тестовый период. Есть почти у всех, от 7 до 30 дней. Пользуйтесь: замерьте пинг и посмотрите TPS до того, как платить за месяц.

    Чем платить. У OVH и Hetzner железо дешевле, но российской картой их не оплатить. Российские провайдеры принимают СБП и МИР, выбор среди них сейчас нормальный. Конкретных советовать не буду, цены и качество меняются быстрее, чем обновляется статья.

    Какую систему ставить

    При заказе провайдер спросит, какую ОС развернуть. Берем Ubuntu 26.04 LTS - вышла в апреле 2026, поддержка до апреля 2031. LTS в названии означает долгую поддержку, и для сервера это единственный разумный выбор: обновления безопасности будут приходить пять лет, а система под Вами не поменяется.

    Ubuntu 24.04 LTS тоже живая, если провайдер еще не завез свежую. Debian отличается мелочами, все команды из статьи работают и там.

    Промежуточные версии вроде 25.10 не берите. Поддержка у них девять месяцев, и через год Вы будете переустанавливать систему вместо того, чтобы заниматься сервером.

    Под все системы сразу
    Termius - им пользуюсь сам. Хранит список серверов, ключи, подключается в один клик, есть SFTP для файлов и приложения на телефон. Бесплатного тарифа хватает полностью: SSH, SFTP и проброс портов там есть. Платный добавляет синхронизацию между устройствами, но без нее спокойно живется.

    Tabby - открытая альтернатива, тоже под все три системы, SFTP встроен.

    macOS и Linux
    Ставить ничего не обязательно: ssh уже есть, файлы копируются через scp и rsync, а в Nautilus или Dolphin можно открыть sftp://адрес как обычную папку. Из отдельных программ на macOS хорош ForkLift - двухпанельный менеджер, правит файл прямо на сервере и заливает обратно при сохранении.

    Windows
    ssh
    встроен и тут, просто откройте Терминал и пишите как в Linux, PuTTY сегодня не обязателен. Хочется полноценный Linux под рукой - ставьте WSL, все команды из статьи заработают один в один. Для файлов - WinSCP или MobaXterm, который заменяет PuTTY и WinSCP разом.

    Красивый клиент безопасности не добавляет. Она берется от ключей и настроенной машины, а не от интерфейса.

    Первые десять минут

    Провайдер прислал письмо с IP, пользователем root и паролем. Заходим:
    Код:
    ssh [email protected]
    
    Первый раз спросит про отпечаток ключа - отвечаем yes. Дальше вводим пароль, символы при вводе не отображаются, это нормально.

    Сразу про пароли. Придумывать их головой не надо: то, что придумал человек, подбирается заметно быстрее случайного. Машина сделает лучше:
    Код:
    openssl rand -base64 18
    
    Команду стоит запомнить, дальше по статье она пригодится не раз - так генерируется любой пароль или секрет. Результат сразу в менеджер паролей, запоминать эти строки не нужно.

    Если менеджера паролей еще нет - посмотрите, какие бывают. Bitwarden - самый популярный выбор, работает везде и синхронизируется через облако. 1Password - платный, но удобнее всех. KeePassXC - хранит все в одном файле на Вашем диске, без всякого облака.

    Меняем пароль root на свой, сгенерированный:
    Код:
    passwd
    
    Обновляем систему. Это первое, что стоит сделать на любой новой машине:
    Код:
    apt update && apt upgrade -y
    
    После обновления перезагружаемся. Если в обновления попало ядро или системные библиотеки, они начнут работать только после перезагрузки. Понять, нужна ли она, просто - при следующем заходе по SSH система сама напишет в приветствии:
    Код:
    *** System restart required ***
    
    Увидели эту строку - перезагружаемся и не тянем:
    Код:
    reboot
    
    Связь оборвется, это нормально. Через полминуты-минуту заходим снова, и строка должна пропасть.

    Пока Вы на пустой машине, перезагружаться можно спокойно. Дальше, когда на ней будут игроки, для этого понадобится выбрать время - но это уже другая история и другая часть.

    Заодно Вы наверняка заметили, что Ubuntu вываливает при каждом входе пол-экрана текста с рекламой Ubuntu Pro. Это лечится, но чтобы не отвлекаться сейчас - разберем в самом конце статьи, вместе с цветным приглашением.

    Ставим то, что понадобится дальше:
    Код:
    apt install -y tmux curl jq htop unzip
    
    Часовой пояс - чтобы время в логах совпадало с Вашим:
    Код:
    timedatectl set-timezone Europe/Moscow
    
    Заводим себе пользователя

    Под root постоянно сидеть не надо. Одна опечатка в команде с rm - и восстанавливать нечего. Заводим обычного пользователя и дальше живем под ним:
    Код:
    PASS=$(openssl rand -base64 18)
    adduser --disabled-password --gecos "" mc
    echo "mc:$PASS" | chpasswd
    usermod -aG sudo mc
    echo "Пароль mc: $PASS"
    
    Пароль тут генерируется той же командой, что и выше, только сразу кладется в переменную и оттуда устанавливается пользователю. Последняя строка печатает его в консоль - копируйте в менеджер паролей. Группа sudo дает право выполнять команды от имени root, когда это правда нужно - через sudo команда.

    Случайное значение получается один раз, в первой строке, и дальше просто лежит в переменной. Поэтому повторный echo "$PASS" покажет тот же пароль - так и надо, ведь именно он установлен на аккаунт. Живет переменная до конца сессии: вышли по SSH и зашли снова - она уже пустая. Скопировали пароль в менеджер - можно убрать за собой через unset PASS и clear.

    Дальше мы отключим вход по паролю совсем, и останется он только для sudo - вводить его по SSH Вы не будете.

    Проверяем, что новый пользователь работает, не закрывая текущую сессию:
    Код:
    ssh [email protected]
    sudo whoami
    
    Вторая команда должна ответить root. Если да - переключаемся на этого пользователя насовсем.

    Оба раза вводим пароль mc, тот самый, что напечатала команда выше. При sudo это особенно сбивает с толку, но пароль root тут ни при чем: sudo проверяет, что Вы действительно тот пользователь, под которым сидите, и уже потом смотрит, состоите ли Вы в группе sudo. В приглашении это и написано - password for mc.

    Вопрос всплывает регулярно, поэтому разберу сразу.

    sh на Ubuntu - это не bash, а dash, урезанная оболочка. Отсюда классическая ловушка: человек пишет в скрипте #!/bin/sh, использует внутри массивы или [[ ]], и все падает с невнятной ошибкой.

    bash - оболочка по умолчанию, в ней Вы и работаете. Ее и оставляем, а до ума доведем в конце статьи, там есть отдельный блок про настройку терминала.

    zsh - то, что некоторые советуют ставить сразу. Штука приятная, но на сервер я бы ее не тащил. Выигрыш небольшой: тут Вы либо сидите в tmux с одним процессом, либо выполняете отдельные команды, а не живете в терминале. А главное - люди путаются и начинают писать #!/bin/zsh в скриптах, после чего те не запускаются на машине без zsh.

    Если zsh все-таки хочется - ставьте его себе, а root не трогайте никогда: сломается zsh или не окажется его в аварийном режиме, и заходить будет нечем. На своей рабочей машине - что угодно, там это окупается.

    И правило на всю статью: скрипты пишем с #!/bin/bash. Не /bin/sh, потому что это dash, и не /bin/zsh, потому что его на сервере может не быть.

    Машина готова. В следующей части закроем главную дверь - вход по SSH.

    >>> Часть 2: Ключи вместо паролей (тык)
     
    Последнее редактирование: 4 сен 2026
  2. Автор темы
    iForgotPassword

    iForgotPassword Активный участник Пользователь

    Баллы:
    66
    Имя в Minecraft:
    iForgotPassword
    Это продолжение статьи Работа с VPS и безопасность сервера от А до Я. Начало - в первом посте темы. << Часть 1: Выбор машины, клиенты и первые шаги

    Часть 2. Ключи вместо паролей

    Итак, машина есть, пользователь заведен. Займемся входом.

    Пароль на SSH - это то, что можно подобрать. И подбирают: как только Ваш IP появляется в диапазонах провайдера, боты начинают стучаться круглосуточно. Не потому, что Вы кому-то интересны, а потому, что они перебирают весь интернет подряд. Загляните потом в lastb и посмотрите, сколько попыток накопилось за сутки.

    Ключ подобрать нельзя. Поэтому делаем так: настраиваем вход по ключу, проверяем, что он работает, и только потом отключаем пароль.

    Порядок именно такой. Отключите пароль до проверки ключа - и останетесь снаружи собственной машины.

    Делаем ключ

    Все команды в этом разделе выполняются на Вашем компьютере, а не на сервере.
    Код:
    ssh-keygen -t ed25519 -C "мой ноутбук"
    
    Разберем по частям.

    -t ed25519 - тип ключа. Современный: короткий, быстрый, надежный. RSA на 4096 бит тоже годится, но выбирать его сегодня незачем.

    -C "мой ноутбук" - комментарий, который припишется в конец публичного ключа. На работу он не влияет вообще, это подпись для Вас. Писать можно что угодно - "ноут", "рабочий комп", "домашний", смайлик. Смысл появляется, когда ключей несколько: открываете список ключей на сервере и по подписям понимаете, какой откуда. Без комментария там будет одна невнятная строка, и через год Вы не вспомните, чей это ключ.

    Дальше спросит, куда сохранить - жмем Enter, путь по умолчанию подходит. Спросит парольную фразу - вот ее лучше поставить. Она шифрует сам файл ключа, и если ноутбук у Вас уведут, ключом не воспользуются.

    Получилось два файла в папке ~/.ssh, и разница между ними принципиальная.

    id_ed25519 - приватный ключ. Он не покидает Ваш компьютер. Никогда и никуда. Не в чат, не в тикет поддержке, не в архив с бэкапом, не на "надежный" файлообменник, не другу "на пять минут посмотреть". Про этот файл знаете только Вы. Кто получил его - получил Ваш сервер, и парольная фраза тут последняя линия обороны, а не первая.

    id_ed25519.pub - публичный ключ. Он на то и публичный: его можно выкладывать куда угодно, хоть на стену во ВКонтакте. Ничего плохого с ним не сделать, по нему нельзя восстановить приватный. Именно его мы и кладем на сервер.

    Отличить их просто. Публичный - одна строка, начинается с ssh-ed25519 и заканчивается Вашим комментарием. Приватный - многострочный блок с BEGIN OPENSSH PRIVATE KEY в начале. Увидели слово PRIVATE - значит это тот файл, который никому.

    Кладем ключ на сервер
    Код:
    ssh-copy-id [email protected]
    
    Спросит пароль последний раз - и на этом с паролями закончим.

    Что команда делает: заходит на сервер под указанным пользователем и дописывает содержимое Вашего id_ed25519.pub в файл ~/.ssh/authorized_keys там. Заодно создает папку .ssh, если ее не было, и выставляет правильные права на нее.

    Папка ~/.ssh есть и у Вас на компьютере, и на сервере, но файлы в ней разного назначения.

    На Вашем компьютере
    • id_ed25519 - приватный ключ, которым Вы доказываете, что Вы это Вы.
    • id_ed25519.pub - публичный, копия которого уходит на сервера.
    • known_hosts - отпечатки серверов, куда Вы уже заходили. Именно его заполняет тот самый вопрос "yes/no" при первом подключении. Если сервер вдруг предъявит другой отпечаток, ssh откажется подключаться и громко предупредит - это защита от подмены сервера.
    • config - Ваши сокращения для подключений, про него ниже.

    На сервере
    • authorized_keys - список публичных ключей, которым разрешен вход под этим пользователем. Обычный текстовый файл, одна строка - один ключ.

    Как это работает. При подключении сервер смотрит в authorized_keys и предлагает клиенту задачку, решить которую можно только имея соответствующий приватный ключ. Клиент решает ее у себя и отправляет ответ. Сам приватный ключ при этом никуда не передается - ни по сети, ни куда-либо еще. Поэтому перехватить его в момент входа нельзя, а подобрать нереально.

    Отсюда следствие, которое стоит понимать: доступ дает не "аккаунт с паролем", а конкретный файл на конкретной машине. Хотите зайти с другого компьютера - либо генерируете там свой ключ и добавляете его в authorized_keys, либо аккуратно переносите существующий. Первое правильнее: у каждой машины свой ключ, и потерянный ноутбук отзывается удалением одной строки на сервере.

    Про права. SSH придирчив к правам на эти файлы и просто откажется работать, если они слишком свободные. Если что-то пошло не так, лечится так:
    Код:
    chmod 700 ~/.ssh
    chmod 600 ~/.ssh/authorized_keys ~/.ssh/id_ed25519
    chmod 644 ~/.ssh/id_ed25519.pub
    
    Это на сервере. У себя на компьютере - то же самое для своей папки.

    Загляните в ~/.ssh/authorized_keys на сервере через месяц-другой. Строк там должно быть ровно столько, сколько у Вас машин. Лишняя строка - тема части 11.

    На Windows команды ssh-copy-id может не быть. Тогда так:
    Код:
    type $env:USERPROFILE\.ssh\id_ed25519.pub | ssh [email protected] "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys"
    
    В Termius и других клиентах с интерфейсом ключ добавляется через настройки подключения, руками ничего копировать не надо.

    Проверяем. Открываем новое окно терминала, не закрывая текущее, и заходим:
    Код:
    ssh [email protected]
    
    Если зашли без пароля (или с парольной фразой от ключа) - все получилось.

    Печатать ssh [email protected] каждый раз надоедает. Создаем на своем компьютере файл ~/.ssh/config:
    Код:
    Host mc
        HostName 203.0.113.10
        User mc
        IdentityFile ~/.ssh/id_ed25519
        ServerAliveInterval 60
    
    Теперь достаточно ssh mc. И scp файл mc:~/ тоже работает.

    ServerAliveInterval шлет пакет раз в минуту, чтобы сессия не отваливалась молча, когда Вы отошли от компьютера.

    Закрываем вход по паролю

    Теперь на сервере правим /etc/ssh/sshd_config:
    Код:
    sudo nano /etc/ssh/sshd_config
    
    Находим и приводим к такому виду (строки могут быть закомментированы решеткой, ее убираем):
    Код:
    PermitRootLogin no
    PasswordAuthentication no
    PubkeyAuthentication yes
    
    PermitRootLogin no запрещает заходить сразу под root. Работаем под своим пользователем, а права повышаем через sudo.

    Перед перезапуском проверьте конфиг:
    Код:
    sudo sshd -t
    
    Команда молчит - значит ошибок нет. Ругается - читаем, что именно не понравилось, и правим. Перезапускать сломанный sshd не надо.

    Применяем:
    Код:
    sudo systemctl restart ssh
    
    И снова: не закрывая текущую сессию, откройте новую и убедитесь, что заходите. Только после этого можно закрывать старую.

    В Ubuntu 24.04 и новее служба может называться ssh, а не sshd. Если systemctl ругается, что юнита нет - попробуйте второе имя.

    Совет "перевесьте SSH с 22 на 2222" встречается в каждой второй инструкции. Разберем честно.

    Защиты это не добавляет. Тот, кто целенаправленно смотрит на Вашу машину, найдет открытый порт сканированием за несколько секунд. Прятать сервис от того, кто умеет искать, бессмысленно.

    Что это правда дает - тишину в логах. Массовые боты долбятся в 22 и дальше не идут, поэтому lastb и auth.log перестают быть простыней из тысяч строк. А когда логи чистые, в них видно настоящие события - вот в этом польза.

    Решайте сами. Если меняете:
    Код:
    Port 2222
    
    в sshd_config, и обязательно сначала разрешите новый порт в файрволе, иначе после перезапуска попасть на машину будет нельзя. Про ufw - следующая часть.

    Заходить потом надо с указанием порта: ssh -p 2222 mc@адрес. В ~/.ssh/config это строка Port 2222.

    Ставим fail2ban

    Пароли мы отключили, подбирать боту нечего. Но он об этом не знает и продолжает стучаться, тратя ресурсы на каждое соединение. fail2ban читает логи, замечает тех, кто ломится, и блокирует их адреса на время.

    Порядок тут важен: сначала конфиг, потом установка. Пакет при установке сразу запускает службу, а fail2ban при старте разбирает уже накопленный лог, а не только новые записи.

    А накопиться там могло всякое. Пока мы разбирались с ключами, Вы могли несколько раз ввести пароль, промахнуться ником, подсунуть не тот ключ или постучаться не на тот адрес - каждая такая попытка легла в auth.log с Вашим же IP. Наберется больше пяти за десять минут, и fail2ban забанит Вас через секунду после установки, еще до того, как Вы доберетесь до конфига.

    Поэтому идем в обратном порядке.

    Шаг 1. Узнаем свой адрес. Прямо в текущей сессии:
    Код:
    echo $SSH_CLIENT | awk '{print $1}'
    
    Это надежнее сайтов вида "мой ip": показывает ровно тот адрес, который видит сервер, со всеми провайдерскими преобразованиями.

    Шаг 2. Пишем конфиг до установки. Папки /etc/fail2ban еще нет, создаем ее сами:
    Код:
    sudo mkdir -p /etc/fail2ban
    sudo nano /etc/fail2ban/jail.local
    
    Вставляем содержимое, подставив свой адрес вместо 203.0.113.55:
    Код:
    [DEFAULT]
    bantime = 1h
    findtime = 10m
    maxretry = 5
    ignoreip = 127.0.0.1/8 203.0.113.55
    
    [sshd]
    enabled = true
    
    В nano вставка - Ctrl+Shift+V или правая кнопка мыши, сохранение - Ctrl+O и Enter, выход - Ctrl+X.

    Читается конфиг так: пять неудачных попыток за десять минут - бан на час. Адреса из ignoreip не банятся никогда.

    Кому привычнее vim - sudo vim /etc/fail2ban/jail.local, дальше i для вставки и :wq для сохранения с выходом. Отдельный touch для создания файла не нужен, любой редактор создаст его при сохранении.

    Свой конфиг мы кладем отдельным файлом, а не правим стандартный jail.conf: его перезапишет следующее обновление пакета. Файл jail.local пакет не трогает и при установке не затирает - проверено.

    Шаг 3. Теперь ставим.
    Код:
    sudo apt install fail2ban
    
    Apt покажет список пакетов и спросит Do you want to continue? [Y/n] - отвечаем Y и Enter. Заглавная буква в скобках означает вариант по умолчанию, так что можно просто нажать Enter. Такой вопрос будет появляться при каждой установке, дальше по статье я на нем останавливаться не буду. Не хотите, чтобы спрашивал - добавляйте к команде -y, как мы делали с apt upgrade -y.

    Служба поднимется сама и сразу с Вашим конфигом. Проверяем:
    Код:
    sudo systemctl enable --now fail2ban
    sudo fail2ban-client status sshd
    
    Вторая команда покажет, сколько адресов уже поймано. Через сутки цифра Вас удивит.

    Симптом узнается сразу: связь есть, но SSH не отвечает вообще, будто машины нет.

    Заходим через аварийную консоль провайдера (про нее ниже) и разбаниваем себя:
    Код:
    sudo fail2ban-client set sshd unbanip 203.0.113.55
    
    Если своего адреса Вы не знаете или под баном оказалось несколько - проще снять все разом:
    Код:
    sudo fail2ban-client unban --all
    
    Ботов это тоже отпустит, но они вернутся в бан при следующей же попытке, так что терять нечего.

    Посмотреть, кто сейчас забанен:
    Код:
    sudo fail2ban-client banned
    
    Заодно допишите свой адрес в ignoreip, если забыли, и перечитайте конфиг:
    Код:
    sudo systemctl restart fail2ban
    
    Ждать тоже вариант: при bantime = 1h бан снимется сам через час. Но если Вы в этот момент что-то чинили на сервере, час покажется долгим.

    Отдельно про динамический адрес. Если провайдер выдает Вам новый IP при каждом переподключении, ignoreip помогает только до следующей смены. Тут единственная настоящая страховка - знать, где в панели провайдера аварийная консоль.

    Рано или поздно это случается со всеми. Спокойно.

    У любого нормального провайдера в панели есть аварийная консоль - VNC или что-то похожее. Это как монитор, подключенный к машине напрямую: SSH там не нужен, и никакой файрвол ее не отрежет. Заходите через нее под root, правите конфиг, перезапускаете службу.

    Найдите эту кнопку в панели заранее, пока все работает. Искать ее в момент, когда доступа нет, - удовольствие ниже среднего.

    Второй вариант, если консоли нет: у многих провайдеров есть режим восстановления, когда машина грузится с внешнего образа, а Ваш диск подключается как обычный раздел. Дольше, но тоже решает.

    И правило, которое я повторяю третий раз за часть, потому что оно того стоит: не закрывайте рабочую сессию, пока не проверили новую. Пока старое окно открыто, Вы можете откатить любую ошибку.

    Итог части

    Вход по ключу работает, пароли отключены, под root напрямую не зайти, fail2ban отбивает назойливых, аварийная консоль найдена в панели.

    Дальше самое интересное - разберемся, что с этой машины вообще видно из интернета.

    Если хочется подробнее про SSH - есть руководство на русском и документация Ubuntu про ключи. По fail2ban - вот эта статья.

    >>> Часть 3: Файрвол и топология портов (тык)
     
    Последнее редактирование: 4 сен 2026
  3. Автор темы
    iForgotPassword

    iForgotPassword Активный участник Пользователь

    Баллы:
    66
    Имя в Minecraft:
    iForgotPassword
    Это продолжение статьи Работа с VPS и безопасность сервера от А до Я. Начало - в первом посте темы. << Часть 2: Ключи вместо паролей

    Часть 3. Файрвол и топология портов

    Итак, машина есть, заходим по ключу. Пора разобраться, что с этой машины вообще видно из интернета.

    Правило тут одно, и оно простое. Наружу смотрят два порта: SSH и тот, куда заходят игроки. Больше ничего. Выглядит очевидно, но на практике наружу торчит то RCON с паролем из примера, то порт базы, то сервер Paper, который вообще не должен быть виден.

    Что слушает машина прямо сейчас, покажет одна команда:
    Код:
    sudo ss -tulpn
    
    Сервер мы еще не ставили, поэтому вывод будет коротким - примерно такой:
    Код:
    Netid  State   Recv-Q  Send-Q   Local Address:Port    Peer Address:Port
    tcp    LISTEN  0       4096           0.0.0.0:22           0.0.0.0:*
    tcp    LISTEN  0       4096              [::]:22              [::]:*
    
    Две строки, и обе про SSH. Первая - для обычных адресов, вторая - для IPv6, [::] это его запись того же самого. Так и должно быть.

    Сидим мы под пользователем mc, поэтому дальше почти везде нужен sudo. Без него команда отработает, но не покажет, какому процессу принадлежит порт.

    Теперь главное, на что смотреть в этом выводе - колонка Local Address, а точнее то, что стоит до двоеточия.

    0.0.0.0 или * означает "слушаю на всех интерфейсах", то есть достучаться можно из интернета. Для порта 22 это нормально, мы же заходим снаружи.

    127.0.0.1 означает "слушаю только сам себя". До такого порта из интернета не добраться в принципе, никакие правила файрвола для него не нужны.

    Разницу между этими двумя записями запомните сразу. Когда поставим сервер, база данных и RCON должны оказаться на 127.0.0.1, а на 0.0.0.0 останутся ровно два порта.

    На Ubuntu ufw уже установлен, но выключен. Если его нет - sudo apt install ufw.

    ВАЖНО! Сначала разрешаем SSH, только потом включаем файрвол. Иначе Вы отрежете себя от собственной машины и заходить придется через аварийную консоль в панели провайдера. И не закрывайте текущую сессию, пока не убедились, что открывается новая.

    Код:
    sudo ufw default deny incoming     # входящие запрещены по умолчанию
    sudo ufw default allow outgoing    # исходящие разрешены
    sudo ufw allow OpenSSH             # то же самое, что allow 22/tcp
    sudo ufw allow 25565/tcp           # порт, куда заходят игроки
    sudo ufw enable
    
    Последняя команда спросит Proceed with operation (y|n)? и предупредит, что может оборвать SSH. Отвечаем y. Предупреждение печатается всегда, ufw просто не знает, разрешили ли Вы SSH. Мы разрешили - строкой выше. А текущую сессию ufw не рвет в любом случае: уже установленные соединения он пропускает, закрываются только новые подключения к запрещенным портам.

    Смотрим, что получилось:
    Код:
    sudo ufw status numbered
    
    Номера нужны, чтобы удалять правила: sudo ufw delete 3 уберет третье.

    Если меняли порт SSH в прошлой части, разрешайте его, а не 22. И проверяйте вход в новом окне до того, как закроете текущее.

    Пара слов о том, что под капотом. ufw это не отдельный файрвол, а надстройка над nftables, который встроен в ядро. Писать правила nftables руками можно, но для одной машины с сервером Minecraft это лишняя работа. ufw закрывает задачу четырьмя командами.

    Теперь про то, зачем это все.

    Если у Вас один сервер без прокси - Вы уже закончили. Открыт SSH, открыт 25565, дальше можно листать до раздела про RCON. А вот если серверов несколько и перед ними стоит Velocity, начинается самое интересное.

    Почему открытый порт сервера Paper это дыра

    Схема с прокси построена на доверии. Игрок стучится в Velocity, тот проверяет его, решает, куда отправить, и передает серверу Paper ник и UUID. Сервер Paper эти данные не перепроверяет - он берет их на веру, потому что так работает modern forwarding. В этом весь смысл: авторизация происходит один раз, на прокси.

    А теперь представьте, что порт сервера Paper открыт наружу. Атакующий поднимает у себя на компьютере свой Velocity, прописывает в нем Ваш IP и порт, и заходит напрямую. Мимо авторизации. Мимо ботфильтра. Под любым ником, который сам себе придумает. В том числе под Вашим, со всеми правами.

    Про это пишут сами разработчики Velocity, прямым текстом в документации: modern forwarding не заменяет файрвол, настройте файрвол.

    Вариант 1: все на одной машине

    Самый частый случай и самое простое решение. В server.properties каждого сервера Paper пишем:
    Код:
    server-ip=127.0.0.1
    
    Все. Сервер теперь слушает только локальный интерфейс, из интернета до него не достучаться в принципе, никаких правил в ufw добавлять не нужно. Velocity обращается к нему по 127.0.0.1:25566 и прекрасно себя чувствует, они же на одной машине.

    В velocity.toml это выглядит так:
    Код:
    [servers]
    auth = "127.0.0.1:25566"
    main = "127.0.0.1:25567"
    
    И сразу про порты, чтобы потом не переделывать. У каждого сервера порт свой, повторяться они не могут физически: два процесса один и тот же порт не займут, второй просто не стартует с ошибкой Address already in use.

    25565 отдаем прокси, и только ему. Это стандартный порт Minecraft, тот самый, который клиент подставляет сам, когда игрок вбивает адрес без двоеточия. Значит, на нем должно висеть то, куда игроки заходят - Velocity. Серверам Paper раздаем что угодно дальше по списку: 25566, 25567, 25568 и так далее. Схема получается такая:

    Код:
    25565  Velocity      <- сюда заходят игроки
    25566  auth          <- только с 127.0.0.1
    25567  main          <- только с 127.0.0.1
    
    Порт сервера Paper задается в его server.properties строкой server-port=25566, и он же прописывается в velocity.toml. Разъехались эти два числа - прокси будет стучаться в пустоту и писать в консоль, что сервер недоступен.

    Заводить сервера Paper на 25565 "потому что так привычно" не надо даже на тестовой машине. Один раз перепутаете, какой процесс где, и полдня будете искать, почему игроки попадают мимо авторизации.

    Вариант 2: сервера на разных машинах

    Тут 127.0.0.1 уже не поможет, машины разные и общаться им надо через сеть. Значит, нужен файрвол: на машине с сервером Paper открываем его порт только для IP прокси.
    Код:
    sudo ufw allow from 203.0.113.10 to any port 25566 proto tcp
    
    Где 203.0.113.10 это адрес машины с Velocity. Для всех остальных порт остается закрытым.

    Если провайдер дает приватную сеть между Вашими машинами - берите ее. Тогда сервера общаются по внутренним адресам, наружу этот трафик не выходит вообще, и правило в ufw становится страховкой, а не единственной защитой.

    forwarding-secret это пароль, а не настройка

    Связка прокси и сервера держится на общем секрете. У Velocity в velocity.toml:
    Код:
    player-info-forwarding-mode = "modern"
    forwarding-secret-file = "forwarding.secret"
    
    У Paper в config/paper-global.yml:
    Код:
    proxies:
      velocity:
        enabled: true
        online-mode: true
        secret: 'сюда содержимое forwarding.secret'
    
    Тут есть на чем споткнуться.

    online-mode в Paper должен совпадать с online-mode прокси. Делаете пиратский сервер - значит false в обоих местах. Не совпало, и игроки получают невнятные ошибки входа, а Вы полдня ищете причину.

    Сам секрет генерируем случайным, а не придумываем:
    Код:
    openssl rand -base64 32 > forwarding.secret
    chmod 600 forwarding.secret
    
    А дальше начинается самое обидное. Этот файл регулярно уезжает туда, куда не надо. В публичный git вместе с конфигами. В архив с бэкапом, который потом полгода лежит в Загрузках. В скриншот консоли, выложенный на форум с вопросом "почему не работает". Утек секрет - и вся схема с прокси превращается в тыкву, потому что зайти напрямую теперь может любой, у кого этот секрет есть. Заменить его недолго, вопрос в том, чтобы вообще заметить утечку.

    Подробности про режимы форвардинга - в документации Velocity, про секцию proxies в Paper - в справочнике по paper-global.yml.

    RCON это удаленная консоль сервера. Штука полезная - через нее скрипт бэкапа шлет save-off, а панель управления выполняет команды. Проблема в том, как ее обычно включают:
    Код:
    enable-rcon=true
    rcon.port=25575
    rcon.password=
    
    Пустой пароль, порт наружу. У кого есть RCON, у того есть консоль сервера. А у кого консоль, тот на сервере хозяин: раздать себе оп, выгрузить плагины, остановить сервер.

    Правильно так. Если RCON Вам не нужен - enable-rcon=false и забыли. Если нужен - пароль случайный, длинный, и обязательно:
    Код:
    rcon.password=<длинная случайная строка>
    
    плюс порт закрыт в ufw. При server-ip=127.0.0.1 RCON тоже слушает только локально, так что вариант с одной машиной решает и эту проблему заодно.

    Query (enable-query) отдает наружу список игроков, версию и описание сервера. Нужен он мониторингам вроде minecraft-statistic. Если Вы им не пользуетесь - выключайте. Открытый query это лишняя строчка в поисковиках по открытым портам, а по таким спискам боты и ходят.

    Проверить себя просто: sudo ss -tulpn и глазами по колонке адресов. Все, что не 127.0.0.1 и не 25565, должно иметь очень хорошее объяснение.

    Грабля, на которую наступают все: Docker и ufw

    Если Вы запускаете сервер или базу в Docker - читайте внимательно. Здесь проверка через ufw врет.

    Docker сам правит правила сетевого фильтра, причем в обход ufw. Когда Вы публикуете порт строкой вида -p 5432:5432 или через ports в docker-compose, Docker прописывает свое правило раньше, чем ufw успевает сказать "запрещено". В итоге sudo ufw status показывает, что порт закрыт, а порт при этом открыт всему интернету.

    Лечится одной правкой. Публикуем порт не наружу, а на локальный адрес:
    Код:
    ports:
      - "127.0.0.1:5432:5432"
    
    Вместо привычного "5432:5432". Теперь контейнер доступен только с самой машины, и ufw тут вообще не при чем.

    Проверьте это прямо сейчас, если у Вас что-то крутится в Docker. Смотрим sudo ss -tulpn и ищем строки с 0.0.0.0 рядом с портами контейнеров. Особенно это касается баз данных - про них разговор отдельный, в части 6.

    Пара слов про DDoS

    Раз уж мы про сеть. Какая-то базовая защита от мусорного трафика сегодня есть почти у каждого провайдера, и небольшому серверу этого обычно хватает. Серьезная защита - отдельная платная услуга, стоит она заметных денег и нужна проектам, до которых надо сначала дорасти. В этой статье мы туда не идем.

    Все проверки выше мы делали с самой машины, и это половина картины. sudo ss -tulpn показывает, что процесс слушает порт. Дойдет ли до этого процесса пакет из интернета - другой вопрос, потому что по дороге стоят ufw и файрвол провайдера, который на некоторых тарифах включен по умолчанию и про который забывают.

    Поэтому проверяем с другой машины. Самое простое:
    Код:
    nc -zv 203.0.113.10 22       # SSH, должен открыться
    nc -zv 203.0.113.10 5432     # база, НЕ должен
    nc -zv 203.0.113.10 25575    # RCON, НЕ должен
    
    Для открытого порта Вы увидите succeeded, для закрытого - Connection refused или таймаут. Второе даже лучше первого: при отказе понятно, что порт есть, но закрыт, а таймаут означает, что пакет просто выбросили молча.

    Порт 25565 сейчас тоже ответит отказом, и это правильно: в ufw мы его разрешили, но слушать его пока некому - сервер поставим в следующей части. Вернитесь к проверке после запуска.

    Онлайн-проверялки портов тоже подойдут, если под рукой нет второй машины. И очевидное: сканировать имеет смысл только свои адреса.

    Итог части

    Прямо сейчас у Вас должно быть сделано вот что: ufw включен, наружу открыты только SSH и порт, куда будут заходить игроки, и проверено это с другой машины, а не с самой себя.

    Остальное из этой части - правила, которые понадобятся в следующих, когда появится что настраивать. Сервера Paper либо на 127.0.0.1, либо доступны только с IP прокси. RCON выключен или сидит на локальном адресе с нормальным паролем. Query выключен. Контейнеры Docker публикуются на 127.0.0.1. Секрет форвардинга случайный, с правами 600 и не в git.

    Возвращайтесь сюда после каждой установки чего-то нового: поставили базу, поставили панель, добавили контейнер - прогнали sudo ss -tulpn и посмотрели, не появилось ли лишней строки с 0.0.0.0.

    По ufw есть хорошее руководство на русском и документация Ubuntu, если захотите копнуть глубже.

    >>> Часть 4: Java, запуск и systemd (тык)
     
    Последнее редактирование: 4 сен 2026
  4. Автор темы
    iForgotPassword

    iForgotPassword Активный участник Пользователь

    Баллы:
    66
    Имя в Minecraft:
    iForgotPassword
    Это продолжение статьи Работа с VPS и безопасность сервера от А до Я. Начало - в первом посте темы. << Часть 3: Файрвол и топология портов

    Часть 4. Java, запуск и systemd

    Машина закрыта, порты на месте. Пора поставить туда сервер.

    Java

    Версиям Minecraft 26.x нужна Java 25. Не "желательно", а иначе сервер не стартует: на Java 21 он падает сразу с ошибкой про версию класс-файла. Velocity 4.x требует того же.

    Сборок Java несколько, и для наших задач они равнозначны - Temurin, Zulu, Corretto. Ставим Temurin:
    Код:
    sudo apt install -y wget apt-transport-https gpg
    wget -qO - https://packages.adoptium.net/artifactory/api/gpg/key/public | sudo tee /etc/apt/trusted.gpg.d/adoptium.asc
    echo "deb https://packages.adoptium.net/artifactory/deb $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/adoptium.list
    sudo apt update && sudo apt install -y temurin-25-jdk
    java -version
    
    Последняя команда должна показать 25.

    Актуальная инструкция всегда есть в документации PaperMC.

    Где что будет лежать

    Перед тем как что-то заливать, решим с раскладкой. Правило одно: один сервер - одна папка в домашнем каталоге пользователя mc. Для связки из части 3 это выглядит так:
    Код:
    /home/mc/
      proxy/         Velocity, сюда подключаются игроки
      authServer/    Paper, авторизация
      mainServer/    Paper, основной мир
      scripts/       скрипты запуска, бэкапа, обновления
    
    Если сервер один и прокси нет, остаются mainServer и scripts. Будет три сервера Paper - добавится четвертая папка рядом. Схема от этого не меняется.

    Имена папок произвольные, но поменяв их, поменяйте и везде дальше по статье: эти же имена попадут в юнит systemd, в скрипт запуска и в список бэкапа.

    Держать сервера в домашнем каталоге, а не в /opt или /srv, удобно по одной причине: все уже принадлежит mc, и вопросов с правами не возникает вообще. Под systemd работает любой вариант.

    Заливаем файлы

    Здесь дорога раздваивается.

    Вариант 1: сборка уже есть. Вы собирали сервер у себя, накидали плагинов, настроили конфиги и теперь переносите готовое на VPS. Об этом следующие абзацы.

    Вариант 2: локально ничего нет. Базу будете готовить сразу на машине. Заливать тогда нечего, разберем этот случай отдельно, сразу после первого.

    Итак, вариант 1. Собранную сборку - плагины, конфиги, мир - копируем со своего компьютера. На каждую папку своя команда:
    Код:
    rsync -avz --progress ./Server/ [email protected]:~/mainServer/
    
    Разберем ее по частям, чтобы Вы могли подставить свое.

    ./Server/ - папка на Вашем компьютере, та самая, в которой лежат plugins, server.properties и остальное. Путь указывается относительно каталога, из которого запускаете команду.

    [email protected]:~/mainServer/ - куда. До двоеточия то же самое, что Вы пишете при ssh, после - папка на сервере. ~ разворачивается в /home/mc, так что это та самая /home/mc/mainServer из схемы выше. Создавать ее заранее не надо, rsync сделает сам.

    -a сохраняет права и вложенные папки, -z жмет трафик по дороге, -v и --progress показывают, что сейчас передается и сколько осталось.

    Слеш в конце источника решает. ./Server/ со слешем означает "содержимое папки", без слеша - "саму папку". Во втором случае на сервере получится ~/mainServer/Server/, и дальше Вы будете искать, почему ничего не запускается.

    Для связки из трех серверов команд будет три, по одной на папку:
    Код:
    rsync -avz --progress ./Proxy/       [email protected]:~/proxy/
    rsync -avz --progress ./AuthServer/  [email protected]:~/authServer/
    rsync -avz --progress ./MainServer/  [email protected]:~/mainServer/
    
    rsync удобнее scp тем, что докачивает после обрыва и при повторном запуске передает только изменения. Для больших миров это решающий аргумент.

    Ядро в этот набор не входит - его машина скачает сама, про это ниже. Логи и кеш тоже тащить незачем: --exclude=logs/ --exclude=cache/.

    Кто предпочитает мышкой - подключайтесь по SFTP через тот же Termius, WinSCP или ForkLift и кладите папки туда же.

    Вариант 2: собираем сразу на машине

    Переносить нечего, поэтому просто создаем папки по схеме выше:
    Код:
    mkdir -p ~/proxy ~/authServer ~/mainServer ~/scripts
    
    Лишние просто не создавайте. Все остальное сервер сделает сам при первом запуске: конфиги, папку plugins, мир. От Вас нужно только ядро - его качаем прямо на машине, про это следующий блок.

    Первый запуск ядра закончится сразу и с руганью: сервер создаст eula.txt со строкой eula=false и остановится. Меняем на true:
    Код:
    nano ~/mainServer/eula.txt
    
    Второй запуск уже сгенерирует мир и поднимет сервер. После этого появится server.properties, который мы правили в части 3 - порты, server-ip, online-mode. Плагины закидываем в ~/mainServer/plugins по SFTP или тем же rsync, по одному, с перезапуском после каждого сомнительного.

    Собирать сборку на боевой машине удобно ровно до первого игрока. Дальше каждый опыт с новым плагином ставится на живых людях, так что заведите локальную копию и проверяйте изменения там.

    Тащить jar через свой домашний интернет незачем, сервер скачает его сам и быстрее. У PaperMC для этого есть API.

    Готовый скрипт лежит тут: MinecraftLinuxUtils, файл mc-jar.sh.
    Код:
    git clone https://github.com/BrainRTP/MinecraftLinuxUtils.git ~/scripts
    chmod +x ~/scripts/*.sh
    sudo mkdir -p /opt/mc/jars && sudo chown mc /opt/mc/jars
    
    ~/scripts/mc-jar.sh paper 26.2 ~/mainServer
    
    Скрипт спрашивает у API последнюю сборку, сверяет контрольную сумму и кладет jar в общий каталог, а в папку сервера ставит ссылку на него. Повторный запуск ничего не качает, если сборка уже свежая.

    Зачем общий каталог, если сервер один. Незачем, если один. Смысл появляется, когда серверов несколько - об этом ниже.

    Скрипт запуска

    Голый java -jar paper.jar для боевого сервера не годится: он не переживет падение и не переживет закрытие терминала. Нужен скрипт.

    Создаем ~/mainServer/start.sh:
    Код:
    #!/bin/bash
    cd "$(dirname "$0")" || exit 1
    
    MIN_RAM="4G"
    MAX_RAM="4G"
    JAR="mainServer-paper.jar"
    
    AIKAR="-XX:+UseG1GC -XX:+ParallelRefProcEnabled -XX:MaxGCPauseMillis=200 \
    -XX:+UnlockExperimentalVMOptions -XX:+DisableExplicitGC -XX:+AlwaysPreTouch \
    -XX:G1NewSizePercent=30 -XX:G1MaxNewSizePercent=40 -XX:G1HeapRegionSize=8M \
    -XX:G1ReservePercent=20 -XX:G1HeapWastePercent=5 -XX:G1MixedGCCountTarget=4 \
    -XX:InitiatingHeapOccupancyPercent=15 -XX:G1MixedGCLiveThresholdPercent=90 \
    -XX:G1RSetUpdatingPauseTimePercent=5 -XX:SurvivorRatio=32 \
    -XX:+PerfDisableSharedMem -XX:MaxTenuringThreshold=1"
    
    while true; do
        java -Xms$MIN_RAM -Xmx$MAX_RAM \
            --sun-misc-unsafe-memory-access=allow \
            $AIKAR -jar "$JAR" nogui
        echo "Сервер остановлен. Перезапуск через 5 секунд, Ctrl+C для выхода."
        sleep 5
    done
    
    Код:
    chmod +x ~/mainServer/start.sh
    
    Что тут важно.

    -Xms равен -Xmx. Куча сразу занимает свой размер и не растет рывками во время игры. И помните, что это память сервера, а не машины: на машине с 8 ГБ серверу отдают 5-6, остальное системе.

    --sun-misc-unsafe-memory-access=allow убирает предупреждение, которым на Java 25 сыплет библиотека JOML при старте. Без флага сервер работает, просто в логе висит простыня.

    Флаги Aikar - стандартный набор для настройки сборщика мусора, его используют почти все. Значения выше рассчитаны на кучу до 12 ГБ, для большей нужны другие. Подробный разбор - в статье про оптимизацию и в документации Paper.

    Цикл while поднимает сервер заново, если тот упал.

    tmux: чтобы сервер жил без Вас

    Запустить ./start.sh прямо по SSH нельзя - закрыли терминал, умер сервер. Нужна фоновая сессия.
    Код:
    tmux new -s mc          # создать сессию с именем mc
    ./start.sh              # запускаем сервер уже внутри нее
                            # отключиться: Ctrl+B, отпустить, затем D
    tmux attach -t mc       # вернуться к консоли
    tmux ls                 # список сессий
    
    Сессия живет, пока жива машина. Отключились по SSH, закрыли ноутбук, уехали - сервер работает, и в любой момент можно вернуться и увидеть консоль.

    Префикс у tmux - Ctrl+B. Нажали, отпустили, потом отдельно D. У старого screen префикс Ctrl+A, и это единственное, что стоит про него знать: начинайте сразу с tmux.

    Четырех команд выше хватает, чтобы держать сервер, но tmux умеет больше - окна, панели, прокрутка истории, свои сочетания в ~/.tmux.conf. С первого раза это в голове не укладывается, так что держите под рукой шпаргалки: на русском и tmuxcheatsheet.com на английском. Подробнее - статья на Хабре и Getting Started в вики tmux.

    Сочетания можно посмотреть и не выходя из сессии: Ctrl+B, затем ?. Выход из этого списка - q.

    systemd: чтобы сервер поднимался сам

    tmux решает вопрос "не умереть вместе с SSH", но не решает "подняться после перезагрузки машины". Для этого есть systemd.

    Создаем /etc/systemd/system/minecraft.service:
    Код:
    [Unit]
    Description=Minecraft server
    After=network-online.target
    Wants=network-online.target
    
    [Service]
    User=mc
    WorkingDirectory=/home/mc/mainServer
    ExecStart=/bin/bash /home/mc/mainServer/start.sh
    Restart=on-failure
    RestartSec=10
    KillSignal=SIGTERM
    TimeoutStopSec=120
    
    NoNewPrivileges=true
    PrivateTmp=true
    ProtectSystem=strict
    ReadWritePaths=/home/mc
    ProtectKernelTunables=true
    ProtectKernelModules=true
    RestrictSUIDSGID=true
    
    [Install]
    WantedBy=multi-user.target
    
    Код:
    sudo systemctl daemon-reload
    sudo systemctl enable --now minecraft
    journalctl -u minecraft -f
    
    TimeoutStopSec=120 - даем серверу время сохранить мир перед выключением. Значение по умолчанию меньше, и при перезагрузке машины сервер могут прибить на середине записи чанков.

    Блок без пустых строк в середине - это ограничения. Стоят они семь строк, а закрывают целый класс проблем: процесс не может повысить себе права, видит свой отдельный /tmp, а всю файловую систему видит только на чтение, кроме явно разрешенной /home/mc. Если однажды в plugins окажется не то, разгуляться ему будет негде.

    Одна оговорка. При таком запуске консоли у Вас нет: процесс работает без терминала, вводить команды некуда. Варианта два - либо включить RCON и слать команды через него, либо не использовать systemd, а держать сервер в tmux.

    Комбинируем: systemd запускает не сам сервер, а сессию tmux с ним.
    Код:
    ExecStart=/usr/bin/tmux new-session -d -s mc -c /home/mc/mainServer 'bash start.sh'
    ExecStop=/usr/bin/tmux send-keys -t mc stop Enter
    Type=forking
    RemainAfterExit=yes
    
    Машина перезагрузилась - сервер поднялся сам. Зашли по SSH, сделали tmux attach -t mc - вот Вам консоль.

    ExecStop тут важен: он шлет серверу stop, а не убивает процесс, и мир сохраняется штатно.

    Если у Вас связка из части 3 - прокси и два сервера Paper - копипастить конфиг и скрипт три раза не хочется.

    Один jar на всех. Ядро лежит в /opt/mc/jars, а в папке каждого сервера стоит ссылка на него. Экономия места тут ни при чем, 30 мегабайт ничего не решают. Смысл в том, что обновление становится одним действием - поменяли файл, и все сервера на следующем рестарте поднялись на одинаковой сборке. Иначе через полгода половина серверов на одном билде, половина на других, и Вы ловите баг, который воспроизводится через раз.

    Ссылка называется по имени сервера - mainServer-paper.jar, authServer-paper.jar. Мелочь, но в выводе ps auxfww сразу видно, какой процесс кому принадлежит, и не надо гадать по номерам.

    Шаблонный юнит systemd. Один файл на все сервера, вместо трех почти одинаковых. Называем его [email protected], и дальше:
    Код:
    sudo systemctl enable --now paper@mainServer
    sudo systemctl enable --now paper@authServer
    
    Внутри юнита имя после собачки подставляется вместо %i, так что WorkingDirectory=/home/mc/%i сам разворачивается в нужную папку.

    Готовый юнит и скрипты - в MinecraftLinuxUtils, там же папка example с раскладкой на прокси и два сервера: порты расставлены, обертки запуска написаны, остается подставить свои пути.

    И главное: не запускайте сервер от root. Paper ругается на это в логе большими буквами, и не зря. Плагин, запущенный от root, может на машине все. Работаем под отдельным пользователем, как завели в части 1. Если файлы уже лежат от root - передайте их:
    Код:
    sudo chown -R mc:mc /home/mc
    
    Итог части

    Java 25 стоит, сервер залит, скрипт запуска с флагами и автоперезапуском написан, сервер живет в tmux или поднимается через systemd, все работает не от root.

    >>> Часть 5: Docker и когда он оправдан (тык)
     
    Последнее редактирование: 4 сен 2026
  5. Автор темы
    iForgotPassword

    iForgotPassword Активный участник Пользователь

    Баллы:
    66
    Имя в Minecraft:
    iForgotPassword
    Это продолжение статьи Работа с VPS и безопасность сервера от А до Я. Начало - в первом посте темы. << Часть 4: Java, запуск и systemd

    Часть 5. Docker и когда он оправдан

    Про Docker спрашивают часто, и ответ на главный вопрос короткий: для одного сервера на одной машине он не нужен. Обычный systemd из прошлой части проще и работает не хуже.

    Дальше - для тех, у кого случай не такой простой.

    Что Docker вообще дает

    Контейнер - это упакованное окружение: своя Java, свои библиотеки, свои настройки. Оно не пересекается с системой и с соседними контейнерами.

    Отсюда польза, которая появляется в трех ситуациях.

    Серверов на машине несколько, и им нужны разные версии Java. Один на 26.2 с Java 25, второй легаси на 1.12.2 с Java 8. Без контейнеров это возня с update-alternatives и путями, с контейнерами - две строчки в конфиге.

    Сборку надо переносить между машинами или разворачивать повторно. Весь сервер описан одним файлом, который лежит в git.

    Нужны жесткие лимиты. Сколько бы плагин ни просил памяти, больше выданного он не получит и машину за собой не утащит.

    Стандарт де-факто - образ itzg/minecraft-server. Он сам качает нужное ядро, ставит нужную Java и поднимает сервер по переменным окружения.

    Ставим Docker:
    Код:
    sudo apt install -y docker.io docker-compose-v2
    sudo usermod -aG docker mc
    
    После второй команды перезайдите по SSH, иначе группа не подхватится.

    Создаем ~/mainServer/docker-compose.yml:
    Код:
    services:
      mc:
        image: itzg/minecraft-server
        container_name: main
        network_mode: host
        restart: unless-stopped
        environment:
          EULA: "TRUE"
          TYPE: PAPER
          VERSION: "26.2"
          MEMORY: "4G"
          SERVER_PORT: "25567"
          ONLINE_MODE: "FALSE"
          USE_AIKAR_FLAGS: "TRUE"
          TZ: "Europe/Moscow"
        volumes:
          - ./data:/data
    
    Запуск и консоль:
    Код:
    docker compose up -d
    docker compose logs -f
    docker attach main       # выйти без остановки: Ctrl+P, затем Ctrl+Q
    
    restart: unless-stopped заменяет systemd: контейнер поднимется и после падения, и после перезагрузки машины. Останется остановленным только если Вы сами его остановили.

    Файлы сервера лежат в ./data обычными файлами - бэкапить и править их можно как всегда.

    Про сеть, и это не мелочь. По умолчанию Docker поднимает контейнер в режиме bridge, то есть весь трафик идет через NAT. Для веб-приложения это незаметно, для игрового сервера - лишняя задержка на каждом пакете. Ставьте network_mode: host, тогда контейнер работает напрямую с сетью машины.

    По процессору разницы с обычным запуском практически нет, память съедает лишние 100-200 МБ.

    Грабля с файрволом, продолжение

    В части 3 я про это уже говорил, но здесь ей самое место, поэтому повторю с деталями.

    Docker сам правит правила сетевого фильтра и делает это в обход ufw. Когда Вы публикуете порт строкой ports: - "5432:5432", Docker прописывает свое правило раньше, чем до дела доходит ufw. В итоге sudo ufw status честно показывает "закрыто", а порт открыт всему интернету.

    Человек смотрит на вывод ufw, видит "закрыто" и спокойно идет спать. Это худший вид дыры - та, которую Вы уже проверили.

    Лечится указанием адреса перед портом:
    Код:
    ports:
      - "127.0.0.1:5432:5432"
    
    Вместо привычного "5432:5432". Теперь контейнер доступен только с самой машины.

    При network_mode: host секции ports вообще нет, контейнер слушает как обычный процесс, и ufw работает штатно. Еще одна причина использовать host для игровых серверов.

    Проверить себя: ss -tulpn и глазами по колонке адресов. Все, что стоит на 0.0.0.0 и не является портом игроков, должно иметь очень хорошее объяснение.

    Группа docker равна root. Пользователь в этой группе может запустить контейнер, примонтировать в него корень системы и делать там что угодно. Формально он не root, фактически - да. Поэтому добавляйте в docker только тех, кому и так доверяете полностью.

    Образы берем официальные. На Docker Hub кто угодно может выложить что угодно с похожим названием. Смотрите на число загрузок и на ссылку с исходниками.

    Версию фиксируйте. Тег latest означает "что там сегодня выложили". Для сервера, который должен работать, а не удивлять, лучше указать конкретную версию.

    Обновляйте образы. Контейнер не обновляется сам, и Java внутри него застревает на той версии, что была на момент сборки:
    Код:
    docker compose pull && docker compose up -d
    

    Панели: Pelican и Pterodactyl

    Раз уж зашла речь про контейнеры - пара слов про панели, они работают поверх Docker.

    Pterodactyl и Pelican - это веб-интерфейс, где сервера создаются кнопкой, а каждый живет в своем контейнере. Можно раздать доступ администраторам, не выдавая им SSH, поставить лимиты и смотреть консоль через браузер. Pelican отпочковался от Pterodactyl и развивается быстрее, но до сих пор в бете. Pterodactyl консервативнее и обновляется реже. Форматы описаний серверов у них общие, перейти можно в обе стороны.

    Кому это надо. Тому, кто раздает сервера: хостинг, сетка проектов, команда админов с разными правами. Порог примерно такой - три и больше сервера на машине либо больше одного человека с доступом.

    Кому не надо. Всем остальным. Панель тащит за собой Docker, PHP, базу данных, Redis и веб-сервер, а еще открывает наружу веб-интерфейс, который надо обновлять и прикрывать. Демон, который управляет контейнерами, работает с правами, равными root. То есть вместо одного процесса Вы получаете стек из шести компонентов и новую точку входа - в статье, где половина текста про закрытие лишних портов, это стоит назвать своими именами.

    Если решились - ставьте панель по официальной документации, обязательно за HTTPS, и не на ту же машину, где крутятся сами сервера, если есть возможность разнести.

    Итог части

    Один сервер на одной машине - systemd, Docker не нужен. Несколько серверов, разные версии Java, перенос между машинами - Docker оправдан, с network_mode: host и публикацией портов на 127.0.0.1. Панель - только если раздаете доступ другим людям.

    >>> Часть 6: База данных и доступ к ней (тык)
     
    Последнее редактирование: 4 сен 2026
  6. Автор темы
    iForgotPassword

    iForgotPassword Активный участник Пользователь

    Баллы:
    66
    Имя в Minecraft:
    iForgotPassword
    Это продолжение статьи Работа с VPS и безопасность сервера от А до Я. Начало - в первом посте темы. << Часть 5: Docker и когда он оправдан

    Часть 6. База данных и доступ к ней

    Пока сервер один, база чаще всего не нужна: плагины хранят все в файлах, и этого хватает. Она появляется, когда серверов становится несколько и данные надо делить между ними - права LuckPerms, аккаунты авторизации, экономика, журнал CoreProtect. Плюс сайт и донат, если они есть.

    Что вообще выбирать

    У большинства серьезных плагинов в конфиге есть выбор хранилища. Набор обычно такой: sqlite или h2 - файл рядом с плагином, mysql или postgresql - отдельная СУБД.

    Файловые варианты хороши тем, что не требуют вообще ничего. Плохи тем, что файл лежит на этой машине и только на ней: второй сервер к нему не подключится, а скопировать его на живую значит рискнуть попасть в момент записи.

    Поэтому простое правило: если СУБД у Вас уже стоит - переводите на нее все, что умеет, и не думайте над каждым плагином отдельно. Данные оказываются в одном месте, бэкап становится одной командой вместо охоты за .db по папкам плагинов, и восстановление тоже одной.

    Есть и побочный эффект. Файл SQLite - это просто файл: кто добрался до папки сервера, тот забрал его целиком и открыл у себя. К базе нужен еще и пароль, а если она вынесена на отдельную машину - то и доступ к ней.

    Стена тут не абсолютная: пароль от базы лежит в конфиге плагина там же, в папке сервера. Но планку это поднимает, а при вынесенной базе поднимает заметно.

    PostgreSQL или MySQL. MySQL и его форк MariaDB в Minecraft-комьюнити исторически популярнее, и плагины поддерживают их чаще. Но если плагин умеет и то и другое - берите PostgreSQL. Он строже относится к данным и не портит их молча, у него понятнее устроены права и ровнее работает выгрузка. Дальше в части все примеры на нем.

    Разберем два случая, и они принципиально разные.

    Случай первый: база на той же машине

    Так и надо делать, пока можете. Проще, быстрее и безопаснее - трафик не выходит за пределы машины.

    В репозитории Ubuntu 24.04 лежит PostgreSQL 16 - вполне рабочий, но не последний. Ставим 18 из репозитория самого PostgreSQL:
    Код:
    sudo apt install -y postgresql-common
    sudo /usr/share/postgresql-common/pgdg/apt.postgresql.org.sh
    sudo apt install -y postgresql-18
    
    Вторая строка остановится и покажет вот такое:
    Код:
    This script will enable the PostgreSQL APT repository on apt.postgresql.org on
    your system. The distribution codename used will be noble-pgdg.
    
    Press Enter to continue, or Ctrl-C to abort.
    
    noble-pgdg и есть репозиторий для Ubuntu 24.04, noble - ее кодовое имя. Жмем Enter. Скрипт добавит ключ и файл /etc/apt/sources.list.d/pgdg.list, обновит списки пакетов и на этом закончит. Сам PostgreSQL ставится третьей командой.

    Кому хватает шестнадцатой версии - можно обойтись одним sudo apt install -y postgresql без всяких репозиториев. Дальше все то же самое, только пути будут /etc/postgresql/16/.

    Проверяем, что база не смотрит наружу. PostgreSQL из коробки слушает только localhost, и это хорошо, но глазами взглянуть все равно стоит:
    Код:
    sudo ss -tulpn | grep 5432
    
    Должно быть 127.0.0.1:5432. Если видите 0.0.0.0:5432 - база открыта интернету, и это надо чинить прямо сейчас: в /etc/postgresql/18/main/postgresql.conf строка listen_addresses = 'localhost'. Порт 5432 в ufw не открываем никогда - равно как и 3306, если где-то остался MySQL.

    Соблазн понятный: завести одного пользователя с полными правами и вписать его во все плагины. Не надо.

    Плагин работает от имени того пользователя, которого Вы ему дали. Если у него полные права - он может дотянуться до любой базы на сервере. Плагин с сюрпризом внутри доберется до аккаунтов авторизации, а безобидный, но кривой, однажды снесет не свою таблицу.

    Правильно - своя база и свой пользователь на каждое:
    Код:
    sudo -u postgres psql
    
    CREATE USER lp WITH PASSWORD 'длинный_случайный_пароль';
    CREATE DATABASE luckperms OWNER lp;
    REVOKE CONNECT ON DATABASE luckperms FROM PUBLIC;
    
    CREATE USER auth WITH PASSWORD 'другой_длинный_пароль';
    CREATE DATABASE auth OWNER auth;
    REVOKE CONNECT ON DATABASE auth FROM PUBLIC;
    
    \q
    
    OWNER отдает базу пользователю целиком, отдельные GRANT после этого не нужны.

    Строка с REVOKE CONNECT важнее, чем кажется. Без нее в PostgreSQL подключиться к базе может любой существующий пользователь, даже если своих таблиц там у него нет. С ней пользователь lp про базу auth даже не узнает.

    Пароли генерируем, а не придумываем:
    Код:
    openssl rand -base64 24
    
    И не переиспользуем один на все. Смысл разделения именно в том, что утечка одного пароля не открывает все остальное.

    Конфиги плагинов с паролями от базы - такие же секреты, как ключи SSH. В git они попадать не должны, в скриншот на форум тоже. Перед тем как выложить конфиг с вопросом "почему не работает", пробегитесь по нему глазами.

    Случай второй: база на отдельной машине

    Тут начинается интересное. Первым делом главный тезис:

    Запросов к базе через интернет быть не должно.

    Это не про паранойю, а про то, что так проще. Есть два способа сделать правильно.

    Приватная сеть провайдера. Многие дают между Вашими машинами внутреннюю сеть с адресами вида 10.0.0.x. Трафик по ней наружу не выходит вообще. Смотрите в панели раздел "приватная сеть" или "внутренняя сеть" - если он есть, дальше можно не читать: указываете базе слушать внутренний адрес, и она недоступна из интернета в принципе.

    WireGuard. Если приватной сети нет, поднимаем свою. WireGuard - это VPN между Вашими машинами: настраивается парой конфигов, работает быстро, и после настройки машины видят друг друга по внутренним адресам, как будто стоят в одной комнате. База слушает адрес внутри туннеля, снаружи ее нет. Документация Ubuntu разбирает настройку по шагам.

    Оба варианта дешевле и надежнее, чем возиться с шифрованием соединений. Сделали так - можете считать базу локальной и вернуться к первому случаю.

    Бывает, что приватной сети нет, а VPN поднять не дают. Тогда так.

    Слушаем конкретные адреса, а не все. В postgresql.conf:
    Код:
    listen_addresses = 'localhost,10.0.1.5'
    password_encryption = 'scram-sha-256'
    ssl = on
    ssl_cert_file = '/etc/ssl/postgres/server.crt'
    ssl_key_file  = '/etc/ssl/postgres/server.key'
    ssl_min_protocol_version = 'TLSv1.2'
    log_connections = 'all'
    
    Звездочка в listen_addresses означает "все интерфейсы" и здесь не годится.

    Только hostssl в pg_hba.conf. Файл читается сверху вниз, и выигрывает первое совпавшее правило. Обычная строка host, оказавшаяся выше, пустит клиента без шифрования, и Ваш hostssl ниже уже не сработает.
    Код:
    local   all   all                          peer
    hostssl appdb appuser 203.0.113.42/32      scram-sha-256
    hostssl all   all     0.0.0.0/0            reject
    
    Последняя строка - явный запрет для всех остальных.

    И вот главное, что проваливают чаще всего. Со стороны клиента sslmode=require шифрует соединение, но не проверяет, с тем ли сервером Вы говорите. Шифрованный канал к чужой машине - это шифрованный канал к чужой машине. Нужен verify-full с указанием корневого сертификата:
    Код:
    psql "host=db.example.com dbname=appdb user=appuser sslmode=verify-full sslrootcert=/etc/ssl/ca.crt"
    
    Отдельно: scram-sha-256 защищает пароль при входе, но не результаты запросов. Без TLS вся выборка идет по сети открытым текстом - вместе с никами, IP игроков и всем остальным.

    Проверить, что все включилось:
    Код:
    SHOW ssl;          -- должно быть on
    SHOW hba_file;     -- путь к действующему pg_hba.conf
    
    И в ufw открываем 5432 только для адреса сервера Minecraft, а не для всех.

    Подробности - в документации PostgreSQL: pg_hba.conf и настройка TLS.

    Что еще стоит увести с игровой машины

    Раз уж зашла речь про отдельные машины. Общее правило: все, что смотрит в интернет и не является самим сервером, лучше держать отдельно.

    Сайт и донат - в первую очередь. Причин две, и вторая важнее первой. Сначала очевидная: канал и процессор у машины общие, и мусорный трафик на сайт уронит заодно и сервер. А теперь вторая: сайт на CMS с плагинами - самая частая точка входа вообще. Пробили сайт - а атакующий уже на машине с миром, конфигами и ключами. Туда же отправляется панель из части 5, если она у Вас есть.

    Про Velocity на своей машине отдельно, потому что тут ходит красивое заблуждение. Звучит оно так: точка входа одна, значит досить будут ее, а те, кто уже играет, продолжат играть. Не продолжат. Через прокси идет не только вход, а весь трафик игрока, каждый пакет всю сессию. Velocity держит два соединения на каждого - с клиентом и с сервером Paper - и перекладывает пакеты между ними. Умер прокси - отключились все, включая тех, кто играет второй час.

    Обратный случай работает именно так, как хочется: упал сервер Paper - Velocity замечает это и перекидывает игрока на запасной, не выкидывая из сети.

    Но разносить все равно стоит, просто польза другая. Машина с серверами не получает ни байта мусорного трафика: миры сохраняются штатно, ничего не падает и не бьется. Наружу известен только адрес прокси, а сервера Paper атакующий просто не видит. И восстановление сводится к тому, чтобы поднять одну машину, а не разгр****ь последствия на всех.

    То есть разнесение превращает "все легло и часть данных побилась" в "полчаса нельзя зайти". Разница большая.

    Смысл в скрытом адресе есть ровно до тех пор, пока он скрыт. Чаще всего его сдают старые записи DNS, объявления в мониторингах с прошлого переезда и скриншоты консоли на форумах.

    Бэкап базы - отдельная история

    Скопировать файлы базы на живую нельзя по той же причине, по которой нельзя копировать мир без save-off: получите набор файлов из разных моментов времени. Нужен дамп.
    Код:
    sudo -u postgres pg_dumpall > /var/backups/mc/db.sql
    
    Останавливать базу не надо: дамп снимается на живой и дает согласованный слепок.

    Если где-то остался MySQL или MariaDB, там это mysqldump --single-transaction --all-databases. Флаг обязателен, без него дамп либо заблокирует таблицы, либо соберет их из разных моментов времени.

    Дамп базы и архив мира снимайте рядом по времени. Если мир от четырех утра, а база от полудня, при восстановлении получите мир, в котором постройки есть, а прав на них нет. Идеально они все равно не совпадут, но чем ближе, тем меньше расхождений разгр****ь.

    Про бэкапы вообще - следующие части, сначала про то, как вовремя замечать проблемы.

    Итог части

    База на той же машине слушает только localhost. У каждого плагина своя база и свой пользователь с правами только на нее. Пароли случайные и разные. Если база на отдельной машине - приватная сеть провайдера или WireGuard, а не запросы через интернет. Дамп базы снимается отдельно от мира и примерно в то же время. Сайт и панель живут не на игровой машине.

    >>> Часть 7: Мониторинг и алерты (тык)
     
    Последнее редактирование: 4 сен 2026
  7. Автор темы
    iForgotPassword

    iForgotPassword Активный участник Пользователь

    Баллы:
    66
    Имя в Minecraft:
    iForgotPassword
    Это продолжение статьи Работа с VPS и безопасность сервера от А до Я. Начало - в первом посте темы. << Часть 6: База данных и доступ к ней

    Часть 7. Мониторинг и алерты

    Про мониторинг обычно вспоминают в тот момент, когда сервер уже лежит и надо понять почему. А смысл в обратном - узнать о проблеме до того, как о ней напишут игроки.

    Начнем с того, что можно посмотреть руками.

    Что смотреть в первую очередь

    btop - процессы, память, процессор, диск и сеть на одном экране.
    Код:
    sudo apt install btop
    btop
    
    Он нагляднее старого htop и читается без привычки. Выход - q.

    Место на диске. Заканчивается регулярно, обычно из-за логов:
    Код:
    df -h
    
    Вывод выглядит примерно так:
    Код:
    Filesystem      Size  Used Avail Use% Mounted on
    tmpfs           1.2G  1.1M  1.2G   1% /run
    /dev/vda1        73G  4.0G   66G   6% /
    tmpfs           5.9G  1.1M  5.9G   1% /dev/shm
    tmpfs           5.0M     0  5.0M   0% /run/lock
    tmpfs           1.2G   12K  1.2G   1% /run/user/1000
    
    Строк много, а смотреть надо на одну. Все, что начинается с tmpfs, к диску отношения не имеет: это временные файловые системы в оперативной памяти, они всегда почти пустые и чистятся при перезагрузке.

    Ваш диск - это строка с /dev/ в начале, смонтированная в /. Здесь это /dev/vda1: 73 гигабайта всего, 4 занято, 66 свободно, Use% = 6. На остальные строки можно не смотреть вообще.

    А теперь то, про что забывают.
    Код:
    df -i
    
    Код:
    Filesystem      Inodes  IUsed   IFree IUse% Mounted on
    /dev/vda1      4849664 143792 4705872    3% /
    
    Это inodes - счетчик самих файлов, независимый от их размера. Их количество на разделе ограничено. Плагин, который пишет по файлу на каждое действие, способен упереться в этот предел, когда места на диске еще полно. Выглядит это издевательски: df -h показывает свободные гигабайты, а сервер падает с ошибкой "нет места на устройстве". Если в колонке IUse% под сотню - вот и ответ. В примере выше все в порядке: 143 тысячи файлов из почти пяти миллионов возможных.

    Смотреть надо на обе команды сразу и сравнивать Use% с IUse%. Пока они растут вместе - все нормально. Когда второе убежало вперед, где-то плодятся мелкие файлы, и искать их лучше до того, как сервер упрется в потолок.

    Кто съел место:
    Код:
    du -h --max-depth=1 /home/mc | sort -h
    
    Команда показывает размер каждой папки. Повторяем ее внутри самой толстой, пока не найдем виновника. В девяти случаях из десяти это logs или база CoreProtect.

    Спускаться руками по папкам быстро надоедает, поэтому есть ncdu - тот же du, но с навигацией стрелками:
    Код:
    sudo apt install ncdu
    ncdu /home/mc
    
    Он один раз обходит дерево и дальше дает ходить по нему, сразу показывая размеры и долю каждой папки полоской. Enter - войти, стрелка влево - назад, d - удалить выбранное, q - выход. Одного запуска хватает, чтобы найти все, что разрослось.

    Есть еще gdu - то же самое, но читает диск в несколько потоков и на большом мире считает заметно быстрее. А для df -h есть duf: те же данные, но таблицей и без строк про tmpfs. Оба в репозитории Ubuntu.

    Смотрите на память сервера через free -h - и видите, что почти вся занята. Это нормально и лечить не надо.

    Причин две. Во-первых, Linux использует свободную память под дисковый кеш и отдает ее приложениям по первому требованию. В строке вывода смотреть надо на колонку available, а не на free.

    Во-вторых, Java. Вы указали -Xmx4G, процесс сразу занял четыре гигабайта и держит их, даже когда данных внутри на полтора. Куча не отдается системе обратно, это устройство сборщика мусора, а не утечка.

    Поэтому реальное состояние памяти сервера смотрят не в системе, а внутри Java - через spark.

    Что мерить внутри сервера

    spark уже встроен в Paper, ставить ничего не надо.

    /tps - показывает тики в секунду. 20 это норма, ниже - сервер не успевает.

    /spark health - сводка по памяти, процессору и диску одной командой.

    /spark profiler start и через несколько минут /spark profiler stop - выдаст ссылку на отчет, где видно, какой именно плагин съедает время. Это основной инструмент, когда TPS просел и непонятно почему.

    Timings, которые советуют старые статьи, с версии 1.21 отключены по умолчанию и объявлены устаревшими. Профилируем через spark.

    Графики: netdata

    Если хочется видеть историю, а не текущий момент - netdata. Ставится одной командой, сразу начинает собирать все метрики машины и рисовать графики.

    Только не выставляйте его наружу. По умолчанию netdata поднимает веб-интерфейс на порту 19999, и там видно все про Вашу машину: процессы, соединения, нагрузку. Отдавать это интернету незачем. Либо ограничьте его локальным адресом, либо ходите через туннель:
    Код:
    ssh -L 19999:localhost:19999 [email protected]
    
    После этого открывайте localhost:19999 у себя в браузере - трафик пойдет внутри SSH-соединения, а порт наружу останется закрытым.

    Алерт в Telegram при входе по SSH

    Штука, которую стоит поставить всем. Если пароли отключены и вход только по ключу, успешный вход - событие редкое. Значит, уведомление о нем почти не шумит, а пользы приносит много: вошли не Вы - узнаете об этом сразу, а не через месяц.

    Сначала бот. Пишем в Telegram @BotFather, команда /newbot, получаем токен вида 123456789:AA.... Затем пишем своему боту любое сообщение - без этого он не сможет писать Вам первым. И узнаем свой chat id:
    Код:
    curl -s "https://api.telegram.org/bot<ТОКЕН>/getUpdates" | jq '.result[0].message.chat.id'
    
    Теперь скрипт. Кладем токен отдельно от кода, в /etc/ssh-notify.conf:
    Код:
    TG_TOKEN="123456789:ваш_токен"
    TG_CHAT_ID="000000000"
    
    Код:
    sudo chmod 600 /etc/ssh-notify.conf
    
    Сам скрипт /usr/local/bin/ssh-notify:
    Код:
    #!/bin/bash
    [ "${PAM_TYPE:-}" = "open_session" ] || exit 0
    [ -r /etc/ssh-notify.conf ] || exit 0
    . /etc/ssh-notify.conf 2>/dev/null || exit 0
    
    TEXT="Вход по SSH
    Сервер: $(hostname -f)
    Пользователь: ${PAM_USER}
    Откуда: ${PAM_RHOST:-локально}
    Время: $(date '+%d.%m.%Y %H:%M:%S')"
    
    ( curl -s -m 10 -X POST "https://api.telegram.org/bot${TG_TOKEN}/sendMessage" \
        --data-urlencode "chat_id=${TG_CHAT_ID}" \
        --data-urlencode "text=${TEXT}" >/dev/null 2>&1 ) &
    
    exit 0
    
    Код:
    sudo chmod 750 /usr/local/bin/ssh-notify
    
    Подключаем. В конец /etc/pam.d/sshd добавляем строку:
    Код:
    session optional pam_exec.so seteuid /usr/local/bin/ssh-notify
    
    Перезапускать ничего не нужно, PAM читает конфиг при каждом входе. Откройте новую сессию и проверьте, что сообщение пришло.

    Два важных момента.

    Первый. Скрипт обязан всегда возвращать 0. Ненулевой код из pam_exec способен заблокировать вход на сервер. Отсюда exit 0 в конце и подавление ошибок везде. Не убирайте их.

    Второй. Уведомление вешаем именно на PAM, а не на /etc/profile.d, как советуют многие инструкции. Хук на profile.d срабатывает только при запуске интерактивной оболочки: он промолчит при ssh сервер команда, промолчит при подключении по SFTP и промолчит, если оболочку вызвали напрямую. То есть уведомление придет ровно тогда, когда заходите Вы, и не придет в остальных случаях. PAM ловит любой вход.

    Готовый скрипт вместе с примером конфига лежит в MinecraftLinuxUtils - файлы ssh-notify.sh и ssh-notify.conf.example.

    Токен в теле скрипта - плохая идея, поэтому мы и вынесли его в отдельный файл. Скрипт с зашитым токеном уезжает в git, потом в архив с бэкапом, потом лежит в Загрузках на ноутбуке, и замечают это обычно поздно.

    Уведомление о входе - не единственное, что полезно знать заранее. Простейший вариант - скрипт в cron, который шлет сообщение только когда что-то не так.

    Место на диске:
    Код:
    0 * * * * [ $(df --output=pcent / | tail -1 | tr -dc '0-9') -gt 85 ] && /usr/local/bin/notify "Диск заполнен более чем на 85%"
    
    Сервер жив:
    Код:
    */5 * * * * ss -tulpn | grep -q ':25565' || /usr/local/bin/notify "Порт 25565 не слушается"
    
    Грабли с cron стандартные и стоят упоминания. У него урезанный PATH, поэтому в скриптах пишите полные пути до команд. И обязательно перенаправляйте вывод в лог, иначе не узнаете, что задача падает уже месяц:
    Код:
    0 4 * * * /home/mc/scripts/backup.sh >> /home/mc/scripts/backup.log 2>&1
    
    Строку расписания можно проверить на crontab.guru - он расшифровывает ее словами.

    Итог части

    btop под рукой, df -i проверяется наравне с df -h, spark стоит на сервере, уведомление о входе по SSH настроено с токеном в отдельном файле, критичные проверки висят в cron с логом.

    >>> Часть 8: Бэкапы (тык)
     
    Последнее редактирование: 4 сен 2026
  8. Автор темы
    iForgotPassword

    iForgotPassword Активный участник Пользователь

    Баллы:
    66
    Имя в Minecraft:
    iForgotPassword
    Это продолжение статьи Работа с VPS и безопасность сервера от А до Я. Начало - в первом посте темы. << Часть 7: Мониторинг и алерты

    Часть 8. Бэкапы

    Итак, сервер живой, за ним следят. Осталось сделать так, чтобы в один не самый прекрасный день все это не пропало.

    Большинство делает это руками, и способ рабочий - если знать про грабли.

    Способ первый: упаковать и скачать себе

    Заходим по SSH, пакуем папку сервера в архив, забираем через SFTP. Просто, понятно, работает.

    Но сначала одна команда, без которой все остальное бессмысленно.

    Сервер пишет мир на диск постоянно. Начнете паковать на живую - часть файлов попадет в архив до записи, часть после, и получите набор кусков из разных моментов времени. Такой архив разворачивается с ошибками или молча теряет куски мира. Узнаете Вы об этом в день, когда он понадобится.

    Поэтому в консоль сервера, перед архивацией:
    Код:
    save-off
    save-all flush
    
    Первая говорит "перестань писать", вторая - "сбрось на диск все, что накопил". После копирования обязательно возвращаем:
    Код:
    save-on
    
    Посмотрите свободное место до, а не после.
    Код:
    df -h
    du -sh ~/mainServer
    
    Архив пишется на тот же диск, и места под него нужно примерно как под сами данные. Забитый под ноль диск - отдельное развлечение: сервер тоже не может писать.

    Что тащить и что не тащить. Мир, конфиги и данные плагинов - обязательно. Логи, отчеты о падениях, кеши и тайлы карт - незачем, они пересоздаются, а весят прилично.
    Код:
    cd ~
    tar -czf mainServer.tar.gz \
        --exclude=logs --exclude=crash-reports --exclude=cache \
        --exclude=libraries --exclude=versions \
        mainServer/
    
    Что тащить обязательно, помимо мира. Права LuckPerms, базу авторизации, экономику, журнал CoreProtect. Ну восстановите Вы карту, а у всех слетели права и балансы - и что дальше?

    Не одним куском, а по частям.
    Код:
    tar -czf plugins.tar.gz mainServer/plugins/
    tar -czf world.tar.gz   mainServer/world/
    
    Оборвалась закачка - перекачиваете один кусок, а не все заново.

    Если места на диске нет совсем - можно паковать прямо в сеть, минуя запись на сервере:
    Код:
    ssh [email protected] 'tar -czf - -C /home/mc mainServer' > mainServer.tar.gz
    
    Архив собирается на лету и приземляется сразу у Вас.

    Про обрывы. Скачивание архива по SFTP при разрыве связи начинается заново. rsync докачивает с места обрыва:
    Код:
    rsync -avz --partial --progress [email protected]:~/mainServer.tar.gz ./
    
    Для мира на несколько гигабайт разница решающая.

    Проверьте, что скачалось целиком. Считаем sha256sum на обеих сторонах и сравниваем. Или хотя бы убеждаемся, что архив читается:
    Код:
    tar -tzf mainServer.tar.gz > /dev/null && echo "архив целый"
    

    Способ рабочий, но руками Вы это сделаете раза два, а потом забудете. А беда случается не в тот день, когда Вы вспомнили про бэкап.

    Способ второй: чтобы делалось само

    И сразу главное: копия на том же диске - это не копия.

    На старте она лучше, чем ничего: от кривого обновления плагина или от собственного rm -rf спасет. Но диск умрет вместе с сервером и копией. Провайдер заблокирует аккаунт - Вы потеряете и то и другое разом. А тот, кто получил root, первым делом посмотрит, где лежат бэкапы.

    Так что локальная копия - это промежуточный шаг, а не решение.

    Второй VPS - самый дешевый вариант, если он у Вас уже есть. Забираем через rsync по SSH.

    Домашний компьютер - работает, только пока Вы про это помните и компьютер включен.

    S3-совместимое хранилище - то, что нужно. Платите за реально занятое место, доступно всегда, не зависит от Вашего провайдера. У Selectel и VK есть российские S3, оплата картой.

    Backblaze B2 - дешевле остальных, если платежное средство позволяет.

    FTP-хранилище провайдера - его нередко дают в довесок к VPS, иногда десятки гигабайт без доплаты. Место лежит не на Вашей машине, и этого уже достаточно. С restic он дружит не напрямую, поэтому ему отведена отдельная часть.

    Я показываю вариант с S3, потому что он не требует держать вторую машину.

    restic

    restic - то, что стоит освоить один раз. Он шифрует на Вашей стороне, дедуплицирует (второй бэкап того же мира занимает не второй объем, а только разницу), хранит снапшоты и умеет восстанавливать любой из них.
    Код:
    sudo apt install restic
    
    Сначала пароль репозитория. Это новый пароль, а не тот, что у Вас от хранилища - им restic шифрует данные. Придумывать не надо, генерируем и кладем в файл, чтобы задание в cron потом обошлось без Вас:
    Код:
    openssl rand -base64 24 > ~/.restic-pass
    chmod 600 ~/.restic-pass
    cat ~/.restic-pass
    
    Показанную строку сразу в менеджер паролей. Потеряете - данные не расшифровать никак, восстановления пароля у restic нет по устройству.

    Дальше задаем адрес репозитория и создаем его:
    Код:
    export RESTIC_REPOSITORY="s3:https://s3.example.com/mc-backup"
    export RESTIC_PASSWORD_FILE="$HOME/.restic-pass"
    export AWS_ACCESS_KEY_ID="..."
    export AWS_SECRET_ACCESS_KEY="..."
    
    restic init
    
    init создает репозиторий и делается один раз. При повторном запуске restic скажет, что репозиторий уже существует.

    Снимаем копию:
    Код:
    restic backup /home/mc/mainServer --exclude=logs --exclude=cache
    
    Вот эту команду Вы и будете гонять по расписанию.

    Архив тут нигде не собирается, и это не упущение. restic режет данные на куски примерно по мегабайту, считает от каждого хеш и кладет их на хранилище уже сжатыми: в репозитории второй версии работает zstd, и включен он по умолчанию. Обернуть все это в tar.gz снаружи только помешает. Поток gzip меняется целиком от одной правки в начале, так что каждая ночная копия улетала бы полным объемом вместо разницы, да и вытащить из архива один плагин, не прочитав его до конца, не выйдет. А на мире Minecraft gzip почти ничего и не выигрывает: региональные файлы .mca внутри уже сжаты, процессор он займет, а объем собьет на проценты.

    Проверить, что сжатие работает, можно командой restic cat config - в выводе должно быть version 2. Если стоит единица, значит на момент init у Вас был restic старее 0.14: обновите его и выполните restic migrate upgrade_repo_v2.

    Смотрим, что получилось:
    Код:
    restic snapshots
    
    Команда покажет список снятых копий с датами и ID. Восстановлению отведена следующая часть. Там и про разворачивание любой копии, а не только последней, и про то, как вытащить одну папку или скачать все архивом.

    Файл с паролем лежит на той же машине, что и сервер. Это плата за бэкапы без Вашего участия: получивший root прочитает и его. Копию в менеджере паролей держите обязательно - она понадобится ровно тогда, когда машины уже не будет.

    Чистим старое - тут restic хорош тем, что политика описывается словами:
    Код:
    restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 6 --prune
    
    Семь ежедневных, четыре еженедельных, шесть ежемесячных. Остальное удаляется.

    Если хочется просто заливать готовые архивы в облако без всей этой машинерии - есть rclone. Он умеет три десятка хранилищ и работает как cp в облако.

    А если у провайдера FTP, а не S3 - напрямую restic такое хранилище не понимает, зато умеет ходить через rclone, а тот с FTP работает. Настройка со всеми граблями - шифрование, обфускация пароля, ошибка с самоподписанным сертификатом - разобрана в Части 9.

    Готовый скрипт, который уже умеет и save-off, и restic, лежит в MinecraftLinuxUtils - файл backup.sh. Он шлет серверу нужные команды через tmux, снимает копию, возвращает save-on и применяет политику хранения. Что не тащить в архив, задается отдельным файлом, чтобы не лезть в сам скрипт.

    Режима у него два, переключаются переменной MODE в начале файла. tar пакует папку сервера в .tar.gz в /var/backups/minecraft и удаляет копии старше KEEP_DAYS дней - это на первое время, пока внешнего хранилища нет. Перед упаковкой скрипт смотрит свободное место и пропускает сервер, если его не хватает, а готовый архив проверяет на читаемость. restic отправляет копию в репозиторий по RESTIC_REPO и RESTIC_PASSWORD_FILE, а старое чистит через restic forget. Каждая копия помечается именем сервера из servers.conf, чтобы при восстановлении было чем их различать.

    Есть и альтернатива - minecraft-backup, он умеет tar и restic, работает со screen, tmux, RCON и docker.

    Вешаем на cron:
    Код:
    crontab -e
    
    Код:
    0 4 * * * /home/mc/scripts/backup.sh >> /home/mc/scripts/backup.log 2>&1
    
    Каждый день в четыре утра, вывод в лог. Про урезанный PATH у cron и полные пути помним из прошлой части.

    Не забудьте базу. Если она у Вас есть, дамп снимается отдельно и примерно в то же время - разбирали в части 6.

    Итог части

    Перед копированием - save-off и save-all flush, после - save-on. Копируется не только мир, но и все данные плагинов. Копия уезжает с машины, а не лежит рядом с оригиналом. Снимается по расписанию, а не когда вспомнили. Разворачивается и проверяется хотя бы раз в несколько месяцев.

    Дальше - как разворачивать снятое обратно, доставать из копии отдельную папку и что делать, если провайдер выдал FTP вместо S3.

    >>> Часть 9: Восстановление и хранилище провайдера (тык)
     
    Последнее редактирование: 4 сен 2026
  9. Автор темы
    iForgotPassword

    iForgotPassword Активный участник Пользователь

    Баллы:
    66
    Имя в Minecraft:
    iForgotPassword
    Это продолжение статьи Работа с VPS и безопасность сервера от А до Я. Начало - в первом посте темы. << Часть 8: Бэкапы

    Часть 9. Восстановление и хранилище провайдера

    Копии снимаются каждую ночь. Осталось разобраться, как разворачивать их обратно. И заодно закрыть случай, когда провайдер выдал вместо S3 обычный FTP.

    Что лежит внутри репозитория

    Цепочки инкрементов у restic нет. Каждый снапшот - это полное дерево файлов и папок, где для каждого файла записан упорядоченный список хешей его кусков. Сами куски лежат в общей куче репозитория.

    Отсюда две вещи. Любой снапшот разворачивается сам по себе, соседние ему не нужны: схемы "полный раз в неделю плюс шесть инкрементов", где потеря звена рушит всю неделю, тут нет. И forget не ломает оставшиеся копии, prune выкидывает только куски, на которые больше никто не ссылается.

    Слово "разница" относится к тому, что уезжает на хранилище во время бэкапа. Файл с прежними размером и временем правки restic даже не перечитывает, изменившийся режет на куски и заливает лишь новые. При восстановлении все собирается обратно. restic идет по дереву снапшота, тянет куски по хешам, расшифровывает, разжимает и пишет файл. Часть кусков попала в репозиторий месяц назад, часть в ту самую ночь, а состояние на выходе ровно то, что было на момент снятия копии.

    Имя куска и есть его хеш, так что битые данные вылезут при чтении сами. Проверить весь репозиторий разом - restic check --read-data.

    Разворачиваем последнюю копию
    Код:
    restic restore latest --target /tmp/restore
    
    Файлы лягут в /tmp/restore с полным путем внутри, то есть в /tmp/restore/home/mc/mainServer. Живую папку сервера не трогаем, сначала смотрим, что развернулось.

    Когда нужна не последняя копия

    Свежая копия не всегда та, что нужна. Спавн снесли вчера днем, а ночью это аккуратно уехало в бэкап. Смотрим, что лежит в репозитории:
    Код:
    restic snapshots
    
    Код:
    repository fe1bb34b opened (version 2, compression level auto)
    ID        Time                 Host         Tags        Paths
    ---------------------------------------------------------------------------
    af3c7e3f  2026-09-03 14:21:46  ihor.online              /home/mc/mainServer
    db9ec93f  2026-09-03 15:09:22  ihor.online  velocity    /home/mc/proxy
    ba987c89  2026-09-03 15:09:23  ihor.online  auth        /home/mc/authServer
    74b9a6f5  2026-09-03 15:10:36  ihor.online  velocity    /home/mc/proxy
    43e3b9e3  2026-09-03 15:10:38  ihor.online  auth        /home/mc/authServer
    366723bb  2026-09-03 15:10:39  ihor.online  main        /home/mc/mainServer
    ---------------------------------------------------------------------------
    6 snapshots
    
    latest - это просто удобное слово вместо ID самой свежей записи. Вместо него подставляется любой ID из левого столбца, восьми символов достаточно:
    Код:
    restic restore af3c7e3f --target /tmp/restore
    
    В снапшот можно заглянуть, не разворачивая его:
    Код:
    restic ls 366723bb /home/mc/mainServer/plugins
    restic diff af3c7e3f 366723bb
    
    ls показывает, что внутри копии, diff - чем две копии отличаются. Второе выручает, когда ищете ночь, после которой все сломалось.

    Когда серверов несколько

    Записей шесть, хотя сервера три. Снапшот покрывает ровно те пути, что переданы команде backup, а связка из прокси и двух серверов Paper копируется тремя отдельными командами. Плюс тут два запуска подряд, в 15:09 и 15:10, и самый первый ручной прогон в 14:21 без метки.

    Вот на таком списке latest и подводит. Он возьмет одну самую свежую запись, а не все три, так что развернется сервер, чья копия снялась последней.

    Поэтому копии помечают при снятии:
    Код:
    restic backup /home/mc/proxy --tag velocity
    
    Метка работает как фильтр, latest считается уже внутри нее:
    Код:
    for t in main velocity auth; do
        restic restore latest --tag "$t" --target /tmp/restore
    done
    
    Все три уезжают в одну папку, пути внутри не пересекаются. У af3c7e3f метки нет, потому что копия снималась руками. Такие отбираются по пути, --path /home/mc/mainServer вместо --tag.

    Ретенцию метки не ломают. forget сам группирует копии по хосту и путям, так что --keep-daily 7 держит семь ежедневных на каждый сервер, а не семь на всех.

    Доставать можно и по частям, а не все сразу:
    Код:
    restic restore b4e18d05 --target /tmp/restore --include /home/mc/mainServer/plugins/LuckPerms
    
    Слетели права, а мир целый - незачем разворачивать сорок гигабайт ради одной папки.

    Скачать копию архивом

    restore раскладывает файлы по папке. Если привычнее .tar.gz одним файлом, есть dump. Он отдает снапшот в стандартный вывод сразу упакованным в tar или zip, архив собирается на лету:
    Код:
    restic dump latest /home/mc/mainServer -a tar | gzip > mainServer.tar.gz
    
    Место на диске VPS при этом расходуется. Чтобы не расходовать, тянем архив сразу к себе, как в прошлой части с tar:
    Код:
    ssh [email protected] 'export RESTIC_REPOSITORY="rclone:backup:mc-backup" \
      RESTIC_PASSWORD_FILE=/home/mc/.restic-pass; \
      restic dump latest /home/mc/mainServer -a tar' | gzip > mainServer.tar.gz
    
    Пути внутри архива по умолчанию полные, от корня. Чтобы корнем стала папка сервера, есть синтаксис снапшот:папка:
    Код:
    restic dump latest:/home/mc/mainServer / -a tar > mainServer.tar
    
    Нужен zip - ключ -a zip. Нужен один плагин - вместо папки сервера путь к нему.

    Гонять dump по расписанию смысла нет. restic разожмет данные, а gzip тут же сожмет их обратно, и хуже. Это команда на разовое "хочу архив в руках".

    Бэкап, который не проверяли, бэкапом не является

    Это самая частая история: копии снимались год, а в нужный момент выяснилось, что архивы пустые, или в них нет плагинов, или пароль от restic потерян.

    Проверка занимает полчаса один раз:
    Код:
    restic restore latest --target /tmp/proverka
    
    Разворачиваете копию на локальной машине, запускаете сервер, заходите в игру. Мир на месте, права работают, балансы целы - значит бэкап рабочий, и Вы теперь это знаете, а не надеетесь.

    Повторяйте раз в несколько месяцев и после каждого серьезного изменения в сборке.

    Заодно проверите, что помните процедуру восстановления. Разбираться в ней первый раз в ночь после аварии - удовольствие сомнительное.

    Хранилище провайдера вместо S3

    В прошлой части restic ходил в S3. Но к VPS нередко идет в довесок FTP-хранилище на десятки гигабайт, и раз оно оплачено, пусть работает.
    FTP restic не поддерживает: у него из коробки локальный диск, SFTP, REST, S3 и несколько облаков. Зато он умеет работать через rclone, а rclone умеет FTP. Репозиторий тогда адресуется как rclone:имя:папка, и restic сам запускает и останавливает rclone.

    Сначала выясняем, умеет ли сервер шифрование:
    Код:
    printf 'FEAT\r\nQUIT\r\n' | nc адрес_хранилища 21
    
    В ответе придет список возможностей сервера. Ищем в нем строку AUTH TLS: если она есть, шифрование поддерживается, и включать его мы будем на своей стороне, в конфиге rclone - до него дойдем через абзац.

    Включать обязательно. Без шифрования логин и пароль идут по интернету открытым текстом. Сами данные restic шифрует до отправки, так что содержимое архивов не утечет в любом случае, а вот доступ к хранилищу - вполне.

    Ставим rclone. Пароль в конфиг кладется не как есть, а обфусцированным - для этого есть команда rclone obscure. Аргументом его передавать не надо, иначе он осядет в истории команд, поэтому подаем через ввод:
    Код:
    sudo apt install rclone
    
    printf 'Пароль от хранилища: '
    read -rs FTPPASS; echo
    printf '%s\n' "$FTPPASS" | rclone obscure -
    unset FTPPASS
    
    Пароль тут - тот, что выдал провайдер вместе с логином от хранилища. Придумывать его не надо, ищите в панели или в письме о подключении услуги.

    Вводится он вслепую, на экране ничего не появится - так и задумано. В ответ получите строку для конфига, а последняя команда уберет пароль из памяти оболочки.

    Результат вписываем в ~/.config/rclone/rclone.conf:
    Код:
    [backup]
    type = ftp
    host = 203.0.113.20
    user = ваш_логин
    pass = строка из rclone obscure
    explicit_tls = true
    
    Вот последняя строка и есть то самое шифрование. explicit_tls означает "подключись по обычному порту 21 и сразу подними TLS" - это и есть AUTH TLS из вывода выше. Если хранилище вместо этого просит порт 990, то там другой режим: убираете explicit_tls, пишете tls = true и port = 990. Обе строки одновременно не работают.
    Код:
    chmod 600 ~/.config/rclone/rclone.conf
    rclone lsd backup:
    
    Последняя команда проверяет, что подключение работает.

    И скорее всего именно тут Вы получите ошибку вида failed to verify certificate: x509: cannot validate certificate ... because it doesn't contain any IP SANs. Ничего Вы не сломали: у провайдерских хранилищ почти всегда стоит самоподписанный сертификат, выписанный на внутреннее имя машины. Убедиться можно так:
    Код:
    openssl s_client -connect адрес:21 -starttls ftp </dev/null 2>/dev/null \
      | openssl x509 -noout -subject -issuer -ext subjectAltName
    
    Совпали subject и issuer - сертификат самоподписанный. Пусто в subjectAltName - проверить его нечем ни по адресу, ни по имени. Подставлять доменное имя вместо IP бесполезно: rclone написан на Go, а тот поле CN давно не смотрит и требует именно список альтернативных имен.

    Лечится еще одной строкой в тот же ~/.config/rclone/rclone.conf, в секцию backup:
    Код:
    no_check_certificate = true
    
    Шифрование при этом остается - пароль и данные по-прежнему не идут открытым текстом. Теряется проверка, то есть уверенность, что на том конце именно Ваше хранилище. Для бэкапов размен приемлемый: restic шифрует данные до отправки, и подменивший сторону получит набор нечитаемых блоков.

    Если провайдер дает доменное имя хранилища - пишите в host его, а не IP. На проверку сертификата это не повлияет, но при смене адреса чинить ничего не придется.

    Можно и не собирать конфиг руками: мастер rclone config проведет по шагам, спросит пароль скрытым вводом и запишет файл сам. И про obscure - это не шифрование, а обфускация. Развернуть ее обратно может любой, у кого есть файл. Отсюда и chmod 600.

    Дальше все как в прошлой части, меняется только адрес репозитория:
    Код:
    export RESTIC_REPOSITORY="rclone:backup:mc-backup"
    export RESTIC_PASSWORD_FILE="$HOME/.restic-pass"
    
    Первая строка - куда класть данные. Адрес читается по двоеточиям: rclone - идти через rclone, а не напрямую, backup - имя секции из rclone.conf, mc-backup - папка на хранилище. Ключи доступа тут не нужны, за них отвечает конфиг rclone.

    Вторая строка - откуда брать пароль репозитория. Если Вы шли по статье подряд, файл уже есть. Если начинаете отсюда или делаете на другой машине, создайте его:
    Код:
    openssl rand -base64 24 > ~/.restic-pass
    chmod 600 ~/.restic-pass
    cat ~/.restic-pass
    
    Показанную строку сразу в менеджер паролей.

    Это отдельный пароль, а не тот, что у Вас от FTP-хранилища. Хранилище пускает к месту, где лежат файлы, а этот пароль шифрует их содержимое. Файл нужен, чтобы cron ночью снял копию без Вашего участия: не задать RESTIC_PASSWORD_FILE - и restic на следующем шаге попросит придумать пароль прямо в консоли, с повтором, а потом будет спрашивать его при каждом запуске. Потеряете - данные не расшифровать никак.

    Теперь три команды:
    Код:
    restic init
    restic backup /home/mc/mainServer --exclude=logs --exclude=cache
    restic snapshots
    
    init создает на хранилище сам репозиторий: служебные папки и файл с ключом шифрования. Делается один раз, при повторном запуске restic скажет, что репозиторий уже существует.

    backup снимает копию папки сервера, --exclude выкидывает логи и кеш - они пересоздаются, а место занимают. Вот эту строку и вешают на расписание.

    snapshots показывает список снятых копий с датами и размером. Появилась после первого запуска строка - значит все работает.

    А вот чего делать не надо - монтировать FTP в папку и класть туда обычный репозиторий restic. Такие советы попадаются, в том числе в инструкциях самих провайдеров. FUSE поверх FTP не дает ни нормальных блокировок, ни атомарного переименования, а restic на них рассчитывает. Репозиторий рано или поздно побьется, и узнаете Вы об этом при восстановлении.

    Пароль от хранилища и пароль репозитория restic - разные вещи. Первый открывает доступ к месту, второй расшифровывает данные. Записывайте оба и храните порознь.

    Итог части

    Разворачивается любой снапшот, а не только последний, ID берется из restic snapshots. Серверов несколько - отбираем по --tag. Нужна одна папка - --include. Нужен архив файлом - dump. Восстановление проверяется заранее, а не в ночь аварии. FTP-хранилище подключается через rclone и только с TLS.

    Дальше поговорим о том, откуда на серверы Minecraft на самом деле приходит беда.

    >>> Часть 10: Защита самого Minecraft (тык)
     
  10. Автор темы
    iForgotPassword

    iForgotPassword Активный участник Пользователь

    Баллы:
    66
    Имя в Minecraft:
    iForgotPassword
    Это продолжение статьи Работа с VPS и безопасность сервера от А до Я. Начало - в первом посте темы. << Часть 9: Восстановление и хранилище провайдера

    Часть 10. Защита самого Minecraft

    Итак, машина закрыта, порты на месте, бэкапы едут по расписанию. Казалось бы, можно выдохнуть.

    Только вот беда к серверам Minecraft приходит совсем не оттуда, откуда ее ждут. Никто не будет неделю подбирать Ваш SSH-ключ. Гораздо проще дождаться, пока Вы сами скачаете плагин с сайта, где раздают платные ресурсы, и запустите его на своем сервере от имени своего же пользователя. Никакой файрвол от этого не спасает. Вы открыли дверь и занесли гостя на руках.

    Так что эта часть - не про сеть, а про то, что живет внутри сервера.

    Откуда берем плагины

    Официальные площадки: Modrinth, SpigotMC, Hangar от PaperMC, GitHub самого разработчика. Все.

    Сайты со "слитыми" версиями платных плагинов - отдельная история, и разговор тут не про мораль. Разговор про то, что бизнес-модель таких площадок построена ровно на этом. Человек, которому очень нужен платный плагин, скачает и запустит что угодно. Проверять он не будет, потому что не умеет и не за этим пришел. Ровно поэтому туда и складывают то, что складывают.

    И самое неприятное. Такой плагин обычно работает. Правда работает, делает ровно то, что обещано в описании. Просто вдобавок он тихо создает себе оператора при заходе игрока с определенным ником, или слушает команду в чате, или ходит на чужой сервер за инструкциями. Вы месяцами ничего не замечаете, потому что замечать нечего.

    Есть инструменты, которые смотрят внутрь jar и ищут подозрительное. Работают они статически, то есть разбирают байт-код, ничего не запуская.

    PluginScan от Rikonardo - самый известный. Есть версия для браузера, причем считает она полностью у Вас на компьютере и файл никуда не отправляет. Но авторы сами советуют брать консольную версию, она быстрее и надежнее. Находки раскладываются по четырем уровням от low до critical.

    BackdoorDetector v2 - консольная утилита, разбирает каждый плагин в отдельной временной папке и кеширует результаты по хешу, чтобы не проверять одно и то же дважды.
    Код:
    java -jar BackdoorDetector-v2.jar scan plugin.jar AI_MODERN
    
    Чего эти сканеры не могут. Авторы PluginScan пишут об этом в документации сами. Инструмент ищет приемы, которые могут использоваться во вред, но у которых есть и совершенно законное применение. Плагин, который ходит в сеть, может тянуть обновление, а может и не обновление. Плагин, который читает файлы, может читать свой конфиг.

    Отсюда два следствия. Пустой отчет не означает, что плагин чистый. Под слоем обфускации сканер не видит почти ничего. А красный отчет не означает, что плагин плохой. Ложных срабатываний хватает, особенно у сигнатурных сканеров вроде MCAntiMalware, где на это жалуются прямо в отзывах.

    Поэтому прогоняйте двумя-тремя инструментами и относитесь к результату как к поводу посмотреть глазами, а не как к приговору.

    Открываем и смотрим. Декомпилятор превращает байт-код обратно в читаемую Java. Recaf удобен тем, что сразу дает и байт-код, и дерево классов, JADX проще для беглого просмотра.

    На что смотреть в первую очередь:
    • Сравнение ника игрока с чем-то захардкоженным. Особенно рядом с выдачей прав.
    • Обращения к setOp, addAttachment, dispatchCommand с консоли.
    • Сетевые вызовы к адресам, которые никак не связаны с площадкой плагина.
    • Строки, собираемые по символам или раскодируемые из Base64 прямо в рантайме. Прятать так обычный текст незачем.
    • Слова вроде blackspigot или spigotunlock - следы того, что jar прошел через "разлочку".

    Разбираться в этом с нуля тяжело, и это нормально. Если непонятно, а плагин очень нужен - спросите на форуме, приложив отчет сканера. Тут лучше выглядеть новичком, чем через месяц искать, кто снес спавн.

    Оп - это не "права администратора"

    Самая частая ошибка на молодых серверах - раздать оп всем, кому нужно чуть больше обычного игрока. Модератору, строителю, другу, который "просто посмотрит".

    Проблема в том, что оп это не роль, а выключатель всех проверок разом. Оператор проходит мимо большинства проверок прав в плагинах, ему доступны /stop и /op, для него открыты командные блоки. Выдавая оп строителю, Вы выдаете ему сервер целиком - вместе с возможностью выдать оп кому-то еще.

    Правильный путь - LuckPerms и группы под конкретные задачи. Строителю права WorldEdit в его регионе, модератору бан и мут, и ничего сверх. Оп при этом не нужен вообще никому, включая Вас. Заведите себе группу с нужными правами и снимайте оп после разовых работ.

    Заодно загляните в ops.json прямо сейчас. Файл маленький, читается за десять секунд, и иногда там обнаруживаются очень интересные никнеймы.

    Если оп кому-то все же нужен, у него есть уровни, и что дает каждый - расписано на вики. В server.properties:
    Код:
    op-permission-level=4
    
    Четверка стоит по умолчанию и означает "может все", включая /stop и раздачу опа другим. Уровень 2 дает команды вроде /gamemode и /tp, но уже не дает выключить сервер и не дает плодить новых операторов. Для модератора этого обычно достаточно, и цена вопроса - одна строка.

    Код:
    function-permission-level=2
    
    Тот же уровень, но для функций и датапаков. Ставить выше, чем нужно, смысла нет.

    Код:
    enable-command-block=false
    
    Командные блоки нужны далеко не всем сборкам. Не пользуетесь - выключите, это минус целый класс проблем.

    Код:
    spawn-protection=0
    
    Тут наоборот, ноль обычно правильный ответ. Радиус защиты спавна работает мимо WorldGuard и регулярно мешает плагинам, а сам спавн все равно закрывают регионом.

    Правки в server.properties подхватываются только при перезапуске. Перезагрузка конфигов через /reload тут не поможет, да и вообще /reload на живом сервере - отдельный способ сделать себе плохо.

    Чем закрывать. Правами, а не отдельным плагином-блокировщиком. У каждой команды есть свой узел прав, и группе по умолчанию его достаточно запретить:
    Код:
    /lp group default permission set minecraft.command.op false
    /lp group default permission set bukkit.command.plugins false
    /lp group default permission set bukkit.command.version false
    
    Узлы вида minecraft.command.имя отвечают за ванильные команды, bukkit.command.имя - за команды самого сервера, у плагинов узел написан в описании. Не знаете узел - включите /lp verbose on, выполните команду и посмотрите, что покажет отчет.

    Пространства имен. У каждой команды есть еще и полная форма с префиксом того, кто ее зарегистрировал, и работает она точно так же:
    Код:
    /minecraft:op Steve
    /bukkit:plugins
    
    Права проверяются по узлу, поэтому обе формы закрываются одной строкой. А вот блокировщик, который сверяет введенный текст со списком запрещенных, префиксную запись пропустит - это еще один довод в пользу прав.

    Разведка. /pl и /ver выдают полный список плагинов с версиями. Для игрока это бесполезно, а вот тому, кто ищет на Вашем сервере знакомую дырявую версию, экономит все время. Закрываем для всех, кроме себя.

    На прокси. Velocity со своим набором команд - отдельная поверхность. /server позволяет ходить между серверами напрямую, что в схеме с авторизацией означает "мимо авторизации". Права на прокси раздаются так же, LuckPerms умеет работать и там.

    Проверять надо с обычного аккаунта, а не со своего. Со своего у Вас все закрыто и все прекрасно.

    Второй фактор для админских аккаунтов

    На пиратском сервере ник это единственное удостоверение личности. Увели аккаунт - вместе с ним уехали и все права. Поэтому админам ставят отдельную проверку поверх обычного входа. Плагин требует еще один пароль или код из приложения, и до его ввода аккаунт не может ничего, даже с полными правами.

    Стоит понимать границы. От человека, который получил доступ к консоли или к файлам сервера, это не спасает - он просто выключит плагин. Это защита от угона аккаунта, не от компрометации машины.

    Ботфильтр

    Ботоводы приходят рано или поздно ко всем, кто попал в списки серверов. Выглядит это как сотни заходов в секунду с разных адресов. Сервер захлебывается на этапе подключения, живые игроки зайти не могут.

    Из живого: Sonar и LimboFilter от Elytrium. Оба ставятся на прокси и отсеивают ботов до того, как те доберутся до серверов Paper. Ставить их заранее, а не в момент атаки, когда Вы уже не можете зайти в консоль.

    CoreProtect ставим до того, как понадобится

    CoreProtect пишет, кто какой блок поставил и снес, кто что взял из сундука, когда и с какого адреса. Обычно его ставят как средство от гриферов, но по-настоящему он выручает после инцидента. Только по этой базе можно понять, что произошло, когда и под каким ником.

    Разница простая. С CoreProtect Вы откатываете действия конкретного игрока за конкретный период. Без него - раскатываете вчерашний бэкап и теряете день у всех.

    База при этом должна попадать в бэкап. Она лежит рядом с сервером, и в аварии теряется вместе с ним, а без журнала разбираться будет не по чему. Файл SQLite уедет вместе с папкой плагина, а если журнал в MySQL, дамп снимается отдельно, как в части 6.

    Обновления

    Скучно, но работает лучше всего остального в этой части.

    Ядро обновляем регулярно. Paper чинит эксплойты дюпов и крашеров постоянно, и почти всегда это происходит тихо, без громких новостей. Сервер, который полтора года стоит на одной сборке, дырявый примерно по определению.

    Плагины - туда же, особенно те, что торчат наружу: авторизация, ботфильтр, все, что общается с сетью. И обратное правило: плагин, который не обновлялся два года и чей автор пропал, надо не обновлять, а менять. Заброшенный плагин это не стабильность, а мина с таймером.

    Обновляться лучше не в пятницу вечером и не перед наплывом игроков. Сначала бэкап, потом обновление, потом проверка. Ровно в таком порядке.

    Итог части

    Плагины только с официальных площадок, новые jar прогоняем сканером и не верим пустому отчету. Оп не выдается никому, права через LuckPerms. Опасные команды закрыты вместе с префиксными формами, /pl и /ver спрятаны. На прокси стоит ботфильтр, на серверах Paper - CoreProtect. Ядро и плагины свежие.

    А в следующей части поговорим о том, что делать, если все это Вы прочитали слишком поздно.

    >>> Часть 11: Аудит: сервер могли взломать (тык)
     
  11. Автор темы
    iForgotPassword

    iForgotPassword Активный участник Пользователь

    Баллы:
    66
    Имя в Minecraft:
    iForgotPassword
    Это продолжение статьи Работа с VPS и безопасность сервера от А до Я. Начало - в первом посте темы. << Часть 10: Защита самого Minecraft

    Часть 11. Аудит: сервер могли взломать

    Эта часть - на случай, когда что-то идет не так, а что именно, непонятно.

    Типичная картина. TPS просел без единой ошибки в логах. Или процессор загружен под сотню, когда на сервере два игрока. Или провайдер прислал письмо про подозрительный исходящий трафик. Или машина перезагрузилась сама. Или в консоли мелькает команда, которую Вы не вводили.

    Каждый из этих симптомов по отдельности может ничего не значить. Кривой плагин умеет все то же самое. Но проверить дешевле, чем не проверить.

    Правило номер один: не топчите улики. Пока Вы не сняли данные - не перезагружайте машину, не удаляйте найденные файлы и не пытайтесь "почистить". После перезагрузки все процессы, все открытые сетевые соединения и половина следов исчезнут, а Вы останетесь с ощущением, что что-то было, но что - уже не узнать. Сначала собираем, потом думаем.

    Что собираем

    Все команды ниже только читают. Систему они не меняют, запускать их безопасно даже если тревога окажется ложной.

    Складываем в отдельную папку и пакуем в архив. Запускать от root.

    Код:
    #!/bin/bash
    # Только чтение. Систему не меняет. Запуск: sudo bash collect.sh
    OUT="/root/audit_$(date +%Y%m%d_%H%M%S)"
    mkdir -p "$OUT"
    run() { { echo "\$ $2"; eval "$2"; } > "$OUT/$1.txt" 2>&1; }
    
    run os          "cat /etc/os-release; uname -a; uptime -p; timedatectl"
    run users       "getent passwd; echo; getent group"
    run uid0        "grep ':0:' /etc/passwd"
    run sudoers     "cat /etc/sudoers; cat /etc/sudoers.d/* 2>/dev/null"
    run ssh-config  "sshd -T"
    run ssh-keys    "find /root /home -name authorized_keys -exec ls -l {} \; -exec cat {} \;"
    run ps          "ps auxfww"
    run proc-tmp    "ls -l /proc/*/exe 2>/dev/null | grep -E '/tmp|/var/tmp|/dev/shm'"
    run proc-del    "ls -l /proc/*/exe 2>/dev/null | grep deleted"
    run net         "ip a; ip r; cat /etc/resolv.conf; ss -tulpn"
    run firewall    "ufw status verbose; iptables -S; nft list ruleset"
    run systemd     "systemctl list-units --type=service --state=running; systemctl list-timers --all"
    run units       "ls -lR /etc/systemd/system/ 2>/dev/null"
    run cron        'crontab -l; cat /etc/crontab; ls -l /etc/cron.*; for u in $(cut -d: -f1 /etc/passwd); do echo "-- $u"; crontab -lu $u 2>/dev/null; done'
    run logins      "last -30; lastb -30; lastlog"
    run logs        "tail -300 /var/log/auth.log; journalctl -u ssh --no-pager | tail -100"
    run packages    "dpkg -l; apt list --upgradable 2>/dev/null"
    run ld-preload  "cat /etc/ld.so.preload; cat /etc/ld.so.conf.d/*"
    run modules     "lsmod"
    run suid        "find / -xdev -perm -4000 -type f -exec ls -l {} \;"
    run recent      "find /etc /bin /sbin /usr/bin /usr/sbin -xdev -mtime -30 -type f -exec ls -l {} \;"
    run tmp         "ls -laR /tmp /var/tmp /dev/shm"
    run autostart   "cat /etc/profile; cat /etc/profile.d/*; cat /root/.bashrc /home/*/.bashrc /home/*/.zshrc 2>/dev/null"
    run history     "tail -100 /root/.bash_history; tail -100 /home/*/.bash_history"
    
    tar -czf "$OUT.tar.gz" -C /root "$(basename $OUT)"
    echo "Готово: $OUT.tar.gz"
    
    Отработает за пару минут, дольше всего думает find по всему диску. Полная версия, с проверкой SGID, world-writable, dkms, PAM и еще десятком блоков, лежит в MinecraftLinuxUtils - файл collect-audit.sh.

    В архиве будут Ваши секреты. Публичные ключи, внутренние адреса, имена хостов, куски конфигов, а в истории команд запросто найдется пароль, который Вы когда-то ввели одной строкой. Прежде чем показывать этот архив кому-нибудь на форуме, откройте и вычистите. Приватных ключей скрипт не выводит, только имена файлов, но проверить лишним не будет.

    Как читать то, что собралось

    Смысл не в том, чтобы найти файл с надписью "вирус". Смысл в том, чтобы найти то, чего Вы не можете объяснить.

    uid0.txt - строка должна быть одна, про root. Второй пользователь с нулевым UID означает, что разговор окончен и можно переходить к части 12.

    ssh-keys.txt - здесь считаем ключи и сверяем с числом своих машин. Комментарий в конце ключа вида user@laptop ни о чем не говорит, он пишется руками и подделывается за секунду. Не сходится количество - вопрос закрыт.

    proc-tmp.txt - процессы, запущенные из /tmp, /var/tmp или /dev/shm. Нормальному софту там жить незачем. Почти всегда это находка.

    proc-del.txt - процессы, у которых исполняемый файл удален. Бывает и в мирной жизни: обновили пакет, а сервис не перезапустили. Но если удаленный файл лежал в /tmp, это уже не про обновления.

    ld-preload.txt - на обычной Ubuntu файла /etc/ld.so.preload просто нет. Если он появился и это не Вы - находка серьезная: через него подгружают библиотеку в каждый запускаемый процесс, чтобы прятать файлы и соединения от Ваших же команд.

    cron.txt и systemd.txt - ищем то, что не создавали. Внимание на строки с curl, wget, base64 и путями во временные каталоги. Про cron помнят все, поэтому закрепляются сейчас чаще через таймеры systemd - пролистайте list-timers целиком.

    suid.txt - список программ, которые выполняются с правами владельца. Стандартный набор Ubuntu невелик, и он гуглится за минуту. Свежий SUID у копии bash или у чего-то в /tmp - это прямой ответ на все вопросы.

    recent.txt - что менялось в системных каталогах за месяц. Сверяйте с /var/log/apt/history.log: почти все изменения должны объясняться установкой пакетов. Одинокий свежий файл в /usr/bin без соответствующей записи в истории apt - повод остановиться.

    logins.txt - в lastb будут сотни неудачных попыток, и это норма, боты стучатся круглосуточно. Смотреть надо на last, то есть на успешные входы. Незнакомый адрес там весит больше, чем весь остальной файл.

    logs.txt - тут ищем не записи, а их отсутствие. Ровный провал по времени посреди журнала означает, что журнал подчистили.

    history.txt - пустая история у пользователя, который точно работал руками, или HISTFILE, перенаправленный в /dev/null, сами по себе ничего не доказывают. Но объяснение им нужно.

    Скрипт выше проверяет машину. Но если к Вам зашли через плагин, следы будут не в /etc, а в папке сервера.

    Что лежит в plugins
    Код:
    ls -lt plugins/
    
    Ключ -t сортирует по времени изменения, свежее сверху. Дальше вопрос простой: каждый jar в этом списке Вы ставили сами? Дата у каждого совпадает с тем, когда Вы его ставили? Файл, который изменился сам по себе через месяц после установки, менялся не сам по себе.

    Кто получал права
    Код:
    grep -iE "opped|deopped|gamemode|/op " logs/*.log logs/*.gz
    
    Выдача оператора пишется в лог. Ищем строки, где оп получал кто-то, кому Вы его не давали, и смотрим на время - оно даст точку отсчета для всего остального.

    Куда сервер ходит
    Код:
    ss -tunp | grep java
    
    Установленные исходящие соединения процесса Java. Нормальный сервер Minecraft общается с игроками, с сессионными серверами Mojang (при online-mode=true) и с bStats. Постоянное соединение с адресом, который Вы не узнаете, объяснения требует. Особенно если параллельно процессор занят непонятно чем - майнер выглядит ровно так.

    Что творилось в мире
    Если стоит CoreProtect, у Вас есть журнал. /co lookup u:ник t:7d покажет все действия игрока за неделю, /co inspect даст историю конкретного блока. Это единственный способ отличить "гриферы снесли спавн" от "кто-то зашел под чужим ником".

    Логи ротируются и сжимаются, поэтому не забывайте про logs/*.gz. Grep по ним работает через zgrep. И скопируйте папку logs целиком до того, как начнете что-то менять - она перезапишется при следующем старте.

    Полезная привычка на будущее: снимите такой архив прямо сейчас, пока все хорошо, и положите в бэкап. Через полгода это будет эталон, с которым можно сравнить. Половина работы при разборе инцидента уходит именно на вопрос "а так было всегда или нет".

    Отдать дамп нейросети

    Читать двадцать файлов глазами долго, а модели такое разбирают неплохо. Файлы текстовые и небольшие, так что закинуть их в чат и попросить разбор - рабочий вариант, особенно если Вы не знаете, как выглядит норма.

    Только не заливайте архив целиком. В нем ключи, адреса, конфиги и история команд с паролями. Отдавать стоит те файлы, где секретов нет - ps, proc-tmp, cron, systemd, suid, recent, modules, а из logs и logins сначала вырезать адреса и имена.

    Промпт, с которого можно начать:
    Код:
    Ниже вывод диагностических команд с сервера Ubuntu, на котором работает сервер Minecraft.
    Проверь его на признаки компрометации: лишние пользователи с UID 0, незнакомые ключи SSH,
    процессы из /tmp и /dev/shm, задания cron и юниты systemd, которых там быть не должно,
    свежие изменения в системных каталогах, подозрительный SUID.
    По каждой находке напиши, что именно насторожило и какой командой проверить это руками.
    Отдельно перечисли, чего в этих данных не хватает для вывода.
    Менять что-либо на сервере не предлагай, мне нужен только разбор.
    
    Дальше спрашивайте точечно. Например, "вот список юнитов systemd, какие из них не входят в стандартную Ubuntu 26.04" - на таких вопросах толку больше, чем на просьбе проверить все сразу.

    Ответ читайте как список версий, а не как вердикт. Модель уверенно назовет вредоносным нормальный сервис и так же уверенно пропустит настоящую находку. Каждую версию проверяем командами из этой части, решение остается за Вами.

    Готовые сканеры

    rkhunter и chkrootkit проверяют систему по базе известных признаков и сверяют хеши системных файлов. Lynis решает другую задачу - он не ищет взлом, а оценивает, насколько прилично настроена машина, и выдает список рекомендаций. Для профилактики Lynis полезнее двух первых.

    Ставятся из репозитория, запускаются одной командой:
    Код:
    sudo apt install rkhunter chkrootkit lynis
    rkhunter --check --skip-keypress
    chkrootkit
    lynis audit system
    
    И сразу про то, почему им нельзя верить на слово.

    Во-первых, они ищут известное. Свежее или самодельное они не увидят, а ложных срабатываний выдадут прилично - у rkhunter половина предупреждений на нормальной машине оказывается ерундой.

    Во-вторых, и это важнее. Вы запускаете эти программы на той самой машине, которую проверяете. Они пользуются системными командами, а системные команды могли подменить - ровно для этого и существует трюк с ld.so.preload. Получается, что Вы спрашиваете у подозреваемого, не он ли это сделал.

    Поэтому по-хорошему серьезная проверка делается снаружи: диск подключается к другой системе или машина грузится в аварийный режим провайдера, и файлы смотрят оттуда. Для домашнего сервера это перебор, но знать про ограничение стоит - чистый отчет rkhunter не означает, что все чисто.

    Итог части

    Собрали данные, ничего не трогая. Прошлись по файлам и выписали все, чему нет объяснения. Дальше два варианта.

    Если непонятного не нашлось - выдыхаем, скорее всего дело в плагине или в конфиге. Заодно теперь есть эталонный снимок системы, положите его в бэкап.

    Если нашлось хоть что-то из списка выше - переходим к следующей части. Там про то, что делать дальше, и ответ Вам не понравится.

    >>> Часть 12: Если подтвердилось, и частые вопросы (тык)
     
  12. Автор темы
    iForgotPassword

    iForgotPassword Активный участник Пользователь

    Баллы:
    66
    Имя в Minecraft:
    iForgotPassword
    Это продолжение статьи Работа с VPS и безопасность сервера от А до Я. Начало - в первом посте темы. << Часть 11: Аудит: сервер могли взломать

    Часть 12. Если подтвердилось, и частые вопросы

    Вы прошлись по срезу из прошлой части и нашли то, чему нет объяснения. Левый ключ в authorized_keys, процесс из /dev/shm, таймер systemd, который Вы не создавали.

    Ответ Вам не понравится.

    Взломанную машину не лечат

    Удалить найденное и жить дальше - решение, которое напрашивается само. Оно неправильное, и вот почему.

    Вы нашли что-то. Убедиться, что нашли все, Вы не можете. Закрепляются в системе не одним способом, а несколькими сразу, специально на случай, если один найдут: ключ в authorized_keys, запись в cron, таймер systemd, правка в .bashrc, подмененный бинарник, модуль ядра. Уберете три из шести - через неделю все вернется, и в следующий раз Вы этого уже не заметите.

    Плюс момент из прошлой части: Вы ищете следы теми же инструментами, которые могли подменить.

    Поэтому машину не чинят. С нее переезжают.

    1. Снимите данные, пока ничего не трогали. Срез из части 11 и папку logs сервера. Если провайдер умеет делать снапшот диска - сделайте, это бесплатная страховка на случай, если позже понадобится что-то уточнить.

    2. Уведите сервер на техработы. Машину целиком не выключайте, пока не забрали все нужное, но игроков внутри быть не должно. Как именно закрыться - дело вкуса: порт прокси в ufw, заглушка вместо сервера, объявление в дискорде. Рассчитывайте не на час простоя, а на вечер и больше. Лучше сказать это игрокам сразу, чем потом переносить сроки.

    3. Заберите данные. Мир, конфиги, базы плагинов. Забирать надо, но не jar-файлы - ядро и плагины скачаете заново с официальных площадок. Именно в них, скорее всего, и была причина.

    4. Поднимите новую машину. Не переустановку системы на старой, а именно новый VPS, с нуля. Пройдите по частям 1-4 этой статьи.

    5. Восстановите из бэкапа, снятого ДО компрометации. Дату Вы примерно знаете из среза: по времени появления левого файла или ключа. Если бэкапы старше этой даты не сохранились - разворачивайте самый ранний из имеющихся и проверяйте руками.

    6. Смените все секреты. Все. Не те, которые кажутся затронутыми, а все, что было на той машине:
    • ключи SSH - генерируйте новые, старые удалите отовсюду;
    • forwarding-секрет Velocity;
    • пароль RCON;
    • пароли всех пользователей базы данных;
    • токены ботов Telegram и Discord;
    • пароли панелей, API-ключи хранилищ бэкапов;
    • пароли администраторов в игре.
    Все это лежало на машине открытым текстом в конфигах, и считать иначе оснований нет.

    7. Только теперь снимайте техработы. Есть сомнения, что успели проверить все - подержите закрытым еще день, это дешевле второго захода. Первые пару недель смотрите на срез и на журнал входов внимательнее обычного.

    Отдельно: если у Вас есть CoreProtect, посмотрите его журнал до того, как раскатывать бэкап. Иногда выясняется, что "взлома" не было, а был администратор, у которого увели аккаунт. Это другая история и лечится она проще.

    FAQ

    Отрезал себе SSH, что делать
    Аварийная консоль в панели провайдера. Она не зависит ни от SSH, ни от файрвола. Найдите эту кнопку заранее, пока все работает.

    ufw включил, а порт все равно открыт
    Скорее всего, порт публикует Docker - он пишет правила мимо ufw. Лечится публикацией на 127.0.0.1, разбирали в части 5.

    Игроки заходят мимо авторизации
    Открыт порт сервера Paper. Ставьте server-ip=127.0.0.1, если все на одной машине, или разрешайте порт только с IP прокси. Часть 3.

    Failed to verify username
    Заход с пиратского клиента на сервер с online-mode=true. Значение должно совпадать на прокси и на всех серверах Paper.

    Место на диске есть, а сервер пишет "нет места"
    Кончились inodes. Проверяйте df -i, а не только df -h. Часть 7.

    YOU ARE RUNNING THIS SERVER AS AN ADMINISTRATIVE OR ROOT USER
    Заведите отдельного пользователя и передайте ему файлы: chown -R mc:mc /home/mc.

    TPS просел, а в логах чисто
    /spark profiler start
    , через несколько минут /spark profiler stop. Отчет покажет, какой плагин ест время.

    Нужен ли антиддос
    Базовая защита есть почти у всех провайдеров, небольшому серверу этого хватает. Если понадобится больше, принцип везде один: домен смотрит на сеть фильтрации, она чистит трафик и отдает Вам, а Ваш адрес наружу не светится.

    Оранжевое облако Cloudflare тут не поможет, оно проксирует только HTTP и HTTPS. Тот же фокус для произвольного TCP у них называется Spectrum, и Minecraft там поддерживается с тарифа Pro. Под Minecraft сделан TCPShield - схема та же, на стартовом тарифе дают терабайт трафика в месяц без оплаты.

    И то и другое бессмысленно без одного шага. Порт игроков в ufw открывается только для адресов сервиса, правилом из части 3. Иначе любой, кто однажды узнал Ваш IP, бьет напрямую мимо всей фильтрации, а адрес утекает через старые записи DNS и скриншоты консоли.

    Заодно включите PROXY protocol - в velocity.toml секция advanced, строка haproxy-protocol = true, и то же самое на стороне сервиса. Без него все игроки приходят с адресов фильтрации, и баны по IP перестают работать.

    Отдельно про Cloudflare для российской аудитории: его сейчас режут через блокировку TLS ECH, так что часть игроков просто не подключится.

    Напоследок

    Все, что описано в статье, настраивается за один вечер и дальше работает само. Вечер против недели, которую иначе придется потратить на последствия - размен, который стоит сделать заранее.

    Дальше идет часть-приложение: инструменты и мелочи, которые серверу не нужны, а Вам пригодятся.

    >>> Часть 13: Инструменты и мелочи для удобства (тык)
     
  13. Автор темы
    iForgotPassword

    iForgotPassword Активный участник Пользователь

    Баллы:
    66
    Имя в Minecraft:
    iForgotPassword
    Это продолжение статьи Работа с VPS и безопасность сервера от А до Я. Начало - в первом посте темы. << Часть 12: Если подтвердилось, и частые вопросы

    Часть 13. Инструменты и мелочи для удобства

    Сервер работает, машина закрыта, бэкапы снимаются. Эта часть - про то, без чего можно жить, но с чем жить приятнее. Ничего обязательного тут нет, берите что понравится.

    Vim: почему нет подсветки

    Открываете server.properties, а там однотонная серая простыня. Дело не в настройках: на голой Ubuntu стоит vim-tiny, который редактировать умеет, а подсвечивать синтаксис не умеет вообще. Сколько ни пиши syntax on, ничего не появится.
    Код:
    sudo apt install vim
    
    После этого подсветка включится сама.

    Создаем ~/.vimrc:
    Код:
    set number
    set relativenumber
    set tabstop=4
    set scrolloff=8
    syntax enable
    
    set nobackup
    set nowritebackup
    set noswapfile
    
    " Вернуться туда, где закрыл файл в прошлый раз
    au BufReadPost * if line("'\"") > 1 && line("'\"") <= line("$") | exe "normal! g'\"" | endif
    
    " Русская раскладка внутри vim
    set keymap=russian-jcukenwin
    set iminsert=0
    set imsearch=0
    
    Что тут важного.

    relativenumber нумерует строки относительно курсора: нужная строка на 8 ниже - пишете 8j и сразу там.

    nobackup, nowritebackup и noswapfile - не косметика. Без них рядом с конфигами остаются файлы вроде .server.properties.swp, а потом вы гадаете, откуда они взялись и что будет, если их удалить.

    Строка с BufReadPost возвращает курсор туда, где вы были в прошлый раз.

    Про раскладку отдельно. Три последние строки дают русскую раскладку внутри vim: Ctrl+^ в режиме вставки переключает туда и обратно, а системная остается английской, так что команды vim продолжают работать. Кто хоть раз пытался выйти из vim с русской раскладкой, поймет.

    О выходе: :wq сохранить и выйти, :q! выйти без сохранения. Не реагирует - сначала Esc, потом команда.

    tmux по-взрослому

    В части 4 мы взяли от tmux четыре команды, чтобы сервер не умирал вместе с SSH. Теперь про остальное.

    Голый tmux неудобен в мелочах: окна нумеруются с нуля, разделение висит на % и кавычке, мышь выключена, имена окон слетают. Лечится одним файлом.

    Код:
    set -g default-terminal "screen-256color"
    
    # Префикс: Ctrl+A вместо Ctrl+B
    unbind C-b
    set -g prefix C-a
    bind C-a send-prefix
    
    # Нумерация с единицы
    set -g base-index 1
    setw -g pane-base-index 1
    
    # Разделение окна: | и -
    unbind %
    bind | split-window -h
    unbind '"'
    bind - split-window -v
    
    # Перечитать конфиг
    unbind r
    bind r source-file ~/.tmux.conf
    
    # Размер панелей стрелками vim и зум на m
    bind -r h resize-pane -L 5
    bind -r j resize-pane -D 5
    bind -r k resize-pane -U 5
    bind -r l resize-pane -R 5
    bind -r m resize-pane -Z
    
    # Мышь и выделение как в vim
    set -g mouse on
    setw -g mode-keys vi
    bind-key -T copy-mode-vi 'v' send -X begin-selection
    bind-key -T copy-mode-vi 'y' send -X copy-selection
    unbind -T copy-mode-vi MouseDragEnd1Pane
    
    # Имена окон не слетают
    set -g allow-rename off
    
    # Плагины
    set -g @plugin 'tmux-plugins/tpm'
    set -g @plugin 'tmux-plugins/tmux-sensible'
    set -g @plugin 'tmux-plugins/tmux-resurrect'
    set -g @plugin 'tmux-plugins/tmux-continuum'
    set -g @resurrect-capture-pane-contents 'on'
    set -g @continuum-restore 'on'
    
    run '~/.tmux/plugins/tpm/tpm'
    
    Менеджер плагинов ставится одной командой, потом внутри tmux жмем префикс и заглавную I:
    Код:
    git clone https://github.com/tmux-plugins/tpm ~/.tmux/plugins/tpm
    

    Теперь по пунктам, что это дает.

    Префикс. Ctrl+B набирается неудобно, поэтому его часто меняют на Ctrl+A. Если меняете - строка bind C-a send-prefix обязательна, иначе потеряете Ctrl+A в самом bash, где он переводит курсор в начало строки.

    Нумерация с единицы. По умолчанию первое окно нулевое, и переходить к нему надо префиксом и нулем - то есть тянуться через всю клавиатуру мимо единицы.

    Разделение окна. Запомнить, что % делит по вертикали, а кавычка по горизонтали, невозможно, а | и - выглядят как то, что делают.

    Переименование окон. Префикс и запятая, вводите имя, Enter. Вместо 0:bash 1:bash 2:bash внизу появляется 1:velocity 2:main 3:logs. Строка allow-rename off нужна затем, чтобы имя не слетело обратно при следующей же команде.

    Копирование. Префикс и [ входит в режим выделения, он же режим прокрутки лога. Дальше стрелки или / для поиска, v начать, y скопировать, q выйти. Вставка - префикс и ].

    Строка про MouseDragEnd1Pane неочевидная, но без нее выделение мышью схлопывается ровно в момент отпускания кнопки.

    Скопированное ложится в буфер tmux, а не Вашего компьютера. Чтобы попадало сразу туда - set -g set-clipboard on, работает при поддержке OSC 52 терминалом. Termius, iTerm2 и Windows Terminal умеют.

    Сессии переживают перезагрузку. Ради этого и ставится менеджер плагинов. resurrect сохраняет раскладку окон и содержимое панелей, continuum делает это сам и восстанавливает при старте. Перезагрузили машину - окна на месте, с теми же именами.

    Сервер это не поднимает и systemd из части 4 не заменяет: восстанавливаются окна и их содержимое, а не запущенные в них процессы.

    Смотрим файлы и логи

    bat - это cat с подсветкой синтаксиса и номерами строк. Конфиги и логи читаются легче.
    Код:
    sudo apt install bat
    echo "alias bat='batcat'" >> ~/.bashrc
    
    Алиас тут не каприз. В Ubuntu имя bat занято другим пакетом, поэтому бинарник называется batcat. Без алиаса примеры из интернета будут отвечать "команда не найдена".

    less уже стоит и умеет больше, чем кажется: /текст поиск, n к следующему, G в конец, g в начало, q выход. Самое полезное - F, он превращает less в tail -f прямо на месте, а Ctrl+C возвращает обратно.

    ripgrep - поиск по файлам вместо grep -r. Команда называется rg, ищет рекурсивно и без флагов. Найти, в каком из тридцати конфигов упоминается параметр - ровно его задача:
    Код:
    sudo apt install ripgrep
    rg "mysql" ~/mainServer/plugins
    
    Экономим движения

    Ctrl+R ищет по истории: начинаете печатать кусок команды, он достает ее целиком.

    !! повторяет предыдущую команду. Отсюда главный трюк: забыли sudo - пишете sudo !! и не набираете все заново.

    !$ подставляет последний аргумент предыдущей команды: посмотрели файл через cat, теперь nano !$.

    В самой строке: Ctrl+A в начало, Ctrl+E в конец, Ctrl+W стереть слово слева, Ctrl+U стереть всю строку. cd - возвращает в предыдущую папку, watch -n 5 'df -h /' повторяет команду каждые пять секунд.

    В конец ~/.bashrc:
    Код:
    alias ll='ls -lah'
    alias mc-log='tail -f ~/mainServer/logs/latest.log'
    alias ports='sudo ss -tulpn'
    
    Применить без перезахода - source ~/.bashrc. Смысл не в экономии символов, а в том, что длинную команду с путями не надо вспоминать через месяц.

    И про сеть. mtr (sudo apt install mtr-tiny) заменяет ping и traceroute разом и показывает, на каком узле по дороге начинаются потери: когда игроки жалуются на лаги, а сервер по всем приборам здоров, ответ обычно здесь.

    Графики и статистика

    В части 7 мы смотрели на текущее состояние машины. Здесь - про историю и про проект, а не про железо.

    Уровень первый: Plan. Player Analytics поднимает свою веб-страницу со статистикой. Один jar работает и на Paper, и на Velocity, данные кладет в SQLite или базу из части 6. График онлайна, состав аудитории и длительность сессий есть сразу.

    Веб-морда Plan по умолчанию смотрит наружу. Правило из части 3 работает и тут: вешаем на 127.0.0.1 и ходим через туннель. Порт в ufw не открываем.

    Уровень второй: Grafana. Когда нужны свои метрики рядом с метриками машины. Стек тяжелый - несколько сервисов, которые едят память и требуют ухода. Браться стоит, когда btop и алертов из части 7 перестало хватать.

    Prometheus собирает и хранит числа, Grafana рисует. Метрики машины отдает node_exporter, метрики серверов - UnifiedMetrics, он работает и на Paper, и на Velocity. Для базы из части 6 есть postgres_exporter. Дашборды с нуля не рисуют, готовые импортируются по номеру, а алерты Grafana умеют в Telegram и заменяют самописные проверки в cron.

    Графана - тоже веб-морда. Наружу не выставляем, пару admin/admin меняем сразу после установки.

    Затевают это не ради процессора и памяти, их видно и в btop. Интересны онлайн по часам и дням недели, длительность сессий, воронка входа от прокси до основного сервера и возвращаемость - сколько из вчерашних новичков зашли сегодня и сколько через неделю. Общий график этого не показывает, потому что онлайн держится за счет притока, пока старые уходят.

    Отдельный трюк - поддомены. mc, yt и vk на одном домене ведут на один прокси, Velocity видит, какой адрес игрок вбил в клиент, и метка уходит в метрику. Дальше видно, сколько человек пришло с ролика, а сколько из группы.

    Наводим порядок в терминале

    Три мелочи, которые делаются один раз и дальше просто есть.

    Доводим bash до ума

    Открываем ~/.bashrc и добавляем в конец:
    Код:
    HISTSIZE=10000
    HISTFILESIZE=20000
    HISTCONTROL=ignoreboth
    HISTTIMEFORMAT="%d.%m.%Y %H:%M:%S  "
    
    HISTSIZE и HISTFILESIZE - сколько команд помнит текущая сессия и сколько остается в файле. В Ubuntu по умолчанию 1000 и 2000, так что на живой машине история вытесняется за пару недель.

    HISTCONTROL=ignoreboth не пишет в историю повторы подряд и команды, набранные с пробела в начале - последнее выручает, когда в команде мелькает пароль. У обычного пользователя эта строка в .bashrc уже есть, а у root файл минимальный, там нет ни ее, ни размеров истории.

    HISTTIMEFORMAT добавляет к каждой команде дату и время, и это не про удобство. Без нее history показывает голый список без отметок, и когда придется разбираться, что происходило на машине (как в части 11), толку от нее не будет. Ставьте сразу, задним числом метки не появятся.

    Дополнение команд по Tab живет в отдельном пакете, на серверных образах его часто нет:
    Код:
    sudo apt install bash-completion
    
    Заработает в новой сессии, так что перезайдите по SSH.

    Убираем простыню при входе

    При каждом заходе Ubuntu вываливает пол-экрана: приветствие, ссылки на документацию, загрузку процессора, список адресов, рекламу Ubuntu Pro. Это MOTD - скрипты в /etc/update-motd.d/.

    Сначала смотрим, кто что печатает - набор отличается от версии к версии:
    Код:
    for f in /etc/update-motd.d/*; do echo "=== $f"; sudo "$f"; done
    
    Отключаем лишнее снятием флага исполнения:
    Код:
    cd /etc/update-motd.d
    sudo chmod -x 00-header 10-help-text 50-landscape-sysinfo 50-motd-news
    sudo chmod -x 90-updates-available 91-contract-ua-esm-status
    sudo chmod -x 91-release-upgrade 95-hwe-eol
    
    Каких-то файлов может не быть, это нормально. Именно chmod -x, а не удаление: удаленные вернутся при обновлении пакета, снятый флаг переживет его.

    А вот 98-reboot-required не трогайте - это та самая строка про перезагрузку. По той же причине не советую ~/.hushlogin: он глушит и строку Last login, по которой иногда замечают чужой вход.

    Строка приглашения

    Это то, что bash печатает перед каждой командой - пользователь, машина, текущая папка. По умолчанию она серая, и в длинном выводе глазом не найти, где закончилась одна команда и началась следующая. Задается переменной PS1 в ~/.bashrc каждого пользователя.

    Готовый вариант - путь синим, пользователь фиолетовым:
    Код:
    PS1='[\[\e[38;5;68m\]\w\[\e[0m\]]\[\e[38;5;141m\]\u\[\e[0m\]:\$ '
    
    Применить без перезахода: source ~/.bashrc.

    Свое собирается в генераторе с превью - советую bash-prompt-generator.org, есть также ezprompt.net и omar.io/ps1gen со справочником.

    Две вещи проверьте в строке от генератора. Цветовые коды должны быть завернуты в \[ и \], иначе при наборе длинной команды строка начнет затирать сама себя. И в конце нужен \$, а не \\$: первое подставляет # для root и $ для остальных, второе всегда рисует $.

    Обычным пользователям заготовка уже есть: в ~/.bashrc закомментирована строка force_color_prompt=yes. У root такой нет.

    Итог части

    Ничего из этого серверу не нужно - нужно Вам. Поставьте vim с подсветкой, положите конфиг tmux, заведите пару алиасов. Графики - когда появятся вопросы, на которые отвечает только история.

    Спасибо за прочтение. Вопросы и поправки пишите в комментарии, статья будет дополняться.

    Это последняя часть. Вернуться к оглавлению - в первый пост.
     
  14. Автор темы
    iForgotPassword

    iForgotPassword Активный участник Пользователь

    Баллы:
    66
    Имя в Minecraft:
    iForgotPassword
    резерв
     
  15. Автор темы
    iForgotPassword

    iForgotPassword Активный участник Пользователь

    Баллы:
    66
    Имя в Minecraft:
    iForgotPassword
    резерв
     
  16. Overwrite

    Overwrite Активный участник Пользователь

    Баллы:
    98
    Имя в Minecraft:
    OverwriteMC
    Я в шоке, оно не только живое но и даже наверное нормально написано
     

Поделиться этой страницей