17 октября 2026 года на части узлов виртуализации FirstVDS работала программа, которая подключала диски клиентских VPS в режиме «только чтение» и копировала с них пароли, SSH-, API- и облачные ключи, токены, конфигурационные файлы и историю команд. Так сам хостер описал инцидент в заявлении в блоге. ForestsNet Cloud сообщила о втором канале утечки через ту же уязвимость VMmanager 6: из базы панели ушли root-пароли, заданные через панель, и пароли VNC-консоли (заявление ForestsNet). Вендор выпускает исправления, FirstVDS пока сохраняет ограничения доступа к панели, но обновления не отзывают уже украденное: скопированные секреты остаются рабочими, пока владелец сервера их не заменит и не отзовёт.
Ниже — порядок ротации, который наша редакция собрала по рекомендациям FirstVDS, ForestsNet и ISPsystem и по официальной документации OpenSSH, GitHub, GitLab и облаков. Команды — для Ubuntu 22.04/24.04 и Debian 12. Первичную настройку — пользователя, первый ключ, Fail2ban и UFW — мы разбирали в гайде «Как настроить SSH на VPS», здесь только замена и отзыв. Хронология атаки и список пострадавших хостеров — в нашей новости о взломе FirstVDS через VMmanager. Факты приводим на 21.10.2026.
Что считать скомпрометированным после утечки
У FirstVDS правило жёсткое: если в личном кабинете пришло уведомление, исходите из того, что данные с сервера скопированы. Отсутствие письма означает только то, что на узле не нашли следов атаки, а не гарантию. ForestsNet советует клиентам других хостеров на VMmanager 6 пройти те же шаги, не дожидаясь писем.
Утечка шла двумя путями, и от пути зависит список того, что придётся менять.
| Откуда утекло | Что могло уйти | Что делать |
|---|---|---|
| Диск VPS (копирование с узла виртуализации) | Закрытые ключи в ~/.ssh, хеши паролей из /etc/shadow, .env и конфиги приложений, ~/.bash_history, ~/.aws/credentials, ~/.docker/config.json, ~/.git-credentials, ~/.npmrc, host keys в /etc/ssh/ssh_host_* |
Перевыпустить всё, отозвать старое в источнике, заменить host keys |
| База панели VMmanager | Список серверов и IP, email учётной записи, root-пароли, заданные при заказе, переустановке или смене через панель, пароли VNC | Сменить root- и VNC-пароль, перейти на вход только по ключу |
| Ваш ноутбук или рабочий ПК | Закрытый SSH-ключ, который хранился только на рабочем компьютере, не мог быть скопирован с диска VPS. Другие общие секреты, лежавшие и на сервере, — могли | Ключ лучше всё равно заменить и проверить authorized_keys на всех серверах |
Публичная часть ключа из authorized_keys секретом не считается: по ней нельзя войти. Опасен закрытый ключ. Если он лежал на сервере — для git pull, выгрузки бэкапов или перехода на соседние машины, — с ним можно войти везде, где стоит его пара. Хеши из /etc/shadow атакующий перебирает офлайн и без ограничения по времени, так что короткие пароли пользователей стоит считать раскрытыми.
Перечень целей сборщика у SecurityLab шире: базы KeePass и ключи GPG, пароли и cookies браузеров, настройки Docker, строки подключения к PostgreSQL, MySQL, MongoDB и Redis, пароли репозиториев Restic и Borg, креды AWS, GCP, Azure и OpenStack, конфиги Kubernetes и Terraform, токены GitHub, GitLab и npm, ключи OpenAI и Anthropic. Список того, что лежит именно у вас, удобно собрать с самого сервера:
sudo find /root /home /etc /opt /srv /var/www \( -name 'id_rsa' -o -name 'id_ecdsa' -o -name 'id_ed25519' \
-o -name '*.pem' -o -name '.env' -o -name '.git-credentials' -o -name '.npmrc' -o -name '.pgpass' \
-o -name '.my.cnf' -o -name 'wp-config.php' -o -name '*.kdbx' -o -path '*/.aws/credentials' \
-o -path '*/.docker/config.json' -o -path '*/.kube/config' \) 2>/dev/null
sudo find /root /home -maxdepth 2 -type f -name '.*_history' -exec grep -HiE 'pass(word)?=|token|secret|api[_-]?key' {} +
Вторая команда ищет пароли и токены, введённые прямо в командной строке, во всех историях оболочки, включая закрытые домашние каталоги. История команд была в списке скопированного у FirstVDS, так что найденное в ней тоже идёт в ротацию. Пустой результат не означает, что секретов нет: шаблон ловит не все способы записи паролей и токенов.

