Договор закрыт, акт подписан, пароль от сервера сменили, и все выдохнули. Но у подрядчика на руках, возможно, остался ключ, который он сам же и добавил в authorized_keys. В репозитории лежит токен, выпущенный «на пару дней». А в планировщике сидит задание, которое когда-то запускало миграцию, а потом так и осталось. Ничто из этого не обязательно сделано со злым умыслом: люди работают так, как удобно, а удобство оставляет следы. Вопрос в том, как завершить временный доступ так же аккуратно, как его выдавали.

Ответ начинается не в конце проекта, а до первой команды: чем точнее описана выдача, тем проще отзыв. Если вы не знаете, что именно подрядчик имел право делать, то после работы вам нечем будет сверять результат.

Сначала работа, потом права

Фраза «дайте ему root, он разберётся» звучит экономно, но на деле переносит всю неопределённость на вас. Начните с одного предложения, которое описывает задачу: «перенести сайт на новую версию PHP и настроить обновление сертификата», «развернуть n8n за обратным прокси», «починить деплой приложения в контейнере». Из этого предложения выводится всё остальное.

Разложите задачу на действия и посмотрите, какие из них требуют каких полномочий:

  • читать логи и конфигурацию — достаточно обычного пользователя с доступом на чтение нужных каталогов;
  • перезапускать один сервис — нужно право на конкретную команду, а не на всю систему;
  • ставить пакеты и править системные файлы — это уже повышение привилегий, и его стоит ограничить по времени;
  • управлять контейнерами — отдельный разговор, к нему мы вернёмся ниже.

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

Условный пример: задача и необходимые полномочия
ДействиеЧто нужноКогда заканчивается
Просмотр конфигурации и логовЛичный пользователь, чтение каталогов приложенияС закрытием задачи
Правка конфигурации веб-сервераЗапись в один каталог или sudo на узкий набор командПосле теста конфигурации
Перезапуск сервисаПраво на конкретную командуС закрытием задачи
Доступ к репозиториюОтдельная роль с правом на одну ветку или проектПо окончании договора

У такой таблицы два применения. Во-первых, она определяет, что выдавать. Во-вторых, она превращает финальную проверку из размытого «посмотрим, всё ли убрали» в сверку: каждая строка выданного права должна иметь пару в виде строки отозванного.

Заодно решите, что на ваш VPS вообще допустимо делать от имени подрядчика, а что нет. Если у вас есть сервер на тарифе вроде Ba или Uab, на котором живёт и рабочее приложение, и тестовая песочница, подрядчику лучше работать в той части, где нет данных клиентов. Ресурсы тарифа тут ни при чём: речь о том, что граница доступа проще, когда она совпадает с границей задачи. Если рабочее и тестовое можно развести по разным серверам, разделение получится честнее любых правил внутри одной системы.

Отдельная личность вместо общего администратора

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

Правильнее завести отдельного пользователя именно для этого человека. Имя должно быть понятным: например, с фамилией или названием компании и годом. Через полгода по одному имени в /etc/passwd вы вспомните, откуда оно взялось. Хорошо также оставить в поле комментария учётной записи ссылку на задачу или дату, до которой доступ запланирован.

Вход — только по ключу. Подрядчик присылает вам публичную часть, приватная остаётся у него; пароль по сети вообще не участвует. Вы кладёте публичный ключ в файл authorized_keys этого пользователя и больше нигде. Если вам нужно, чтобы доступ сам «затух», у OpenSSH в записи ключа есть опции ограничения, но проверьте по документации вашей версии, что именно поддерживается: версии на серверах разные, и я не буду утверждать, что нужная опция есть у вас.

Повышение привилегий выдавайте через sudo с точным списком. Не «ALL», а перечень команд, которые нужны в таблице выше. Это не недоверие, а ограничение ущерба от случайной ошибки: опечатка в команде с узким sudo обойдётся дешевле, чем та же опечатка от root.

Когда нужен промежуточный хост

Иногда сервер напрямую не открыт наружу, а доступ идёт через бастион. В клиенте SSH для этого есть ProxyJump: он подключает клиента через промежуточный хост и пробрасывает TCP-соединение до назначения (документация ssh_config). Для подрядчика это удобно, потому что он указывает один параметр в конфигурации. Для вас это ещё одно место, где живёт его учётная запись, и при отзыве его тоже придётся проверить. Если бастион есть, пользователь заводится и закрывается в двух местах, а не в одном.

Отдельно предупредите подрядчика про пересылку агента. В той же документации сказано, что agent forwarding требует осторожности: тот, кто получит доступ к пересланному агенту, может использовать загруженные идентификаторы для аутентификации, хотя сам материал ключа напрямую не извлекается. Практический вывод простой: не включайте пересылку агента «на всякий случай», если задача без неё решается. Подрядчику вы не доверяете меньше, чем коллегам; просто пересланный агент превращает чужой сервер в место, где ваши ключи временно можно применять.

Каналы, о которых забывают

SSH — самый заметный путь на сервер, но не единственный. Если вы мысленно отключаете только его, остальное остаётся открытым. Пройдитесь по списку каналов, которыми подрядчик реально пользовался.

Панель и учётная запись провайдера

Если подрядчик заходил в панель управления сервером, ему нужна своя запись, а не ваша. Заведите её с минимально возможной ролью, если панель такое позволяет. Если нет, лучше вообще не давать панель и самим выполнять редкие операции вроде перезагрузки или переустановки образа. Эти действия нужны нечасто, и подрядчик просит их выполнить, а не делает сам.

Git и деплой

