PunchSoldiers arrest two suspects over burning of vigilante office in PlateauESPNSources: Pulisic set to sign new AC Milan contractThe Jerusalem PostGolan urges opposition to unite with Arab parties to gain 65-seat majorityDaily MaverickFEED THE DREAM: Content creator Olivia Viglatzis fights hunger one budget meal at a timeBollywood HungamaRamayana producer Namit Malhotra calls Los Angeles premiere his “Oscar moment” ahead of November 5 releaseEl ComercioLa hoja de ruta de Estados Unidos para Venezuela: reformas, vacancia de Delcy Rodríguez y eleccionesInquirerManila Water Philippine Ventures boosts N. Luzon supply for El NiñoZDF heuteEntdecken Sie das ZDF-NachrichtenstudioNU.nlTientallen servers Limburg ingenomen om verspreiding beelden kindermisbruikWirtualna PolskaKatastrofa kolejowa w Hiszpanii. Są nagrania z miejsca wypadkuRai NewsSpagna, scontro tra treni, precipitano vagoni: decine di morti e centinaia di feritiRTL BoulevardKristen Bell had bijna-doodervaring bij opnames Nobody Wants This
The Daily Newsstand · Free, Always
Wednesday, October 28, 2026

Агент с root на моём VPS: как не дать ему отрезать себе доступ

Translate

Агент только что поставил fail2ban и проверяет работу. Вход по ключу есть. По паролю нет, так и задумано. Ещё пара проверок для верности, и где-то на пятой неудачной попытке fail2ban банит IP, с которого всё это идёт. Агента. И вас заодно, потому что агент сидит на вашем ноутбуке.

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

В моём бенчмарке Claude Opus 5.5 без подсказок так банит сам себя в 7 прогонах из 60. Модель умная, просто ей никто не сказал, что свой IP надо добавить в ignoreip до первого запуска fail2ban.

Из таких мелочей и вырос vps-forge. Это открытый Agent Skill: инструкции и несколько bash-скриптов, которые Claude Code, Codex, Cursor и другие агенты подгружают, когда речь заходит о сервере. Расскажу про главную часть, автоматический откат, про грабли по дороге и про бенчмарк, включая неудобные цифры.

Почему «будь осторожен» не работает

Сломанный sshd_config обычно выглядит как рабочий. sshd -t молчит, reload проходит, текущая сессия жива. А новый вход уже не пускает: AllowUsers deploy там, где вы admin, или AllowGroups, в которую забыли добавить себя.

У агентов есть своя ловушка. Они часто держат одно подключение через ControlMaster, и проверка ssh host true идёт по старому каналу. Новый sshd и новые правила файрвола она не трогает. Проверка зелёная, войти нельзя.

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

Педаль машиниста для sshd

Перед каждой правкой SSH, файрвола, sudo или PAM агент делает vps-forge-deadman arm. Скрипт снимает снапшот конфигов и заводит systemd-таймер на 5 минут. Агент меняет конфиг, заходит заново через ssh -o ControlPath=none и, если вход прошёл, делает disarm. Если не прошёл, ему ничего делать не надо. Через 5 минут таймер вернёт всё как было.

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

Где откат оказался хуже ошибки

Перезагрузка. Таймер из systemd-run живёт в памяти. Сервер перезагрузился, таймера нет, отката нет. Теперь arm ещё и включает юнит, который при загрузке видит «взведено, но не снято» и откатывает сам.

nftables. Если сервис nftables до arm был выключен, откат его останавливал. Логично, пока не посмотришь в ExecStop: там nft flush ruleset. Вместе с откатываемым правилом улетали правила ufw, баны fail2ban и цепочки Docker. Теперь удаляются только таблицы из откатываемого конфига.

ufw по-русски. Скрипт проверял, включён ли ufw, по строке Status: active. На русской локали ufw пишет «Состояние: активен». Скрипт решал, что файрвол был выключен, и при откате выключал его. Неудачная правка файрвола заканчивалась сервером без файрвола вообще. Лечится одной строкой LC_ALL=C, но заметить это можно только на неанглийской системе.

Бан никуда не девается. Конфиги вернулись, а IP всё ещё в бане. Поэтому arm запоминает адрес SSH-клиента, а откат его разбанивает. Под sudo переменной SSH_CLIENT уже нет, так что адрес приходится доставать из окружения родительского процесса.

Всё это теперь гоняется в CI: контейнер с настоящим systemd, тест ломает sshd, файрвол и fail2ban, перезагружает контейнер во взведённом состоянии и проверяет, что всё вернулось.

Файрвол в панели провайдера откатить нельзя, он не на сервере. Там работает только порядок: сначала добавить новое правило, проверить вход, потом убрать старое.

Не верите? Сломайте

