MAATRIX / Блог / Подрядчик закончил работу: как закрыть за ним двери

Подрядчик закончил работу: как закрыть за ним двери

MAATRIX

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

Принцип наименьших привилегий с первого дня

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

Правильный подход — выдавать точечный доступ под конкретную задачу, а не «с запасом на всякий случай»:

  • отдельный пользователь на сервере с личным SSH-ключом, а не общий root-логин — подробно про такую схему есть отдельный разбор, как раздать доступ команде без выдачи root;
  • права через sudo только на нужные команды, а не полный sudo ALL=(ALL) NOPASSWD: ALL;
  • API-токен со скоупом ровно под задачу (например, только read к одному репозиторию или доступ к одному бакету), а не мастер-ключ ко всей инфраструктуре;
  • доступ к репозиторию как collaborator к конкретному репо, а не приглашение в организацию с правами владельца;
  • если платформа поддерживает временные учётные записи или доступ с датой истечения — используйте это сразу, а не рассчитывайте вспомнить отозвать вручную.

Ключевая мысль: доступ, который выдан узко и по задаче, автоматически становится подозрительным и заметным, когда задача закрыта — вы точно знаете, что именно нужно отозвать. Доступ «на всякий случай» растворяется в общей массе прав и остаётся незамеченным годами.

SSH-ключи: ищем и отзываем всё, что осталось

Это самая частая дыра — потому что ключ добавить легко, а вспомнить про него потом некому. Проверьте на каждом сервере, где мог появиться доступ подрядчика:

