Что на самом деле атакует новый сервер — и что на самом деле его отнимает
Направьте свежий IPv4-адрес в интернет — и незапрошенный трафик появится почти сразу. Не потому, что кто-то заметил именно вас: всё адресное пространство сканируется непрерывно, и хост, который отвечает на порту 22, просто присоединяется к уже идущей очереди. За первые сутки типичный новый сервер фиксирует от нескольких сотен до нескольких десятков тысяч попыток SSH-аутентификации, почти все — на root, admin, ubuntu, test и десяток других предсказуемых имён, с паролями из списка, который почти не менялся за десять лет. Это похоже на атаку. На самом деле это больше похоже на погоду.
Это различие важно, потому что именно оно определяет, какие меры заслуживают ваших пятнадцати минут. Подбор паролей в таком объёме не снижается при отключении парольной аутентификации — он исчезает полностью. Не остаётся ни остаточного риска, которым нужно управлять, ни настроек, ни частоты, которую нужно отслеживать. А вот две вещи, которые на самом деле отнимают небольшие серверы у их владельцев, куда менее эффектны: приложение, смотрящее в интернет и работающее на версии с опубликованной, уже многомесячной уязвимостью, и учётные данные, утёкшие где-то в другом месте и всё ещё подходящие здесь. Ни то ни другое не экзотика, и ни то ни другое не имеет никакого отношения к тому, на каком порту слушает SSH.
Есть и третий сценарий отказа, и на хостинге, который никогда не спрашивал, кто вы, он самый распространённый из всех. Вы сами блокируете себе доступ. У массового провайдера это выливается в надоедливый тикет в поддержку: вы отвечаете на несколько вопросов, доказываете, что вы — тот человек, что указан в биллинге, и кто-то сбрасывает вам доступ. Здесь такого пути не существует — не потому, что это осознанный и неохотный выбор политики, а потому, что личность, которую нужно было бы проверить, никогда и не собиралась. Вместо этого есть доступ, привязанный к аккаунту, а не к человеку: аварийная консоль KVM-over-VNC в панели, ежечасные снимки с хранением семь дней и возможность загрузиться с чего-то совсем другого. Этого достаточно. Но достаточно только в том случае, если вы хоть раз воспользовались этим до того, как это понадобилось по-настоящему.
Поэтому чек-лист ниже выстроен по значимости последствий, а не по моде. Сначала — меры, которые полностью устраняют целый класс атак, в конце — те, что просто снижают фоновый шум, а ещё раньше всех остальных — шаг, который ничего не стоит и спасает вечер: убедиться, что вы всё ещё можете попасть внутрь.
| Чего боятся | Как это происходит на самом деле | Что это останавливает | Что не останавливает |
|---|---|---|---|
| SSH-подбор пароля | Автоматические сканирования, тысячи в день, типовые списки имён | PasswordAuthentication no — полностью | Нестандартный порт; более длинный пароль |
| Сервис с известной уязвимостью | Сканер сверяет баннер версии спустя недели после выхода патча | Автоматические обновления безопасности плюс политика перезагрузок | Файрвол, если порт всё равно должен быть открыт |
| Переиспользованные или утёкшие учётные данные | Успешный вход с первой попытки, с адреса, которого вы никогда не видели | Аутентификация только по ключу; уникальные учётные данные для каждого сервиса | Ограничение частоты запросов — одна попытка не похожа на всплеск |
| Открытый внутренний сервис | База данных, кэш или эндпоинт метрик, привязанные к wildcard-адресу | Привязка к loopback; запрет входящих по умолчанию для обоих семейств адресов | Предположение, что сервис приватный, потому что вы никогда на него не ссылались |
| Потеря собственного доступа | Вы что-то укрепили, вышли — а изменение оказалось ошибочным | Вторая открытая сессия, снимок, проверенная консоль | Уверенность |
Проложите путь отступления, прежде чем закрывать дверь
Вот последовательность, которая оставляет людей за бортом, и при этом каждый её шаг сам по себе правилен: отредактировать конфигурацию SSH, перенести порт, отключить вход по паролю, включить файрвол, дать демону перечитать конфигурацию, закрыть терминал. Ошибка не в каком-то отдельном изменении. Она в том, что сессия, в которой вы печатаете команды, была единственным доказательством того, что хоть что-то из этого сработало, — а вы избавились от неё, так и не проверив.
Правило, которое делает весь этот чек-лист безопасным, умещается в одно предложение: держите рабочую сессию открытой и проверяйте каждое изменение из второй. Откройте новый терминал, подключитесь заново, выполните id, повысьте права через sudo. Если это сработало — изменение реально, и первую сессию можно закрывать. Если не сработало — у вас всё ещё есть оболочка, из которой можно всё откатить. Это касается файрвола, sshd, ключа нового пользователя и всего остального, что может отказать вам от двери.
Прежде всего этого настройте внеполосный доступ. В панели BitVPS у каждой строки сервера есть кнопка Консоль, которая открывает сессию KVM-over-VNC к экрану виртуальной машины. Это не оболочка по сети — это именно экран и клавиатура машины, — поэтому она продолжает работать, даже когда sshd не запускается, когда файрвол отбрасывает все входящие пакеты и когда неправильно настроена сеть. Откройте её один раз прямо сейчас, на сервере, который вы ещё не сломали. Убедитесь, что видите приглашение входа. Это займёт тридцать секунд и станет разницей между ошибкой и полноценной аварией.
Сделайте и снимок. На каждом тарифе они ежечасные, с хранением семь дней, и восстанавливаются примерно за тридцать секунд, — это превращает «я вставил не то правило nft» из вечера восстановления в простой откат. А для изменений, в которых вы по-настоящему не уверены, страховка стоит одной строки: systemd-run --on-active=10min /usr/sbin/ufw --force disable ставит таймер, который через десять минут отключит файрвол, если вы его не отмените. Если новые правила работают — вы отменяете таймер. Если они заблокировали вам доступ — машина сама впускает вас обратно, даже если вас нет рядом.
SSH: четыре директивы, которые всё решают, — и файл, который их переопределяет
Создавайте ключ на своей машине, никогда — на сервере: ssh-keygen -t ed25519 -C "laptop-2026". В 2026 году Ed25519 — правильный выбор по умолчанию: короткие ключи, быстрая проверка, никаких параметров, в которых можно ошибиться, и поддержка в любом OpenSSH, собранном за последнее десятилетие. RSA на 4096 битах не является небезопасным — он просто больше и медленнее без какой-либо выгоды. Установите пароль на приватный ключ; ssh-agent позволяет вводить его один раз за сессию, и это единственное, что стоит между украденным ноутбуком и каждым вашим сервером.
Скопируйте публичную половину на сервер с помощью ssh-copy-id и убедитесь, что можете войти по ней, прежде чем что-либо отключать. Права доступа имеют значение, и OpenSSH намеренно строго их проверяет: ~/.ssh с правами 700, authorized_keys с правами 600, оба принадлежат пользователю. Если ключ молча не работает, почти всегда дело в правах доступа, и sshd -e -d, запущенный на переднем плане, сообщит об этом одной строкой.
Дальше практически всю работу делают четыре директивы в /etc/ssh/sshd_config. Всё остальное в этом файле — дело вкуса.
| Директива | Установите в | Что она делает | Что сломается, если ошибиться |
|---|---|---|---|
PasswordAuthentication | no | Полностью убирает пароль как способ входа — весь поток подбора паролей становится не просто медленнее, а невозможным | Вы останетесь без доступа, если ваш ключ на самом деле не был установлен. Сначала проверьте — во второй сессии |
KbdInteractiveAuthentication | no | Закрывает вторую дверь в ту же комнату — интерактивный путь PAM всё ещё может принять пароль, даже если первая директива уже установлена | Ломает легитимные схемы двухфакторной аутентификации на основе PAM. Оставляйте включённой, только если вы её используете |
PermitRootLogin | prohibit-password | Root всё ещё может войти по ключу (полезно для автоматизации), но никогда — по паролю | no строже и вполне подходит — при условии, что ваш sudo-пользователь работает. Сначала проверьте это |
PubkeyAuthentication | yes | Метод, который вы оставляете. Здесь явное указание лучше значения по умолчанию | Ничего, но стоит прописать явно, чтобы будущее редактирование не убрало его незаметно |
AllowUsers / AllowGroups | ваш пользователь или группа | Необязательно, но это самая ценная необязательная строка — демон отказывает всем остальным учётным записям ещё до аутентификации | Опечатка запрещает доступ всем. Устанавливайте её последней и из второй сессии |
А теперь то, на чём попадаются даже опытные администраторы. В Debian 12, Ubuntu 22.04 и во всех более новых версиях /etc/ssh/sshd_config начинается со строки Include /etc/ssh/sshd_config.d/*.conf, а OpenSSH использует первое найденное значение для каждого параметра. Облачные образы кладут в этот каталог свои drop-in-файлы — часто такой, что устанавливает PasswordAuthentication yes, — и поскольку include стоит в самом начале, этот drop-in-файл побеждает строку, которую вы аккуратно отредактировали двумястами строками ниже. Файл утверждает одно, а демон делает другое.
Решение — никогда не доверять файлу. Спросите у самого демона, что он в итоге получил: sshd -T | grep -Ei 'passwordauth|permitrootlogin|kbdinteractive|pubkeyauth' выводит действующую конфигурацию после применения всех include, match-блоков и значений по умолчанию. Одна эта команда стоит больше, чем весь остальной раздел. Дополните её командой sshd -t, которая проверяет синтаксис и завершается с ненулевым кодом при ошибке, и только потом выполните systemctl reload ssh — перечитывание конфигурации не разрывает существующие сессии, так что даже неудачное перечитывание оставит вашу текущую оболочку в живых.
Что касается переноса SSH с порта 22: это уберёт, возможно, 95% объёма ваших логов и ноль процентов риска. Любой сканер, о котором стоит беспокоиться, проверяет все 65,535 портов, а те немногие, что этого не делают, всё равно никогда не прошли бы аутентификацию по одним лишь ключам. Переносите порт, если шум вас раздражает или мешает мониторингу, но не записывайте это в меры безопасности — а если всё же переносите, держите номер порта ниже 1024: такие порты вправе занимать только привилегированные процессы, так что непривилегированный процесс не сможет занять его, если демон вдруг остановится.
Файрвол: запрет входящих по умолчанию и та половина, о которой все забывают
У файрвола на сервере с одной задачей одна работа: сделать множество доступных портов равным множеству портов, для которых вы можете назвать причину. Запретить входящие по умолчанию, разрешить исходящие, разрешить обратно установленные и связанные соединения, а затем открыть те две-три вещи, которые вы действительно запускаете. Если у вас Debian или Ubuntu, ufw — это четыре команды, и этого достаточно; если вы предпочитаете один читаемый файл правил, nftables — современный ответ на уровне ядра и то, во что в итоге пишет сам ufw.
Порядок действий важен ровно в одном месте: разрешите свой порт SSH прежде, чем включать политику. Сначала ufw allow 22/tcp, затем ufw default deny incoming, затем ufw enable. В обратном порядке шаг включения оборвёт ту самую сессию, в которой вы печатаете команды, — это переживаемо, если вы прошли предыдущий раздел и у вас всё ещё есть консоль, а иначе это классический способ рано закончить вечер.
Забытая половина — это IPv6. Каждый сервер здесь получает маршрутизируемый /64, а сервисы по умолчанию привязываются к обоим семействам адресов. Набор правил, написанный только на iptables, управляет исключительно IPv4 и ничего не говорит про IPv6, так что тщательно защищённая файрволом база данных остаётся доступной из всего интернета по другому протоколу. ufw управляет обоими, когда установлено IPV6=yes в /etc/default/ufw (это значение по умолчанию в текущих релизах — проверьте, а не полагайтесь на это), а nftables обходит проблему структурно: набор правил table inet покрывает оба семейства одним набором правил. Если и писать вручную только одну строку конфигурации файрвола — пусть это будет она.
Затем есть Docker, который не столько обходит ваш файрвол, сколько заходит снизу, под ним. Публикация порта через -p 5432:5432 добавляет правила DNAT в таблицу nat, а они проверяются раньше цепочек фильтрации, которыми управляет ufw. В результате контейнер доступен из интернета, пока ufw status показывает порт как закрытый, и из года в год это застаёт врасплох людей, запустивших базу данных в контейнере за политикой, которую они считали запрещающей всё. Вместо этого публикуйте на loopback — -p 127.0.0.1:5432:5432 — либо поместите контейнер во внутреннюю сеть и обращайтесь к нему из другого контейнера по имени.
| Во что вы верите | Как обстоит дело на самом деле | Как проверить одной командой |
|---|---|---|
| «Файрвол запрещает всё, что я не разрешил» | Верно только для IPv4, если вы написали правила только для IPv4 | ip6tables -L -n или nft list ruleset |
| «База данных не открыта наружу, она в контейнере» | Опубликованный порт доступен независимо от политики фильтрации | docker ps --format '{{.Ports}}' |
| «На этом порту ничего не слушает» | Что-то перезапустилось и заново привязалось к нему уже после того, как вы в последний раз проверяли | ss -tulpn |
| «Порт закрыт, это подтвердило моё сканирование» | Сканирование с самой машины проверяет loopback, а не интернет | Сканируйте со второго хоста или со своего ноутбука |
Что на самом деле слушает порты — команда, которая кладёт конец догадкам
Одна команда отвечает на самый полезный вопрос об открытости сервера наружу: ss -tulpn. Она перечисляет каждый прослушивающий TCP- и UDP-сокет вместе с процессом, которому он принадлежит, и важен именно столбец локального адреса. 127.0.0.1:5432 означает, что подключиться может только эта машина. 0.0.0.0:5432 означает, что подключиться может любой адрес, способный достучаться до машины, — с единственным ограничением в виде файрвола. [::]:5432 означает то же самое, но по IPv6, и это та строка, которую обычно пробегают взглядом не глядя.
Список постоянных нарушителей всегда один и тот же. Redis, который почти всю свою историю привязывался широко и поставлялся вовсе без аутентификации. PostgreSQL и MySQL — когда конфигурационный файл правили, чтобы принимать соединения «от приложения», а потом приложение переехало. MongoDB и Elasticsearch, которые вдвоём обеспечили добрый десяток лет заголовков об утечках данных именно по этой причине. Memcached, чей UDP-слушатель стал одним из любимых усилителей трафика. Prometheus node_exporter, который с готовностью расскажет о внутреннем устройстве вашего хоста любому, кто спросит. Docker API на порту 2375 — это удалённое выполнение кода, притворяющееся REST-интерфейсом. И сервер для разработки, который кто-то запустил «буквально на минуту» в панели tmux одиннадцать месяцев назад.
Правило простое и не требует размышлений: если сервису не нужен доступ из интернета, привяжите его к loopback. Всё, для чего он лично вам нужен, продолжит работать — ssh -L 5432:127.0.0.1:5432 user@server даёт локальный порт, который туннелируется через соединение, которому вы уже доверяете, а если доступ нужен нескольким машинам, приватный интерфейс WireGuard даёт им диапазон адресов, который никогда не касается публичного интернета. Ни то ни другое ничего не стоит, и оба варианта не защищают сервис, а полностью убирают его из поверхности атаки.
Проверяйте снаружи, а не изнутри. То, что порт помечен как отфильтрованный на самой машине, — это утверждение о вашем собственном стеке loopback; то, что до порта не может достучаться вторая машина, — это уже доказательство. Подключитесь со своего ноутбука или с другого сервера и проверьте порты, которые должны быть открыты, и один, который должен быть закрыт, — тест, который не может провалиться, не является тестом.
| Сервис | Типичное значение по умолчанию | Что отдаёт открытый наружу экземпляр | Решение |
|---|---|---|---|
| Redis | Широкая привязка, исторически без пароля | Полный доступ на чтение и запись, а также известный способ записи файлов от имени пользователя redis | Привяжите к loopback; установите requirepass; держите protected-mode включённым |
| PostgreSQL / MySQL | Loopback, пока кто-нибудь это не изменит | Весь ваш набор данных, если пароль слабый или переиспользованный | Привяжите к loopback; обращайтесь через SSH или WireGuard |
| Docker API | Выключен — если только его не включил какой-нибудь туториал | Выполнение кода на хосте с правами, эквивалентными root | Никогда не открывайте его наружу; используйте вместо этого SSH-контекст |
| node_exporter / метрики | Все интерфейсы, порт 9100 | Имена хостов, точки монтирования, версии, запущенные процессы | Привяжите к loopback; собирайте метрики через приватный интерфейс |
| Отправка почты, веб-панели администрирования | Все интерфейсы — по необходимости | Поверхность для подбора паролей, которую ключи защитить не могут | Вот здесь ограничение частоты запросов действительно уместно |
Fail2ban и CrowdSec: чего они стоят, когда паролей больше нет
Это пункт, который большинство чек-листов ставят почти в самое начало, а большинство администраторов переоценивают, так что стоит быть точным. При PasswordAuthentication no подбор пароля не может увенчаться успехом — не редко, не с трудом, а никак. Блокировка адреса после пяти неудачных попыток не делает невозможную аутентификацию ещё невозможнее. На уровне SSH fail2ban даёт вам более тихий auth.log, чуть меньше нагрузки на CPU при установке соединений и графики мониторинга, которые проще читать. Это реальные преимущества. Но это эксплуатационная гигиена, а не мера безопасности, и если записать их не в ту графу, в итоге можно получить надёжно защищённый файл логов — и ни разу не пропатченное приложение.
Инструменты такого класса по-настоящему оправдывают себя на любом уровне, где всё ещё есть пароль: форма входа веб-приложения, порт отправки почты, админка Nextcloud или WordPress, сервер IMAP. Их нельзя перевести на аутентификацию по открытому ключу, так что подбор пароля остаётся возможным, и ограничение частоты запросов — это реальная защита. CrowdSec добавляет сверху общую ленту репутации адресов, что по-настоящему полезно на уровне HTTP — вы выигрываете за счёт адресов, чьё плохое поведение уже видели другие, — и почти не имеет значения на уровне SSH, если ключи обязательны.
Стоит назвать два способа выстрелить себе в ногу. Первый — заблокировать самого себя, обычно во время тестирования чего-то в три часа ночи; включите свои собственные диапазоны в ignoreip и помните, что общий адрес или адрес за carrier-grade NAT означает, что, блокируя «атакующего», можно заодно заблокировать пару сотен ни в чём не повинных людей. Второй — инструмент должен быть способен читать логи, которые он фильтрует: на дистрибутивах, которые пишут только в journal systemd, файловый бэкенд по умолчанию молча следит за файлом, в который больше ничего не попадает, а джейл сидит себе, выглядит здоровым и ничего не делает.
Честная рекомендация: ставьте, если шум вас раздражает, настраивайте на уровне приложения, где это действительно важно, и не засчитывайте его дважды. Если нужна та же тишина, но совсем без обслуживания, — директивы MaxStartups и LoginGraceTime в демоне SSH вместе с аутентификацией только по ключу закрывают большую часть этой задачи и никогда не заблокируют вам доступ к собственной машине.
Патчинг: скучная мера, которая предотвращает большинство реальных взломов
Откройте любой разбор инцидента с участием небольшого сервера, смотрящего в интернет, — и картина повторяется: у уязвимости был патч, патч был доступен неделями или месяцами, и никто его не установил. Полезное чтение здесь — каталог активно эксплуатируемых уязвимостей CISA, именно потому, что это не список теоретической опасности — это список того, что прямо сейчас эксплуатируется в реальном мире, и в нём преобладают проблемы с уже опубликованными исправлениями. Поэтому автоматические обновления безопасности — это самые окупаемые пятнадцать секунд на всей этой странице.
В Debian и Ubuntu установите unattended-upgrades, включите его командой dpkg-reconfigure -plow unattended-upgrades и убедитесь, что в /etc/apt/apt.conf.d/50unattended-upgrades выбран именно security-репозиторий. Проверьте, что он в самом деле что-то делает, командой unattended-upgrades --dry-run --debug, которая печатает, какие именно пакеты будут взяты и почему, — справочный материал здесь — страница Debian wiki. В Rocky и Alma ту же работу делает dnf-automatic с apply_updates = yes и включённым таймером. Ограничение только security-репозиторием — осознанный выбор: это конфигурация, которая почти никогда ничего не ломает, а это единственный вид автоматизации, который люди оставляют включённым надолго.
Дальше явно решите вопрос с перезагрузками, потому что именно здесь эта мера обычно даёт слабину. Установка нового пакета ядра не заменяет ядро, которое сейчас работает, а замена разделяемой библиотеки не перезапускает демоны, которые уже отобразили старую копию в память. needrestart в Debian и Ubuntu точно перечисляет, какие сервисы всё ещё держат удалённые библиотеки; обновление ядра требует перезагрузки — без вариантов, а живой патчинг (live-patching) — платная функция, которая не появится на инстансе за $8.50. Либо настройте Unattended-Upgrade::Automatic-Reboot с тихим временем Automatic-Reboot-Time, либо внесите ежемесячную перезагрузку в свой календарь и относитесь к ней как к обслуживанию, а не как к инциденту.
Наконец, знайте, чего автоматические обновления не покрывают, потому что именно в этом пробеле люди и попадаются. Всё, что установлено в обход пакетного менеджера, для него невидимо: бинарник, скачанный из релиза на GitHub, зависимость языковой среды выполнения из pip, npm или cargo, тема или плагин внутри веб-приложения и, прежде всего, образы контейнеров. Хост, который сообщает о нуле ожидающих обновлений, вполне может работать на базовом образе, собранном два года назад, — и ничто на машине вам об этом не скажет. Если вы используете контейнеры, регулярная пересборка и повторное скачивание образов по расписанию — это часть патчинга, а не отдельная задача.
| Система | Механизм | Что покрывает | Что незаметно упускает |
|---|---|---|---|
| Debian / Ubuntu | unattended-upgrades, только security-репозиторий | Пакеты дистрибутива, применяются каждую ночь | Работающее ядро и отображённые в память библиотеки — до перезагрузки |
| Rocky / Alma | dnf-automatic с apply_updates = yes | То же самое, через таймер systemd | Тот же пробел с перезагрузкой; проверяйте через dnf needs-restarting |
| Контейнеры | Ничего, по умолчанию | Ничего | Всё — образ заморожен на момент сборки |
| Зависимости языковой среды | Ничего, по умолчанию | Ничего | Реальную поверхность атаки вашего приложения |
Пользователи, sudo и двери, которые незаметно открываются заново
Создайте обычного пользователя, добавьте его в sudo или wheel, установите туда свой ключ и перестаньте входить как root. Причина не в том, что root с ключом опасен сам по себе, — это однофакторная учётная запись с универсально известным именем и без записи о том, какой именно человек ею пользовался. Именованная учётная запись плюс sudo даёт журнал аудита, позволяет работать нескольким операторам без общих учётных данных и делает вопрос «кто это запустил» в итоге отвечаемым. Однако не выдавайте ей NOPASSWD на всё подряд и не используйте затем эту же учётную запись для веб-сервиса.
Сервисы тоже не должны работать от root, и systemd делает бюджетную версию такой изоляции почти бесплатной. Drop-in-файл, созданный командой systemctl edit <unit> и содержащий NoNewPrivileges=yes, ProtectSystem=strict, ProtectHome=yes и PrivateTmp=yes, запирает скомпрометированный демон в файловой системе, доступной только для чтения, без пути к повышению привилегий, — четыре строки, описанные в документации systemd.exec, которые превращают многие уязвимости приложений из «root на хосте» в «строчку с ошибкой в логе». systemd-analyze security оценивает каждый юнит на машине и подсказывает, какие из них стоят потраченных пяти минут.
Держите authorized_keys в честном состоянии. Один ключ на устройство, у каждого — комментарий с названием устройства, а всё, что вы не можете опознать, — удаляйте. Перечитывайте файл после запуска любого инструмента provisioning, потому что cloud-init, конвейеры сборки образов и панели управления добавляют туда собственные записи, а ключ, который добавили не вы, — это ключ, который вы не можете отозвать. То же касается учётных записей: пользователь по умолчанию, оставшийся от образа, с паролем, который установил скрипт и которым никто с тех пор не пользовался, — это вход, за которым вы не следите.
Самый неочевидный пункт в этом списке — проброс агента. ForwardAgent yes позволяет процессу на удалённом хосте использовать ваш локальный приватный ключ на всё время, пока вы подключены, — именно для этого он и создан, и именно поэтому root на машине, которую вы не полностью контролируете, может одолжить ваш ключ, чтобы добраться до любого другого сервера, который этому ключу доверяет. Используйте вместо этого ProxyJump (ssh -J bastion target): он даёт ту же доступность за счёт туннелирования соединения, а ваш ключ никогда не становится пригодным для использования на промежуточном хосте. Если проброс агента всё же необходим, делайте это для конкретного хоста в ~/.ssh/config, но никогда — глобально.
Резервные копии: мера, которая превращает плохой день в потерянные полдня
Всё, что было выше, снижает вероятность плохого дня. Резервные копии определяют, во сколько этот плохой день обойдётся, а у провайдера, который намеренно не может вас идентифицировать, они важны сильнее, чем обычно: здесь нет пути через поддержку, который восстановит вашу машину из чужой копии, нет процедуры восстановления доступа к аккаунту, которая незаметно выдаст вам восстановление данных, и нет биллинговой записи с привязанным номером телефона. У вас есть ровно то, что вы сами сохранили.
Рабочая схема здесь скучна до неприличия. restic или borg, шифрование на самом сервере — так, чтобы данные были нечитаемы ещё до того, как пересекут сеть, — и отправка на вторую машину, а в идеале и во вторую юрисдикцию, что превращается из отдельного проекта в галочку, когда одна и та же панель предлагает Исландию, Нидерланды, Румынию и Швейцарию. Ежечасные снимки, включённые в каждый тариф, — по-настоящему хороший первый рубеж, и они прекрасно покрывают случай «я час назад сломал конфигурацию». Но они не покрывают файл, который вы удалили пять недель назад, и живут на той же инфраструктуре, что и то, что они защищают. Снимки и резервные копии решают разные задачи — используйте и то, и другое.
Сделайте репозиторий доступным только на дозапись (append-only). Именно эта деталь отличает резервную копию от простой копии: скомпрометированный сервер хранит учётные данные к собственной цели резервного копирования, а всему, что умеет удалять, рано или поздно прикажут удалять. И borg serve --append-only, и rest-server --append-only у restic обеспечивают это на принимающей стороне, так что машина может записывать новые снимки, но не может удалить старые. Чистку (prune) выполняйте откуда-то ещё, по расписанию, с учётными данными, которые сервер никогда не видел.
Две последние вещи, которые обычно пропускают. Пароль от репозитория не должен жить только на той машине, которую он защищает, — запишите его, положите в менеджер паролей где-то ещё, относитесь к нему как к seed-фразе, которой он по сути и является. И восстановите его хотя бы раз. Резервная копия, которую вы ни разу не восстанавливали, — это гипотеза; обычный способ узнать, что правило исключения незаметно пропустило дамп базы данных, — это восстановление, в котором вы не можете позволить себе ошибиться. Разверните инстанс Starter, восстановите резервную копию в него, проверьте данные, уничтожьте инстанс. Это стоит $8.50 и один час, и только так всё остальное на этой странице становится репетицией, а не первой попыткой.
От чего защита сервера вас не убережёт
Она не защитит диск от того, у кого физически оказался этот диск. Каждая мера на этой странице предполагает, что операционная система работает и применяет свои собственные правила; ни одна из них не действует на выключенной машине, чьё хранилище уже сняли образом. Это другая проблема с другим ответом — см. полное шифрование диска, удалённую разблокировку и что реально достаётся при изъятии, — и честный вывод в том, что защита сервера и шифрование обороняются от непересекающихся угроз. Сделав одно, вы не делаете отчасти и другое.
Она не исправит приложение, которое вы запускаете. Сервер может пройти здесь каждую проверку — и всё равно быть потерян из-за устаревшего форума, административного эндпоинта без аутентификации, шаблонизатора, вычисляющего пользовательский ввод, или токена, закоммиченного в публичный репозиторий. Защита хоста сокращает радиус поражения при компрометации приложения — именно для этого и нужна описанная выше изоляция средствами systemd, — но она не аудирует ваш код, и самый распространённый способ потерять небольшой сервер — это всё ещё что-то, что установил сам администратор.
Она не изменит того, кто знает, что вы арендовали эту машину. Идеально защищённый сервер, оплаченный с аккаунта биржи с пройденной верификацией личности и заказанный на личный email, — это идеально защищённый сервер с вашим именем на нём. Уровень безопасности и уровень идентификации независимы друг от друга, и второй решается на этапе оформления заказа, а не в командной оболочке — о том, где на самом деле возникают эти связи, рассказывает материал насколько на самом деле отслеживаем хостинг, оплаченный криптовалютой.
И она ничего не может поделать с анализом трафика. Наблюдатель, способный видеть ваши соединения, узнаёт их форму — тайминги, объём, кто с кем говорит — независимо от того, насколько хорошо настроены конечные точки. Шифрование скрывает содержимое, а не сам факт разговора, и никакая тонкая настройка sshd этого не изменит.
Чек-лист в порядке выполнения
Восемь шагов. Первые четыре — это те самые пятнадцать минут из заголовка, и они устраняют целые категории риска; последние четыре занимают больше времени и как раз отличают сервер, который вы можете защитить, от сервера, который вы просто настроили. В каждой строке указано, как понять, что шаг сработал, потому что непроверенный шаг защиты — это шаг, который вы на самом деле не сделали.
| № | Шаг | Как понять, что сработало |
|---|---|---|
| 1 | Откройте консоль в панели, убедитесь, что видите приглашение входа, сделайте снимок | Вы увидели приглашение входа на сервере, который ещё не был сломан |
| 2 | Создайте пользователя не-root, установите свой ключ ed25519, проверьте sudo | Во втором терминале вход под этим пользователем и повышение прав проходят успешно |
| 3 | Отключите парольную и keyboard-interactive аутентификацию | sshd -T сообщает passwordauthentication no |
| 4 | Файрвол с запретом входящих по умолчанию для IPv4 и IPv6 | nft list ruleset показывает оба семейства адресов; сканирование с другого хоста это подтверждает |
| 5 | Проверьте прослушивающие сокеты, перепривяжите внутренние сервисы к loopback | ss -tulpn не показывает ни одной wildcard-привязки, которую вы не можете объяснить |
| 6 | Включите автоматические обновления безопасности и политику перезагрузок | unattended-upgrades --dry-run --debug выбирает security-репозиторий |
| 7 | Изолируйте сервисы средствами systemd, приведите в порядок authorized_keys | Оценки systemd-analyze security улучшаются; у каждого ключа есть имя |
| 8 | Зашифрованная резервная копия append-only на другой площадке, хотя бы раз восстановленная | Вы восстановили её на одноразовый инстанс, и данные оказались на месте |
Ничего из этого не экзотика, и в этом весь смысл. На небольшом сервере важны именно те меры, которые устраняют целый класс проблемы, а не управляют ею: никаких паролей, никаких неожиданных слушателей, никаких непропатченных пакетов, ни одной-единственной копии данных. Всё остальное — дело вкуса, а вкус обходится дёшево, как только четыре несущих изменения внедрены и проверены.
Если вы разворачиваете машину прямо сейчас, а не читаете это заранее, вся последовательность спокойно укладывается в первую же сессию: разверните инстанс, один раз откройте консоль, чтобы убедиться, что она работает, и выполните восемь шагов по порядку ещё до того, как устанавливать хоть что-то ещё. У сервера, защищённого до того, как на нём появился хоть один сервис, нет никакого legacy, с которым приходилось бы разбираться, — это единственное преимущество нового сервера, и длится оно примерно сутки.