Сначала убедиться, что на сервере нет чужих
Новые секреты на сервере с закладкой уйдут тем же путём, что и старые. FirstVDS советует, если атакующий мог войти до смены доступов, проверить ~/.ssh/authorized_keys, новых пользователей, cron и systemd-сервисы, а заодно журналы входов после 17 октября. ForestsNet пишет о заражении панелей с 11 октября, поэтому журнал берём с этой даты:
sudo last -a | head -30
sudo journalctl -u ssh --since "2026-10-11" | grep -E 'Accepted|Failed' | tail -50
awk -F: '$3 == 0 {print $1}' /etc/passwd
getent passwd | awk -F: '$7 !~ /(nologin|false)$/ {print $1, $6, $7}'
sudo ls -la /etc/cron.d /var/spool/cron/crontabs
systemctl list-unit-files --type=service --state=enabled
Это первичная проверка. Ищите входы с незнакомых IP, второго пользователя с UID 0, чужие задания cron и службы с правдоподобными именами вроде «system-update». Senko Digital в постмортеме описывает у атакующих фейковые системные службы и бэкдоры. Если явных признаков не нашлось, чистота системы этим не подтверждается: следы могли удалить, а вредоносная служба может быть не включена в автозапуск. Если что-то подобное нашлось, менять ключи на этой системе бессмысленно: MHost советует переразвернуть сервер и восстановиться из проверенного бэкапа, а ForestsNet предупреждает, что одной переустановки мало и секреты всё равно нужно ротировать. Как держать такие копии отдельно от сервера — в гайде по бэкапам VPS и в разборе, чем снапшот не заменяет бэкап.
Для каждого секрета дальше действует одна операция из четырёх шагов: выпустить новый, подключить его там, где он используется, проверить работу, отозвать старый. Окно между последними двумя шагами держите минимальным — минуты, а не дни.
Шаг 1. Новый SSH-ключ ed25519 на чистой машине
Ключ создают на своём ноутбуке или рабочем ПК, где нет признаков заражения, и никогда на пострадавшем сервере. Команда одна и та же для Linux, macOS и PowerShell в Windows:
ssh-keygen -t ed25519 -a 100 -C "admin@laptop-2026-10-21" -f ~/.ssh/id_ed25519_2026
ssh-keygen -lf ~/.ssh/id_ed25519_2026.pub
Тип -t ed25519 указываем явно. По release notes OpenSSH, ssh-keygen создаёт Ed25519 по умолчанию только с версии 9.5 (октябрь 2023). В Ubuntu 22.04 стоит OpenSSH 8.9, в Debian 12 — 9.2, и без флага там получится RSA. В Ubuntu 24.04 версия 9.6, флаг не помешает.
Флаг -a задаёт число раундов KDF при шифровании закрытого ключа. По man ssh-keygen, больше раундов — медленнее проверка пароля и выше стойкость к перебору, если файл ключа украдут. Значение по умолчанию зависит от версии OpenSSH, поэтому в команде оно задано явно. Комментарий -C с именем, устройством и датой нужен для следующего шага: по нему старые и новые ключи в authorized_keys различаются с первого взгляда.
Passphrase при генерации обязателен. Без него закрытый ключ, скопированный вместе с диском, работает сразу. Чтобы не вводить пароль при каждом входе, ключ добавляют в агент: ssh-add ~/.ssh/id_ed25519_2026. Ключи для разных задач делайте разными: личный вход, deploy key для каждого репозитория, ключ для CI. GitHub в документации по deploy keys прямо запрещает использовать один deploy key в нескольких репозиториях.
Шаг 2. Замена ключа в authorized_keys на всех серверах
Новый ключ добавляют, пока старый ещё пускает на сервер, и проверяют вход в отдельном окне терминала. Текущую сессию не закрывайте до конца шага:
ssh-copy-id -i ~/.ssh/id_ed25519_2026.pub user@203.0.113.10
ssh -F /dev/null -S none -o PreferredAuthentications=publickey -o IdentitiesOnly=yes -i ~/.ssh/id_ed25519_2026 user@203.0.113.10
Флаги во второй строке отключают пользовательский и системный конфиги (-F /dev/null), уже открытое мультиплексированное соединение (-S none) и вход по паролю (man ssh, man ssh_config). Если вход прошёл, сервер пустил именно по новому ключу.
Если серверов много, тот же ssh-copy-id прогоняют циклом по алиасам из ~/.ssh/config: for h in web1 db1 backup1; do ssh-copy-id -i ~/.ssh/id_ed25519_2026.pub "$h"; done. В Windows команды ssh-copy-id нет, там ключ добавляют вручную — как это сделать, показано в гайде «Как подключиться к серверу по SSH».
Дальше аудит: какие ключи вообще пускают на сервер и каким пользователям. Команда выводит отпечаток и комментарий каждой строки всех файлов authorized_keys:
sudo sh -c 'for f in /root/.ssh/authorized_keys* /home/*/.ssh/authorized_keys*; do [ -f "$f" ] && echo "== $f" && ssh-keygen -lf "$f"; done'
sudo sshd -T | grep -i authorizedkeys
Вторая строка показывает, откуда sshd на самом деле читает ключи. По man sshd_config, по умолчанию это .ssh/authorized_keys и .ssh/authorized_keys2. Лишний путь или AuthorizedKeysCommand, которых вы не ставили, требуют проверки: выясните, кто это настроил — образ, провайдер или другой администратор — и какие ключи этот источник разрешает.
Удалять старый ключ надёжнее по его публичной части, а не по комментарию: комментарий может совпасть у разных ключей, а тот же ключ мог попасть в файл с другой подписью. Сначала на своей машине посмотрите отпечаток старого ключа (ssh-keygen -lf ~/.ssh/id_ed25519.pub) и сверьте его с выводом аудита выше. Затем на сервере, под тем пользователем, чей файл правите:
cp ~/.ssh/authorized_keys ~/.ssh/authorized_keys.bak-2026-10-21
OLD='AAAAC3NzaC1lZDI1NTE5AAAA...' # вторая колонка из старого .pub-файла
grep -vF "$OLD" ~/.ssh/authorized_keys.bak-2026-10-21 > ~/.ssh/authorized_keys
ssh-keygen -lf ~/.ssh/authorized_keys
Команда убирает все вхождения этого ключа, с любыми комментариями и опциями. Повторите её для каждого пользователя и каждого сервера, а заодно для дополнительных путей, если аудит их показал. На серверах FirstVDS по умолчанию есть SSH-ключ поддержки с адресами 85.198.118.171 и 85.198.75.83. Компания заявляет, что этот ключ не скомпрометирован: он хранится на отдельном сервере с шифрованием. Оставлять его или нет — решение владельца; удаление ключа отключит доступ поддержки по этому ключу.
Ключи живут не только на серверах. Если провайдер подставляет ключ из личного кабинета при переустановке ОС, замените его и там, иначе старый вернётся вместе с новой системой.