Агент не нужен, это обычный bash-скрипт. На сервере, который не жалко, держите текущую сессию открытой:

curl -fsSLO https://raw.githubusercontent.com/mountain-beaver/vps-forge/v1.1.1/skills/vps-forge/scripts/deadman.sh
less deadman.sh
sudo install -m 755 deadman.sh /usr/local/sbin/vps-forge-deadman
sudo vps-forge-deadman arm 1
echo 'AllowUsers deploy' | sudo tee /etc/ssh/sshd_config.d/50-users.conf
sudo sshd -t && sudo systemctl reload ssh

Новый вход (ssh -o ControlPath=none you@server) больше не пускает. Через минуту sudo vps-forge-deadman status в старой сессии пишет ROLLED BACK, файла нет, вход снова работает.

Настоящая запись, только ожидание сокращено

Настоящая запись, только ожидание сокращено

Грабли из облачных образов

Самая коварная: cloud-init. На многих VPS лежит sshd_config.d/50-cloud-init.conf с PasswordAuthentication yes. sshd берёт первое прочитанное значение, файлы читает по алфавиту. Так что популярный совет «положите свой 99-hardening.conf» не делает ничего, пароль как работал, так и работает. Файл навыка называется 00-vps-forge.conf, а результат проверяется через sshd -T.

Ещё несколько. В Debian 12 нет auth.log, и fail2ban по умолчанию просто нечего читать. Docker публикует порты в обход ufw, и ufw status об этом молчит. В Oracle Cloud в образе своё правило REJECT в iptables. А Coolify ходит на свой же хост по SSH под root, и строгий PermitRootLogin no ломает следующий деплой.

Бенчмарк

Стенд: настоящие ВМ в QEMU, Debian 12 и 13, Ubuntu 22.04, 24.04 и 26.04. Четыре сценария, от «сделай продакшен под Docker» до «перенеси SSH и поставь fail2ban». Каждый по три раза без навыка и с ним, всего 120 прогонов Opus 5.5. Пользователь в промпте офлайн, агент решает сам.

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

Без навыка выходит от 79 до 92 баллов из 100, с навыком 99 или 100.

Без навыка

С навыком

ssh-audit без провалов

4 / 60

60 / 60

Оповещение о входе по SSH

16 / 30

30 / 30

Файрвол включён

46 / 60

60 / 60

Перебор паролей банится

50 / 60

60 / 60

Root не может войти

30 / 60

57 / 60

Агент забанил сам себя

7 / 60

0 / 60

Потеря доступа

0 / 60

0 / 60

Каждая проверка, которая различается, по прогонам

Каждая проверка, которая различается, по прогонам

Последняя строка важная. Доступ не потерял никто, и без навыка тоже. То есть сам откат бенчмарк не доказал: Opus аккуратен и так. Это страховка на редкий случай, и в 120 прогонах он не выпал.

Что эти цифры не значат

Лучше сказать заранее.

Opus и без навыка хорош. Нетронутый сервер получает от 25 до 42 баллов, агент без подсказок от 79 до 92.

Около 70% разницы дают три проверки, которые совпадают с настройками самого навыка: чистый ssh-audit, запрет root и оповещение о входе. Без них на новых VPS выходит 100 на 100. Честный остаток: fail2ban, который реально банит на работающем сервере (6 из 15 без навыка, 15 из 15 с ним), включённый файрвол и ноль самобанов.

Судью и навык писал один человек. Навык целится в ssh-audit, судья его запускает. Независимый Lynis почти не двигается: 69 и 71, 72 и 72, 64 и 72, 73 и 72.

Сценарии не слепые, первые три использовались, пока навык дорабатывался. Вопросы пользователю не тестировались, все прогоны в режиме «я офлайн». Навык медленнее и примерно втрое дороже: $1.87 против $0.62 за прогон. И модель одна, Opus 5.5, плюс четыре ручных прогона GLM-5.3.

Стенд и сценарии лежат в bench/. Прогоните на другой модели, будет интересно.

Как попробовать

npx skills add mountain-beaver/vps-forge -g

Для Claude Code есть плагин, для Claude.ai zip. Дальше пишете агенту обычным языком: «У меня новый VPS на 203.0.113.10, захожу под root. Сделай нормальный продакшен-сервер под Docker, с бэкапами и оповещениями на телефон».

Один совет: убедитесь, что перед первой правкой SSH агент выполнил vps-forge-deadman arm. Если нет, навык не подгрузился, и страховки нет.

Чего не ждать

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

Код под MIT, без телеметрии: github.com/mountain-beaver/vps-forge. Если найдёте, как навык может закрыть доступ, пишите в issue. Вопросы в комментариях, отвечу.

View the original on Хабр →

KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.