for f in /home/*/.ssh/authorized_keys /root/.ssh/authorized_keys; do
  echo "== $f =="
  cat "$f" 2>/dev/null
done

Сверьте отпечатки с тем, что действительно выдавали:

ssh-keygen -lf /home/contractor/.ssh/authorized_keys

Если ключ подрядчика найден — удалите строку из файла (а не весь файл, если аккаунтом пользуются и другие):

sed -i '/AAAAC3NzaC1lZDI1NTE5.../d' /home/contractor/.ssh/authorized_keys

Отдельно проверьте источники, о которых часто забывают:

  • ключ, добавленный в панели облачного провайдера как «SSH key» при создании сервера — если он привязан к аккаунту, он попадёт в authorized_keys на всех новых серверах, которые вы создадите позже;
  • ключ, зашитый в cloud-init/Terraform/Ansible-конфигурацию — он будет накатываться повторно при каждом провижининге, пока его не удалят из исходников;
  • если ключ подрядчика физически совпадает с ключом, который вы сами используете где-то ещё (бывает, когда для скорости выдали доступ к своему же ключу) — такой ключ нельзя просто отозвать, придётся перевыпускать пару и обновлять её везде.

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

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

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

Арендовать сервер

Временные учётные записи и доступы в панелях

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

getent passwd | awk -F: '$3 >= 1000 && $3 < 65534 {print $1, $3, $6}'
lastlog | grep -v "Never logged in"

lastlog покажет, кто вообще заходил в последнее время — если аккаунт подрядчика не использовался месяцами, это лишний повод удостовериться, что он больше не нужен, и удалить:

userdel -r contractor_account

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

  • /etc/sudoers.d/ — отдельные файлы с правами часто заводят именно под временного человека и забывают удалить вместе с самим правилом;
  • учётные записи в БД, если подрядчик работал с данными напрямую: psql -c "\du" для PostgreSQL, SELECT user, host FROM mysql.user; для MySQL — удалите роль и отзовите гранты (DROP ROLE/DROP USER), а не просто заблокируйте вход;
  • логины в панели управления хостингом, в админке CMS, в мониторинге (Grafana, Zabbix, Uptime Kuma) — везде, где заводили отдельного пользователя «на время работы»;
  • если для доступа использовался общий пароль (от VPN, от админки, от Wi-Fi в офисе) — его нужно сменить, а не просто «убрать подрядчика из списка», потому что пароль он всё равно запомнил или сохранил.

API-токены и ключи, выданные под задачу

Токены — самая незаметная категория, потому что они не привязаны к «человеку» так очевидно, как логин, и живут в конфигах, переменных окружения, CI-секретах. Составьте список того, что могло быть выдано под конкретную задачу:

  • токен облачного провайдера (DigitalOcean, Hetzner, AWS IAM-ключ) для автоматизации, которую настраивал подрядчик;
  • personal access token в GitHub/GitLab, если доступ выдавали не через приглашение в репозиторий, а через токен;
  • ключи сторонних сервисов — Cloudflare API-токен, ключ Sentry, тестовый или боевой ключ платёжного шлюза;
  • любые .env-файлы или конфиги с секретами, которые подрядчик мог скопировать себе на локальную машину для отладки.

По каждому найденному токену — отозвать через консоль сервиса, а не просто удалить упоминание из конфига (иначе ключ продолжит действовать, просто вы перестанете его использовать). Для GitHub deploy-ключей это можно сделать и из терминала:

gh repo deploy-key list --repo yourorg/yourrepo
gh repo deploy-key delete <key-id> --repo yourorg/yourrepo

Отдельно стоит держать в голове: если подрядчик работал со своего устройства и видел секрет (пусть даже не забирал его специально), вы не можете гарантировать, что он не сохранился локально — в истории терминала, в кэше IDE, в скриншоте. Единственный надёжный способ закрыть этот риск — ротация самого секрета, а не доверие к тому, что он «наверняка удалил». Логика та же, что при утечке токена в публичный репозиторий — только здесь утечки может и не быть, но принцип «раз мог видеть — считаем скомпрометированным» тот же, подробнее он разобран в статье про то, что отзывать, если токен утёк в публичный репозиторий.

Доступ к репозиториям, CI/CD и деплою

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

Доступ к репозиторию. Уберите его из списка collaborators или из команды в организации:

gh api -X DELETE repos/yourorg/yourrepo/collaborators/contractor-username

Если доступ выдавался на уровне организации (а не одного репозитория) — это как раз пример того самого широкого доступа «для удобства», который потом сложнее отследить: проверьте вручную список членов организации и их роли.

CI/CD-секреты. Если у подрядчика был доступ к пайплайну (например, он настраивал деплой), он мог видеть значения секретов в логах сборки или иметь доступ к переменным окружения раннера. Проверьте:

  • список masked/protected переменных в настройках CI — не остались ли там значения, которые вводились вручную при подрядчике и с тех пор не менялись;
  • не зарегистрирован ли отдельный self-hosted раннер, поднятый подрядчиком для задачи — если да, отзовите его регистрационный токен и снимите раннер с проекта;
  • отдельные deploy keys, добавленные напрямую в репозиторий (Settings → Deploy keys) в дополнение к личному SSH-ключу подрядчика — это частая причина, почему доступ остаётся даже после того, как самого подрядчика убрали из collaborators.

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

Забытые firewall-правила и временные туннели

Эта категория — самая «человеческая»: правило открывали не по злому умыслу, а чтобы подрядчику было удобнее работать, и без спешки закрыть его никто не обязан. Проверьте на каждом сервере, с которым работал подрядчик:

  • правила firewall, разрешающие доступ с конкретного IP-адреса — обычно это порт SSH, RDP или админка сервиса, открытые точечно «пока идёт работа»:
ufw status numbered

Найдите строку с IP-адресом или диапазоном подрядчика и удалите правило по номеру:

ufw delete <номер_правила>

Для iptables — то же самое, но без удобной нумерации, ищите правило по -s <IP> в выводе iptables -L -n --line-numbers и удаляйте по номеру строки командой iptables -D;

  • временные туннели для удалённой отладки — reverse SSH-туннель (ssh -R), процессы ngrok, frp, rathole, поднятые «чтобы подрядчик мог достучаться до локального стенда» и забытые запущенными:
ps aux | grep -E 'ngrok|frpc|autossh|ssh -R'
crontab -l
systemctl list-units --type=service | grep -iE 'autossh|ngrok|frp'
  • если для доступа поднимали VPN — отдельный WireGuard-пир или OpenVPN-сертификат для подрядчика. Уберите пира из конфига WireGuard и перезагрузите интерфейс, для OpenVPN — отзовите сертификат через easy-rsa (./easyrsa revoke contractor + обновление CRL), а не просто удалите файл конфигурации у себя — сертификат без отзыва продолжит работать, если копия осталась у подрядчика;
  • правила в security group облачного провайдера или в панели VPS, разрешающие доступ с конкретного IP — они живут отдельно от firewall внутри самой ОС, и про них легко забыть, если фокус был только на ufw/iptables.

Чек-лист офбординга и регулярный аудит доступов

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

Что проверитьГде искатьКак закрыть
SSH-ключиauthorized_keys на всех серверах, панель провайдера, cloud-init/IaC-конфигиУдалить строку/ключ, обновить исходники провижининга
Системные аккаунтыgetent passwd, /etc/sudoers.d/userdel -r, удалить sudo-правило
Учётки в БД и панелях\du в psql, mysql.user, админки сервисовDROP USER/DROP ROLE, удалить логин
Общие паролиVPN, Wi-Fi, админки, где не было персональных логиновСменить пароль, разослать новый доверенным людям
API-токеныКонсоли облака и сервисов, .env-файлыОтозвать в консоли сервиса, перевыпустить
Репозитории и CICollaborators, org members, deploy keys, CI-переменныеУдалить из репозитория/организации, отозвать deploy key, обновить секреты
Firewall-правилаufw status, iptables -L, security groups в облакеУдалить правило по IP подрядчика
Туннели и VPNЗапущенные процессы, cron, WireGuard/OpenVPN конфигиУбить процесс, снять из автозапуска, отозвать пира/сертификат

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

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

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

Арендовать сервер

Нужны сами нейросети для контента?

Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.

Частые вопросы

Не помню точно, какие доступы выдавали подрядчику полгода назад — с чего начать?

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

Обязательно ли менять все пароли и токены, если подрядчик чинил один небольшой скрипт?

Нет, объём ротации должен соответствовать объёму доступа. Если он видел один токен к одному сервису — ротируйте именно его. Если у него был root на сервере или доступ к общей базе — считайте скомпрометированным всё, к чему с этого сервера был доступ, и ротируйте по списку.

Подрядчик работал со своего ноутбука — как убедиться, что секреты не остались у него?

Никак, и не стоит на этом строить безопасность. Единственная надёжная гарантия — ротация всего, что он видел, а не расчёт на то, что он всё удалил у себя.

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

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

Обсудить статью, задать вопрос или начать новую тему

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

Перейти в сообщество →