Шаг 3. Запрет входа по паролю и PermitRootLogin
Пароли root из базы панели — главный риск для тех, у кого до дисков дело не дошло. ForestsNet и Senko советуют перейти на вход по SSH-ключам и отключить вход по паролю. По man sshd_config, PasswordAuthentication и KbdInteractiveAuthentication по умолчанию включены, а PermitRootLogin стоит в prohibit-password: root входит только по ключу.
Настройки удобно вынести в отдельный файл. В Ubuntu строка Include /etc/ssh/sshd_config.d/*.conf стоит в начале sshd_config (документация Ubuntu Server), а sshd берёт первое найденное значение каждого параметра. По man sshd_config, маска в Include раскрывается в лексическом порядке, так что файл 00-after-leak.conf обычно читается раньше остальных и перекрывает, например, оставленный образом PasswordAuthentication yes. Проверьте ls /etc/ssh/sshd_config.d/: имён, которые сортируются выше, быть не должно.
sudo tee /etc/ssh/sshd_config.d/00-after-leak.conf >/dev/null <<'EOF'
PasswordAuthentication no
KbdInteractiveAuthentication no
PubkeyAuthentication yes
PermitRootLogin prohibit-password
EOF
sudo sshd -t && sudo systemctl restart ssh
sudo sshd -T -C user=deploy,addr=198.51.100.7,host=client.example | grep -E '^(passwordauthentication|kbdinteractiveauthentication|permitrootlogin|pubkeyauthentication) '
sudo grep -rni '^ *match' /etc/ssh/sshd_config /etc/ssh/sshd_config.d/
Блоки Match могут переопределять глобальные настройки для отдельных пользователей и адресов. С ключом -C sshd применяет подходящие Match для заданных параметров подключения (man sshd), поэтому проверяйте вывод для своих реальных пользователей и адресов. В выводе должны быть passwordauthentication no и kbdinteractiveauthentication no — одного запрета паролей мало.
Если вы входите под обычным пользователем с sudo, ставьте PermitRootLogin no. После перезапуска проверьте вход новым ключом в новом окне и только потом закрывайте старую сессию. ISPsystem требует пускать по SSH к узлам VMmanager только доверенные адреса, а гендиректор RUVDS Никита Цаплин (Forbes) советует клиентам то же самое для их серверов. В UFW это одно правило вида sudo ufw allow from 198.51.100.7 to any port 22 proto tcp, после которого общее разрешение на 22-й порт нужно удалить. Как не отрезать себе доступ при этом, мы разбирали в гайде по файрволу на Linux, а защиту от перебора — в статье про Fail2ban.
Шаг 4. Host keys сервера: когда перевыпускать
Host keys в /etc/ssh/ssh_host_*_key подтверждают клиенту, что он подключается к вашему серверу. С украденным закрытым host key атакующий может выдать свою машину за ваш сервер при перехвате трафика, и клиент не увидит предупреждения. Перевыпуск нужен, если хостер сообщил о копировании диска или сервер разворачивали из образа, который мог утечь. Если утекла только база панели, host keys там не было, но замена занимает минуту и вреда не принесёт.
sudo mkdir -p /root/hostkeys-old-2026-10-21
sudo mv /etc/ssh/ssh_host_* /root/hostkeys-old-2026-10-21/
sudo ssh-keygen -A
sudo sshd -t && sudo systemctl restart ssh
for f in /etc/ssh/ssh_host_*_key.pub; do ssh-keygen -lf "$f"; done
Старые ключи сначала убираем: по man, ssh-keygen -A создаёт host keys всех типов по умолчанию, только если их ещё нет. Уже открытая сессия перезапуск sshd переживает. Отпечатки из последней команды сохраните — с ними клиенты сверятся при первом подключении. Когда новые ключи проверены, папку со старыми удалите: держать скомпрометированные ключи на диске незачем.
На всех машинах, откуда ходят на сервер, появится предупреждение «REMOTE HOST IDENTIFICATION HAS CHANGED». Это ожидаемо. Старую запись удаляют по имени и по IP; если SSH висит на нестандартном порту, запись хранится в виде [server.example.com]:2222. Затем подключаются заново:
ssh-keygen -R server.example.com
ssh-keygen -R 203.0.113.10
ssh-keygen -R '[server.example.com]:2222'
ssh user@server.example.com
При подключении ssh покажет отпечаток нового host key и спросит, доверять ли ему. Отвечайте «yes» только после сверки с отпечатком, полученным по доверенному каналу: из уже открытой сессии на сервере или из консоли в личном кабинете хостера.
Обновите known_hosts и в автоматике: в секретах CI, в Ansible, на серверах бэкапов.
Шаг 5. Пароли root, пользователей, VNC и баз данных
FirstVDS просит сменить все пароли, которые хранились на сервере: root и пользователей, баз данных, панелей сайтов, почты, облаков, CI/CD и платёжных систем. Длинный случайный пароль генерируется одной командой, а задаётся интерактивно, чтобы не попасть в историю:
openssl rand -base64 24
sudo passwd root
sudo passwd deploy
Пароль VNC-консоли меняют в панели хостера. У FirstVDS панель VMmanager для клиентов закрыта «скорее всего, надолго», и управление переносится в личный кабинет, так что порядок смены VNC-пароля уточните у поддержки.
В MySQL пароль меняют командой ALTER USER 'app'@'localhost' IDENTIFIED BY 'новый_пароль'; — для каждого пользователя, включая root и учётки с хостом %. В MySQL 8 есть двойные пароли: если когда-то меняли пароль с RETAIN CURRENT PASSWORD, старый остаётся рабочим. По документации MySQL, его снимает ALTER USER 'app'@'localhost' DISCARD OLD PASSWORD;.
В PostgreSQL удобнее команда \password имя_роли внутри sudo -u postgres psql. Документация PostgreSQL предупреждает, что пароль в ALTER ROLE ... PASSWORD уходит на сервер открытым текстом и может осесть в истории клиента и логе сервера, а \password этого избегает. Установку и доступы PostgreSQL мы разбирали в отдельном гайде.
Шаг 6. Отзыв токенов и ключей в GitHub, GitLab, облаках и сервисах
Новый токен без отзыва старого ничего не защищает. Каждый ключ из таблицы ниже проходит полный цикл за один подход: выпустить новый, прописать его в .env, CI и конфиги и перезапустить сервис (как — в шаге 7), проверить работу и только потом отозвать старый. GitHub в руководстве по утёкшим секретам называет отзыв у провайдера главным шагом: удаление секрета из кода не мешает им пользоваться. Чистка истории git идёт последней. FirstVDS и ITLDC отдельно просят проверить копии старых ключей в скриптах, CI/CD и агентах.
| Сервис | Что отозвать | Где |
|---|---|---|
| GitHub | SSH-ключи аккаунта, deploy keys, personal access tokens | Settings → SSH and GPG keys → Delete; репозиторий → Settings → Deploy keys; Settings → Developer settings → Personal access tokens (SSH, deploy keys, PAT) |
| GitLab | Personal access tokens, deploy keys, переменные CI/CD | Edit profile → Access → Personal access tokens → Revoke или Rotate; проект → Settings → Repository → Deploy keys → Disable (PAT, deploy keys) |
| AWS | Access keys пользователей IAM | IAM → Users → Security credentials: создать новый ключ, старый Deactivate, затем Delete; в CLI — aws iam update-access-key и aws iam delete-access-key (документация) |
| Google Cloud | Ключи сервисных аккаунтов | gcloud iam service-accounts keys disable KEY_ID --iam-account=..., затем удаление (документация) |
| Yandex Cloud | Статические ключи доступа и API-ключи сервисных аккаунтов | IAM → Сервисные аккаунты → ключ → Удалить; yc iam access-key list и yc iam access-key delete, yc iam api-key delete (статические ключи, API-ключи) |
| npm | Токены публикации из ~/.npmrc |
npm token list, затем npm token delete ID (документация) |
| Docker Hub | Personal access tokens из ~/.docker/config.json |
Настройки аккаунта: токен можно деактивировать или удалить в любой момент (документация) |
| Telegram-боты | Токен бота из конфига | Команда /token в BotFather выпускает новый токен, если старый скомпрометирован (документация) |
Несколько деталей легко пропустить. GitLab при Rotate сразу делает старый токен неактивным. AWS советует сначала деактивировать старый ключ и только потом удалять. В Google Cloud отключение ключа не отзывает уже выданные по нему короткоживущие учётные данные — для этого придётся отключать или удалять сам сервисный аккаунт. У npm отзыв в отдельных случаях вступает в силу с задержкой до часа. На GitHub удаление PAT, через который создавали deploy key, удаляет и этот ключ.
После отзыва проверьте журналы источников. У GitHub это Settings → Security log за последние 90 дней с событиями public_key, personal_access_token и oauth_access (документация). GitLab в руководстве по инцидентам советует искать в audit events новые токены, SSH-ключи, пользователей и изменения переменных CI/CD. У платёжных и ИИ-сервисов ключи отзываются в кабинете разработчика по той же схеме: выпустить новый, подключить, удалить старый. 1С-Битрикс 18 октября превентивно перевыпустил client_secret OAuth для потенциально затронутых приложений Маркета и советует владельцам интеграций сверить статистику REST-запросов с 17 октября.
Секреты CI тоже обновляются в источнике. В GitHub Actions это Settings → Secrets and variables → Actions или gh secret set ИМЯ (документация). Токен раннера GitLab хранится на хосте в config.toml: если он лежал на пострадавшем сервере, удалите раннер в настройках CI/CD и зарегистрируйте заново.
Шаг 7. Новые секреты в .env и перезапуск сервисов
Этот шаг выполняется внутри цикла из шага 6 для каждого ключа, до отзыва старого. Приложение держит старые значения в памяти, пока процесс не перезапущен: новый ключ в .env без рестарта не подхватится. Случайные строки для своих секретов (ключи сессий, JWT, внутренние токены) генерирует openssl rand -hex 32. Дальше — перезапуск по типу сервиса:
sudo systemctl restart myapp.service
docker compose up -d --force-recreate
sudo systemctl restart 'php*-fpm'
Для Docker Compose нужен именно up. По документации Docker, docker compose restart не подхватывает изменения конфигурации и переменных окружения, а up -d --force-recreate пересоздаёт контейнеры с сохранением томов. Как ставить и обновлять Docker, мы описывали в гайде по установке Docker на Ubuntu и Debian.
Для WordPress мало сменить пароль базы в wp-config.php. Ключи и соли AUTH_KEY, SECURE_AUTH_KEY и остальные тоже заменяют на новые из генератора WordPress. По документации, это делает недействительными все текущие cookies, и пользователям придётся войти заново. Отдельно меняют пароли администраторов и Application Passwords.
Пароли Restic и Borg были в списке целей сборщика, так что доступы к бэкапам входят в срочную ротацию: пароль репозитория, ключи S3 или SSH-ключ сервера бэкапов. Смена passphrase в Borg не меняет ключи шифрования: по документации Borg, если утекли и passphrase, и ключ, смена пароля не защищает ни прошлые, ни будущие копии в том же репозитории. В таком случае заводите новый репозиторий с новым ключом. Задания cron после ротации проверьте отдельно — как смотреть их вывод, показано в гайде по cron.
Чек-лист ротации по порядку
| № | Действие | Как проверить, что готово |
|---|---|---|
| 1 | Перевести криптовалюту на новые кошельки, созданные на чистом устройстве, если на сервере были кошельки или seed-фразы | Средства на новых адресах; смена пароля кошелька не помогает (FirstVDS, ForestsNet) |
| 2 | Проверить входы, пользователей, cron, systemd и authorized_keys |
Явных признаков не обнаружено (чистота этим не подтверждается); если есть — переустановка из проверенного бэкапа |
| 3 | Собрать список секретов на сервере (find, история команд) |
Есть перечень: что, где, у какого сервиса отзывать |
| 4 | Создать новый SSH-ключ ed25519 с passphrase на чистой машине | ssh-keygen -lf показывает новый ключ с комментарием и датой |
| 5 | Добавить новый ключ на все серверы, удалить старые и лишние | Вход новым ключом работает, в ssh-keygen -lf authorized_keys только нужные ключи |
| 6 | Отключить вход по паролю, настроить PermitRootLogin, ограничить SSH по IP | sshd -T -C ... показывает passwordauthentication no и kbdinteractiveauthentication no |
| 7 | Перевыпустить host keys, обновить known_hosts у клиентов и в CI | При реальном подключении отпечаток совпал с полученным по доверенному каналу |
| 8 | Сменить пароли root, пользователей, VNC, баз данных | Старые пароли не принимаются; в MySQL нет вторичного пароля |
| 9 | Для каждого токена и ключа (GitHub, GitLab, облака, npm, Docker Hub, боты, платёжные сервисы): выпустить новый → прописать в .env, CI и конфиги → перезапустить сервис → проверить → отозвать старый |
Приложения работают на новых ключах, старые удалены или неактивны, журналы источников проверены |
| 10 | Сменить доступы к бэкапам; для Borg при утечке ключа — новый репозиторий | Бэкапы пишутся с новыми доступами, старые не принимаются |
| 11 | Если на сервере были персональные данные клиентов — консультация юриста | Решение по уведомлению принято (FirstVDS) |
Ошибки, из-за которых старый доступ продолжает работать
- Ключ сгенерировали на пострадавшем сервере. Если на нём осталась закладка, новый закрытый ключ уйдёт вслед за старым.
- Правка
sshd_configне применилась. Значение из файла вsshd_config.dпрочитано раньше или его переопределил блок Match. Итог для конкретного пользователя и адреса показываетsshd -T -C. - Закрыли сессию до проверки. Вход по паролю уже выключен, новый ключ не подошёл — остаётся консоль в личном кабинете хостера.
- Забыли автоматику. Старые ключи остаются в known_hosts CI, в секретах GitHub Actions, в скриптах бэкапов и на соседних серверах.
Что сделать сегодня, а что можно отложить
Сегодня — пункты 1–11 чек-листа: всё, что касается предположительно утёкших доступов, включая бэкапы и ограничение SSH по IP. Никита Цаплин из RUVDS называет инцидент утечкой, а не поломкой, и предупреждает, что украденные ключи могут «всплыть» позже, так что каждый день со старыми ключами работает на атакующего. Отложить на неделю можно только то, что не закрывает утечку: перенос секретов из .env в менеджер секретов и тестовое восстановление из бэкапа.
Если после инцидента решите сменить провайдера, сравните предложения в каталоге VPS и в подборке защищённых VPS-хостингов. Ротацию закончите до переезда, чтобы на новый сервер не попали старые секреты, а сам перенос без простоя описан в гайде о переносе сервера к другому провайдеру.