Репозиторий — отдельный мир со своими пользователями. Если подрядчик работает с кодом, предоставьте ему доступ к одному проекту, а ветку с рабочим кодом защитите так, чтобы изменения туда попадали через проверку. Отдельная история — ключи развёртывания и токены CI. Они часто создаются во время работы, чтобы «заработал деплой», и остаются привязанными к человеку, который их выпустил. Если токен выпущен личной учётной записью подрядчика, то после закрытия его учётной записи деплой сломается, а если от общей — он останется живым. И то и другое надо заметить заранее, а не по сообщению об ошибке.

API и секреты приложения

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

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

Docker — отдельная граница

Если на сервере работают контейнеры, возникает особый случай. Соблазнительно выдать подрядчику членство в группе docker: это не root, а «всего лишь» управление контейнерами. Но в документации Docker сказано, что доступ к демону Docker с правами root — существенная граница доверия: управление им может позволить смонтировать файловую систему хоста (Docker Engine security). Иначе говоря, членство в этой группе фактически равно административному доступу к хосту, хотя в списке sudo человека может не быть.

Отсюда два практических вывода. Первый: если подрядчику правда нужен Docker, считайте это полноценной административной выдачей и записывайте в таблицу полномочий как привилегированное действие. Второй: при отзыве проверяйте не только sudo, но и состав группы docker. Именно в таких местах остаётся «тихий» доступ: человек в списке администраторов не значится, а возможностей у него столько же.

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

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации Россия, США, Франция и Великобритания. Оплата картой РФ и по СБП.

АРЕНДОВАТЬ СЕРВЕР

Что на самом деле изменилось на сервере

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

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

Практический порядок сверки такой:

  1. Возьмите таблицу полномочий и список ожидаемых результатов: службы, конфигурации, задания.
  2. Получите перечень того, что есть на сервере в местах, где такие вещи обычно живут: задания планировщика, юниты systemd, пользовательские записи, ключи, правила файрвола.
  3. Для каждого найденного элемента ответьте на вопрос: он относится к согласованной работе? Если да — оставляем и документируем. Если непонятно — спрашиваем подрядчика, а не удаляем.
  4. Всё, что не относится к задаче и даёт вход или выполнение с повышенными правами, обсуждаем и убираем.

Шаг три важен по человеческой причине: чаще всего «непонятное» объясняется одной фразой. Ключ принадлежит тестовому окружению, которое подрядчик собирал у себя; служба нужна для мониторинга, о котором договорились устно. Если удалять сразу, вы рискуете сломать работающее и потерять доверие. Если спросить, вы получите объяснение и запись в документации.

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

Поиск остатков доступа

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

Ключи

Проверьте authorized_keys у всех пользователей, а не только у подрядчика, и особенно у root и у сервисных учётных записей. Подрядчик мог добавить свой ключ туда, где ему было проще, например к пользователю деплоя, чтобы не просить второй раз. Сопоставляйте записи с комментариями и со списком людей, которые должны иметь вход. Любая запись без владельца — вопрос, а не обязательно проблема.

Заодно посмотрите конфигурацию демона SSH на предмет дополнительных путей: нестандартных мест для файлов ключей и включённых способов входа. Они могли быть изменены в рамках работы, и тогда это тоже следует сверить с согласованным объёмом.

Группы и повышение привилегий

Пройдитесь по составу групп с административным смыслом: sudo или wheel (название зависит от системы), docker, а также по файлам правил sudo в отдельном каталоге. Подрядчик мог добавить себе правило отдельным файлом, а не через основную конфигурацию, и при беглом просмотре главного файла оно не видно. Именно сюда попадает и членство в группе Docker, о котором шла речь выше.

Планировщик и systemd

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

Токены и интеграции

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

Сеть

Наконец, правила файрвола и открытые порты. Подрядчик мог открыть порт для отладки или добавить свой адрес в список разрешённых. Это обычная рабочая практика, и убирать такое правило после завершения работы — как закрыть за собой дверь. Сверьте открытые порты с тем, что действительно должно слушать извне.

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

Как закрывать, чтобы ничего не сломать

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

Сначала переведите то, что должно жить, на постоянные учётные данные. Задания по расписанию — на сервисного пользователя без интерактивного входа и без лишних прав. Токены деплоя — на ваши собственные, выпущенные заново. Файлы, принадлежащие подрядчику, — на нужного владельца. Это самый скучный и самый ценный этап: именно он отделяет «убрали человека» от «убрали человека, а приложение работает».

Затем отзовите доступ: удалите ключ из authorized_keys, заблокируйте или удалите учётную запись, уберите из групп, закройте правила sudo, отзовите токены, закройте порты. Если нужна пауза на случай вопросов, блокировка записи — более мягкий шаг, чем удаление, но не оставляйте её навсегда: заблокированная учётная запись всё равно остаётся записью в системе.

И наконец, замените секреты, которые подрядчик мог видеть. После замены перезапустите зависимые службы и проверьте, что они подхватили новые значения. Здесь самая частая ловушка — сервис продолжает работать на старом значении, пока его не перезапустили, а затем падает в неудобное время. Лучше перезапустить его сами в момент, когда вы за ним наблюдаете.

Если подрядчик работал на отдельном VPS, который создан под проект, отзыв становится ещё проще: при необходимости можно переустановить систему и развернуть всё заново из репозитория и бэкапа. Это сильная мера, и пользоваться ей стоит только когда доверие к состоянию сервера потеряно, а не по умолчанию: результаты работы не надо уничтожать без причины. В публичном предложении MAATRIX сервер создаётся автоматически через пару минут после оплаты, что делает подготовку отдельной среды под временный проект вполне реальной, но наличие образов и ОС лучше уточнять в форме заказа, а резервные копии настраиваются отдельно — наличие их во всех тарифах не заявлено.

Проверка, что дверь закрыта, а дом стоит

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

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

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

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